/*
 * 配置コンテナ（.lane__slot--unit など）が上書きできる
 * カードの寸法既定値。コンテナはカードの祖先なので、カード自身ではなく .battle-page
 * 側に置かないと参照できない。設定できるプロパティの一覧は doc/card-assets.md を参照。
 *
 * カードの見た目は、対戦画面（.battle-page）と、対戦画面の外でカードを並べる場所（.card-surface。
 * デッキ選択の画面など）の中で効く。:is() の詳細度は .battle-page だけを書いた場合と同じ。
 */
.battle-page,
.card-surface {
  --card-combat-font-size-base: clamp(0.55rem, 13cqmin, 1.5rem);
  --card-combat-width-base: clamp(0.875rem, 38cqmin, 4rem);
  --card-frame-width: clamp(2px, 0.35vmin, 4px);
  /*
   * Lane 上のカードの Attack/HP の印の位置。縦はカードの高さに対する印の中心の割合、横は印がカードの外へ出る量
   * （カードの幅に対する割合）。0.276395 / 0.633605 は見える額縁の上下半分の中心へ合わせた視覚調整値で、
   * Lane 額縁の紋章中心 Y=350px とは独立した契約（doc/card-assets.md 参照）。
   * 印の配置（.lane__slot--unit など）と、対戦画面の攻撃ゲージ・強化の数字の位置（application.css）が同じ値を参照する。
   * 縦の位置を変えたら、攻撃ゲージが止まる位置（application.css の --battle-lane-frame-bulge-at-*）も求め直す。
   */
  --card-lane-attack-center: 0.276395;
  --card-lane-health-center: 0.633605;
  --card-lane-mark-overhang: calc(1 / 14);
  /*
   * Lane 上のカードの Attack/HP の印の大きさ。カードの高さに対する割合で、下限・上限を持つ。
   * 印の配置（.lane__slot--unit）と、攻撃ゲージの出発点（application.css）が同じ値を参照する。
   */
  --card-lane-mark-size-ratio: 0.24;
  --card-lane-mark-size-min: 1.5rem;
  --card-lane-mark-size-max: 4rem;
  /*
   * 印の画像（attack-stat.webp・health-stat.webp。256px 四方）の、上下の真ん中の高さでの左右の透明な余白
   * （画像の幅に対する割合）。攻撃ゲージは、余白を除いた見える縁から出す。画像を描き直したら測り直す。
   */
  --card-attack-mark-side-padding: calc(27 / 256);
  --card-health-mark-side-padding: calc(32 / 256);
}

/*
 * 額縁画像の配置は、アセットの実寸とカード内容領域の比から導く。
 * 縦向き: 額縁 768x1024 / 内容領域 640x896（640幅の5:7）。
 * 生の小数を書くと再書き出しで寸法が変わったときに追随できないため式で残す。
 */
:is(.battle-page, .card-surface) .card {
  --card-clip: inset(0);
  --card-frame-image-width: calc(768 / 640 * 100%);
  --card-frame-image-height: calc(1024 / 896 * 100%);
  --card-frame-image-left: calc(-1 * (768 - 640) / 2 / 640 * 100%);
  --card-frame-image-top: calc(-1 * (1024 - 896) / 2 / 896 * 100%);
  aspect-ratio: 5 / 7;
  background-color: #000;
  box-sizing: border-box;
  clip-path: var(--card-clip);
  color: #f7f8fa;
  container-name: battle-card;
  container-type: size;
  height: 100%;
  isolation: isolate;
  justify-self: center;
  max-height: var(--card-max-height, none);
  max-width: 100%;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  position: relative;
  width: auto;
}

/*
 * カード固有のイラストは Card コンポーネントが --card-art-image で渡す。
 * ::before は「変数があればそれ、なければ全種別共通のデフォルトアート」を描く。
 * 画像を取得できなくても黒い下地・額縁・数値は残る。
 */
:is(.battle-page, .card-surface) .card::before {
  background-color: #000;
  background-image: var(--card-art-image, url("/assets/cards/default-73e5e803.webp"));
  background-position: center;
  background-size: cover;
  border-radius: inherit;
  clip-path: var(--card-clip);
  content: "";
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: -1;
}

