/* チュートリアル（章一覧・章の再生・解説モーダル・図解）。
   すべて body.tp の下でのみ効かせる（Render版へ影響させない）。
   ページ本体は絶対にスクロールさせない: 伸び縮みする所は minmax(0,1fr) で受け、
   はみ出す可能性のある箇所だけ .tp-scroll を付ける。

   ■ アニメーションの鉄則（バグ再発防止）
   位置を transform で決めている要素（.tpt-piece / .tpt-dia-item）に、
   transform を含む @keyframes を当ててはいけない。CSS アニメーションはインラインスタイルより
   優先されるため、再生中だけ位置指定が捨てられ、要素が左上（原点）へ飛ぶ。
   動かしたいときは中の子要素（.tpt-disc など）を動かすか、opacity だけを動かす。
   tests/tutorial.test.js がこの規則を機械的に検査している。 */

/* ══ 章一覧 ══════════════════════════════════ */

body.tp .tpt-list { display: flex; flex-direction: column; gap: var(--tp-4); }
body.tp .tpt-lead { margin-top: var(--tp-1); }

body.tp .tpt-progress { display: flex; flex-direction: column; gap: var(--tp-2); }
body.tp .tpt-progress-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--tp-3);
}
body.tp .tpt-count { font-size: 13px; font-weight: 700; color: var(--tp-text); letter-spacing: .04em; }
body.tp .tpt-bar {
  height: 6px;
  border-radius: var(--tp-r-pill);
  background: var(--tp-surface-3);
  overflow: hidden;
}
/* 伸びる帯は width ではなく transform で動かす。width のアニメーションは毎フレーム
   レイアウトをやり直すため、章を進めるたびに進捗バーと周りの行がガタついていた。
   角丸は親（overflow:hidden + border-radius）に任せる。i 側に付けると縮尺で歪む。 */
body.tp .tpt-bar i {
  display: block; width: 100%; height: 100%;
  transform-origin: left center;
  transform: scaleX(0);
  background: var(--tp-accent);
  transition: transform .3s ease;
}

body.tp .tpt-chno {
  font-family: var(--f-num), var(--f-body);
  font-size: 15px; font-weight: 700;
  color: var(--tp-text-dim);
}
body.tp .tpt-chrow.done .tpt-chno { color: var(--tp-ok); }

/* 2部構成（基本ルール編 / 実戦編）。見出しの下に、その部の章だけを並べる。 */
body.tp .tpt-parts { display: flex; flex-direction: column; gap: var(--tp-5); }
body.tp .tpt-part { display: flex; flex-direction: column; gap: var(--tp-2); }
body.tp .tpt-part-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--tp-3);
}
body.tp .tpt-part-t { color: var(--tp-accent); }
body.tp .tpt-part-est { flex: none; }
body.tp .tpt-part-sub { margin-top: calc(var(--tp-1) * -1); }

body.tp .tpt-alldone { display: flex; flex-direction: column; gap: var(--tp-2); }
body.tp .tpt-alldone-t { color: var(--tp-accent); }
body.tp .tpt-alldone-go { margin-top: var(--tp-2); }

/* ══ 章の再生 ════════════════════════════════ */

body.tp .tpt-stage {
  display: grid;
  /* 列を minmax(0,1fr) にしないと、長い章名（英語）で grid の列が広がって画面外へはみ出す */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: var(--tp-3);
  overflow: hidden;
  padding-bottom: 0;
}

/* 章名（最大2行）と決着帯が入れ替わっても高さが動かないよう、先に場所を取っておく。
   予約しないと、決着した瞬間に上の帯の高さが変わり、その差だけ盤が伸び縮みして跳ねる。 */
