/* =========================================================================
 CHASE 盤面のスタイル

 ★index.html から切り出したもの（2026-08-26）。中身は変えていない。
   id セレクタ（.board/.grid/.layer/.me/.hit/.gate/.count）だけ class に直した。
   理由: 1画面に盤面を2つ置く（対面2画面分割の対戦）ため。id は1つしか置けない。

 ★このファイルと board.js は対。片方だけ直すと崩れる。
   色変数（--me/--item/--c0..3 等）は読み込む側の :root が持つ。
 ========================================================================= */
.board{
  --size:min(94vw, calc(100dvh - 200px), 460px);
  width:var(--size); height:var(--size); position:relative;
  background:var(--panel); border-radius:18px; padding:8px;
  box-shadow:0 18px 50px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.05);
}
.board.shake{animation:shake calc(.42s * var(--fxs,1)) cubic-bezier(.36,.07,.19,.97)}
@keyframes shake{
  10%,90%{transform:translateX(-3px)} 20%,80%{transform:translateX(5px)}
  30%,50%,70%{transform:translateX(-8px)} 40%,60%{transform:translateX(8px)}
}
.grid{position:absolute; inset:8px; display:grid; gap:2px}
/* ★市松模様は廃止。壁を0%にした今、色が違う＝何かある、でないと誤読される
   （プレイテストで、素のマスを「何か置いてある」と誤読された 2026-08-26） */
.cell{border-radius:5px; background:var(--tile-a); position:relative}
.cell.wall{background:var(--wall); box-shadow:inset 0 2px 0 var(--wall-top), inset 0 -2px 0 rgba(0,0,0,.3)}
.cell.wreck{background:var(--wreck); box-shadow:inset 0 0 0 2px rgba(0,0,0,.25)}
.cell.wreck::after{
  content:""; position:absolute; inset:22%;
  background:
    linear-gradient(56deg,transparent 46%,rgba(0,0,0,.45) 46%,rgba(0,0,0,.45) 54%,transparent 54%),
    linear-gradient(-40deg,transparent 46%,rgba(0,0,0,.35) 46%,rgba(0,0,0,.35) 54%,transparent 54%);
}
.cell.legal{background:rgba(52,211,153,.14); box-shadow:inset 0 0 0 2px rgba(52,211,153,.34)}
/* ★マス自体に印を出す。追手が上に乗っても輪は隠れない（プレイテスト指摘 2026-08-26） */
.cell.hasitem{box-shadow:inset 0 0 0 3px rgba(251,191,36,.9), inset 0 0 12px rgba(251,191,36,.25)}
.cell.hasitem.legal{box-shadow:inset 0 0 0 3px rgba(251,191,36,.9), inset 0 0 0 5px rgba(52,211,153,.35)}
.cell.hasflag{box-shadow:inset 0 0 0 3px rgba(52,211,153,.55)}
.cell.hasflag.open{box-shadow:inset 0 0 0 3px rgba(52,211,153,1), inset 0 0 14px rgba(52,211,153,.4)}
/* ★危険マス: そこへ行くと次の手で捕まる。壁や残骸で回り込みが読めない理不尽を消す。
   考えること（回る順・どこで相討ちさせるか）は消さない */
.cell.danger{background:rgba(248,113,113,.18) !important;
             box-shadow:inset 0 0 0 2px rgba(248,113,113,.6) !important}
/* ★先読み: Shift＋矢印（長押しでも）で、その手の結果を色つきで見せる */
.ghost{position:absolute; left:0; top:0; pointer-events:none; opacity:.95}
/* ★「その場に留まる（＝きみのマスへ飛び込んで弾き返される）」の印。
   ずいくん実プレイ 2026-08-27:「なんで赤いやつは【なくなったと思った】点線まるなの？」
   破線＋薄い は【消えた】に読める。読み違えると規則そのものを誤解するので、
   実線・不透明の二重リングにして「ここに居続ける」を表す。
   ★意味は tutorial で教える（記号だけで伝わると思わない）。 */
.ghost.stay{opacity:1}
.ghost .g{position:absolute; inset:16%; border-radius:50%;
          border:3px solid currentColor;
          box-shadow:inset 0 0 0 3px rgba(4,7,11,.9), 0 0 0 2px rgba(4,7,11,.9)}
.ghost .g::after{content:""; position:absolute; inset:30%; border-radius:50%;
          background:currentColor; box-shadow:0 0 0 2px rgba(4,7,11,.9)}
/* ★行き先は「その個体の色の太い矢印」で出す（2026-08-26 ずいくん指摘）
   破線の丸は、行き先のマスに先客（別の追手・拾い物・旗）が居ると下に潜って読めない。
   黒フチを4方向の drop-shadow で付けているのは、どんな色のマスの上でも輪郭が残るため。 */
.ghost .arw{position:absolute; inset:13%; background:currentColor;
  transform:rotate(var(--deg));
  clip-path:polygon(50% 2%, 100% 48%, 73% 48%, 73% 98%, 27% 98%, 27% 48%, 0% 48%);
  filter:drop-shadow(0 2px 0 rgba(4,7,11,.95)) drop-shadow(0 -2px 0 rgba(4,7,11,.95))
         drop-shadow(2px 0 0 rgba(4,7,11,.95)) drop-shadow(-2px 0 0 rgba(4,7,11,.95));}
/* ★合体するマスの先読み（ずいくん指定 2026-08-28）
     「長押しするときにはちょっと角融合をする感じのイメージを
       プレビューでちょっとだけでも見せてあげると分かりやすいかも」

   ★輪（照準）にしない。ずいくん指摘「scopeマークは倒せる感じになっちゃう」——
     きみはハンターを倒せない。狙えるように見える印は規則そのものを誤解させる。
   ★💥 も使わない。すでに .cell.danger が【きみが捕まるマス】に使っている。
     同じ絵に2つの意味を持たせると、どちらの意味でも読めなくなる。
   ★弾けさせない。これは【これから起きること】であって結果ではない。
     寄る→触れる→融合色の芯が生まれる、まででループして戻す。
   ★時間は他の演出と同じ物差し（--fxs）に乗せる。ここだけ固定秒にすると
     ?fxspeed= で伸ばしたときに先読みだけ取り残される。 */
/* ★重なりの実測（2026-08-28・在庫を全走査 11,982手／合体 2,692件）
     ① 合体マス と 生存個体の行き先矢印 が重なる :   39件 (1.4%)
     ② 合体マス と きみの行き先 が重なる         :  920件 (34%) ← 日常的に起きる
     ③ 合体マス と 湧きマス が重なる             :   11件（湧きには印を出さないので実害なし）
     ④ 1手で合体マスが2か所以上                  :    0件
   「ありえない」と推測していたが**外れていた**ので、重なっても読める形にする。
   ②は 枠(.cell.previewme の内側の線) と 中身(この印) なので共存できる。
   ①は矢印と正面から重なるため、**矢印を上**にする（矢印は行き先＝より基本の情報）。 */
.ghost.fuse{opacity:1; z-index:1}
.ghost:not(.fuse){z-index:2}
.ghost.fuse .fb{position:absolute; inset:41%; border-radius:50%;
  box-shadow:0 0 0 2px rgba(4,7,11,.9), 0 0 6px rgba(0,0,0,.45);
  animation:fusePre calc(1.9s * var(--fxs,1)) ease-in-out infinite;
  will-change:transform}
@keyframes fusePre{
  0%,10%{transform:translate(var(--tx),var(--ty)) scale(1)}
  46%   {transform:translate(0,0) scale(1.14)}
  62%   {transform:translate(0,0) scale(1.14)}
  88%,100%{transform:translate(var(--tx),var(--ty)) scale(1)}
}
/* ★「迎える側」＝規則の上で動かない個体（old === dest・実測 16.4%）。
     ずいくん「迎えられる側の敵はそこにいて、反対側が合流してくる感じ」
     「orange small dot should stay」
   動かさず、小さいまま受け止める。ここが動くと【どちらが寄ってきたか】が消える。 */