/*
 * Portrait frames are 768×1024 around 640×640 artwork. The percentage overhang
 * keeps their transparent padding outside the card's 5:7 content box.
 *
 * --card-frame-image-* size and place the frame ARTWORK. The separate
 * --card-frame-width (declared with the other base values at the top of this
 * file) is the drawn frame's line thickness and insets the stats layer; the two
 * are unrelated despite the shared prefix.
 */
:is(.battle-page, .card-surface) .card--face-up::after {
  background-image: var(--card-rarity-frame);
  background-position: center;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  content: "";
  height: var(--card-frame-image-height);
  left: var(--card-frame-image-left);
  pointer-events: none;
  position: absolute;
  top: var(--card-frame-image-top);
  width: var(--card-frame-image-width);
  z-index: 1;
}

:is(.battle-page, .card-surface) .card--type-unit {
  clip-path: none;
  overflow: visible;
}

:is(.battle-page, .card-surface) .card--lane {
  aspect-ratio: 7 / 5;
  height: auto;
  max-height: 100%;
  width: min(100cqw, 140cqh);
}

/*
 * Lane frames are 1024×768. Their crest alignment and combat-stat positions are
 * independent visual contracts; changing one must not be used to derive the other.
 * The extra .card class makes the Lane assignment stronger than rarity rules, so
 * the result is independent of source order.
 *
 * This block is deliberately placed BEFORE the rarity blocks below. That order is
 * what makes the guard testable: drop the extra .card and the later rarity rules
 * win on equal specificity, so the Lane frame assertions in
 * test/system/card_harness_test.rb fail. Do not move the rarity blocks above this
 * one without replacing that guard.
 */
:is(.battle-page, .card-surface) .card.card--lane.card--type-unit {
  --card-clip: polygon(
    7% 0,
    93% 0,
    100% 50%,
    93% 87%,
    7% 87%,
    0 50%
  );
  /* Lane: 額縁 1024x768 / 内容領域 896x640（640高の7:5）。
     この寸法を変えたら、レアリティが下がる演出の箱（application.css の .rarity-drop__layer・.rarity-drop__card）の式も合わせて直す。 */
  --card-frame-image-width: calc(1024 / 896 * 100%);
  --card-frame-image-height: calc(768 / 640 * 100%);
  --card-frame-image-left: calc(-1 * (1024 - 896) / 2 / 896 * 100%);
  --card-frame-image-top: calc(-1 * (768 - 640) / 2 / 640 * 100%);
  --card-rarity-frame: var(--card-lane-rarity-frame);

  background-color: transparent;
}

/*
 * 正方形のユニット絵を7:5へ切り抜き、35%位置に合わせると上約10%・下約19%が隠れる。
 * さらに下辺の切り欠きがあるため、顔を置く上寄りの焦点領域をLaneで残す。
 */
:is(.battle-page, .card-surface) .card.card--lane.card--type-unit::before {
  background-position: center 35%;
}

:is(.battle-page, .card-surface) .card--type-unit.card--rarity-common {
  --card-lane-rarity-frame: url("/assets/cards/rarity-common-lane-0348f00e.webp");
  --card-rarity-frame: url("/assets/cards/rarity-common-unit-2ec90ff7.webp");
}

:is(.battle-page, .card-surface) .card--type-unit.card--rarity-rare {
  --card-lane-rarity-frame: url("/assets/cards/rarity-rare-lane-860d2712.webp");
  --card-rarity-frame: url("/assets/cards/rarity-rare-unit-d91a0d87.webp");
}

:is(.battle-page, .card-surface) .card--type-unit.card--rarity-epic {
  --card-lane-rarity-frame: url("/assets/cards/rarity-epic-lane-c77e2dfe.webp");
  --card-rarity-frame: url("/assets/cards/rarity-epic-unit-acc45d85.webp");
}

:is(.battle-page, .card-surface) .card--type-unit.card--rarity-legendary {
  --card-lane-rarity-frame: url("/assets/cards/rarity-legendary-lane-5dca34c5.webp");
  --card-rarity-frame: url("/assets/cards/rarity-legendary-unit-5c05d49c.webp");
}

/*
 * Action と Field は上端の頂点だけカード枠の外へ出す。clip-path に現れる 4.7619% は
 * --card-top-protrusion (5%) を、突出ぶんを含めた高さ 105% に対する比へ直した値
 * (5 / 105 = 1/21)。突出量を変えたら 4.7619% も protrusion / (100 + protrusion) で
 * 計算し直す。CSS では % 同士を割れないため、値を直接書いている。
 */