body.tp .tpt-top {
  display: flex; align-items: center; justify-content: space-between; gap: var(--tp-3);
  min-height: 34px;
}
body.tp .tpt-chname {
  flex: 0 1 auto;
  min-width: 0; /* これが無いと英語の長い章名が縮まず、画面の外へはみ出す */
  font-family: var(--f-display);
  font-size: 13px; font-weight: 700; letter-spacing: .04em;
  line-height: 1.25;
  color: var(--tp-text-dim);
  /* 1行だと 375px 幅で英語の第2章・日本語の第12章が途中で切れる。2行までは折り返す。 */
  overflow: hidden; text-overflow: ellipsis; white-space: normal;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
/* 決着の帯。盤に文字を重ねないため、章名の位置に出す（章名は決着中だけ伏せる）。 */
body.tp .tpt-verdict {
  flex: 0 1 auto; min-width: 0;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px var(--tp-2);
  border: 1px solid var(--tp-accent-deep);
  border-radius: var(--tp-r-pill);
  background: rgba(216, 178, 90, .12);
  color: var(--tp-accent);
  font-family: var(--f-display);
  font-size: 12px; font-weight: 700; letter-spacing: .04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  animation: tpt-verdict-in .5s ease;
}
body.tp .tpt-verdict[hidden] { display: none !important; }
/* 文字は「帯そのもの」ではなく、この中の1行に切らせる。text-overflow は自分の中の
   インライン内容にしか効かないので、inline-flex の親（.tpt-verdict）に書いても、
   子の <span> は素のまま断ち切られる ── 320px 幅の英語で
   「Victory — their king has fall」と単語の途中で切れていたのがこれ。 */
body.tp .tpt-verdict-t {
  min-width: 0; display: block;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.tp .tpt-verdict-ico { flex: none; display: grid; place-items: center; }
body.tp .tpt-verdict-ico .tp-ico { width: 13px; height: 13px; }
body.tp .tpt-top.won .tpt-chname { display: none; }
@keyframes tpt-verdict-in { from { opacity: 0; } to { opacity: 1; } }

body.tp .tpt-dots { display: flex; gap: 4px; flex: none; }
body.tp .tpt-dots i {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--tp-surface-3);
}
body.tp .tpt-dots i.past { background: var(--tp-accent-deep); }
body.tp .tpt-dots i.on { background: var(--tp-accent); width: 14px; border-radius: var(--tp-r-pill); }

body.tp .tpt-arena-slot { min-height: 0; display: flex; }

/* ── 盤 ───────────────────────────────────── */

/* 盤・手札・操作ボタンの3段。カード拡大と山札えらびはこの3段の外（絶対配置の覆い）に出す
   ので、開いても閉じても盤に残る高さは変わらない（position: relative はその受け皿）。 */
body.tp .tpt-arena {
  position: relative;
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; gap: var(--tp-2);
}
body.tp .tpt-boardwrap { flex: 1 1 auto; min-height: 0; display: grid; place-items: center; }

/* 盤の寸法・格子は実プレイ（style.css の #board / .cells）と同じ作り。
   3×4 と目地 4px は style.css がそのまま持っている値で、board.js の位置計算
   （CELL_GAP）と対になっている。片方だけ変えると駒とマスがズレる。
   合図（cue）の色をここで決める: 金は「カードの対象・進化・王を討てるマス・直前の手」に
   使われているので、次に触る場所の合図だけは白〜シアンにして意味の衝突を避ける。 */
body.tp .tpt-arena { --tpt-cue: #8fe9ff; --tpt-cue-soft: rgba(143, 233, 255, .5); }
body.tp .tpt-board {
  --tpt-cell: 60px;
  position: relative;
  width: calc(var(--tpt-cell) * 3 + 8px);
  height: calc(var(--tpt-cell) * var(--rows) + 12px);
}
body.tp .tpt-cells {
  display: grid;
  grid-template-columns: repeat(3, var(--tpt-cell));
  grid-template-rows: repeat(var(--rows), var(--tpt-cell));
  gap: 4px;
}
/* 決着したら盤ごと灯す。位置を持たない疑似要素なので transform を使ってよい。 */
body.tp .tpt-board.won::after {
  content: '';
  position: absolute; inset: -7px;
  border: 1px solid var(--gold);
  border-radius: 6px;
  box-shadow: 0 0 24px rgba(216, 178, 90, .38), inset 0 0 34px rgba(216, 178, 90, .16);
  animation: tpt-victory 1.5s ease-in-out infinite;
  pointer-events: none;
}
@keyframes tpt-victory { 50% { opacity: .38; } }
/* マスの見た目は style.css の .cell と同じ二層グラデ＋角丸。
   実プレイの盤は「石畳のタイル」に見えるが、チュートリアルだけ角の立った升目だったため、
   同じゲームの盤に見えていなかった。 */
body.tp .tpt-cell {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 4px;
  background:
    linear-gradient(158deg, rgba(234, 225, 205, .05), transparent 45%),
    linear-gradient(180deg, #1c1530, #151026);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, .5);
  transition: box-shadow .2s, border-color .2s;
  cursor: pointer;
}
/* 王の効果範囲（常時・薄く）。style.css の .cell.king-range と同じ値。
   マスの ::after を使うのは、::before を「直前の手」の印が使うため（本家と同じ割り当て）。 */
body.tp .tpt-cell.range {
  border-color: rgba(157, 123, 255, .34);
  box-shadow: inset 0 0 12px rgba(107, 79, 192, .2);
}
body.tp .tpt-cell.range::after {
  content: '';
  position: absolute; inset: 4px;
  border: 1px dashed rgba(157, 123, 255, .18);
  border-radius: 3px;
  pointer-events: none;
}
/* 範囲そのものを教える章だけ、薄い表示の上に強い枠を重ねる */
body.tp .tpt-cell.range-lit {
  border-color: rgba(157, 123, 255, .5);
  box-shadow: inset 0 0 16px rgba(107, 79, 192, .34);
}
body.tp .tpt-cell.range-lit::after { border-color: rgba(157, 123, 255, .45); }

/* 直前の一手。実プレイ（style.css の .cell.last-from / .last-to）は金枠＋金の菱形だが、
   テストプレイ版では**金を決着（.hl-capture-king）に譲る**（設計ガイドライン §6.1・決定7）。
   金枠＋金の菱形が「直前の一手」と「王を討てる一手」の両方に付いていたため、相手が王を
   動かした直後に敵王が金をまとい、勝てる手と見分けがつかなかった。
   色の語彙（緑=動ける／赤=討つ／紫=屍と範囲／灰=できない／金=札・効果・決着）に
   「直前の一手」は無いので、新しい色は足さず**無彩色（骨の白）**で描き、形も細い線にする。
   ここに置くのは重ね順のため ── 直前の一手は「済んだこと」なので、これから打てる手の印
   （hl-*）と同じマスに来たら必ず負ける（後に書いた規則が box-shadow を上書きする）。
   実プレイはもう1段 .cell.last-mid（多段移動の中継マス）を持つが、こちらには無い ──
   player.js が渡すのは最後の1脚だけで、2回移動する章（加速）を act に使っていないため。 */
body.tp .tpt-cell.last-from { box-shadow: inset 0 0 0 2px rgba(234, 225, 205, .16); }
body.tp .tpt-cell.last-to { box-shadow: inset 0 0 0 2px rgba(234, 225, 205, .42), inset 0 0 14px rgba(234, 225, 205, .1); }
body.tp .tpt-cell.last-to::before {
  content: '';
  position: absolute; top: 3px; right: 3px;
  width: 10px; height: 2px;
  background: rgba(234, 225, 205, .7);
  pointer-events: none;
}

/* 移動先ハイライト（style.css の .cell .hl と同じ寸法・同じ形） */
body.tp .tpt-hl {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  border-radius: 4px;
  pointer-events: none;
}
body.tp .tpt-cell.hl-move { border-color: var(--venom-deep); }
body.tp .tpt-cell.hl-move .tpt-hl::before {
  content: ''; width: 26%; height: 26%; border-radius: 50%;
  background: radial-gradient(circle, var(--venom-glow), transparent 70%);
  border: 2px solid var(--venom);
  animation: tpt-hl-pulse 1.2s ease-in-out infinite;
}
/* 入れ替え。実プレイは字の記号を置いているが、絵柄記号は使わない規約なので
   「行き来する」意味を破線のリングと光で表す（大きさは実プレイの円に合わせる）。 */
body.tp .tpt-cell.hl-swap { border-color: var(--soul); }
body.tp .tpt-cell.hl-swap .tpt-hl::before {
  content: ''; width: 40%; height: 40%; border-radius: 50%;
  border: 2px dashed var(--soul);
  box-shadow: 0 0 12px var(--soul-glow), inset 0 0 8px rgba(157, 123, 255, .35);
  animation: tpt-hl-pulse 1.2s ease-in-out infinite;
}
body.tp .tpt-cell.hl-capture {
  border-color: var(--blood);
  box-shadow: inset 0 0 16px rgba(224, 71, 95, .3);
}
body.tp .tpt-cell.hl-capture .tpt-hl::before {
  content: ''; width: 52%; height: 52%; border-radius: 50%;
  border: 2px solid var(--blood);
  outline: 1px solid rgba(224, 71, 95, .4); outline-offset: 4px;
  animation: tpt-hl-pulse .9s ease-in-out infinite;
}
body.tp .tpt-cell.hl-revive {
  border-color: var(--soul);
  box-shadow: inset 0 0 18px rgba(157, 123, 255, .35);
}
/* 敵の王を討てるマス（決着の一手）。実プレイは絵柄の字を置いているが規約で使えないので、
   金の菱形（王冠の宝珠）と金枠・赤い外光で「王を落とす一手」を示す。 */
body.tp .tpt-cell.hl-capture-king {
  border-color: var(--gold);
  box-shadow: inset 0 0 22px rgba(216, 178, 90, .4), 0 0 18px rgba(224, 71, 95, .35);
}
body.tp .tpt-cell.hl-capture-king .tpt-hl::before { border-color: var(--gold); outline-color: rgba(216, 178, 90, .45); }
body.tp .tpt-cell.hl-capture-king .tpt-hl::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  width: 30%; height: 30%;
  transform: translate(-50%, -50%);
  background: var(--gold);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  filter: drop-shadow(0 0 8px rgba(216, 178, 90, .9));
  animation: tpt-pulse .8s ease-in-out infinite;
}
/* 次にタップすべきマス（台本の期待どおりの移動先）。金は他の意味で埋まっているので白〜シアン。 */
body.tp .tpt-cell.cue {
  border-color: var(--tpt-cue);
  box-shadow: inset 0 0 0 2px var(--tpt-cue-soft), inset 0 0 20px rgba(143, 233, 255, .26);
}
/* 護られていて入れないマス（身代わりゴーレムが立っている間の王）。
   図解の「できない」（.tpt-dia-mark.m-blocked）とまったく同じ灰の禁止標識を盤にも出す
   ── 記号の意味は置き場所が変わっても1つ（設計ガイドライン §5.5・§6.1・決定7）。
   これが無いと、相手が王のマスへ入れずに脇へ逸れたとき「勝手に逸れた」ようにしか見えない
   （第12章はその1点が持ち帰りなのに、盤には印が1つも無かった）。
   印は駒より前面・円盤より外側（図解と同じ鉄則。円盤 62%・王 70% なので 78% を下限）。
   最初に 62% で置いたところ、王の円盤（70%）の下に完全に隠れて1ピクセルも見えなかった
   ── 図解で 22 か所を不可視にしたのとまったく同じ間違い（図解監査 C-1）。 */
body.tp .tpt-cell.hl-blocked .tpt-hl { z-index: 3; }
body.tp .tpt-cell.hl-blocked .tpt-hl::before {
  content: '';
  width: 78%; height: 78%; border-radius: 50%;
  border: 2px solid var(--tp-text-mute);
  background:
    linear-gradient(45deg, transparent 46%, var(--tp-text-mute) 46%, var(--tp-text-mute) 54%, transparent 54%),
    rgba(10, 8, 18, .42);
}
@keyframes tpt-pulse { 50% { transform: translate(-50%, -50%) scale(1.14); opacity: .7; } }
@keyframes tpt-hl-pulse { 50% { transform: scale(1.14); opacity: .75; } }

body.tp .tpt-pieces { position: absolute; inset: 0; pointer-events: none; }

body.tp .tpt-piece {
  position: absolute; left: 0; top: 0;
  width: var(--tpt-cell); height: var(--tpt-cell);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  pointer-events: auto; cursor: pointer;
  transition: transform .34s cubic-bezier(.22, 1.2, .36, 1);
}
/* 円盤の寸法・影は style.css の .piece .p-disc と同じ（62%・王は 70%）。 */
body.tp .tpt-disc {
  width: 62%; height: 62%;
  border-radius: 50%;
  display: grid; place-items: center;
  position: relative;
  background: radial-gradient(circle at 38% 30%, #241b40, #120d22 70%);
  border: 2px solid var(--line-lit);
  box-shadow: 0 4px 10px rgba(0, 0, 0, .65), inset 0 1px 1px rgba(234, 225, 205, .1);
  transition: border-color .2s, box-shadow .2s;
}
body.tp .tpt-piece svg { width: 68%; height: 68%; }
body.tp .tpt-piece .bone { fill: currentColor; }
body.tp .tpt-piece .pit { fill: var(--ink); }
body.tp .tpt-piece .crown { fill: var(--gold); }
/* 名札は style.css の .piece .p-name と同じ字（マス比 .105・700・字間 .04em・影付き）。
   ただし下限だけは残す: チュートリアルは手札や語りに高さを譲るぶんマスが 28px まで縮むので、
   比率そのままだと 3px になって読めない。長い名前（身代わりゴーレムの屍）は
   幅をマス1つぶんで打ち切って盤の外枠を越えさせない。 */
body.tp .tpt-nm {
  font-size: clamp(9px, calc(var(--tpt-cell) * .105), 12px);
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--bone-dim);
  text-shadow: 0 1px 2px #000;
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.tp .tpt-piece.mine { color: var(--venom); }
body.tp .tpt-piece.mine .tpt-disc { border-color: rgba(140, 235, 90, .5); }
body.tp .tpt-piece.theirs { color: var(--blood); }
body.tp .tpt-piece.theirs .tpt-disc { border-color: rgba(224, 71, 95, .5); }
body.tp .tpt-piece.corpse { color: var(--soul); opacity: .92; }
body.tp .tpt-piece.corpse .tpt-disc {
  border-style: dashed; border-color: var(--soul-deep);
  background: radial-gradient(circle at 50% 42%, #1c1433, #0e0a1c 75%);
}
body.tp .tpt-piece.corpse .tpt-nm { color: var(--soul-deep); }
/* 正体の割れた屍: 倒れた眷属の姿を淡く見せ、死のしるしを重ねる（style.css と同じ値） */
body.tp .tpt-piece.corpse.known .tpt-disc svg { opacity: .66; filter: grayscale(.4) brightness(.9); }
body.tp .tpt-piece.king .tpt-disc {
  width: 70%; height: 70%;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .7), 0 0 16px rgba(216, 178, 90, .12), inset 0 1px 1px rgba(234, 225, 205, .12);
}
body.tp .tpt-piece.promoted .tpt-nm { color: var(--gold); }
body.tp .tpt-piece.promoted .tpt-disc {
  border-color: var(--gold-deep);
  box-shadow: 0 4px 10px rgba(0, 0, 0, .65), 0 0 12px rgba(216, 178, 90, .28);
}
/* 「選択中」と「カードの対象候補」は実プレイと同じ色分けにする。
   どちらも金にしていた頃は、自分で選んだ駒とカードが狙える駒が見分けられなかった。
   選択中 = 緑（style.css の .piece.selected）／対象候補 = 金＋脈動（.piece.card-target）。 */
body.tp .tpt-piece.sel { z-index: 5; }
body.tp .tpt-piece.sel .tpt-disc {
  border-color: var(--venom);
  box-shadow: 0 0 0 2px rgba(140, 235, 90, .35), 0 0 22px var(--venom-glow);
}
body.tp .tpt-piece.cand .tpt-disc {
  border-color: var(--gold);
  box-shadow: 0 0 0 2px rgba(216, 178, 90, .4), 0 0 18px rgba(216, 178, 90, .5);
  animation: tpt-hl-pulse 1.1s ease-in-out infinite;
}
/* カード効果が乗っている駒（style.css の .piece.buffed と同じ印） */
body.tp .tpt-piece.buffed::after {
  content: '◈';
  position: absolute; top: 6%; right: 12%;
  font-size: calc(var(--tpt-cell) * .17);
  color: var(--gold);
  text-shadow: 0 0 8px rgba(216, 178, 90, .8);
  pointer-events: none;
}
body.tp .tpt-piece.focus::before,
body.tp .tpt-piece.cue::before,
body.tp .tpt-piece.tgt::before {
  content: '';
  position: absolute; inset: 6%;
  border: 2px solid var(--gold);
  border-radius: 50%;
  animation: tpt-ring 1.4s ease-in-out infinite;
  pointer-events: none;
}
/* 次にタップすべきコマ。何をすればよいか迷わせないための最優先の合図。
   金は「カードの対象・進化・王を討てるマス・直前の手」で埋まっているので、合図だけ別の色にする。 */
body.tp .tpt-piece.cue::before {
  inset: 2%;
  border-width: 3px;
  border-color: var(--tpt-cue);
  box-shadow: 0 0 14px var(--tpt-cue-soft), inset 0 0 12px rgba(143, 233, 255, .3);
}
/* 移動先として選べるマスに立っているコマ（＝倒せる敵）。マスの光はコマに隠れるため。 */
body.tp .tpt-piece.tgt::before {
  inset: 2%;
  border-color: var(--blood);
  box-shadow: 0 0 14px rgba(224, 71, 95, .5);
}
@keyframes tpt-ring {
  0%, 100% { transform: scale(1); opacity: .85; }
  50% { transform: scale(1.1); opacity: .35; }
}

/* 選んだ墓場が盤に据えられる演出。
   .tpt-piece 自身の transform は位置決めに使っているので、動かすのは中の円盤だけ。 */
body.tp .tpt-piece.placing {
  animation: tpt-place-in .46s ease backwards;
  animation-delay: calc(var(--pi, 0) * .14s);
}
body.tp .tpt-piece.placing .tpt-disc,
body.tp .tpt-piece.placing .tpt-nm {
  animation: tpt-place-drop .46s cubic-bezier(.22, 1.2, .36, 1) backwards;
  animation-delay: calc(var(--pi, 0) * .14s);
}
@keyframes tpt-place-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes tpt-place-drop {
  0% { transform: translateY(-46%) scale(.35); filter: brightness(2.4); }
  70% { filter: brightness(1.35); }
  100% { transform: none; filter: none; }
}

/* 正体が割れた屍に重ねる死のしるし。実プレイ（style.css の .corpse-mark）と同じ
   「右下のバッジ」にする。素の SVG を右上に置いていた頃は、同じ盤の同じ状態なのに
   実戦と印の位置も形も違って見えていた。 */
body.tp .tpt-mark {
  position: absolute; right: -9%; bottom: -9%;
  width: 44%; height: 44%;
  display: grid; place-items: center;
  color: #d0bdee;
  background: radial-gradient(circle at 40% 30%, #271c44, #0f0a1d 78%);
  border: 1px solid var(--soul-deep);
  border-radius: 50%;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .65);
}
body.tp .tpt-mark svg { width: 76%; height: 76%; filter: drop-shadow(0 0 4px rgba(157, 123, 255, .35)); }

/* 進める方向の矢印。姿・濃さは style.css の .piece .dir と同じ値に揃える。
   置く場所だけは違う: --dx / --dy に**実際のオフセット**が入るので（pieceface.js）、
   矢印は届く距離に比例して外側へ出る。1マスぶんは 0.395 セルで実プレイと同じ位置。
   遠距離を一律 0.485 セルで描いていた頃は、グールの1マス目と2マス目が重なって潰れ、
   《飛躍》（8方向×2マス）と《アンデッド強化薬》（8方向×1マス）が同じ絵になっていた
   （図解監査 #24・C-4）。距離を持たない矢印は「どこまで届くのか」を描けない。 */
body.tp .tpt-dir {
  position: absolute; left: 50%; top: 50%;
  width: calc(var(--tpt-cell) * .115);
  height: calc(var(--tpt-cell) * .1);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
  background: currentColor;
  opacity: .42;
  pointer-events: none;
  transform: translate(-50%, -50%)
    translate(calc(var(--dx) * var(--tpt-cell) * .395), calc(var(--dy) * var(--tpt-cell) * .395))
    rotate(var(--ang));
}
/* 2マス以上先へ届く矢印は細く薄くする（位置は上の式がそのまま距離を持つので触らない）。 */
body.tp .tpt-dir.far {
  opacity: .26;
  width: calc(var(--tpt-cell) * .09);
}
/* 選んだ駒・対象候補の矢印は濃く（可動域を読ませたい場面なので）。屍は蘇生後の予告。 */
body.tp .tpt-piece.sel .tpt-dir,
body.tp .tpt-piece.cand .tpt-dir { opacity: .85; }
body.tp .tpt-piece.corpse .tpt-dir { opacity: .42; }
body.tp .tpt-piece.corpse.sel .tpt-dir,
body.tp .tpt-piece.corpse.cand .tpt-dir { opacity: .75; }

/* ── 手札・カード拡大・山札えらび・操作ボタン ───────────────
   札の姿（.card / .zoom-card / .pick-card）は実プレイの style.css がそのまま持っている。
   ここでは「チュートリアルの器」と「次に触る場所の合図」だけを足す。
   札の寸法・種別色・使えない札の灰色を書き写さないこと（実プレイと二重定義になる）。 */

/* 手札の段。高さは board.js の layoutFan() が「札の実寸 × 縮小率」から**実測して書く**。
   ここの値は最初の1フレームぶんの控え（style.css が .card の寸法を変えても追従できるよう、
   120/112/110 といった札の高さを CSS へ書き写さない）。扇は下端を軸に置く。
   章の途中で空になっても JS 側が段を畳まないので、盤の高さは動かない。 */
body.tp .tpt-hand {
  flex: none;
  height: var(--tpt-hand-h, 92px);
  display: flex; justify-content: center; align-items: flex-end;
  overflow: visible; /* 持ち上げた札（.card.raised）が切れないように */
}
/* 扇ごと縮小する。実プレイの .card は寸法も文字も style.css が決めているので、
   幅・高さだけを上書きすると効果文（.c-text の min-height:32px）がはみ出す。
   縮小なら実プレイと 1:1 で同じ絵のまま小さくなり、そのぶんの高さが盤に回る。
   既定でも .72 まで縮めてある ── 実機で「カードを使う場面の盤が小さすぎる」と
   指摘されたのがここで、札は小さくても4枚あることが読めれば足りる。
   使える幅の割り戻し（clientWidth / scale）は board.js の layoutFan() が対になっている。 */
body.tp .tpt-arena { --tpt-hand-s: .72; }
body.tp .tpt-fan {
  flex: 1 1 auto; min-width: 0;
  display: flex; justify-content: center; align-items: flex-end;
  padding-bottom: 4px;
  transform: scale(var(--tpt-hand-s, 1));
  transform-origin: 50% 100%;
}
/* 効果文の中のカード参照（実プレイではボタンで別の札を開ける）。チュートリアルでは
   開く先が無いので、見た目はそのままに、タップは下の札へ通す。 */
body.tp .tpt-fan .card-link,
body.tp .tpt-veil .card-link { pointer-events: none; }

/* 次に触るべき札・ボタンの合図。盤の合図と同じ色（白〜シアン）で統一し、
   金＝ゲーム内の意味（対象・進化・王を討てるマス）と混ざらないようにする。
   ここで動かすのは box-shadow だけ。手札の .card は扇の回転を transform で持っているので、
   transform を動かす @keyframes を当てると扇が壊れる（tests/tutorial.test.js の規則）。 */
body.tp .tpt-fan .card.cue,
body.tp .tpt-veil .pick-card.cue,
body.tp .tpt-use.cue,
body.tp .tpt-pickok.cue,
body.tp .tpt-act.cue {
  border-color: var(--tpt-cue);
  box-shadow: 0 0 0 2px rgba(143, 233, 255, .35), 0 0 18px rgba(143, 233, 255, .35);
  animation: tpt-cue-glow 1.5s ease-in-out infinite;
}
@keyframes tpt-cue-glow {
  50% { box-shadow: 0 0 0 3px rgba(143, 233, 255, .5), 0 0 26px rgba(143, 233, 255, .5); }
}

/* 実行ボタンの隣に取り消し（やめる）を並べる。実プレイのコンテキストバーと同じ並び。
   【最重要】ボタンが無いときも高さを空けたままにする。出し入れで段の高さが変わると、
   fit() が測り直して盤が縮み、蘇生の確認を出した瞬間に盤がガタッと上へずれていた。 */
body.tp .tpt-actbar {
  flex: none;
  height: 44px;
  display: flex; align-items: center; justify-content: center; gap: var(--tp-2);
}
body.tp .tpt-act, body.tp .tpt-cancel {
  height: 40px; min-height: 0;
  min-width: 0; max-width: 100%;
  white-space: nowrap; overflow: hidden; /* 長い文言でも2行にせず、段の高さを守る */
}
body.tp .tpt-act { padding: 0 var(--tp-4); font-size: 14px; }
body.tp .tpt-act[hidden] { display: none !important; }
body.tp .tpt-cancel {
  padding: 0 var(--tp-3); font-size: 13px;
  background: var(--tp-surface-2);
  color: var(--tp-text-dim);
}
body.tp .tpt-cancel[hidden] { display: none !important; }

/* ── 覆い（カード拡大・山札えらび）─────────────────
   実プレイの #modal-card / #modal-picker と同じ手順を、盤の上だけで再現する器。
   絶対配置なので、開閉しても盤・手札・ボタンに配られる高さは 1px も動かない。
   語り（.tpt-say）は覆わない ── 「この札を選びましょう」を読みながら操作できるように。 */
/* z-index は選択中のコマ（.tpt-piece.sel = 5）と持ち上げた札（インラインで最大 10 前後）より
   上に置く。同じ重なり文脈にいるので、低いと札やコマが覆いを突き抜けて見える。 */
body.tp .tpt-veil { position: absolute; inset: 0; z-index: 20; display: grid; }
body.tp .tpt-veil[hidden] { display: none !important; }
body.tp .tpt-veil-back { position: absolute; inset: 0; background: rgba(4, 2, 10, .82); }
/* 札は縦に詰まっても読めるようにスクロールさせ、《使用する》《戻る》は必ず見える位置に残す。 */
body.tp .tpt-veil-in {
  position: relative;
  min-height: 0;
  display: grid; grid-template-rows: minmax(0, 1fr) auto;
  justify-items: center; align-content: center;
  gap: var(--tp-3);
  padding: var(--tp-2);
}
body.tp .tpt-veil-body {
  min-height: 0; max-width: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--tp-2);
  overflow-y: auto;
}
body.tp .tpt-veil-act { display: flex; justify-content: center; gap: var(--tp-2); flex-wrap: wrap; }
body.tp .tpt-veil-act .tp-btn { min-height: 42px; padding: 0 var(--tp-5); font-size: 14px; }
body.tp .tpt-zback { background: var(--tp-surface-2); color: var(--tp-text-dim); }
/* 山札えらびの見出しと説明。実プレイのピッカー（#picker-title / #picker-desc）と同じ2行組み。 */
body.tp .tpt-pick-title {
  font-family: var(--f-display);
  font-size: 15px; font-weight: 700; letter-spacing: .04em;
  color: var(--tp-accent);
  text-align: center;
}
body.tp .tpt-pick-lead {
  font-size: 12.5px; color: var(--tp-text-dim); letter-spacing: .04em;
  text-align: center;
}
body.tp .tpt-pick-row {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start;
  gap: var(--tp-2);
  max-width: 100%;
}
/* 山札えらびの札（.pick-card）の縦長比・行の切り方は style.css の .pick-card が持つ。
   以前はここだけで比を固定していたが、実プレイ側も同じ比に揃えたので二重に持たない。 */

/* ── 語り（吹き出し）───────────────────────────
   盤とは「別の行」に置く。重ねないので、盤面が語りに隠れることが構造的に起こらない（要件G4）。 */

body.tp .tpt-say {
  position: relative;
  min-height: 88px;
  display: flex; align-items: center;
  padding: var(--tp-3) var(--tp-4);
  border: 1px solid var(--tp-line-lit);
  border-radius: var(--tp-r);
  background: var(--tp-surface-2);
  box-shadow: var(--tp-shadow);
}
body.tp .tpt-say::before {
  content: '';
  position: absolute; top: -7px; left: 28px;
  width: 12px; height: 12px;
  transform: rotate(45deg);
  border-left: 1px solid var(--tp-line-lit);
  border-top: 1px solid var(--tp-line-lit);
  background: var(--tp-surface-2);
}
body.tp .tpt-say-t { font-size: 15px; line-height: 1.75; color: #f7f2e6; }
/* 要点は金色の太字で立てる（文言側が <b> を付ける） */
body.tp .tpt-say-t b,
body.tp .tpt-ex-p b { color: var(--tp-accent); font-weight: 700; }
body.tp .tpt-say.shake { animation: tpt-shake .34s ease; }
@keyframes tpt-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

body.tp .tpt-foot { display: flex; flex-direction: column; gap: var(--tp-2); }
/* 中央寄せは外側の flex で行い、文章は必ず内側の1要素にまとめる。
   文章を直接 flex の子にすると、強調の <b> がそれぞれ flex アイテムになって
   「手札の / 飛躍 / →…」のように分断されて折り返す。 */
/* ヒントの行は描かない。操作の指示は語り（.tpt-say）と盤・札の合図（.cue）だけで足り、
   その 34px ＋ 余白を手札（実プレイと同じ 120px の札）へ回している。
   DOM 自体は player.js 側で外す方針だが、万一戻っても盤の高さを食わないよう、
   ここでも畳んでおく（この規約を CSS 側にも残す意味を兼ねる）。 */
body.tp .tpt-hint { display: none !important; }

/* 「1つ前へ戻る」＋「次へ」の行。戻るは小さなアイコンだけにして、
   主役（次へ）の存在感を下げない。操作ステップでは次へが消え、戻るだけが残る。 */
body.tp .tpt-nav { display: flex; align-items: stretch; gap: var(--tp-2); }
body.tp .tpt-back {
  flex: none;
  width: var(--tp-tap); min-height: var(--tp-tap);
  border: 1px solid var(--tp-line-lit);
  background: var(--tp-surface-2);
  color: var(--tp-text-dim);
}
body.tp .tpt-back[hidden] { display: none !important; }
body.tp .tpt-nav .tpt-next { flex: 1 1 auto; }
/* 誤操作のときは語り（.tpt-say）が揺れる。ヒントの行は描かないので、そちらの点滅は持たない。 */

/* ── 解説モーダル ───────────────────────────── */

/* 見出し・本文・図解が上下の余白を分け合う形にする。
   以前は本文を下寄せ（flex-end）にしていたため、見出しの下に 250px 以上の空白ができ、
   文章と図解だけが画面の下半分に固まっていた（実機の指摘）。
   本文は自分の帯の中央に置き、図解はそれより少し上寄りの位置から下を占める。 */
body.tp .tpt-explain {
  position: absolute; inset: 0;
  z-index: var(--tp-z-modal);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* 図解の段は「その章の図が要る高さ」まで（上限 --tpt-dia-cap）。diagram.js が --tpt-dia-h に
     要る高さを書き込む。46% を固定で与えていた頃は、行数の少ない図で器の 7割が空いたまま
     残っていた（図解監査 #25）── マスの寸法は幅で頭打ちになるので、余った高さは図解では
     使えず、本文と余白へ返した方が読みやすい。
     上限を変数にしてあるのは、実物大の札を出す章だけ広げるため（diagram.js が上書きする）。
     既定のままだと札に押されて盤が潰れる。 */
  --tpt-dia-cap: 56%;
  /* 実物大の札を出す章だけの上限（diagram.js が --tpt-dia-cap にこれを流し込む）。
     札と盤を同じ段に収めるので既定より広く取る。値を CSS に置くのは、低い端末で
     まとめて詰められるようにするため（JS に数字を書くと @media から動かせない）。 */
  --tpt-dia-cap-hero: 64%;
  /* 図解の段は「縮まない側」にする。以前は minmax(0, …) だったので、本文が長いパネルだけ
     段が押しつぶされ、**同じ章なのにマスの大きさが変わって**いた（320×568 の実測で
     第7章 41px→34px・第9章 46px→38px。決定6「章の中では大きさを統一」に反する）。
     縮む側は本文（.tpt-ex-text は .tp-scroll なので中でスクロールできる）。 */
  grid-template-rows: auto minmax(0, auto) min(var(--tpt-dia-cap), var(--tpt-dia-h, 100%)) auto;
  align-content: space-evenly; /* 余った高さを見出し・本文・図解・操作で等分する */
  gap: var(--tp-2);
  /* 上余白は共通ヘッダーの高さ（--tp-head-h）から取る。数字を書き写すと、ヘッダーの作りが
     変わったときにここだけ取り残される。実際、ヘッダーを2段（44+54px）にした際に 52px の
     直書きが残り、見出しがヘッダーの裏へ 5〜15px 潜っていた。
     .tpt-explain は .tp-screen の padding ではなく inset:0 で全画面に広がるため、
     .tp-screen の上余白は効かない。自前でヘッダーぶんを空ける必要がある。 */
  padding: calc(var(--tp-head-h) + var(--tp-safe-t) + var(--tp-3)) var(--tp-4) max(var(--tp-3), var(--tp-safe-b));
  background: linear-gradient(180deg, #120d24 0%, #0b0817 100%);
}
body.tp .tpt-explain[hidden] { display: none !important; }
body.tp .tpt-ex-title { color: var(--tp-accent); }
body.tp .tpt-ex-text {
  display: flex; flex-direction: column; justify-content: center;
  gap: var(--tp-2);
  padding: var(--tp-2) 0;
}
body.tp .tpt-ex-p {
  font-size: 16px; line-height: 1.85; color: #f7f2e6;
  animation: tpt-fade .32s ease;
}
@keyframes tpt-fade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* 図解の置き場。枠そのものは .tpt-dia::before が「格子の実寸ぶん」だけ描く（下記）。
   ここへ枠を描いていた頃は、行数の少ない図解（4×1・3×1）で枠だけが大きく残り、
   上下に 120px 以上の空白ができて章ごと・パネルごとに大きさが跳ねて見えていた。
   padding は残すこと（消すと fit() が使える面積が広がり、枠が固定トラックからはみ出して切れる）。 */
body.tp .tpt-ex-dia {
  min-height: 0;
  padding: var(--tp-3);
  overflow: hidden;
}
body.tp .tpt-ex-foot {
  display: flex; align-items: center; gap: var(--tp-3);
}
body.tp .tpt-ex-back {
  flex: none;
  width: var(--tp-tap); min-height: var(--tp-tap);
  border: 1px solid var(--tp-line-lit);
  background: var(--tp-surface-2);
  color: var(--tp-text-dim);
}
body.tp .tpt-ex-back[hidden] { display: none !important; }
body.tp .tpt-ex-count { font-family: var(--f-num), var(--f-body); letter-spacing: .1em; margin-right: auto; }
body.tp .tpt-ex-next { min-width: 120px; }

/* ── 札の姿（.cardtile）──────────────────────
   図解の中に置く小さな札。以前は角丸のほぼ正方形（.tpt-dcard 比 0.90）で描いていたため、
   実プレイの札（.card 88×120 ＝ 比 0.733）と姿が食い違い、「カードだと分からない」
   という指摘になった。ここでは .card と同じ比・同じ意匠のまま、効果文だけを落とす。
   比率は cardface.js の CARD_RATIO と同じ値（tests/cardface.test.js が突き合わせる）。
   マークアップは cardface.js の cardTileHtml が唯一の出典。 */

body.tp .cardtile {
  position: relative;
  box-sizing: border-box;
  /* 大きさは「幅」で与える（高さで与えない）。図解の札はマス（grid のセル）の中に
     置かれるが、grid の子の height:% は Chromium では解決されず、代わりに中身の
     最小幅から高さが決まって札がマスからはみ出した。幅は必ず解決する。
     66% ＝ マスの高さの 90% にあたる幅（66 × 120 / 88 = 90）。 */
  width: 66%;
  aspect-ratio: 88 / 120;
  max-height: 100%;
  display: flex; flex-direction: column; align-items: center;
  /* 上は種別バッジ（.ct-kind）が枠にまたがるぶんだけ空ける */
  padding: 13% 7% 7%;
  border: 1px solid var(--line-lit);
  border-radius: var(--tp-r-sm);
  /* 実プレイの .card と同じ地の色（style.css の .card と同じ2枚重ねのグラデーション）。 */
  background:
    linear-gradient(168deg, rgba(234, 225, 205, .07), transparent 40%),
    linear-gradient(180deg, #1e1636, #130d24);
  box-shadow: 0 3px 10px rgba(0, 0, 0, .55);
  color: var(--card-hue, var(--soul));
}
/* 二重罫（.card::before と同じ作り）。これがあると小さくても「札」に見える。 */
body.tp .cardtile::before {
  content: '';
  position: absolute; inset: 2px;
  border: 1px solid rgba(234, 225, 205, .12);
  border-radius: 3px;
  pointer-events: none;
}
/* 種別色。style.css の .card.kind-* は要素名込みのセレクタなので .cardtile には届かない。
   色の値そのものは書かず、実プレイと同じ変数（--venom / --soul / --gold / --blood）を当てる。
   組み合わせがずれていないことは tests/cardface.test.js が style.css 側と突き合わせる。 */
body.tp .cardtile.kind-move { --card-hue: var(--venom); }
body.tp .cardtile.kind-revive { --card-hue: var(--soul); }
body.tp .cardtile.kind-tutor { --card-hue: var(--gold); }
body.tp .cardtile.kind-sorcery { --card-hue: var(--blood); }
body.tp .cardtile.kind-special { --card-hue: var(--blood); }
/* 上辺の種別バッジ（.card の .c-kind と同じ位置・同じ意匠） */
body.tp .ct-kind {
  position: absolute; top: -6px; left: 50%; transform: translateX(-50%);
  font-size: clamp(6px, calc(var(--dia-cell, 44px) * .1), 9px);
  line-height: 1.6; letter-spacing: .1em; font-weight: 700;
  color: var(--ink); background: var(--card-hue, var(--soul));
  border-radius: 2px; padding: 0 4px;
  white-space: nowrap;
}
/* 名前。長い日本語（アンデッド強化薬・ソウルパーティ）は札の中で折り返す。
   折り返し方は実プレイの札（style.css の .card .c-name.multi）とまったく同じ規則にする:
   語の途中では折らず（keep-all）、1語で幅に収まらない時だけ割る（break-word）。
   日本語の折り返し位置は cardface.js の cardNameMarkup が <wbr> で示す。
   overflow-wrap: anywhere にしていた頃は英語が "Shapeshif／t Potion" と語中で折れていた。 */
body.tp .ct-name {
  width: 100%; text-align: center;
  font-size: clamp(7px, calc(var(--dia-cell, 44px) * .13), 12px);
  line-height: 1.1; font-weight: 700; letter-spacing: .02em;
  color: var(--tp-text);
  padding-bottom: 3px;
  border-bottom: 1px solid var(--line);
  word-break: keep-all; overflow-wrap: break-word;
}
body.tp .ct-art { flex: 1; min-height: 0; width: 100%; display: grid; place-items: center; }
body.tp .ct-art svg { width: 64%; height: 64%; filter: drop-shadow(0 0 5px currentColor); opacity: .95; }
body.tp .cardtile.dim { opacity: .4; }
/* 裏向き（山札の上・伏せた札）。表と同じ比・同じ枠のまま絵だけ裏面にする。 */
body.tp .cardtile.back {
  display: grid; place-items: center;
  padding: 6%;
  color: var(--soul-deep);
  background: var(--tp-surface);
}
body.tp .cardtile.back svg { width: 74%; height: 74%; }

/* ── 図解 ────────────────────────────────── */

/* --dia-band は「格子の下に取る手札の帯」の高さ（diagram.js が実測から書き込む）。
   帯を持たない図解では 0 なので、下の calc はどれも今までの式に戻る。 */
body.tp .tpt-dia {
  position: relative; width: 100%; height: 100%;
  --dia-cell: 44px; --dia-band: 0px; --dia-tag: 0px; --dia-hero: 0px;
  /* 枠の大きさの基準。既定はこの図解自身の格子で、章の中で大きさを揃えるときだけ
     diagram.js が「章でいちばん大きい図」の値を書き込む（設計ガイドライン §5.2・決定6）。 */
  --dia-box-cols: var(--dia-cols); --dia-box-rows: var(--dia-rows);
}
/* 枠は「基準の格子 ＋ 手札の帯 ＋ 余白」で描く。--dia-cell / --dia-box-cols / --dia-box-rows は
   diagram.js が .tpt-dia 自身へ書き込むので、行数の少ない章では枠も小さくなり、中身が浮かなくなる。
   余白は .tpt-ex-dia の padding と同じ値に揃える（大きくすると枠がトラックからはみ出す）。 */
body.tp .tpt-dia::before {
  content: '';
  /* 名札（--dia-tag）は枠の外へぶら下がるので、その半分だけ枠ごと上へ寄せる。
     寄せないと、狭い画面で名札の下端が器（.tpt-ex-dia の overflow:hidden）に切られる。 */
  position: absolute; left: 50%; top: calc(50% - var(--dia-tag) / 2 + var(--dia-hero) / 2);
  width: calc(var(--dia-cell) * var(--dia-box-cols) + var(--tp-3) * 2);
  height: calc(var(--dia-cell) * var(--dia-box-rows) + var(--dia-band) + var(--tp-3) * 2);
  transform: translate(-50%, -50%);
  border: 1px solid var(--tp-line);
  border-radius: var(--tp-r);
  background: rgba(0, 0, 0, .28);
}
/* 格子は「格子＋帯＋名札」の合計を中央に置いたときの上半分に来る
   ＝中心が「手札の帯」と「名札」の半分ずつ上へ寄る。 */
body.tp .tpt-dia-cells,
body.tp .tpt-dia-marks,
body.tp .tpt-dia-items {
  position: absolute; left: 50%; top: calc(50% - var(--dia-band) / 2 - var(--dia-tag) / 2 + var(--dia-hero) / 2);
  width: calc(var(--dia-cell) * var(--dia-cols));
  height: calc(var(--dia-cell) * var(--dia-rows));
  transform: translate(-50%, -50%);
}
body.tp .tpt-dia-cells {
  display: grid;
  grid-template-columns: repeat(var(--dia-cols), var(--dia-cell));
  grid-template-rows: repeat(var(--dia-rows), var(--dia-cell));
}
body.tp .tpt-dia-cells i {
  border: 1px solid var(--line);
  background: linear-gradient(180deg, #14102a, #0d0a1c);
}
body.tp .tpt-dia-marks, body.tp .tpt-dia-items { pointer-events: none; }
/* 重ね順: マス → 駒 → 印。印を駒より後ろに置いていたため、駒が乗ったマスの
   「討てる」「できない」が 22 か所で完全に不可視だった（図解監査 C-1）。
   DOM の並びでも印が後だが、順序だけに頼ると並べ替えで黙って壊れるので数でも固定する。 */
body.tp .tpt-dia-cells { z-index: 0; }
body.tp .tpt-dia-items { z-index: 1; }
body.tp .tpt-dia-marks { z-index: 2; }
/* 札（実物大の hero と手札の帯）は駒より前面（z-index: 3）。指定はそれぞれの規則に置く。
   可動域の矢印は「格子の端で切らない」── 盤（board.js）も同じ pieceface を通すので、
   端の駒は盤の外を指す矢印を出す。図解でだけ切ると、同じ絵の読み方が2通りになる。
   そのぶん、格子のすぐ下に手札の帯が来るパネルでは、はみ出した矢印が札の上に乗っていた
   （実測: cardUse の《飛躍》の下向き3本が手札の札へ 31px）。矢印を消すのではなく札を前へ
   出して、矢印が札の**裏へ**回るようにする。印（marks）と札は場所が重ならないので、
   この2つの上下は問わない。 */

/* マークは left/top で置く（JS がインラインで指定）。
   ここで transform を含む @keyframes（tpt-fade など）を当てると、再生中だけ位置指定が
   捨てられてマークが左上のマスへ飛ぶ。実際にそれが「左上に緑の点がチラつく」不具合だった。
   出現の演出は opacity だけを動かす tpt-appear を使う。 */
body.tp .tpt-dia-mark {
  position: absolute;
  width: var(--dia-cell); height: var(--dia-cell);
  animation: tpt-appear .25s ease;
}
@keyframes tpt-appear { from { opacity: 0; } to { opacity: 1; } }
body.tp .tpt-dia-mark::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  animation: tpt-pulse 1.2s ease-in-out infinite;
}
/* 印の意味と寸法は shared/tutorial/diagrams.js の MARK_KINDS が唯一の出典。
   駒に重なりうる印（capture / swap / range / blocked / target / evolved / victory）は、
   円盤（62%・王は 70%）より外側でないと駒の下に隠れて見えない ── 下限は MARK_MIN_ON_PIECE
   の 78%（tests/tutorial.test.js が CSS の実寸と突き合わせる）。
   中央に小さく描く印（m-move の点）は**空きマス専用**。 */
body.tp .tpt-dia-mark.m-move::after {
  width: 26%; height: 26%;
  background: radial-gradient(circle, var(--venom-glow), transparent 70%);
  border: 2px solid var(--venom);
}
body.tp .tpt-dia-mark.m-capture::after {
  width: 80%; height: 80%;
  border: 2px solid var(--blood);
  box-shadow: 0 0 12px rgba(224, 71, 95, .45);
}
body.tp .tpt-dia-mark.m-swap::after {
  width: 82%; height: 82%;
  border: 2px dashed var(--soul);
  box-shadow: 0 0 12px var(--soul-glow);
}
body.tp .tpt-dia-mark.m-range::after {
  width: 84%; height: 84%;
  border: 1px dashed rgba(177, 140, 255, .85);
  background: rgba(107, 79, 192, .16);
  border-radius: var(--tp-r-sm);
}
/* できない。「進めない」を不在（何も起きない）で表さないための印なので、
   駒の上でもはっきり読める大きさの禁止標識（丸に斜線）にする。 */
body.tp .tpt-dia-mark.m-blocked::after {
  width: 80%; height: 80%;
  border: 2px solid var(--tp-text-mute);
  background:
    linear-gradient(45deg, transparent 46%, var(--tp-text-mute) 46%, var(--tp-text-mute) 54%, transparent 54%),
    rgba(10, 8, 18, .5);
}
/* 札の効果を受ける対象にえらばれたコマ・屍。盤の .tpt-piece.cand と同じ「金＋脈動」。
   これが無かったため《シャドウ化》《変身薬》は「盤の駒が無言で入れ替わる」絵になっていた。 */
body.tp .tpt-dia-mark.m-target::after {
  width: 78%; height: 78%;
  border: 2px solid var(--gold);
  box-shadow: 0 0 0 2px rgba(216, 178, 90, .3), 0 0 16px rgba(216, 178, 90, .5);
}
/* 進化した。盤の .tpt-piece.promoted と同じ金で、二重の輪にして「一段上がった」を表す。
   ここに m-range（紫の破線＝王の効果範囲）を流用していたのが記号の多義化の一因だった。 */
body.tp .tpt-dia-mark.m-evolved::after {
  width: 82%; height: 82%;
  border: 2px solid var(--gold);
  outline: 2px solid var(--gold-deep);
  outline-offset: 3px;
  /* 「対象にえらばれた」（m-target）と見分けるため、状態を示すこの印だけは脈動させない。
     色を増やさずに形と動きで分ける（設計ガイドライン §5.5）。 */
  animation: none;
}
/* 勝利条件の顔ぶれ。盤の .tpt-cell.hl-capture-king と同じ「金の枠＋赤い外光」。 */
body.tp .tpt-dia-mark.m-victory::after {
  width: 86%; height: 86%;
  border: 2px solid var(--gold);
  border-radius: var(--tp-r-sm);
  box-shadow: 0 0 16px rgba(224, 71, 95, .45), inset 0 0 14px rgba(216, 178, 90, .22);
}
/* 札の図解用: 選ばれた札 / 効果が発動した札。
   囲む相手が縦長の札（.cardtile）なので、枠も同じ比にしないと札の左右だけ空いて見える。 */
body.tp .tpt-dia-mark.m-pick::after {
  height: 100%; width: auto; aspect-ratio: 88 / 120;
  border: 2px dashed var(--tp-accent);
  border-radius: var(--tp-r-sm);
}
body.tp .tpt-dia-mark.m-cast::after {
  width: 150%; height: 150%;
  background: radial-gradient(circle, rgba(216, 178, 90, .38), transparent 68%);
  animation: tpt-pulse 1s ease-in-out infinite;
}

body.tp .tpt-dia-item {
  position: absolute; left: 0; top: 0;
  width: var(--dia-cell); height: var(--dia-cell);
  display: grid; place-items: center;
  transition: transform .42s cubic-bezier(.22, 1.2, .36, 1);
}
body.tp .tpt-dia-item.no-anim { transition: none; }
body.tp .tpt-dia-item[hidden] { display: none !important; }

/* 図解の駒は盤とまったく同じクラス（.tpt-piece / .tpt-disc / .tpt-nm / .tpt-mark / .tpt-dir）
   で描く。以前は図解専用の .tpt-dp を持っていたため、盤だけ直したときに図解が取り残され、
   死のしるし・王の円盤の拡大・進化スライムの絵・矢印が図解にだけ無い状態になっていた。
   位置は親（.tpt-dia-item）が transform で決めるので、絶対配置と当たり判定だけ打ち消す。 */
body.tp .tpt-dia .tpt-piece {
  position: relative;
  width: 100%; height: 100%;
  /* 置き場（.tpt-dia-item）は display:grid なので、駒は既定の min-width:auto ＝ min-content で
     下限が決まる。長い名札（英語の "Necromancer" は 9px でも 57px）はマス（狭い端末で 37〜47px）
     より広いので、width:100% を書いていても駒の箱がその幅まで押し広がり、隣のマスの名札と
     重なって読めなくなっていた（実測: 375/320 の英語で kingTake・dragonRear・golemGuard ほか）。
     0 にすると名札はマス幅で打ち切られる（.tpt-nm の overflow:hidden ＋ 省略記号）。
     盤（.tpt-piece は position:absolute）では起きないので、図解側だけの打ち消し。 */
  min-width: 0;
  cursor: default; pointer-events: none;
  transition: none;
}
/* 矢印の寸法は盤では --tpt-cell 基準。図解のマスは --dia-cell なので橋渡しする。
   図解のマスは最小 24px まで縮み、盤と同じ比率だと矢印が2〜3pxで潰れるので、
   図解のときだけ少し大きく・濃くして読めるようにする（盤側の値は変えない）。 */
body.tp .tpt-dia { --tpt-cell: var(--dia-cell); }
body.tp .tpt-dia .tpt-dir {
  width: calc(var(--dia-cell) * .16);
  height: calc(var(--dia-cell) * .14);
  opacity: .6;
}
/* 遠距離（グールの2マス目・飛躍）の矢印は盤と同じく細くする。
   .tpt-dir.far（幅 .09）と詳細度が並ぶため、上の .tpt-dia .tpt-dir が後勝ちして
   近接矢印と同じ太さになっていた ── 図解でだけ「2マス先」が近接と見分けられなかった。 */
body.tp .tpt-dia .tpt-dir.far {
  width: calc(var(--dia-cell) * .115);
  opacity: .38;
}

/* ── 図解の手札の帯 ─────────────────────────
   格子の下に、実プレイと同じ縦長の札を並べた「手札」を出す。
   カードを使う手順（4枚から1枚えらぶ →《使う》→ 使い切る）は、盤のマスに札を1枚
   置いただけでは伝わらない（実機の指摘）。高さ（--dia-band）と札の高さ（--dia-cardh）は
   diagram.js が実測から書き込む。 */
body.tp .tpt-dia-hand {
  position: absolute; left: 50%;
  /* 帯は「枠の下端」に置く（--dia-box-rows）。格子の行数で置くと、章の中で行数の少ない
     パネルだけ帯が枠の途中に浮いて、パネルを送るたびに手札の位置が動いて見える。 */
  top: calc(50% + var(--dia-cell) * var(--dia-box-rows) / 2 - var(--dia-band) / 2 - var(--dia-tag) / 2 + var(--dia-hero) / 2);
  transform: translateX(-50%);
  height: var(--dia-band);
  padding-top: 8px;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  pointer-events: none;
  z-index: 3; /* 駒より前面（上の「重ね順」の但し書き）。矢印は札の裏へ回す */
}
body.tp .tpt-dia-hand[hidden] { display: none !important; }
body.tp .tpt-dia-handrow { display: flex; align-items: flex-end; gap: 6px; }
/* 名札の行の高さは diagram.js の HAND_TAG_H（12px）と同じ。ここを変えたら向こうも直す
   ── ずれると札の高さの計算が狂って帯からはみ出す。 */
body.tp .tpt-dia-handtag {
  font-size: 11px; line-height: 12px; letter-spacing: .16em;
  color: var(--tp-text-dim);
  white-space: nowrap;
}
/* 札1枚ぶんの枠。大きさは高さで与え、幅は .cardtile の aspect-ratio が決める
   （比の出典は cardface.js の CARD_RATIO）。 */
body.tp .tpt-dia-hcard {
  position: relative;
  display: block;
  transition: transform .3s ease, opacity .4s ease;
}
body.tp .tpt-dia-hcard .cardtile { width: auto; height: var(--dia-cardh, 60px); }
/* えらんだ1枚: 少し持ち上げて破線で囲む（盤のマークと同じ意匠） */
body.tp .tpt-dia-hcard.pick { transform: translateY(-5px); }
body.tp .tpt-dia-hcard.pick::before {
  content: '';
  position: absolute; inset: -4px;
  border: 2px dashed var(--tp-accent);
  border-radius: var(--tp-r-sm);
}
/* 発動した1枚: 金の光（マークの m-cast と同じ） */
body.tp .tpt-dia-hcard.cast::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  width: 150%; height: 150%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(216, 178, 90, .38), transparent 68%);
  animation: tpt-glow 1s ease-in-out infinite;
}
/* 使い切った1枚: 手札から離れたので姿は消すが、枠（.tpt-dia-hcard）は並びに残す。
   要素ごと外すと残りの札が横へ動いて、別の札に入れ替わったように見える。 */
body.tp .tpt-dia-hcard.used { opacity: 0; }
/* 打てない1枚（1ターンに2枚目は使えない）。格子の m-blocked とまったく同じ灰の禁止標識を
   札の上に重ねる ── 記号の意味は置き場所が変わっても1つ（設計ガイドライン §5.5）。
   「できない」を不在で表さないための印なので、札の幅いっぱいに出す。 */
body.tp .tpt-dia-hcard.blocked::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  width: 78%; height: 0; padding-bottom: 78%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 2px solid var(--tp-text-mute);
  background:
    linear-gradient(45deg, transparent 46%, var(--tp-text-mute) 46%, var(--tp-text-mute) 54%, transparent 54%),
    rgba(10, 8, 18, .5);
}
@keyframes tpt-glow { 50% { opacity: .55; } }

/* ── 実物大の札（.tpt-dia-hero）──────────────
   その図解が名指しするカードを、効果文が読める大きさで格子の上に出す。
   図解用の小さな札（.cardtile）は効果文を落とすので、紹介の主役には使えない
   （設計ガイドライン §5.1・§5.2）。姿は実プレイのカード拡大（.zoom-card）と同じで、
   出典は cardface.js の zoomCardHtml 1か所だけ。
   高さ（--dia-hero）は diagram.js が**幅から**決めて書き込む。章のどれか1枚が札を出すなら
   章の全パネルで同じ高さを取り分けるので、パネルを送ってもマスの大きさは動かない。 */
body.tp .tpt-dia-hero {
  position: absolute; left: 50%;
  /* 「札 ＋ 格子 ＋ 帯 ＋ 名札」をひとまとまりとして器の中央に置いたときの、いちばん上。 */
  top: calc(50% - (var(--dia-cell) * var(--dia-box-rows) + var(--dia-band) + var(--dia-tag) + var(--dia-hero)) / 2);
  transform: translateX(-50%);
  width: 100%; height: var(--dia-hero);
  /* 下の余白は、格子の枠（.tpt-dia::before）が格子より外へ張り出すぶん。
     取らないと、狭い端末で札の下端が枠の上辺に乗る。 */
  box-sizing: border-box; padding-bottom: var(--tp-3);
  display: grid; place-items: center;
  pointer-events: none;
  z-index: 3; /* 駒より前面（上の「重ね順」の但し書き）。矢印は札の裏へ回す */
}
body.tp .tpt-dia-hero[hidden] { display: none !important; }
/* 大きさは取り分けた高さ（--dia-hero）に比例させる。幅を固定すると狭い端末で器から溢れる。 */
body.tp .tpt-dia-hero .zoom-card {
  width: min(100%, calc(var(--dia-hero) * 1.4));
  max-height: 100%;
  padding: calc(var(--dia-hero) * .05) calc(var(--dia-hero) * .07);
  gap: calc(var(--dia-hero) * .022);
  border-radius: var(--tp-r-sm);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .55);
}
body.tp .tpt-dia-hero .z-kind { font-size: calc(var(--dia-hero) * .062); }
body.tp .tpt-dia-hero .z-name { font-size: calc(var(--dia-hero) * .115); letter-spacing: .1em; }
body.tp .tpt-dia-hero .z-art { padding: 0; }
body.tp .tpt-dia-hero .z-art svg {
  width: calc(var(--dia-hero) * .2); height: calc(var(--dia-hero) * .2);
}
body.tp .tpt-dia-hero .z-text {
  font-size: calc(var(--dia-hero) * .073); line-height: 1.5; text-align: center;
}
/* 発動した拍: 金の縁（手札の帯の .cast と同じ語彙）。位置は transform で決めているので、
   ここで動かしてよいのは影と色だけ（位置指定要素に transform の @keyframes を当てない）。 */
body.tp .tpt-dia-hero.cast .zoom-card {
  border-color: var(--tp-accent);
  box-shadow: 0 0 0 2px var(--tp-accent), 0 0 22px rgba(216, 178, 90, .45), 0 6px 18px rgba(0, 0, 0, .55);
}

/* ── 札の図解（魔法カード）────────────────────
   盤の格子を描かず「札の置き場から1枚が出て効果が起きる」絵にする。
   盤のマスに札を並べると、手札から出す動きだと伝わらないため。 */
body.tp .tpt-dia-tag { display: none; }
body.tp .tpt-dia.cards .tpt-dia-cells i { border: none; background: none; }
body.tp .tpt-dia.cards .tpt-dia-cells::before {
  content: '';
  position: absolute; left: -10px; right: -10px; bottom: -7px;
  height: calc(var(--dia-cell) + 7px);
  border: 1px solid var(--tp-line-lit);
  border-radius: var(--tp-r);
  background: linear-gradient(180deg, rgba(157, 123, 255, .05), rgba(157, 123, 255, .15));
}
/* 札置き場の図解は札が主役なので、マスいっぱいまで大きくする（比は保ったまま）。
   70% ＝ マスの高さの 95%（70 × 120 / 88 = 95）。 */
body.tp .tpt-dia.cards .cardtile { width: 70%; }
body.tp .tpt-dia.cards .tpt-dia-tag {
  display: block;
  /* 格子と同じだけ上へ寄せてから、格子の下へぶら下げる（高さの取り分は diagram.js の TAG_H）。
     実物大の札のぶん（--dia-hero）も格子と同じだけ足す ── 足さないと、札を出すパネルだけ
     名札が格子の中へ潜り込んで札の上に重なる（実測: 第8章《発掘》《墓荒らし》）。 */
  position: absolute; left: 50%; top: calc(50% - var(--dia-tag) / 2 + var(--dia-hero) / 2);
  transform: translate(-50%, calc(var(--dia-box-rows) * var(--dia-cell) / 2 + 12px));
  font-size: 11px; letter-spacing: .16em;
  color: var(--tp-text-dim);
  white-space: nowrap;
}
body.tp .tpt-dia.cards .tpt-dia-tag[hidden] { display: none; }

/* ── 章末 ────────────────────────────────── */

body.tp .tpt-end {
  position: absolute; inset: 0;
  z-index: var(--tp-z-modal);
  display: grid; place-items: center;
  /* .tpt-explain と同じ理由でヘッダーぶんは自前で空ける（高さの出典は --tp-head-h だけ） */
  padding: calc(var(--tp-head-h) + var(--tp-safe-t)) var(--tp-4) max(var(--tp-4), var(--tp-safe-b));
  background: rgba(7, 4, 13, .88);
  backdrop-filter: blur(3px);
}
body.tp .tpt-end[hidden] { display: none !important; }
body.tp .tpt-end-card {
  width: 100%; max-width: 340px;
  display: flex; flex-direction: column; gap: var(--tp-3);
  padding: var(--tp-6) var(--tp-5);
  border: 1px solid var(--tp-line-lit);
  border-radius: var(--tp-r-lg);
  background: var(--tp-surface);
  box-shadow: var(--tp-shadow-lg);
  text-align: center;
}
body.tp .tpt-end-ico { color: var(--tp-ok); display: grid; place-items: center; }
body.tp .tpt-end-ico .tp-ico { width: 40px; height: 40px; }
body.tp .tpt-end-t { color: var(--tp-accent); }

/* ── ヒントのトースト ─────────────────────────── */

/* position:absolute だと grid（.tp-screen）の配置対象になって縦に引き伸ばされるため fixed で置く */
body.tp .tpt-toast {
  position: fixed;
  left: 50%; bottom: calc(var(--tp-10) + var(--tp-safe-b));
  transform: translate(-50%, 8px);
  z-index: var(--tp-z-toast);
  max-width: min(88%, 340px);
  padding: var(--tp-2) var(--tp-4);
  border: 1px solid var(--tp-accent-deep);
  border-radius: var(--tp-r-pill);
  background: rgba(20, 14, 38, .96);
  color: var(--tp-accent);
  font-size: 12.5px; line-height: 1.5; text-align: center;
  opacity: 0;
  transition: opacity .2s ease, transform .2s ease;
  pointer-events: none;
}
body.tp .tpt-toast.show { opacity: 1; transform: translate(-50%, 0); }
body.tp .tpt-toast[hidden] { display: none !important; }

/* ヘッダーの自動再生トグル */
body.tp .tpt-auto.on { color: var(--tp-accent); }

/* ── 墓場を選ぶステップ ───────────────────────────
   対局と同じ #pregame（style.css, position:fixed / z-index:96）をそのまま借りる。
   そのままだと共通ヘッダー（#tp-root は z-index:40）ごと覆ってしまい、戻る／ホーム／言語が
   押せなくなるので、選んでいる間だけ #tp-root を前に出し、中身（盤・語り）は伏せる。
   ヘッダーが墓場選びの上に重なるぶん、上の余白をヘッダーの高さぶん確保する。 */
body.tp.tpt-drafting #tp-root { z-index: 97; pointer-events: none; }
body.tp.tpt-drafting #tp-root .tp-screen { visibility: hidden; }
/* #tp-root は全画面の箱なので、素通しにしないと墓石が押せなくなる。ヘッダーだけ受け取る。 */
body.tp.tpt-drafting #tp-root .tp-head { pointer-events: auto; }
body.tp.tpt-drafting #pregame {
  /* ヘッダーの矩形は pointer-events:auto で墓場選びの上に乗り、その帯のタップを食う。
     確保する高さは必ず --tp-head-h から取る（数字を書き写すと、ヘッダーの段数が変わった
     ときにここだけ古い値で残り、押せない帯が生まれる）。 */
  padding-top: calc(var(--tp-head-h) + var(--tp-safe-t));
  padding-bottom: max(var(--tp-3), var(--tp-safe-b));
}