.ghost.fuse .fb.stay{animation-name:fusePreStay}
@keyframes fusePreStay{
  0%,10%{transform:scale(.84)}
  46%,62%{transform:scale(1.02)}
  88%,100%{transform:scale(.84)}
}
.ghost.fuse .fc{position:absolute; inset:35%; border-radius:50%;
  background:var(--fuse,#67e8f9); opacity:0;
  box-shadow:0 0 10px var(--fuse,#67e8f9), 0 0 0 2px rgba(4,7,11,.85);
  animation:fusePreCore calc(1.9s * var(--fxs,1)) ease-in-out infinite;
  will-change:opacity,transform}
@keyframes fusePreCore{
  0%,30%{opacity:0; transform:scale(.5)}
  46%   {opacity:.85; transform:scale(1)}
  62%   {opacity:.95; transform:scale(1.06)}
  80%,100%{opacity:0; transform:scale(.6)}
}
.cell.previewme{box-shadow:inset 0 0 0 3px rgba(52,211,153,.9) !important}
.cell.danger::before{content:"💥"; position:absolute; inset:0; display:flex;
  align-items:center; justify-content:center; font-size:52%; opacity:.9}
/* 何かの上に乗っている追手は少し縮めて、下の印を見せる */
.chaser.onthing .guy{width:62%; height:62%}

.layer{position:absolute; inset:8px; pointer-events:none}
/* ★移動そのものを見せる（2026-08-26 ずいくん指摘「経路がないから意味不明なんやわ」）
   .13s は速すぎて、動いた形跡が目に残らなかった。誰がどこへ動いて、どこでぶつかったのかは
   経路が見えて初めて読める。★止める手段（?still=1 / 演出をひかえめに）は残す。 */
.piece{
  position:absolute; left:0; top:0; display:flex; align-items:center; justify-content:center;
  transition:transform calc(.30s * var(--fxs,1)) cubic-bezier(.33,1.15,.5,1);
}
/* 動いている間だけ、進む向きにすこし伸びる（スライムの慣性） */
.piece .guy{transition:transform calc(.30s * var(--fxs,1)) cubic-bezier(.33,1.15,.5,1); backface-visibility:hidden}

/* ★アニメは【内側の .guy】に当てる。.piece の transform はマスの位置そのものなので、
   そこへ keyframes を重ねると駒が盤外へ飛ぶ（実際に一度そう書いて気づいた）。
   --bx/--by は「行こうとした向き」。board.js が JS から入れる。 */

/* --- ① きみが弾かれる: ハンターのマスへ入ろうとして、盾に当たって戻される ---
   ★keyframes は tools/gen_keyframes.py が作る。手で書かない。
     timing-function は linear（曲線はキーフレームの値そのものに入っている）。 */
.me.blocked .guy{animation:meBlock calc(.76s * var(--fxs,1)) linear;
                 will-change:transform; backface-visibility:hidden}
/* @gen:meBlock */
@keyframes meBlock{
  0%{transform:translate(calc(var(--bx)*1.055%),calc(var(--by)*1.055%)) scale(1.0030,0.9974)}
  2%{transform:translate(calc(var(--bx)*2.097%),calc(var(--by)*2.097%)) scale(1.0060,0.9948)}
  4%{transform:translate(calc(var(--bx)*3.521%),calc(var(--by)*3.521%)) scale(1.0101,0.9912)}
  6%{transform:translate(calc(var(--bx)*5.271%),calc(var(--by)*5.271%)) scale(1.0149,0.9870)}
  8%{transform:translate(calc(var(--bx)*7.367%),calc(var(--by)*7.367%)) scale(1.0204,0.9820)}
  10%{transform:translate(calc(var(--bx)*9.900%),calc(var(--by)*9.900%)) scale(1.0267,0.9761)}
  12%{transform:translate(calc(var(--bx)*12.931%),calc(var(--by)*12.931%)) scale(1.0336,0.9695)}
  14%{transform:translate(calc(var(--bx)*16.390%),calc(var(--by)*16.390%)) scale(1.0407,0.9625)}
  16%{transform:translate(calc(var(--bx)*20.077%),calc(var(--by)*20.077%)) scale(1.0471,0.9560)}
  18%{transform:translate(calc(var(--bx)*23.723%),calc(var(--by)*23.723%)) scale(1.0521,0.9508)}
  20%{transform:translate(calc(var(--bx)*27.022%),calc(var(--by)*27.022%)) scale(1.0548,0.9479)}
  22%{transform:translate(calc(var(--bx)*29.649%),calc(var(--by)*29.649%)) scale(1.0544,0.9483)}
  24%{transform:translate(calc(var(--bx)*31.345%),calc(var(--by)*31.345%)) scale(1.0490,0.9543)}
  26%{transform:translate(calc(var(--bx)*32.024%),calc(var(--by)*32.024%)) scale(1.0363,0.9682)}
  28%{transform:translate(calc(var(--bx)*31.732%),calc(var(--by)*31.732%)) scale(1.0151,0.9915)}
  30%{transform:translate(calc(var(--bx)*30.558%),calc(var(--by)*30.558%)) scale(0.9865,1.0225)}
  32%{transform:translate(calc(var(--bx)*28.618%),calc(var(--by)*28.618%)) scale(0.9544,1.0565)}
  34%{transform:translate(calc(var(--bx)*26.072%),calc(var(--by)*26.072%)) scale(0.9243,1.0872)}
  36%{transform:translate(calc(var(--bx)*23.101%),calc(var(--by)*23.101%)) scale(0.9014,1.1091)}
  38%{transform:translate(calc(var(--bx)*19.869%),calc(var(--by)*19.869%)) scale(0.8886,1.1192)}
  40%{transform:translate(calc(var(--bx)*16.511%),calc(var(--by)*16.511%)) scale(0.8866,1.1171)}
  42%{transform:translate(calc(var(--bx)*13.141%),calc(var(--by)*13.141%)) scale(0.8937,1.1052)}
  44%{transform:translate(calc(var(--bx)*9.856%),calc(var(--by)*9.856%)) scale(0.9077,1.0866)}
  46%{transform:translate(calc(var(--bx)*6.720%),calc(var(--by)*6.720%)) scale(0.9270,1.0643)}
  48%{transform:translate(calc(var(--bx)*3.772%),calc(var(--by)*3.772%)) scale(0.9503,1.0402)}
  50%{transform:translate(calc(var(--bx)*1.037%),calc(var(--by)*1.037%)) scale(0.9759,1.0160)}
  52%{transform:translate(calc(var(--bx)*-1.464%),calc(var(--by)*-1.464%)) scale(1.0013,0.9929)}
  54%{transform:translate(calc(var(--bx)*-3.715%),calc(var(--by)*-3.715%)) scale(1.0246,0.9722)}
  56%{transform:translate(calc(var(--bx)*-5.692%),calc(var(--by)*-5.692%)) scale(1.0445,0.9545)}
  58%{transform:translate(calc(var(--bx)*-7.325%),calc(var(--by)*-7.325%)) scale(1.0603,0.9404)}
  60%{transform:translate(calc(var(--bx)*-8.506%),calc(var(--by)*-8.506%)) scale(1.0713,0.9306)}
  62%{transform:translate(calc(var(--bx)*-9.155%),calc(var(--by)*-9.155%)) scale(1.0772,0.9255)}
  64%{transform:translate(calc(var(--bx)*-9.288%),calc(var(--by)*-9.288%)) scale(1.0782,0.9250)}
  66%{transform:translate(calc(var(--bx)*-9.008%),calc(var(--by)*-9.008%)) scale(1.0752,0.9284)}
  68%{transform:translate(calc(var(--bx)*-8.442%),calc(var(--by)*-8.442%)) scale(1.0694,0.9347)}
  70%{transform:translate(calc(var(--bx)*-7.681%),calc(var(--by)*-7.681%)) scale(1.0612,0.9431)}
  72%{transform:translate(calc(var(--bx)*-6.762%),calc(var(--by)*-6.762%)) scale(1.0508,0.9536)}
  74%{transform:translate(calc(var(--bx)*-5.698%),calc(var(--by)*-5.698%)) scale(1.0381,0.9659)}
  76%{transform:translate(calc(var(--bx)*-4.527%),calc(var(--by)*-4.527%)) scale(1.0240,0.9794)}
  78%{transform:translate(calc(var(--bx)*-3.338%),calc(var(--by)*-3.338%)) scale(1.0104,0.9923)}
  80%{transform:translate(calc(var(--bx)*-2.241%),calc(var(--by)*-2.241%)) scale(0.9994,1.0025)}
  82%{transform:translate(calc(var(--bx)*-1.318%),calc(var(--by)*-1.318%)) scale(0.9929,1.0085)}
  84%{transform:translate(calc(var(--bx)*-0.586%),calc(var(--by)*-0.586%)) scale(0.9907,1.0103)}
  86%{transform:translate(calc(var(--bx)*-0.023%),calc(var(--by)*-0.023%)) scale(0.9917,1.0090)}
  88%{transform:translate(calc(var(--bx)*0.389%),calc(var(--by)*0.389%)) scale(0.9941,1.0063)}
  90%{transform:translate(calc(var(--bx)*0.651%),calc(var(--by)*0.651%)) scale(0.9966,1.0036)}
  92%{transform:translate(calc(var(--bx)*0.754%),calc(var(--by)*0.754%)) scale(0.9984,1.0017)}
  94%{transform:translate(calc(var(--bx)*0.709%),calc(var(--by)*0.709%)) scale(0.9994,1.0006)}
  96%{transform:translate(calc(var(--bx)*0.556%),calc(var(--by)*0.556%)) scale(0.9998,1.0002)}
  98%{transform:translate(calc(var(--bx)*0.360%),calc(var(--by)*0.360%)) scale(1.0000,1.0000)}
  100%{transform:translate(calc(var(--bx)*0.188%),calc(var(--by)*0.188%)) scale(1.0000,1.0000)}
}
/* @endgen */
/* 弾いた側に盾が光る */
.chaser.shield::after{
  content:""; position:absolute; inset:-6%; border-radius:50%;
  border:3px solid rgba(226,240,255,.95);
  box-shadow:0 0 14px rgba(190,220,255,.9), inset 0 0 12px rgba(190,220,255,.55);
  animation:shieldPop calc(.52s * var(--fxs,1)) ease-out forwards;
}
@keyframes shieldPop{
  0%{opacity:0; transform:scale(.55)}
  30%{opacity:1; transform:scale(1.02)}
  100%{opacity:0; transform:scale(1.3)}
}

/* --- ② ハンターが弾かれる: きみのマスに飛び込んで、ぴょんと元へ戻る --- */
.chaser.hopback .guy{animation:hopBack calc(.86s * var(--fxs,1)) linear;
                     will-change:transform; backface-visibility:hidden}
/* @gen:hopBack */
@keyframes hopBack{
  0%{transform:translate(calc(var(--bx)*2.077%),calc(var(--by)*2.077%)) scale(1.0061,0.9946)}
  2%{transform:translate(calc(var(--bx)*4.503%),calc(var(--by)*4.503%)) scale(1.0132,0.9883)}
  4%{transform:translate(calc(var(--bx)*7.930%),calc(var(--by)*7.930%)) scale(1.0228,0.9797)}
  6%{transform:translate(calc(var(--bx)*12.260%),calc(var(--by)*12.260%)) scale(1.0341,0.9696)}
  8%{transform:translate(calc(var(--bx)*17.612%),calc(var(--by)*17.612%)) scale(1.0464,0.9585)}
  10%{transform:translate(calc(var(--bx)*24.125%),calc(var(--by)*24.125%)) scale(1.0596,0.9464)}
  12%{transform:translate(calc(var(--bx)*31.611%),calc(var(--by)*31.611%)) scale(1.0732,0.9340)}
  14%{transform:translate(calc(var(--bx)*39.528%),calc(var(--by)*39.528%)) scale(1.0864,0.9224)}
  16%{transform:translate(calc(var(--bx)*47.299%),calc(var(--by)*47.299%)) scale(1.0983,0.9124)}
  18%{transform:translate(calc(var(--bx)*54.579%),calc(var(--by)*54.579%)) scale(1.1087,0.9045)}
  20%{transform:translate(calc(var(--bx)*61.213%),calc(var(--by)*61.213%)) scale(1.1171,0.8987)}
  22%{transform:translate(calc(var(--bx)*67.082%),calc(var(--by)*67.082%)) scale(1.1224,0.8957)}
  24%{transform:translate(calc(var(--bx)*72.069%),calc(var(--by)*72.069%)) scale(1.1235,0.8970)}
  26%{transform:translate(calc(var(--bx)*76.075%),calc(var(--by)*76.075%)) scale(1.1189,0.9040)}
  28%{transform:translate(calc(var(--bx)*78.985%),calc(var(--by)*78.985%)) scale(1.1077,0.9180)}
  30%{transform:translate(calc(var(--bx)*80.748%),calc(var(--by)*80.748%)) scale(1.0888,0.9394)}
  32%{transform:translate(calc(var(--bx)*81.505%),calc(var(--by)*81.505%)) scale(1.0616,0.9679)}
  34%{transform:translate(calc(var(--bx)*81.489%),calc(var(--by)*81.489%)) scale(1.0281,1.0015)}
  36%{transform:translate(calc(var(--bx)*80.807%),calc(var(--by)*80.807%)) scale(0.9921,1.0365)}
  38%{transform:translate(calc(var(--bx)*79.433%),calc(var(--by)*79.433%)) scale(0.9582,1.0690)}
  40%{transform:translate(calc(var(--bx)*77.359%),calc(var(--by)*77.359%)) scale(0.9303,1.0956)}
  42%{transform:translate(calc(var(--bx)*74.645%),calc(var(--by)*74.645%)) scale(0.9103,1.1148)}
  44%{transform:translate(calc(var(--bx)*71.384%),calc(var(--by)*71.384%)) scale(0.8984,1.1259)}
  46%{transform:translate(calc(var(--bx)*67.680%),calc(var(--by)*67.680%)) scale(0.8944,1.1282)}
  48%{transform:translate(calc(var(--bx)*63.619%),calc(var(--by)*63.619%)) scale(0.8983,1.1211)}
  50%{transform:translate(calc(var(--bx)*59.256%),calc(var(--by)*59.256%)) scale(0.9087,1.1058)}
  52%{transform:translate(calc(var(--bx)*54.627%),calc(var(--by)*54.627%)) scale(0.9240,1.0851)}
  54%{transform:translate(calc(var(--bx)*49.765%),calc(var(--by)*49.765%)) scale(0.9426,1.0619)}
  56%{transform:translate(calc(var(--bx)*44.690%),calc(var(--by)*44.690%)) scale(0.9635,1.0381)}
  58%{transform:translate(calc(var(--bx)*39.444%),calc(var(--by)*39.444%)) scale(0.9858,1.0146)}
  60%{transform:translate(calc(var(--bx)*34.100%),calc(var(--by)*34.100%)) scale(1.0077,0.9928)}
  62%{transform:translate(calc(var(--bx)*28.742%),calc(var(--by)*28.742%)) scale(1.0272,0.9743)}
  64%{transform:translate(calc(var(--bx)*23.426%),calc(var(--by)*23.426%)) scale(1.0434,0.9595)}
  66%{transform:translate(calc(var(--bx)*18.177%),calc(var(--by)*18.177%)) scale(1.0563,0.9481)}
  68%{transform:translate(calc(var(--bx)*13.002%),calc(var(--by)*13.002%)) scale(1.0661,0.9397)}
  70%{transform:translate(calc(var(--bx)*7.895%),calc(var(--by)*7.895%)) scale(1.0724,0.9344)}
  72%{transform:translate(calc(var(--bx)*2.851%),calc(var(--by)*2.851%)) scale(1.0746,0.9326)}
  74%{transform:translate(calc(var(--bx)*-1.984%),calc(var(--by)*-1.984%)) scale(1.0725,0.9346)}
  76%{transform:translate(calc(var(--bx)*-6.148%),calc(var(--by)*-6.148%)) scale(1.0668,0.9396)}
  78%{transform:translate(calc(var(--bx)*-9.023%),calc(var(--by)*-9.023%)) scale(1.0587,0.9469)}
  80%{transform:translate(calc(var(--bx)*-10.289%),calc(var(--by)*-10.289%)) scale(1.0494,0.9556)}
  82%{transform:translate(calc(var(--bx)*-10.157%),calc(var(--by)*-10.157%)) scale(1.0394,0.9649)}
  84%{transform:translate(calc(var(--bx)*-9.134%),calc(var(--by)*-9.134%)) scale(1.0296,0.9741)}
  86%{transform:translate(calc(var(--bx)*-7.658%),calc(var(--by)*-7.658%)) scale(1.0208,0.9823)}
  88%{transform:translate(calc(var(--bx)*-5.983%),calc(var(--by)*-5.983%)) scale(1.0135,0.9889)}
  90%{transform:translate(calc(var(--bx)*-4.280%),calc(var(--by)*-4.280%)) scale(1.0079,0.9937)}
  92%{transform:translate(calc(var(--bx)*-2.755%),calc(var(--by)*-2.755%)) scale(1.0039,0.9969)}
  94%{transform:translate(calc(var(--bx)*-1.596%),calc(var(--by)*-1.596%)) scale(1.0016,0.9988)}
  96%{transform:translate(calc(var(--bx)*-0.846%),calc(var(--by)*-0.846%)) scale(1.0005,0.9996)}
  98%{transform:translate(calc(var(--bx)*-0.410%),calc(var(--by)*-0.410%)) scale(1.0001,0.9999)}
  100%{transform:translate(calc(var(--bx)*-0.172%),calc(var(--by)*-0.172%)) scale(1.0000,1.0000)}
}
/* @endgen */
/* 飛びかかっている間だけ牙を剥く感じに（色は変えない＝個体の識別を壊さない） */
.chaser.hopback .head{filter:brightness(1.35)}

/* --- ③ 相討ち（ずいくん指定 2026-08-27）------------------------------
   「核融合みたいにくっついて 水が弾けるように消える。もっと時間かけていい」

   時間割は board.js の CRASH_MS = 1100ms / BURST_AT = 820ms と対。
   ★秒数を変えるときは board.js の定数も一緒に。片方だけだと
     「弾ける前に湧く」＝因果が逆に見える状態へ戻る。
     0-24%  寄っていく（place の transition）
     24-45% くっつく（2体が1つに溶ける・明るくなる）
     45-72% 膨らむ（表面張力でふくらむ水のたま）
     72-78% 限界
     78-100% 弾けて消える（水しぶきは board.js の splash が出す） */
.piece.gone.crash{animation:crashFade calc(1.8s * var(--fxs,1)) linear forwards;
                  will-change:opacity; backface-visibility:hidden}
/* ★消える仕事は opacity が持つ。形（crashWobble）に最後の急降下をさせない
   ——94〜97% の角の正体がそれだった（2026-08-27 実測）。 */
@keyframes crashFade{0%,74%{opacity:1} 84%{opacity:.72} 92%{opacity:.34} 100%{opacity:0}}
/* ★光は filter:brightness() をやめて【重ねた光の層の opacity】で作る（指摘[2] 2026-08-27）。
   filter のアニメは合成レイヤーに乗らず毎フレーム描き直しになる＝カクつきの主因。
   opacity なら合成だけで済む。見た目は「膨らむほど中から光る」で同じ。 */
.piece.gone.crash::after{
  content:""; position:absolute; inset:-16%; border-radius:50%; pointer-events:none;
  /* ★中ほどの色は【融合色】。芯の白と外の透明は残す——
     全部を融合色にすると光でなく塗りに見える（2026-08-28）。 */
  background:radial-gradient(circle at 50% 50%,
             rgba(224,247,255,.95), var(--fuse, rgba(103,232,249,.5)) 44%, transparent 72%);
  opacity:0; animation:crashGlow calc(1.8s * var(--fxs,1)) linear forwards;
  will-change:opacity;
}
@keyframes crashGlow{
  0%,19%{opacity:0} 34%{opacity:.18} 52%{opacity:.34} 66%{opacity:.55}
  74%{opacity:.74} 80%{opacity:.42} 88%{opacity:.16} 100%{opacity:0}
}
/* ★timing-function は linear。曲線はキーフレームの値そのものに入っている。
   キーフレームは tools/gen_keyframes.py が作る（手で書かない）。
   区間ごとに強い easing を掛けると境目で段がつく
   ——ずいくん 2026-08-27「1996年のゲームみたいにかくかく」の正体がこれ。 */
.piece.gone.crash .guy{animation:crashWobble calc(1.8s * var(--fxs,1)) linear forwards;
                       will-change:transform; backface-visibility:hidden}
/* ★「溶け合う」を見せるための役割分け（指摘[1] 2026-08-27）。
   同じマスで消える2体を同じ動きにすると、重なるだけで【1つになった】瞬間が無い。
   残る側(main)が相手を吸って2体ぶんに太り、吸われる側(sub)は相手の中へ消える。
   どちらが main かは board.js が「消える順」で決める（--ci=0 が main）。 */
.piece.gone.crash.crash-sub .guy{animation-name:crashMerge}
.piece.gone.crash.crash-sub::after{display:none}      /* 光は残る側に1つだけ */
/* ★溶けていく色（ずいくん指定 2026-08-28）
     「消えるときに、この3色の色の中間の融合色を見せてあげることで
       何がくっつくか分かりやすいかも。3体合体して3体ともリスポーンする
       ケースもあり得るから、どれとどれが合体したかが分かりやすい」

   ★色そのものを animate しない。重ねた層の opacity で作る——
     background/filter のアニメは合成に乗らず毎フレーム描き直しになる
     （2026-08-27「1996年のゲームみたいにかくかく」の主因と同じ型）。
   ★.guy の【中】に入っている（board.js が入れる）。外に置くと crashWobble の
     膨らみに追従せず、溶ける色だけが元の大きさで取り残される。
   ★--fuse が無いときは既定の水色。色が読めなかったのに
     「それらしい色」を出さない（＝混ぜたふりをしない）。 */
.piece.gone.crash .guy > .tint{
  position:absolute; inset:9%; border-radius:46%; pointer-events:none;
  background:var(--fuse,#67e8f9); opacity:0;
  animation:fuseTint calc(1.8s * var(--fxs,1)) linear forwards; will-change:opacity}
/* 24-45% で「くっつく」ので、色が寄りはじめるのはその手前から。
   72-78% の限界で最も濃く、弾けたあとは crashFade が全体を持っていく。 */
@keyframes fuseTint{
  0%,20%{opacity:0} 34%{opacity:.30} 48%{opacity:.58}
  62%{opacity:.76} 76%{opacity:.90} 100%{opacity:.90}
}
/* @gen:crashWobble */
@keyframes crashWobble{
  0%{transform:scale(1.0000,1.0000)}
  1%{transform:scale(1.0000,1.0000)}
  2%{transform:scale(1.0000,1.0000)}
  3%{transform:scale(1.0000,1.0000)}
  4%{transform:scale(1.0000,1.0000)}
  5%{transform:scale(1.0000,1.0000)}
  6%{transform:scale(1.0000,1.0000)}
  7%{transform:scale(1.0000,1.0000)}
  8%{transform:scale(1.0000,1.0000)}
  9%{transform:scale(1.0000,1.0000)}
  10%{transform:scale(1.0000,1.0000)}
  11%{transform:scale(1.0000,1.0000)}
  12%{transform:scale(1.0000,1.0000)}
  13%{transform:scale(1.0000,1.0000)}
  14%{transform:scale(1.0000,1.0000)}
  15%{transform:scale(1.0000,1.0000)}
  16%{transform:scale(1.0000,1.0000)}
  17%{transform:scale(1.0045,0.9953)}
  18%{transform:scale(1.0158,0.9840)}
  19%{transform:scale(1.0300,0.9700)}
  20%{transform:scale(1.0526,0.9494)}
  21%{transform:scale(1.0834,0.9226)}
  22%{transform:scale(1.1100,0.9000)}
  23%{transform:scale(1.1321,0.8821)}
  24%{transform:scale(1.1516,0.8666)}
  25%{transform:scale(1.1600,0.8600)}
  26%{transform:scale(1.1581,0.8630)}
  27%{transform:scale(1.1532,0.8704)}
  28%{transform:scale(1.1442,0.8818)}
  29%{transform:scale(1.1228,0.9043)}
  30%{transform:scale(1.1000,0.9300)}
  31%{transform:scale(1.0804,0.9560)}
  32%{transform:scale(1.0623,0.9829)}
  33%{transform:scale(1.0483,1.0064)}
  34%{transform:scale(1.0357,1.0280)}
  35%{transform:scale(1.0300,1.0450)}
  36%{transform:scale(1.0345,1.0560)}
  37%{transform:scale(1.0456,1.0646)}
  38%{transform:scale(1.0600,1.0750)}
  39%{transform:scale(1.0824,1.0890)}
  40%{transform:scale(1.1156,1.1056)}
  41%{transform:scale(1.1535,1.1244)}
  42%{transform:scale(1.1900,1.1450)}
  43%{transform:scale(1.2254,1.1700)}
  44%{transform:scale(1.2623,1.1995)}
  45%{transform:scale(1.2980,1.2293)}
  46%{transform:scale(1.3300,1.2550)}
  47%{transform:scale(1.3587,1.2763)}
  48%{transform:scale(1.3857,1.2958)}
  49%{transform:scale(1.4099,1.3136)}
  50%{transform:scale(1.4300,1.3300)}
  51%{transform:scale(1.4462,1.3448)}
  52%{transform:scale(1.4599,1.3582)}
  53%{transform:scale(1.4722,1.3707)}
  54%{transform:scale(1.4840,1.3831)}
  55%{transform:scale(1.4963,1.3960)}
  56%{transform:scale(1.5100,1.4100)}
  57%{transform:scale(1.5252,1.4252)}
  58%{transform:scale(1.5410,1.4410)}
  59%{transform:scale(1.5575,1.4575)}
  60%{transform:scale(1.5745,1.4745)}
  61%{transform:scale(1.5920,1.4920)}
  62%{transform:scale(1.6100,1.5100)}
  63%{transform:scale(1.6287,1.5287)}
  64%{transform:scale(1.6484,1.5484)}
  65%{transform:scale(1.6686,1.5686)}
  66%{transform:scale(1.6892,1.5892)}
  67%{transform:scale(1.7097,1.6097)}
  68%{transform:scale(1.7300,1.6300)}
  69%{transform:scale(1.7507,1.6507)}
  70%{transform:scale(1.7720,1.6720)}
  71%{transform:scale(1.7931,1.6931)}
  72%{transform:scale(1.8127,1.7127)}
  73%{transform:scale(1.8300,1.7300)}
  74%{transform:scale(1.8462,1.7462)}
  75%{transform:scale(1.8608,1.7608)}
  76%{transform:scale(1.8700,1.7700)}
  77%{transform:scale(1.8750,1.7750)}
  78%{transform:scale(1.8786,1.7786)}
  79%{transform:scale(1.8800,1.7800)}
  80%{transform:scale(1.8738,1.7737)}
  81%{transform:scale(1.8588,1.7586)}
  82%{transform:scale(1.8400,1.7400)}
  83%{transform:scale(1.8158,1.7170)}
  84%{transform:scale(1.7827,1.6865)}
  85%{transform:scale(1.7432,1.6502)}
  86%{transform:scale(1.7000,1.6100)}
  87%{transform:scale(1.6506,1.5629)}
  88%{transform:scale(1.5924,1.5068)}
  89%{transform:scale(1.5280,1.4448)}
  90%{transform:scale(1.4600,1.3800)}
  91%{transform:scale(1.3870,1.3122)}
  92%{transform:scale(1.3067,1.2384)}
  93%{transform:scale(1.2200,1.1580)}
  94%{transform:scale(1.1226,1.0658)}
  95%{transform:scale(1.0200,0.9680)}
  96%{transform:scale(0.9208,0.8737)}
  97%{transform:scale(0.8200,0.7780)}
  98%{transform:scale(0.7150,0.6785)}
  99%{transform:scale(0.6075,0.5767)}
  100%{transform:scale(0.5000,0.4750)}
}
/* @endgen */
/* @gen:crashMerge */
@keyframes crashMerge{
  0%{transform:scale(1.0000,1.0000); opacity:1.0000}
  1%{transform:scale(1.0000,1.0000); opacity:1.0000}
  2%{transform:scale(1.0000,1.0000); opacity:1.0000}
  3%{transform:scale(1.0000,1.0000); opacity:1.0000}
  4%{transform:scale(1.0000,1.0000); opacity:1.0000}
  5%{transform:scale(1.0000,1.0000); opacity:1.0000}
  6%{transform:scale(1.0000,1.0000); opacity:1.0000}
  7%{transform:scale(1.0000,1.0000); opacity:1.0000}
  8%{transform:scale(1.0000,1.0000); opacity:1.0000}
  9%{transform:scale(1.0000,1.0000); opacity:1.0000}
  10%{transform:scale(1.0000,1.0000); opacity:1.0000}
  11%{transform:scale(1.0000,1.0000); opacity:1.0000}
  12%{transform:scale(1.0000,1.0000); opacity:1.0000}
  13%{transform:scale(1.0000,1.0000); opacity:1.0000}
  14%{transform:scale(1.0000,1.0000); opacity:1.0000}
  15%{transform:scale(1.0000,1.0000); opacity:1.0000}
  16%{transform:scale(1.0000,1.0000); opacity:1.0000}
  17%{transform:scale(1.0045,0.9953); opacity:1.0000}
  18%{transform:scale(1.0158,0.9840); opacity:1.0000}
  19%{transform:scale(1.0300,0.9700); opacity:1.0000}
  20%{transform:scale(1.0526,0.9494); opacity:1.0000}
  21%{transform:scale(1.0834,0.9226); opacity:1.0000}
  22%{transform:scale(1.1100,0.9000); opacity:1.0000}
  23%{transform:scale(1.1321,0.8821); opacity:1.0000}
  24%{transform:scale(1.1516,0.8666); opacity:1.0000}
  25%{transform:scale(1.1600,0.8600); opacity:1.0000}
  26%{transform:scale(1.1554,0.8667); opacity:0.9985}
  27%{transform:scale(1.1442,0.8823); opacity:0.9949}
  28%{transform:scale(1.1300,0.9000); opacity:0.9900}
  29%{transform:scale(1.1111,0.9234); opacity:0.9826}
  30%{transform:scale(1.0849,0.9533); opacity:0.9713}
  31%{transform:scale(1.0537,0.9790); opacity:0.9568}
  32%{transform:scale(1.0200,0.9900); opacity:0.9400}
  33%{transform:scale(0.9820,0.9858); opacity:0.9184}
  34%{transform:scale(0.9377,0.9745); opacity:0.8899}
  35%{transform:scale(0.8896,0.9585); opacity:0.8565}
  36%{transform:scale(0.8400,0.9400); opacity:0.8200}
  37%{transform:scale(0.7881,0.9130); opacity:0.7775}
  38%{transform:scale(0.7327,0.8736); opacity:0.7274}
  39%{transform:scale(0.6760,0.8275); opacity:0.6737}
  40%{transform:scale(0.6200,0.7800); opacity:0.6200}
  41%{transform:scale(0.5645,0.7299); opacity:0.5665}
  42%{transform:scale(0.5087,0.6743); opacity:0.5113}
  43%{transform:scale(0.4535,0.6166); opacity:0.4555}
  44%{transform:scale(0.4000,0.5600); opacity:0.4000}
  45%{transform:scale(0.3463,0.5040); opacity:0.3430}
  46%{transform:scale(0.2926,0.4472); opacity:0.2848}
  47%{transform:scale(0.2425,0.3919); opacity:0.2291}
  48%{transform:scale(0.2000,0.3400); opacity:0.1800}
  49%{transform:scale(0.1630,0.2888); opacity:0.1338}
  50%{transform:scale(0.1288,0.2379); opacity:0.0890}
  51%{transform:scale(0.1002,0.1930); opacity:0.0521}
  52%{transform:scale(0.0800,0.1600); opacity:0.0300}
  53%{transform:scale(0.0654,0.1352); opacity:0.0183}
  54%{transform:scale(0.0528,0.1133); opacity:0.0087}
  55%{transform:scale(0.0438,0.0972); opacity:0.0023}
  56%{transform:scale(0.0400,0.0900); opacity:0.0000}
  57%{transform:scale(0.0394,0.0883); opacity:0.0000}
  58%{transform:scale(0.0389,0.0867); opacity:0.0000}
  59%{transform:scale(0.0383,0.0851); opacity:0.0000}
  60%{transform:scale(0.0378,0.0837); opacity:0.0000}
  61%{transform:scale(0.0374,0.0823); opacity:0.0000}
  62%{transform:scale(0.0369,0.0811); opacity:0.0000}
  63%{transform:scale(0.0365,0.0799); opacity:0.0000}
  64%{transform:scale(0.0362,0.0787); opacity:0.0000}
  65%{transform:scale(0.0358,0.0777); opacity:0.0000}
  66%{transform:scale(0.0355,0.0767); opacity:0.0000}
  67%{transform:scale(0.0352,0.0758); opacity:0.0000}
  68%{transform:scale(0.0349,0.0749); opacity:0.0000}
  69%{transform:scale(0.0346,0.0741); opacity:0.0000}
  70%{transform:scale(0.0344,0.0734); opacity:0.0000}
  71%{transform:scale(0.0341,0.0727); opacity:0.0000}
  72%{transform:scale(0.0339,0.0721); opacity:0.0000}
  73%{transform:scale(0.0337,0.0715); opacity:0.0000}
  74%{transform:scale(0.0335,0.0709); opacity:0.0000}
  75%{transform:scale(0.0334,0.0704); opacity:0.0000}
  76%{transform:scale(0.0332,0.0699); opacity:0.0000}
  77%{transform:scale(0.0331,0.0695); opacity:0.0000}
  78%{transform:scale(0.0329,0.0690); opacity:0.0000}
  79%{transform:scale(0.0328,0.0686); opacity:0.0000}
  80%{transform:scale(0.0327,0.0683); opacity:0.0000}
  81%{transform:scale(0.0326,0.0679); opacity:0.0000}
  82%{transform:scale(0.0324,0.0676); opacity:0.0000}
  83%{transform:scale(0.0323,0.0672); opacity:0.0000}
  84%{transform:scale(0.0322,0.0669); opacity:0.0000}
  85%{transform:scale(0.0321,0.0666); opacity:0.0000}
  86%{transform:scale(0.0320,0.0663); opacity:0.0000}
  87%{transform:scale(0.0319,0.0659); opacity:0.0000}
  88%{transform:scale(0.0318,0.0656); opacity:0.0000}
  89%{transform:scale(0.0317,0.0653); opacity:0.0000}
  90%{transform:scale(0.0316,0.0649); opacity:0.0000}
  91%{transform:scale(0.0315,0.0645); opacity:0.0000}
  92%{transform:scale(0.0314,0.0642); opacity:0.0000}
  93%{transform:scale(0.0312,0.0638); opacity:0.0000}
  94%{transform:scale(0.0311,0.0633); opacity:0.0000}
  95%{transform:scale(0.0309,0.0629); opacity:0.0000}
  96%{transform:scale(0.0308,0.0624); opacity:0.0000}
  97%{transform:scale(0.0306,0.0618); opacity:0.0000}
  98%{transform:scale(0.0304,0.0613); opacity:0.0000}
  99%{transform:scale(0.0302,0.0607); opacity:0.0000}
  100%{transform:scale(0.0300,0.0600); opacity:0.0000}
}
/* @endgen */
/* 弾ける瞬間の水のたま（輪郭だけ残して飛び散る） */
.drop{position:absolute; border-radius:50%; pointer-events:none;
      background:radial-gradient(circle at 34% 30%, #e0f7ff, #67e8f9 55%, #22a6c8);
      box-shadow:0 0 8px rgba(103,232,249,.7)}
.ring.splash{border-color:#67e8f9; border-width:4px}

/* --- 丸頭の人型（性別・年齢・国籍を持たない） --- */
.guy{width:76%; height:76%; position:relative}
.guy .head{
  position:absolute; left:6%; right:6%; top:2%; bottom:16%;
  border-radius:44% 44% 40% 40%/48% 48% 38% 38%;
  box-shadow:inset 0 -14% 0 rgba(0,0,0,.16), 0 3px 8px rgba(0,0,0,.42);
}
.guy .foot{position:absolute; left:20%; right:20%; bottom:0; height:14%; border-radius:0 0 40% 40%; opacity:.55}
.guy .eye{position:absolute; width:17%; height:20%; background:#0d1117; border-radius:50%; top:36%; transition:transform calc(.16s * var(--fxs,1))}
.guy .eye.l{left:26%} .guy .eye.r{right:26%}
.guy .brow{position:absolute; width:26%; height:9%; background:#0d1117; border-radius:2px; top:22%}
.guy .brow.l{left:16%; transform:rotate(26deg)} .guy .brow.r{right:16%; transform:rotate(-26deg)}
/* 敵対者: 吊り目・牙・角・赤い眼光。ただし人型のまま（国籍も性別も持たせない） */
.chaser .guy .eye{width:20%; height:16%; border-radius:40% 40% 50% 50%;
  background:#1b0d0d; box-shadow:0 0 6px 1px rgba(255,90,90,.55) inset, 0 0 5px rgba(255,60,60,.5)}
.chaser .guy .eye::after{content:""; position:absolute; inset:22% 18%; border-radius:50%;
  background:#ff5a5a; box-shadow:0 0 6px #ff5a5a}
.chaser .guy .head::before,.chaser .guy .head::after{content:""; position:absolute; top:-13%;
  width:0; height:0; border-left:7% solid transparent; border-right:7% solid transparent;
  border-bottom:15% solid currentColor}
.chaser .guy .head::before{left:14%; transform:rotate(-16deg)}
.chaser .guy .head::after{right:14%; transform:rotate(16deg)}
.chaser.c0 .guy .head{color:var(--c0)} .chaser.c1 .guy .head{color:var(--c1)}
.chaser.c2 .guy .head{color:var(--c2)} .chaser.c3 .guy .head{color:var(--c3)}
.chaser .guy .fang{position:absolute; bottom:24%; width:0; height:0;
  border-left:5% solid transparent; border-right:5% solid transparent;
  border-top:10% solid rgba(255,255,255,.92)}
.chaser .guy .fang.l{left:34%} .chaser .guy .fang.r{right:34%}
.chaser .guy .mouth{position:absolute; left:30%; right:30%; bottom:26%; height:8%;
  background:#1b0d0d; border-radius:0 0 40% 40%}
.me .head{background:var(--me)} .me .foot{background:var(--me)}
.chaser.c0 .head,.chaser.c0 .foot{background:var(--c0)}
.chaser.c1 .head,.chaser.c1 .foot{background:var(--c1)}
.chaser.c2 .head,.chaser.c2 .foot{background:var(--c2)}
.chaser.c3 .head,.chaser.c3 .foot{background:var(--c3)}
.chaser .head{animation:bob 1.1s ease-in-out infinite}
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-5%)}}
/* --- 被攻撃: 目が ✕_✕ になる／殴られて仰け反る --- */
.me.hurt .eye{background:transparent; width:26%; height:26%; top:32%}
.me.hurt .eye.l{left:20%} .me.hurt .eye.r{right:20%}
.me.hurt .eye::before,.me.hurt .eye::after{
  content:""; position:absolute; left:50%; top:50%; width:118%; height:19%;
  background:#0d1117; border-radius:2px; transform-origin:center}
.me.hurt .eye::before{transform:translate(-50%,-50%) rotate(45deg)}
.me.hurt .eye::after{transform:translate(-50%,-50%) rotate(-45deg)}
.me.hurt .head{background:#fca5a5 !important; animation:reel calc(.62s * var(--fxs,1)) ease-out}
@keyframes reel{0%{transform:translateX(0) rotate(0)}
  22%{transform:translateX(-16%) rotate(-14deg)}
  48%{transform:translateX(11%) rotate(9deg)}
  74%{transform:translateX(-5%) rotate(-4deg)}
  100%{transform:translateX(0) rotate(0)}}
/* 追手が殴りかかる動き（当たって弾き返される） */
.chaser.lunge{animation:lunge calc(.52s * var(--fxs,1)) cubic-bezier(.3,1.4,.5,1)}
@keyframes lunge{0%{filter:none}
  30%{filter:brightness(1.5)}
  100%{filter:none}}
.chaser.lunge .head{animation:lungeh calc(.52s * var(--fxs,1)) cubic-bezier(.3,1.4,.5,1)}
@keyframes lungeh{0%{transform:scale(1)}28%{transform:scale(1.35) rotate(-8deg)}
  60%{transform:scale(.92)}100%{transform:scale(1)}}
/* 画面に赤い衝撃 */
.hit{position:absolute; inset:0; border-radius:18px; pointer-events:none; opacity:0;
     box-shadow:inset 0 0 60px 12px rgba(248,113,113,.75); z-index:7}
.hit.on{animation:hitfx calc(.62s * var(--fxs,1)) ease-out}
@keyframes hitfx{0%{opacity:0}12%{opacity:1}100%{opacity:0}}
.piece.pop{animation:pop calc(.34s * var(--fxs,1)) ease-out}
@keyframes pop{0%{transform:scale(1)}45%{transform:scale(1.28)}100%{transform:scale(1)}}
.piece.gone{animation:gone calc(.38s * var(--fxs,1)) ease-in forwards}
@keyframes gone{to{transform:scale(.1) rotate(140deg); opacity:0}}

/* --- 拾い物 --- */
.item{width:38%; height:38%; border-radius:50%; background:var(--item);
      box-shadow:0 0 0 4px rgba(251,191,36,.18), 0 2px 6px rgba(0,0,0,.4);
      animation:pulse 1.5s ease-in-out infinite}
@keyframes pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.13)}}

/* --- ゴール旗。★絶対にロック表示しない（灰色のバツは「壊れてる」に見える） --- */
.flag{width:82%; height:82%; position:relative; transition:calc(.3s * var(--fxs,1))}
.flag i{position:absolute; display:block}
.flag .pole{left:24%; top:4%; width:7%; height:92%; border-radius:3px; background:#cbd5e1}
.flag .base{left:10%; bottom:0; width:44%; height:8%; border-radius:3px; background:#94a3b8}
.flag .cloth{left:31%; top:8%; width:46%; height:34%; background:var(--exit-on);
      clip-path:polygon(0 0,100% 50%,0 100%); transform-origin:left center}
.flag .need{position:absolute; right:-6%; top:-8%; min-width:17px; height:17px; padding:0 4px;
      border-radius:9px; background:var(--item); color:#3b2c05; font-size:11px; font-weight:900;
      display:flex; align-items:center; justify-content:center; box-shadow:0 2px 6px rgba(0,0,0,.5)}
.flag.open .cloth{animation:wave 1.1s ease-in-out infinite}
.flag.open{filter:drop-shadow(0 0 12px rgba(52,211,153,.65))}
.flag.open .need{display:none}
@keyframes wave{0%,100%{transform:scaleX(1) skewY(0deg)}50%{transform:scaleX(1.1) skewY(-6deg)}}

.spark{position:absolute; width:7px; height:7px; border-radius:50%; pointer-events:none}
.float{position:absolute; font-weight:900; font-size:22px; color:#fca5a5;
       text-shadow:0 2px 6px rgba(0,0,0,.6); pointer-events:none;
       animation:floatup calc(1s * var(--fxs,1)) ease-out forwards}
@keyframes floatup{to{transform:translateY(-46px); opacity:0}}

/* ---------- スタート前 / カウントダウン（盤面に重なる） ---------- */
.gate{position:absolute; inset:8px; display:none; align-items:center; justify-content:center;
      z-index:5; border-radius:12px; background:rgba(10,13,18,.55); backdrop-filter:blur(1.5px)}
.gate.show{display:flex}
.gate .gwrap{text-align:center}
.gate .gtitle{font-size:13px; font-weight:800; color:var(--ink); margin-bottom:12px; opacity:.9}
.gate .startbtn{font-size:17px; font-weight:900; color:#08221a; background:var(--me);
      border:none; padding:15px 34px; border-radius:14px; letter-spacing:.06em;
      box-shadow:0 10px 30px rgba(52,211,153,.35); animation:pulse 1.5s ease-in-out infinite}
.gate .hint{margin-top:12px; font-size:11px; color:var(--dim); font-weight:700; line-height:1.6}
.count{position:absolute; inset:8px; display:none; align-items:center; justify-content:center;
      z-index:6; pointer-events:none}
.count.show{display:flex}
.count b{font-size:clamp(60px,20vw,110px); font-weight:900; color:var(--me);
      text-shadow:0 8px 40px rgba(52,211,153,.5); animation:cd 1s ease-out}
@keyframes cd{0%{transform:scale(1.9); opacity:0}25%{transform:scale(1); opacity:1}
              100%{transform:scale(.82); opacity:.25}}
.count .lbl{position:absolute; bottom:22%; left:0; right:0; text-align:center;
      font-size:12px; font-weight:800; color:var(--ink); opacity:.85}
/* --- 湧き（ずいくん指定 2026-08-27「地面から小さい状態から湧いてプヨン！」）---
   ★出る時刻は board.js の SPAWN_WAIT が決める。ここは出かたの担当。
     以前は回転しながら現れる形（rotate -140deg）で、
     ①相討ちの爆散より先に見えてしまい ②「地面から出てきた」に見えなかった。
   ★.piece には place() が transform を書き込むので、駒そのものは動かせない。
     中の .guy を伸び縮みさせる（transform-origin を足元にして「地面から」を作る）。 */
/* 時間割は board.js の SPAWN_WAIT = 1250ms / SPAWN_ANIM = 950ms と対。
   ★「ぷわ〜〜ん」＝ ゆっくり地面から盛り上がって、最後にひと膨らみ。
     速い overshoot（プヨン）だと弾んで見えて、水っぽさが消える。 */
.piece.chaser.hold .guy{opacity:0; transform:scale(.06,.02)}
.piece.spawn .guy{transform-origin:50% 100%;
                  animation:spawnIn calc(1.15s * var(--fxs,1)) linear;
                  will-change:transform, opacity; backface-visibility:hidden}
/* @gen:spawnIn */
@keyframes spawnIn{
  0%{transform:scale(0.0600,0.0200); opacity:0.0000}
  4%{transform:scale(0.1468,0.0736); opacity:0.1791}
  8%{transform:scale(0.2050,0.1100); opacity:0.3400}
  12%{transform:scale(0.2138,0.1160); opacity:0.4588}
  16%{transform:scale(0.2220,0.1220); opacity:0.5600}
  20%{transform:scale(0.2531,0.1585); opacity:0.6534}
  24%{transform:scale(0.3040,0.2232); opacity:0.7391}
  28%{transform:scale(0.3583,0.2877); opacity:0.8213}
  32%{transform:scale(0.4208,0.3577); opacity:0.8997}
  36%{transform:scale(0.4616,0.4038); opacity:0.9557}
  40%{transform:scale(0.4900,0.4370); opacity:0.9900}
  44%{transform:scale(0.5237,0.4817); opacity:0.9987}
  48%{transform:scale(0.5753,0.5597); opacity:1.0000}
  52%{transform:scale(0.6500,0.6800); opacity:1.0000}
  56%{transform:scale(0.7196,0.7776); opacity:1.0000}
  60%{transform:scale(0.7800,0.8493); opacity:1.0000}
  64%{transform:scale(0.8360,0.9050); opacity:1.0000}
  68%{transform:scale(0.9184,0.9759); opacity:1.0000}
  72%{transform:scale(0.9997,1.0427); opacity:1.0000}
  76%{transform:scale(1.0650,1.0950); opacity:1.0000}
  80%{transform:scale(1.0979,1.1186); opacity:1.0000}
  84%{transform:scale(1.0802,1.0990); opacity:1.0000}
  88%{transform:scale(1.0327,1.0492); opacity:1.0000}
  92%{transform:scale(0.9788,0.9938); opacity:1.0000}
  96%{transform:scale(0.9822,0.9929); opacity:1.0000}
  100%{transform:scale(1.0000,1.0000); opacity:1.0000}
}
/* @endgen */
/* 湧いた足元の水たまり。「地面から出てきた」を1枚で伝える */
.piece.spawn::after{
  content:""; position:absolute; left:12%; right:12%; bottom:2%; height:16%;
  border-radius:50%;
  background:radial-gradient(closest-side, rgba(103,232,249,.55), transparent);
  animation:spawnDust calc(1.15s * var(--fxs,1)) ease-out forwards; pointer-events:none;
}
@keyframes spawnDust{
  0%{opacity:0; transform:scale(.15)}
  20%{opacity:.9; transform:scale(1)}
  60%{opacity:.5; transform:scale(1.35)}
  100%{opacity:0; transform:scale(1.8)}
}
body.nomotion .piece.spawn .guy,
body.nomotion .piece.spawn::after{animation:none}
body.nomotion .piece.chaser.hold .guy{opacity:1; transform:none}

/* =========================================================================
   達成の祝い（2026-08-27 ずいくん要求「一個ずつクリアしたら わー って喜ぶ感じ」）

   ★ここはチュートリアル専用ではない。本編でも対戦でも同じ場所で同じ祝いが出る。
     チュートリアルにだけ祝いを置くと、教わったときだけ嬉しくて本番が地味になる
     ——「最初のゴール体験」を作るという目的から外れる。
   ★段は4つ。①拾った ②最後の1個＝旗があいた ③旗に着いた ④章クリア。
     ①②はここ（board）、③④は呼ぶ側（index.html の celebrate）が持つ。
     ★いちばん効くのは②。「集める→開く→向かう」の "開いた!" が今まで無演出だった。
   ★動きを減らす設定（body.nomotion）に必ず従わせる。下端にまとめて置いてある。
   ========================================================================= */

/* --- ① 拾った瞬間 --------------------------------------------------- */
/* .float は既定が赤（+2 のダメージ表示）。祝いは色を変えないと同じ意味に見える */
.float.good{color:var(--item); font-size:26px}
.float.grand{color:var(--me); font-size:30px; letter-spacing:.02em}
.board.bounce{animation:bnc calc(.42s * var(--fxs,1)) cubic-bezier(.3,1.5,.5,1)}
@keyframes bnc{0%,100%{transform:translateY(0)} 40%{transform:translateY(-7px)}}
/* 拾ったマスから広がる輪。粒（spark）だけだと「消えた」に見えて「取れた」に見えない */
.ring{position:absolute; pointer-events:none; border-radius:50%; z-index:8;
      border:3px solid var(--item); opacity:.9}
.ring.me{border-color:var(--me)}

/* --- ② 最後の1個を取って旗が開いた瞬間 ------------------------------- */
/* ★.open は「開いている状態」、.justopen は「いま開いた出来事」。
     状態と出来事を同じクラスで表すと、2手目以降ずっと跳ね続ける。 */
.flag.justopen{animation:flagPop calc(1.1s * var(--fxs,1)) cubic-bezier(.2,1.5,.4,1)}
@keyframes flagPop{
  0%{transform:scale(1) rotate(0)}
  22%{transform:scale(1.55) rotate(-9deg)}
  46%{transform:scale(.94) rotate(5deg)}
  70%{transform:scale(1.16) rotate(-2deg)}
  100%{transform:scale(1) rotate(0)}
}
.flag.justopen .cloth{filter:brightness(1.6) drop-shadow(0 0 10px rgba(52,211,153,.9))}
/* 盤全体が一度だけ緑に光る。「盤の意味が変わった」を一目で分からせる */
.openflash{position:absolute; inset:0; border-radius:18px; pointer-events:none; z-index:7;
           box-shadow:inset 0 0 70px 14px rgba(52,211,153,.75); opacity:0;
           animation:ofl calc(1.05s * var(--fxs,1)) ease-out forwards}
@keyframes ofl{0%{opacity:0}14%{opacity:1}100%{opacity:0}}
/* 開いた旗のマスへ視線を送る（どこへ行けばいいか） */
.cell.hasflag.justopen{animation:flagCell calc(1.1s * var(--fxs,1)) ease-out}
@keyframes flagCell{0%,100%{box-shadow:inset 0 0 0 3px rgba(52,211,153,1), inset 0 0 14px rgba(52,211,153,.4)}
  30%{box-shadow:inset 0 0 0 6px rgba(52,211,153,1), inset 0 0 30px rgba(52,211,153,.9)}}

/* --- 台本チュートリアル: 進める方向のガイド --------------------------- */
/* ★「ここに行け」を盤の上に出す（ずいくん要求6）。文字で書いても盤を見ている人には届かない */
.guidearw{position:absolute; left:0; top:0; pointer-events:none; z-index:9; color:var(--me)}
.guidearw .a{position:absolute; inset:20%; background:currentColor;
  clip-path:polygon(50% 0,100% 46%,72% 46%,72% 100%,28% 100%,28% 46%,0 46%);
  transform:rotate(var(--deg)); animation:garw 1.1s ease-in-out infinite;
  filter:drop-shadow(0 2px 6px rgba(0,0,0,.5))}
@keyframes garw{0%,100%{opacity:.55; transform:rotate(var(--deg)) scale(.86)}
                50%{opacity:1; transform:rotate(var(--deg)) scale(1.06)}}
.cell.guidecell{box-shadow:inset 0 0 0 3px rgba(52,211,153,.95), inset 0 0 18px rgba(52,211,153,.45) !important;
                background:rgba(52,211,153,.20) !important}
/* 止まっている／的外れなとき、行き先の光を強める（呼ぶ側＝台本が判断する） */
.cell.guidecell.urge{background:rgba(52,211,153,.34) !important; animation:urgeCell 1.1s ease-in-out infinite}
@keyframes urgeCell{
  0%,100%{box-shadow:inset 0 0 0 3px rgba(52,211,153,.95), inset 0 0 18px rgba(52,211,153,.45)}
  50%{box-shadow:inset 0 0 0 6px rgba(52,211,153,1), inset 0 0 34px rgba(52,211,153,.95)}
}
.guidearw.urge .a{animation-duration:.72s; filter:drop-shadow(0 0 10px rgba(52,211,153,.95))}
body.nomotion .cell.guidecell.urge{animation:none}
/* 許可されていない方向を押したとき。叱らずに「そっちじゃない」だけ伝える */
.board.nudge{animation:ndg calc(.38s * var(--fxs,1)) ease-out}
@keyframes ndg{0%,100%{transform:translateX(0)}30%{transform:translateX(-5px)}65%{transform:translateX(5px)}}

/* ★演出の最中に押されたとき（ずいくん指摘[3] 2026-08-27）。
   nudge（＝方向が違う）とは【別の絵】にする。理由が違うものを同じ絵にすると、
   「そっちじゃないのか、待たされているのか」が永久に区別できない。
   横に揺らさず、盤の縁がすっと光って戻る＝「いま見ていて」の合図。 */
.board.fxwait{animation:fxw calc(.36s * var(--fxs,1)) ease-out}
@keyframes fxw{
  0%  {box-shadow:inset 0 0 0 0 rgba(148,163,184,0)}
  35% {box-shadow:inset 0 0 0 3px rgba(148,163,184,.62)}
  100%{box-shadow:inset 0 0 0 0 rgba(148,163,184,0)}
}
body.nomotion .board.fxwait{animation:none}

/* ★動きを控えめにする設定への追従。ここを書き忘れると設定が嘘になる */
body.nomotion .ring,
body.nomotion .openflash{display:none}
body.nomotion .flag.justopen,
body.nomotion .cell.hasflag.justopen,
body.nomotion .board.bounce,
body.nomotion .board.nudge,
body.nomotion .guidearw .a{animation:none}