:is(.battle-page, .card-surface) .card--type-action,
:is(.battle-page, .card-surface) .card--type-field {
  --card-top-protrusion: 5%;

  background-color: transparent;
  clip-path: none;
  overflow: visible;
}

:is(.battle-page, .card-surface) .card--type-action {
  --card-clip: polygon(
    50% 0,
    100% 4.7619%,
    100% 100%,
    0 100%,
    0 4.7619%
  );
}

:is(.battle-page, .card-surface) .card--type-action::before,
:is(.battle-page, .card-surface) .card--type-field::before {
  top: calc(-1 * var(--card-top-protrusion));
}

:is(.battle-page, .card-surface) .card--type-action.card--rarity-common {
  --card-rarity-frame: url("/assets/cards/rarity-common-action-f1b75bb7.webp");
}

:is(.battle-page, .card-surface) .card--type-action.card--rarity-rare {
  --card-rarity-frame: url("/assets/cards/rarity-rare-action-916cda4f.webp");
}

:is(.battle-page, .card-surface) .card--type-action.card--rarity-epic {
  --card-rarity-frame: url("/assets/cards/rarity-epic-action-a69c599d.webp");
}

:is(.battle-page, .card-surface) .card--type-action.card--rarity-legendary {
  --card-rarity-frame: url("/assets/cards/rarity-legendary-action-3c5f7708.webp");
}

:is(.battle-page, .card-surface) .card--type-field {
  --card-clip: inset(0 round 50% 50% 0 0 / 4.7619% 4.7619% 0 0);
}

:is(.battle-page, .card-surface) .card--type-field.card--rarity-common {
  --card-rarity-frame: url("/assets/cards/rarity-common-field-7ecffc13.webp");
}

:is(.battle-page, .card-surface) .card--type-field.card--rarity-rare {
  --card-rarity-frame: url("/assets/cards/rarity-rare-field-ddcdacbd.webp");
}

:is(.battle-page, .card-surface) .card--type-field.card--rarity-epic {
  --card-rarity-frame: url("/assets/cards/rarity-epic-field-4d60b5d9.webp");
}

:is(.battle-page, .card-surface) .card--type-field.card--rarity-legendary {
  --card-rarity-frame: url("/assets/cards/rarity-legendary-field-85ccb11c.webp");
}

:is(.battle-page, .card-surface) .card--face-down {
  border-radius: 0.2rem;
}

:is(.battle-page, .card-surface) .card--face-down::before {
  background-image: url("/assets/cards/back-ae193e36.webp");
}

:is(.battle-page, .card-surface) .card p,
:is(.battle-page, .card-surface) .card dl {
  margin: 0;
}

:is(.battle-page, .card-surface) .card__stats {
  inset: var(--card-stats-inset, var(--card-frame-width));
  position: absolute;
  z-index: 2;
}

:is(.battle-page, .card-surface) .card__stat {
  min-width: 0;
}