/* 狭い端末では、ヘッダーぶんの余白と案内の高さで《決定》が画面の外へ出てしまう。
   #pregame は fixed なのでページはスクロールせず、はみ出した分は押せなくなる。
   見出し・余白・墓石の大きさを詰めて、必ず1画面に収める。 */
@media (max-height: 780px) {
  body.tp.tpt-drafting #pregame { padding-top: calc(var(--tp-head-h) - 4px + var(--tp-safe-t)); }
  body.tp.tpt-drafting .pg-title { font-size: 24px; margin: 0 0 2px; }
  body.tp.tpt-drafting .pg-sub { line-height: 1.7; margin-bottom: var(--tp-2); }
  body.tp.tpt-drafting .pg-guide {
    font-size: 12px; line-height: 1.6;
    padding: var(--tp-2) var(--tp-3);
    margin-bottom: var(--tp-2);
  }
  body.tp.tpt-drafting .pg-grid { width: min(86vw, 320px); margin-bottom: var(--tp-3); }
}
/* さらに低い端末（360x640 など）。英語は説明の行数が増えるぶん、もう一段詰める。 */
@media (max-height: 660px) {
  body.tp.tpt-drafting .pg-sub { font-size: 11.5px; }
  body.tp.tpt-drafting .pg-grid { width: min(78vw, 284px); }
}

/* ── 狭い端末（375×667 など）向けの詰め ───────────────
   盤に残る高さは「arena の高さ − 手札の段 − ボタンの段 − 段間の余白」。
   カードを使う章ではこの3つが arena の半分近くを食っていて、盤が実プレイの半分ほどの
   大きさになっていた（実機の指摘「盤が小さく表示されすぎている」）。
   札を縮め・ボタンの段を薄くし・語りを詰めて、その差を全部盤へ回す。 */
@media (max-height: 700px) {
  body.tp .tpt-say { min-height: 74px; padding: var(--tp-2) var(--tp-3); }
  body.tp .tpt-say-t { font-size: 13px; line-height: 1.55; }
  /* 手札は「札そのものを縮める」のではなく、扇ごと縮小する。実プレイの .card は寸法も
     文字も style.css が決めているので、幅・高さだけ上書きすると本文がはみ出す。
     段の高さ（--tpt-hand-h）は board.js が実測して書くので、ここには控えだけ置く。 */
  body.tp .tpt-arena { --tpt-hand-h: 74px; --tpt-hand-s: .62; gap: 6px; }
  body.tp .tpt-actbar { height: 36px; }
  body.tp .tpt-act, body.tp .tpt-cancel { height: 32px; }
  /* 拡大表示も詰める（値は style.css の #sleep-draw .zoom-card と同じ「小さい札」の寸法） */
  body.tp .tpt-veil .zoom-card { width: 190px; }
  body.tp .tpt-veil .zoom-card .z-name { font-size: 21px; }
  body.tp .tpt-veil .zoom-card .z-art svg { width: 64px; height: 64px; }
  body.tp .tpt-veil .zoom-card .z-text { font-size: 11.5px; }
  /* 低い端末では図解の取り分を少し増やす。上限だけを変え、「要る高さだけ取る」仕組み
     （--tpt-dia-h）はそのまま通す ── ここで割合を書き切ると、行数の少ない図で
     器の大半が空いたまま残る（図解監査 #25）。 */
  body.tp .tpt-explain { --tpt-dia-cap: 58%; }
  body.tp .tpt-ex-p { font-size: 15px; line-height: 1.7; }
}