:is(.battle-page, .card-surface) .card__stat-value {
  margin: 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

:is(.battle-page, .card-surface) .card__stat--cost,
:is(.battle-page, .card-surface) .card__stat--attack,
:is(.battle-page, .card-surface) .card__stat--health {
  align-items: center;
  aspect-ratio: 1;
  background-color: transparent;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  border: 0;
  border-radius: 0;
  clip-path: none;
  display: grid;
  font-size: var(--card-cost-font-size, clamp(0.55rem, 12cqmin, 1rem));
  isolation: isolate;
  justify-items: center;
  line-height: 1;
  position: absolute;
  width: var(--card-cost-width, clamp(0.875rem, 28cqmin, 2.25rem));
}

:is(.battle-page, .card-surface) .card__stat--attack,
:is(.battle-page, .card-surface) .card__stat--health {
  font-size: var(--card-combat-font-size, var(--card-combat-font-size-base));
  left: var(--card-combat-left, auto);
  right: var(--card-combat-right, 4%);
  transform: var(--card-combat-transform, none);
  width: var(--card-combat-width, var(--card-combat-width-base));
}

:is(.battle-page, .card-surface) .card__stat--cost .card__stat-value,
:is(.battle-page, .card-surface) .card__stat--attack .card__stat-value,
:is(.battle-page, .card-surface) .card__stat--health .card__stat-value {
  -webkit-text-stroke: clamp(0.25px, 0.8cqmin, 0.75px) #120b07;
  color: #f2d494;
  font-family: "Alfa Slab One", Rockwell, serif;
  font-variant-numeric: tabular-nums;
  font-weight: 400;
  letter-spacing: -0.07em;
  text-shadow: 0 1px 0 rgb(255 247 220 / 50%), 0 2px 2px rgb(0 0 0 / 90%);
}

:is(.battle-page, .card-surface) .card__stat--cost {
  background-image: url("/assets/cards/cost-stat-8494c760.webp");
  left: var(--card-cost-left, 4%);
  top: var(--card-cost-top, 3%);
}

:is(.battle-page, .card-surface) .card__stat--attack {
  background-image: url("/assets/cards/attack-stat-9ff66294.webp");
  bottom: var(--card-attack-bottom, var(--card-combat-bottom, 25%));
  top: var(--card-attack-top, auto);
}

:is(.battle-page, .card-surface) .card__stat--health {
  background-image: url("/assets/cards/health-stat-a55d7806.webp");
  bottom: var(--card-health-bottom, var(--card-combat-bottom, 4%));
  top: var(--card-health-top, auto);
}

/*
 * 印を額縁の線に重ねる置き方。カードを置く場所（縦向きのカードの親）に付ける。
 * 左右の向きは、印の中心を額縁の線の中心に重ねる（印はカードの外へ、左右に印の大きさの半分ずつはみ出す）。
 * 上下の向きは、カードの外へはみ出さない（コストの上端をカードの上端に、体力の下端をカードの下端にそろえる）。
 * コストは左上、体力は右下、攻撃は右の線の上で体力のすぐ上に置く。
 * 印の大きさはカードの幅に比例させ、下限・上限を付けない（小さいカードでも、印がカードに対して同じ割合になる）。
 * 位置は、印を置く層（.card__stats。額縁の線の内側）の端から測る。カードの端は、そこから線の太さだけ外にある。
 * 並べる側は、左右のはみ出し（カードの幅の 15% ずつ）の分の隙間を取る。
 */
.card-marks-on-frame {
  --card-mark-size: 30cqmin;
  --card-mark-side-offset: calc(var(--card-mark-size) / -2 - var(--card-frame-width) / 2);
  --card-mark-edge-offset: calc(-1 * var(--card-frame-width));
  --card-attack-bottom: calc(var(--card-mark-edge-offset) + var(--card-mark-size));
  --card-combat-font-size: 12cqmin;
  --card-combat-right: var(--card-mark-side-offset);
  --card-combat-width: var(--card-mark-size);
  --card-cost-font-size: 12cqmin;
  --card-cost-left: var(--card-mark-side-offset);
  --card-cost-top: var(--card-mark-edge-offset);
  --card-cost-width: var(--card-mark-size);
  --card-health-bottom: var(--card-mark-edge-offset);
}

:is(.battle-page, .card-surface) .card--lane.card--stats-left {
  --card-combat-left: calc(-100% * var(--card-lane-mark-overhang));
  --card-combat-right: auto;
}

:is(.battle-page, .card-surface) .card__stat--decreased .card__stat-value {
  color: #ff6b6b;
}

:is(.battle-page, .card-surface) .card__stat--increased .card__stat-value {
  color: #5ee38c;
}

/* コストは下がるほど得なので、下がれば攻撃力・体力の上がったときと同じ緑、上がれば赤にする */
:is(.battle-page, .card-surface) .card__stat--cheaper .card__stat-value {
  color: #5ee38c;
}

:is(.battle-page, .card-surface) .card__stat--pricier .card__stat-value {
  color: #ff6b6b;
}

/*
 * 能力の印（今は貫通だけ）。レーンのカードの絵の上の隅、攻撃・体力の印がある側に置く。
 * 大きさは Attack/HP マークの幅（--card-combat-width）の 0.45 倍で、マークの幅を変えると一緒に変わる。
 * 盤面の邪魔にならないよう小さく半透明にし、ゆっくり明滅させる（濃さ 45%〜70%、1 往復 3 秒。ユーザーが決めた値）。
 * 矢じりは攻撃する向き（相手の方）を指す。相手のレーン（印が左）では左右を反転する。
 */
:is(.battle-page, .card-surface) .card__ability {
  animation: card-ability-blink 3s ease-in-out infinite;
  aspect-ratio: 1;
  background: radial-gradient(circle at 35% 30%, #ff9a4d, #b8320f 70%);
  border: 1px solid #f2d494;
  border-radius: 50%;
  box-shadow: 0 1px 3px rgb(0 0 0 / 70%);
  padding: 1%;
  pointer-events: none;
  position: absolute;
  right: 9%;
  top: 8%;
  width: calc(var(--card-combat-width, var(--card-combat-width-base)) * 0.45);
  z-index: 3;
}

:is(.battle-page, .card-surface) .card--stats-left .card__ability {
  left: 9%;
  right: auto;
}

:is(.battle-page, .card-surface) .card__ability-icon {
  display: block;
  height: 100%;
  width: 100%;
}

:is(.battle-page, .card-surface) .card--stats-left .card__ability-icon {
  transform: scaleX(-1);
}

:is(.battle-page, .card-surface) .card__ability-icon path {
  fill: none;
  stroke: #fff3d0;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2.4;
}

:is(.battle-page, .card-surface) .card__ability-icon .card__ability-icon-head {
  fill: #fff3d0;
}

/*
 * 毒の印。紫の丸に黄緑のしずく。能力の印（貫通）と反対の上の角に出す（ユーザーが試作の案から選んだ見せ方）。
 * 毒が付いている間ずっと出す。場のカードは輪（data-timer）を出すので明滅させず、はっきり出す。
 * 輪を出さない写し（死亡・破壊・戻す）だけ、能力の印と同じくゆっくり明滅させる。
 */
:is(.battle-page, .card-surface) .card__poison {
  animation: card-ability-blink 3s ease-in-out infinite;
  aspect-ratio: 1;
  background: radial-gradient(circle at 35% 30%, #a35ad8, #4a1a73 70%);
  border: 1px solid #e3cdf7;
  border-radius: 50%;
  box-shadow: 0 1px 3px rgb(0 0 0 / 70%);
  left: 9%;
  padding: 1.5%;
  pointer-events: none;
  position: absolute;
  top: 8%;
  width: calc(var(--card-combat-width, var(--card-combat-width-base)) * 0.45);
  z-index: 3;
}

:is(.battle-page, .card-surface) .card__poison[data-timer] {
  animation: none;
  opacity: 1;
}

/*
 * 次に体力が減るまでの残りを見せる輪（ユーザーが案から選んだ見せ方）。毒の間隔（--poison-interval。battle_rules.json の
 * poison_interval_seconds）で一周し、満ちた瞬間に体力が減る。Card が、今の回の中で経った秒を負の遅れ（--poison-cycle-delay）で渡す。
 * 一周したら満ちたまま止め、体力を減らした配信（基準時刻が進む）で作り直して 0 から回す（サーバーの遅れで、満ちる前に減って見えないように）。
 * チェーンの間（data-paused）は止める。
 * 輪の太さ・色の置き方は、変わる前の砂時計の輪（.card__transform::before）とそろえている。片方を変えるときは、もう片方も見る。
 */
@property --poison-turn {
  inherits: false;
  initial-value: 0deg;
  syntax: "<angle>";
}

:is(.battle-page, .card-surface) .card__poison[data-timer]::before {
  animation: poison-cycle var(--poison-interval, 5s) linear var(--poison-cycle-delay, 0s) 1 forwards;
  background: conic-gradient(#d6a6ff var(--poison-turn), rgb(255 255 255 / 18%) 0);
  border-radius: 50%;
  content: "";
  inset: -16%;
  mask: radial-gradient(farthest-side, transparent 86%, #000 88%);
  position: absolute;
}

:is(.battle-page, .card-surface) .card__poison[data-paused]::before {
  animation-play-state: paused;
}

@keyframes poison-cycle {
  from {
    --poison-turn: 0deg;
  }

  to {
    --poison-turn: 360deg;
  }
}

:is(.battle-page, .card-surface) .card--stats-left .card__poison {
  left: auto;
  right: 9%;
}

:is(.battle-page, .card-surface) .card__poison-icon {
  display: block;
  fill: #b8f06a;
  height: 100%;
  width: 100%;
}

/*
 * バリア（場のユニット）。カードの中央に、カードの高さいっぱいの正六角形の膜を出す（ユーザーの決定、2026-10-21。
 * 中央の印から全体を守る大きさに広げ、カードの形に合わせず正六角形にした）。膜はカードの上下からはみ出さない。
 * 絵が透けて見えるよう、ごく薄く塗り、縁だけを光らせる。2 回分以上なら下の方に回数を出す。回数が変わるたびに作り直すので、
 * 付いた時は内から膨らんで現れる。防いだ時に砕け散る動きは application.css の .barrier-shatter（同じ大きさ・置き場所）。
 */
:is(.battle-page, .card-surface) .card__barrier {
  animation: card-barrier-appear 0.45s cubic-bezier(0.2, 0.8, 0.3, 1.2) both;
  aspect-ratio: 1;
  height: 84%;
  left: 50%;
  pointer-events: none;
  position: absolute;
  top: 4%;
  translate: -50% 0;
  z-index: 3;
}

:is(.battle-page, .card-surface) .card__barrier-icon {
  display: block;
  filter: drop-shadow(0 0 2px rgb(140 220 255 / 55%)) drop-shadow(0 0 6px rgb(80 170 255 / 30%));
  height: 100%;
  opacity: 0.75;
  overflow: visible;
  width: 100%;
}

:is(.battle-page, .card-surface) .card__barrier-shell {
  fill: rgb(150 220 255 / 10%);
  stroke: rgb(216 243 255 / 85%);
  stroke-linejoin: round;
  stroke-width: 2.5;
  vector-effect: non-scaling-stroke;
}

:is(.battle-page, .card-surface) .card__barrier-core {
  fill: none;
  stroke: rgb(216 243 255 / 35%);
  stroke-linejoin: round;
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}

/* 回数は六角形の下の方に出す（ユニットの絵の顔と重ならず読みやすい。ユーザーの指示） */
:is(.battle-page, .card-surface) .card__barrier-count {
  bottom: 10%;
  color: #ffffff;
  font-size: calc(var(--card-combat-width, var(--card-combat-width-base)) * 0.36);
  font-variant-numeric: tabular-nums;
  font-weight: 900;
  left: 0;
  line-height: 1;
  paint-order: stroke fill;
  position: absolute;
  right: 0;
  text-align: center;
  -webkit-text-stroke: 0.14em #0b3354;
}

@keyframes card-barrier-appear {
  from {
    opacity: 0;
    scale: 0.4;
  }

  to {
    opacity: 1;
    scale: 1;
  }
}

/* 動きを減らす設定では、膨らまずに薄く現れるだけにする */
@media (prefers-reduced-motion: reduce) {
  :is(.battle-page, .card-surface) .card__barrier {
    animation-name: card-barrier-fade-in;
  }
}

@keyframes card-barrier-fade-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

/*
 * 札が別のカードに変わる瞬間の、絵のねじれ（CardArtTwist）。絵の層（::before）の上・額縁（::after）の下に重ね、
 * 描画面に前の絵と変化先の絵をねじって描く。描き終えたら外す（その下には変化先の絵が出ている）。
 */
:is(.battle-page, .card-surface) .card__art-twist {
  background-color: #000;
  background-position: center;
  background-size: cover;
  border-radius: inherit;
  display: block;
  height: 100%;
  inset: 0;
  pointer-events: none;
  position: absolute;
  width: 100%;
  z-index: -1;
}

/*
 * 相手の手札・リザーブの裏面のねじれ（OpponentCardBack。相手のその位置の札が変化・融合したとき）。
 * 裏面は絵ではないので、自分の札の絵のねじれ（CardArtTwist の画素のねじれ）の代わりに、裏面ごと回して縮め、戻す。長さは同じ。
 */
:is(.battle-page, .card-surface) .card--back-twist {
  animation: card-back-twist var(--card-back-twist-duration, 1s) ease-in-out;
}

@keyframes card-back-twist {
  50% {
    rotate: 200deg;
    scale: 0.6;
  }

  to {
    rotate: 360deg;
    scale: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  :is(.battle-page, .card-surface) .card--back-twist {
    animation: none;
  }
}

/*
 * 融合の渦（FusionVortex。ユーザーの決定、2026-10-21）。絵の上・額縁の下に重ねる薄い紫の渦。
 * 融合の相手に重ねている間（--hover）はゆっくり回り、送って返事を待つ間（--pending）は少し速く回る。
 * 融合でカードができたら（--twist）、絵のねじれ（CardArtTwist）と同じ長さで、渦もねじれながら縮んで消える。
 * 渦は、中心から外へ伸びる紫の帯（円錐の縞）を、縁で消える円で切り抜いて作る。
 */
:is(.battle-page, .card-surface) .card__fusion-vortex {
  border-radius: inherit;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  position: absolute;
  z-index: -1;
}

:is(.battle-page, .card-surface) .card__fusion-vortex::before {
  aspect-ratio: 1;
  background: repeating-conic-gradient(
    from 0deg,
    rgb(178 120 255 / 0%) 0deg,
    rgb(178 120 255 / 55%) 18deg,
    rgb(120 70 220 / 0%) 45deg
  );
  border-radius: 50%;
  content: "";
  left: 50%;
  mask-image: radial-gradient(circle, rgb(0 0 0 / 100%) 0%, rgb(0 0 0 / 70%) 35%, rgb(0 0 0 / 0%) 70%);
  position: absolute;
  top: 50%;
  translate: -50% -50%;
  width: 170%;
}

:is(.battle-page, .card-surface) .card__fusion-vortex--hover::before {
  animation: fusion-vortex-spin 2.4s linear infinite;
  opacity: 0.55;
}

:is(.battle-page, .card-surface) .card__fusion-vortex--pending::before {
  animation: fusion-vortex-spin 1.2s linear infinite;
  opacity: 0.7;
}

:is(.battle-page, .card-surface) .card__fusion-vortex--twist::before {
  animation: fusion-vortex-twist var(--fusion-twist-duration, 1s) ease-in forwards;
}

@keyframes fusion-vortex-spin {
  to {
    rotate: 360deg;
  }
}

@keyframes fusion-vortex-twist {
  from {
    opacity: 0.75;
    rotate: 0deg;
    scale: 1;
  }

  to {
    opacity: 0;
    rotate: 720deg;
    scale: 0.2;
  }
}

/*
 * 相手の手札・リザーブの裏面の融合の渦（OpponentCardBack）。裏面は小さいので、カードの外まで大きく広げ、濃くして、
 * 裏面の前に出す（ユーザーの決定、2026-10-21）。ねじれの前半は濃いまま回し、後半で縮めて消す。
 */
/*
 * 渦をカードの外まで出すので、相手の裏面だけ切り抜かない（裏面の絵は ::before が角を丸めて持つ）。
 * カードは overflow と clip-path の両方で切り抜くので、両方を外す（枠の外へ出すユニットカードと同じ）。
 */
.battle-header__opponent .card--face-down {
  clip-path: none;
  overflow: visible;
}

.battle-header__opponent .card__fusion-vortex {
  border-radius: 50%;
  inset: -60%;
  overflow: visible;
  z-index: 1;
}

.battle-header__opponent .card__fusion-vortex::before {
  background: repeating-conic-gradient(
    from 0deg,
    rgb(190 130 255 / 0%) 0deg,
    rgb(190 130 255 / 95%) 18deg,
    rgb(130 70 230 / 0%) 45deg
  );
  mask-image: radial-gradient(circle, rgb(0 0 0 / 100%) 0%, rgb(0 0 0 / 90%) 40%, rgb(0 0 0 / 0%) 70%);
  width: 100%;
}

.battle-header__opponent .card__fusion-vortex--twist::before {
  animation-name: opponent-fusion-vortex-twist;
}

/* 相手の変化の渦は青緑にして、融合の紫と見分ける（ユーザーの決定、2026-10-21） */
.battle-header__opponent .card__fusion-vortex--transform::before {
  background: repeating-conic-gradient(
    from 0deg,
    rgb(90 225 205 / 0%) 0deg,
    rgb(90 225 205 / 95%) 18deg,
    rgb(30 160 150 / 0%) 45deg
  );
}

@keyframes opponent-fusion-vortex-twist {
  from {
    opacity: 1;
    rotate: 0deg;
    scale: 1;
  }

  55% {
    opacity: 1;
    rotate: 400deg;
    scale: 1.1;
  }

  to {
    opacity: 0;
    rotate: 720deg;
    scale: 0.2;
  }
}

/*
 * 動きを減らす設定では回さず、薄い渦を止めて出す。自分の札のねじれの間は出さない（絵が入れ替わるので変わったことが分かる）。
 * 相手の裏面は絵が変わらないので、ねじれの間も渦を止めて出し、変化・融合が起きたことを残す。
 */
@media (prefers-reduced-motion: reduce) {
  :is(.battle-page, .card-surface) .card__fusion-vortex--hover::before,
  :is(.battle-page, .card-surface) .card__fusion-vortex--pending::before {
    animation: none;
  }

  :is(.battle-page, .card-surface) .card__fusion-vortex--twist {
    display: none;
  }

  .battle-header__opponent .card__fusion-vortex--twist {
    display: block;
  }

  .battle-header__opponent .card__fusion-vortex--twist::before {
    animation: none;
  }
}

/*
 * 変わる前の砂時計（ユーザーが見本から選んだ見せ方）。変わる 10 秒前から、自分の手札・リザーブの札のコストの印と反対の右上に出す。
 * 出るときに砂時計が逆さにひっくり返り、外周の輪が 10 秒（--transform-duration）で満ちる。輪の描き方は毒の輪と同じで、
 * Card が、10 秒のうち経った秒を負の遅れ（--transform-delay）で渡す。満ちたら、札が変わる配信が届くまで満ちたまま止める。
 * 輪の太さ・色の置き方は毒の輪（.card__poison[data-timer]::before）とそろえている。片方を変えるときは、もう片方も見る。
 * 変化の時計は試合時間と同じくチェーンの間も止まらないので、毒の輪と違い、チェーンの間も止めない。
 */
@property --transform-turn {
  inherits: false;
  initial-value: 0deg;
  syntax: "<angle>";
}

:is(.battle-page, .card-surface) .card__transform {
  aspect-ratio: 1;
  background: radial-gradient(circle at 35% 30%, #5a4a2a, #1f1a10 70%);
  border: 1px solid #f0d99a;
  border-radius: 50%;
  box-shadow: 0 1px 3px rgb(0 0 0 / 70%);
  padding: 1.5%;
  pointer-events: none;
  position: absolute;
  right: 9%;
  top: 8%;
  width: calc(var(--card-combat-width, var(--card-combat-width-base)) * 0.45);
  z-index: 3;
}

:is(.battle-page, .card-surface) .card__transform::before {
  animation: card-transform-cycle var(--transform-duration, 10s) linear var(--transform-delay, 0s) 1 forwards;
  background: conic-gradient(#ffd76a var(--transform-turn), rgb(255 255 255 / 18%) 0);
  border-radius: 50%;
  content: "";
  inset: -16%;
  mask: radial-gradient(farthest-side, transparent 86%, #000 88%);
  position: absolute;
}

@keyframes card-transform-cycle {
  from {
    --transform-turn: 0deg;
  }

  to {
    --transform-turn: 360deg;
  }
}

:is(.battle-page, .card-surface) .card__transform-icon {
  animation: card-transform-flip 4s ease-in-out infinite;
  display: block;
  height: 100%;
  width: 100%;
}

:is(.battle-page, .card-surface) .card__transform-frame {
  fill: none;
  stroke: #f0d99a;
  stroke-linecap: round;
  stroke-width: 1.6;
}

:is(.battle-page, .card-surface) .card__transform-sand {
  fill: #ffd76a;
}

/*
 * 砂時計を、変わるまで 2 秒ごとに逆さにひっくり返す（0.5 秒で半回転し、1.5 秒止める）。
 * 4 秒で 1 回転にし、周期の境目でも向きが戻らないようにする（砂の形は上下で違うので、0 度へ戻ると跳んで見える）
 */
@keyframes card-transform-flip {
  0% {
    transform: rotate(0deg);
  }

  12.5%,
  50% {
    transform: rotate(180deg);
  }

  62.5%,
  100% {
    transform: rotate(360deg);
  }
}

@keyframes card-ability-blink {
  0%,
  100% {
    opacity: 0.45;
  }

  50% {
    opacity: 0.7;
  }
}

/* 動きを減らす設定では明滅させず、いちばん濃いときの濃さで止める */
@media (prefers-reduced-motion: reduce) {
  :is(.battle-page, .card-surface) .card__ability,
  :is(.battle-page, .card-surface) .card__poison {
    animation: none;
    opacity: 0.7;
  }
}

/* 動きを減らす設定では、砂時計をひっくり返さない（輪は残り時間を表すので回す） */
@media (prefers-reduced-motion: reduce) {
  :is(.battle-page, .card-surface) .card__transform-icon {
    animation: none;
  }
}