/* ── さらに低い端末（320×568 など）─────────────────
   ここは max-height:700px の詰めだけでは足りず、盤の最小セル（28px×4段＋目地）すら
   段に収まらない＝ .tpt-stage の overflow:hidden で盤の下が切れていた。
   語り・章名・ボタンの枠をもう一段削って、盤に 4×37px ぶんの高さを確保する。 */
@media (max-height: 600px) {
  body.tp .tpt-stage { gap: var(--tp-2); }
  body.tp .tpt-top { min-height: 28px; }
  body.tp .tpt-say { min-height: 56px; padding: var(--tp-1) var(--tp-2); }
  body.tp .tpt-say-t { font-size: 12px; line-height: 1.5; }
  body.tp .tpt-nav .tpt-next { min-height: 36px; }
  body.tp .tpt-back { min-height: 36px; width: 36px; }
  body.tp .tpt-arena { --tpt-hand-h: 61px; --tpt-hand-s: .5; gap: 6px; }
  body.tp .tpt-actbar { height: 32px; }
  body.tp .tpt-act, body.tp .tpt-cancel { height: 30px; font-size: 12px; }
  body.tp .tpt-veil .zoom-card { width: 168px; }
  body.tp .tpt-veil-act .tp-btn { min-height: 36px; padding: 0 var(--tp-4); font-size: 13px; }
  /* 図解の段は縮まない側なので、低い端末では上限そのものを下げる。下げないと英語の長い本文
     （110字）が入り切らず、1画面で読み切れるはずの1文をスクロールさせることになる
     （320×568 の実測で 5 パネルが 13px あふれていた）。
     本文の字も1段小さくする ── 上限を下げるだけで本文を収めようとすると、
     実物大の札を出す章のマスが 46px → 34px まで落ちて駒が読めなくなる。 */
  body.tp .tpt-explain { --tpt-dia-cap: 54%; --tpt-dia-cap-hero: 60%; }
  body.tp .tpt-ex-p { font-size: 14px; line-height: 1.6; }
}
