/* ==========================================================
   life-modal.css — 通用兴趣模态框与互动特效模块 (LifeCollage Modal + Effects)
   ------------------------------------------------------------
   物理拆分层, 不使用 CSS @import, 在 file:// 协议下零跨域错误。
   在 index.html <head> 中按顺序引入 (后加载覆盖前加载, 15 CSS):

       1.  base.css                    (基础层: 重置 + 变量 + 纸纹 + scrollbar)
       2.  scene.css                   (场景层: 海草 + 潜水员 + 标题 + 进场)
       3.  portfolio-layout.css        (作品集骨架)
       4.  portfolio-grid.css          (作品集卡片栅格)
       5.  resume.css                  (简历, 含 experience-tag 八边形化)
       6.  portfolio-preview.css       (预览: Lightbox + PDF 模态框)
       7.  portfolio-x1-sidebar.css    (X1 侧栏: Markdown + modal)
       8.  contact.css                 (联系表单 + 社交)
       9.  life-layout.css             (兴趣骨架 + 4 张核心卡 + 散落扑克牌)
      10.  life-modal.css              ← 本文件 (通用模态 + 4 主题色 + 流体特效 + 模态响应式)
      11.  life-marathon.css           (Marathon 列表 + 节点 + 响应式) ← 原 lifeCollage.css 一部分
      12.  life-travel.css             (Travel 地图 + 时间轴 + 响应式) ← 原 lifeCollage.css 一部分
      13. life-photography.css        (Photography 骨架位, Backlog #27 待实现)
      14. life-finance.css            (Finance 骨架位, Backlog #27 待实现)
      15. responsive.css              (全局响应兜底)

   完整文件结构与职责归属见 [WORKFLOW.md §4](./WORKFLOW.md).

   包含:
       1. body.modal-open 全局状态 (overflow: hidden + 卡片 pointer-events 关闭)
       2. .collage-modal-overlay + .active 状态
       3. .collage-modal-container (八角形切角 + 物理硬投影 + 顶部 3px 描边)
       4. 4 个模态专属主题色 (#marathon-modal / #travel-modal / #photography-modal / #finance-modal)
       5. .collage-modal-container::before (顶部 3px 描边纸带)
       6. .collage-modal-container::after (切面细线)
       7. .collage-modal-close (关闭按钮, 浏览器 z-index 最大值 2147483647)
       8. .collage-modal-header (含 .collage-modal-icon / -title / -subtitle)
       9. .collage-modal-body (scrollable + user-select: text)
      10. .collage-modal-placeholder (空状态)
      11. 流体波纹 + 物理水波置换 (复用 portfolio-grid.css @keyframes)
      12. 模态专属响应式 (1024 / 768 — 全屏化)
      13. Pointer-events 终极兏底 (模态激活时, 强制顶层 + 底部卡片让位)

   【深海纸雕铁律】
   - 不透明: .collage-modal-container background #0b1116 (深海墨蓝) 全不透明
            overlay 0.96 不透明 (仅 backdrop 弱透明, 容器绝不透明)
   - 硬边: .collage-modal-container border-radius: 0 直角
            八角形切角 (12px 斜切, 与 .project-card 同步)
            .collage-modal-close 允许微圆 (按钮专用)
   - 物理投影:
     · .collage-modal-overlay: 仅 z-index 9999999, 不用模糊
     · .collage-modal-container: 0 8px 20px + 0 40px 80px (双层硬投影)
     · 关闭按钮 hover: 红色 (255, 67, 67, 0.18) 背景
   - 纸带厚度 3-3.5px:
     · .collage-modal-container::before 顶部 3px 描边
     · .collage-card--interactive::before 3.5px 描边 (在 life-layout.css)
   ========================================================== */


/* ==========================================================
   1. body.modal-open 全局状态
   ------------------------------------------------------------
   模态激活时: 锁定 body 滚动 + 关闭所有背景卡片的鼠标事件
   防止 "穿模" 点到后面旋转的卡 (transform hit-area 问题)
   ========================================================== */
body.modal-open {
    overflow: hidden;
}

/* Backlog #45 (2026-06-24): 恢复 OS 光标
   ------------------------------------------------------------
   base.css §3 设了 `body { cursor: none }` (隐藏 OS 光标, 用自定义 diver 潜水员 14 块纸雕拼贴 代替),
   scene.css §5 在 body.modal-open 时 `display: none` diver canvas (Backlog #37: 消除 4% 透光幽灵).
   两者叠加导致: 模态打开时**OS 光标 + diver 都消失**, 用户完全看不到光标.
   修复: 模态打开时恢复 OS 光标 (`cursor: auto !important`), 关闭时 `body.modal-open` 移除, body 回到 `cursor: none`, diver 自然接管.
   这样在 4 个 Life 模态 (Marathon/Travel/Photography/Finance/About Me) + Portfolio Lightbox 5 个 open 函数都生效.
   ========================================================== */
body.modal-open,
body.modal-open * {
    cursor: auto !important;
}

/* 可点击元素 (关闭按钮, 链接) 仍显示手指光标 */
body.modal-open .collage-modal-close,
body.modal-open a,
body.modal-open button,
body.modal-open [role="button"] {
    cursor: pointer !important;
}

/* 拖拽区域 (例如 Travel 地图在模态中可滚动) 显示 grab/grabbing */
body.modal-open .grab-cursor,
body.modal-open .grabbing-cursor {
    cursor: grab !important;
}

/* 模态打开时, 关闭背景兴趣卡的鼠标事件 (避免旋转卡牌的 hit-area 拦截) */
body.modal-open .collage-card--interactive,
body.modal-open .collage-card--large,
body.modal-open #marathon-card,
body.modal-open #travel-card,
body.modal-open #photography-card,
body.modal-open #finance-card {
    pointer-events: none !important;
}


/* ==========================================================
   2. 通用模态框 — Overlay + Container + Active
   ------------------------------------------------------------
   100% 不透明深色卡纸 (overlay 仅微透, 容器完全实色)
   ========================================================== */
.collage-modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: rgba(2, 8, 12, 0.96);
    z-index: 9999999;
    display: none;
    justify-content: center;
    align-items: center;
    opacity: 0;
    overflow: hidden;
    transition: opacity 0.4s cubic-bezier(0.25, 1, 0.5, 1);
}

.collage-modal-overlay.active {
    opacity: 1;
    display: flex;
}

.collage-modal-container {
    width: 86vw;
    max-width: 1080px;
    height: 82vh;
    max-height: 92vh;
    background: #0b1116;                                    /* 深海墨蓝全不透明 */
    border: none;
    border-radius: 0;                                        /* 硬边直角 */
    display: flex;
    flex-direction: column;
    position: relative;
    transform: translateY(30px) scale(0.95);
    transition: transform 0.5s cubic-bezier(0.25, 1, 0.5, 1);
    overflow: visible;
    will-change: transform;
    /* 双层硬投影 (纸雕感) */
    box-shadow:
        0 8px 20px rgba(0, 0, 0, 0.85),
        0 40px 80px rgba(0, 3, 5, 0.95);
}

.collage-modal-overlay.active .collage-modal-container {
    transform: translateY(0) scale(1);
}


/* ==========================================================
   3. 4 个模态框专属主题色 — 顶部描边纸带
   ------------------------------------------------------------
   与兴趣卡主题色严格一致 (Marathon 红 / Travel 橙 / Photography 蓝紫 / Finance 金)
   通过 JS 读取卡片 .theme-color 写入 --card-accent 变量
   ::before 顶部描边纸带读取该变量实现"色彩呼出"
   ========================================================== */
#marathon-modal .collage-modal-container {
    --card-accent: rgba(179, 21, 21, 0.7) !important;
}
#travel-modal .collage-modal-container {
    --card-accent: rgba(255, 159, 67, 0.7) !important;
}
#photography-modal .collage-modal-container {
    --card-accent: rgba(120, 168, 255, 0.7) !important;
}
#finance-modal .collage-modal-container {
    --card-accent: rgba(245, 197, 66, 0.7) !important;
}

/* 模态容器顶部色条 (3px 厚度纸带) */
.collage-modal-container::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: var(--card-accent);
    z-index: 2;
}

/* 切面细线 (z-index 1, 不会盖住顶部色条) */
.collage-modal-container::after {
    content: "";
    position: absolute;
    inset: 0;
    border: 1px solid rgba(255, 255, 255, 0.08);
    pointer-events: none;
    z-index: 1;
}


/* ==========================================================
   4. 关闭按钮 (collage-modal-close) — 浏览器 z-index 最大值
   ------------------------------------------------------------
   z-index: 2147483647 是 32 位 int 最大值, 强制覆盖任何遮挡层
   悬停: 红色背景 + 边框 + 文字 (强调"危险操作"语义)
   ========================================================== */
.collage-modal-close {
    position: absolute !important;
    top: 14px !important;
    right: 14px !important;
    z-index: 2147483647 !important;             /* 浏览器最大 z-index */
    pointer-events: auto !important;
    cursor: pointer !important;
    touch-action: manipulation !important;
    -webkit-tap-highlight-color: rgba(255, 67, 67, 0.3) !important;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: rgba(255, 247, 250, 0.6);
    width: 36px;
    height: 36px;
    border-radius: 0;                            /* 硬边直角 */
    cursor: pointer;
    font-size: 20px;
    line-height: 1;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}

.collage-modal-close:hover {
    background: rgba(255, 67, 67, 0.18);
    border-color: rgba(255, 67, 67, 0.6);
    color: #ff6b6b;
}

/* 给标题区域右侧留出 × 按钮的空间, 避免重叠 */
.collage-modal-header {
    padding-right: 70px !important;              /* 36px 按钮 + 14px 右边距 + 20px 缓冲 */
}


/* ==========================================================
   5. 模态 Header / Title / Subtitle / Icon
   ========================================================== */
.collage-modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 18px 28px 14px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.collage-modal-icon {
    font-size: 24px;
    display: inline-block;
    margin-right: 10px;
    vertical-align: middle;
}

.collage-modal-title {
    font-family: 'Cinzel', 'Playfair Display', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, 'PingFang SC', sans-serif;
    font-size: 1.4rem;
    text-transform: uppercase;
    letter-spacing: 0.2em;
    color: rgba(245, 247, 250, 0.95);
    margin: 0;
    display: inline-block;
    vertical-align: middle;
    font-weight: 400;
}

.collage-modal-subtitle {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, 'PingFang SC', sans-serif;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.15em;
    color: rgba(245, 247, 250, 0.45);
    margin: 4px 0 0 36px;
    font-weight: 300;
}


/* ==========================================================
   6. 模态 Body / Placeholder (可滚动正文 + 空状态)
   ------------------------------------------------------------
   body 强制 user-select: text (允许选择文本, 区别于卡片的 none)
   pointer-events: auto 兜底, 防止"内联 div 被误点穿透"
   ========================================================== */
.collage-modal-body {
    flex: 1 1 0;
    min-height: 0;
    overflow: auto;
    padding: 18px 28px 24px;
    user-select: text;
    -webkit-user-select: text;
    pointer-events: auto !important;
}

.collage-modal-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: rgba(245, 247, 250, 0.4);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, 'PingFang SC', sans-serif;
    font-size: 0.9rem;
    text-align: center;
    padding: 20px;
}


/* ==========================================================
   6.1 Finance 模态标题语 (Tagline)
   ------------------------------------------------------------
   Finance 模态的"灵魂句" — 复用 .collage-modal-body 滚动容器,
   在 placeholder 上方居中放置双语 tagline.
   中文为主 (PingFang SC + 500 weight + 1.8rem), 英文为副
   (Cinzel italic + 0.85rem + 0.55 alpha), 主题色用 finance 紫调.
   ========================================================== */
.collage-modal-tagline {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 36px 24px 12px;
}

.collage-modal-tagline .tagline-zh {
    font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, sans-serif;
    font-size: 1.8rem;
    font-weight: 500;
    letter-spacing: 0.08em;
    color: rgba(245, 247, 250, 0.95);
    margin-bottom: 1.2rem;
    line-height: 1.4;
}

.collage-modal-tagline .tagline-en {
    font-family: 'Cinzel', Georgia, serif;
    font-size: 0.85rem;
    font-style: italic;
    letter-spacing: 0.04em;
    color: rgba(245, 247, 250, 0.55);
    line-height: 1.5;
}


/* ==========================================================
   7. 流体波纹 + 物理水波置换 (互动特效层)
   ------------------------------------------------------------
   潜水员接近兴趣卡时, main.js 添加 .fluid-wake / .physical-water-ripple
   触发 0.8s 动画. @keyframes fluidRipple / realWaterDisplace 已在
   portfolio-grid.css 中定义 (本文件直接复用, 不重复定义).
   ========================================================== */
.collage-card--interactive.fluid-wake {
    animation: fluidRipple 0.8s ease-in-out !important;
    filter: drop-shadow(0 0 15px rgba(0, 242, 254, 0.3)) !important;
}

.collage-card--interactive.physical-water-ripple {
    animation: realWaterDisplace 0.8s cubic-bezier(0.1, 0.8, 0.3, 1) forwards !important;
}


/* ==========================================================
   8. 模态框响应式 (仅容器大小 / 关闭按钮 / 内部 padding)
   ------------------------------------------------------------
   Marathon 卡片 / 列表 / Travel 模态地图时间轴 的响应式保留在 lifeCollage.css
   ========================================================== */

/* 8.1 ≤1024px — 模态宽度 92vw (中等屏收窄) */
@media (max-width: 1024px) {
    .collage-modal-container { width: 92vw; }
}

/* 8.2 768px 以下 — 模态全屏化 (100vw × 100vh) */
@media (max-width: 768px) {
    .collage-modal-container {
        width: 100vw;
        height: 100vh;
        max-height: 100vh;
    }
    .collage-modal-container::after { display: none; }
    .collage-modal-header { padding: 14px 20px 10px; }
    .collage-modal-body { padding: 14px 20px 20px; }
}


/* ==========================================================
   9. Pointer-events 终极兏底 — 修复鼠标事件被卡牌拦截
   ------------------------------------------------------------
   现象: 模态打开后, 旋转的兴趣卡 (.collage-card--interactive 散落扑克牌)
         transform: rotate 创造额外 hit-area, 即使 z-index 低于模态
         仍可能拦截鼠标事件.
   兏底: 模态激活时, 强制提升 overlay z-index + 关闭所有背景卡的事件.
   ========================================================== */

/* 模态激活时: 确保 modal 在最顶层, 强制接收所有鼠标事件 */
.collage-modal-overlay.is-active {
    z-index: 999999 !important;
    pointer-events: auto !important;
}

/* 模态激活时: 关闭底部卡片的鼠标事件, 防止 transform 后的 hit-area 干扰 */
body.modal-open .life-collage-card-grid,
.collage-modal-overlay.is-active ~ * .life-collage-card-grid {
    pointer-events: none !important;
}
body.modal-open .collage-card,
.collage-modal-overlay.is-active ~ * .collage-card {
    pointer-events: none !important;
    /* 不再让 transform 创造新的 hit-area; 视觉上保留但物理上让位 */
    user-select: none !important;
}


/* ==========================================================
   10. 卡牌游戏式「抽卡」过渡动效 (Card Draw Animation)
   ------------------------------------------------------------
   由 card-draw-animation.js 注入 .card-draw-wrapper / .card-draw-clone,
   通过 CSS 变量 --card-draw-tx / --card-draw-ty 传入"卡牌中心 → 视口中心"的位移,
   通过 --card-draw-glow-r / -g / -b 传入 originCard 的 --theme-color RGB 三分量
   (Backlog #28). 在 portfolio / life-collage 共用, 复用原卡牌的视觉层
   (clip-path / --rand-* / 顶部主题色高光), 仅外层 wrapper 走 4 段关键帧动画.

   关键帧节奏 (1.3s, 【Backlog #31】调整后):
     0%        起点: 在原卡位置
     12%       抽卡: 抬升 -28px + 微缩放 1.06 + 倾斜 (rotateX 8° / rotateY 20°)
     22%       补帧 1: 切半消除钟形速度曲线 (从 28% 前移)
     32%       放大 (途中): 飞向中心 55% 路径 + 倾斜收敛 + 缩放 1.22 (从 40% 前移)
     40%       悬停展示 (抵达): 中心位置 + 翻回正面 + 主题色高光最强 (从 55% 前移)
     50%       悬停展示 (呼吸脉冲): scale 1.42, 高光峰值 (从 65% 前移)
     80%       悬停展示 (停留末): 回到 1.4 (从 75% 后移 → hover +260ms ≈ +0.3s)
     100%      消失: 略微缩小至 0.92 + opacity 0, 然后 modal.open 回调

   Backlog #31 改动总览:
     scale-up 阶段: 15%→55% (520ms) 压缩为 12%→40% (416ms, -20%)
     hover 阶段:    55%→75% (260ms) 扩展为 40%→80% (520ms, +100%, ≈ +0.3s)
     fade 阶段:     75%→100% (325ms) 略缩为 80%→100% (260ms, 仍然柔和)
     删除 48% 中间补帧 (40→55 段短到 91ms 是卡顿根源之一, 简化后无此瓶颈)

   主题色高光 (Backlog #28):
     每个 keyframe 内层高光用 rgba(var(--card-draw-glow-r), var(--card-draw-glow-g),
       var(--card-draw-glow-b), <alpha>) 拼出, 让抽卡辉光跟随卡本身的主题色:
       · Marathon 红 #b31515          → 红色辉光
       · Travel 绿 #1f7a6a            → 绿色辉光
       · Photography 蓝紫 #4a5d8f     → 蓝紫辉光
       · Finance 紫 #6b4d8a           → 紫色辉光
       · Portfolio X1 紫 #6b4d8a      → 紫色辉光
       · Portfolio P1 橙 #d97a1f      → 橙色辉光
       · Portfolio M 红 #b31515       → 红色辉光
       · Portfolio S 绿 #1f7a6a       → 绿色辉光
       · Portfolio C 橄榄 #5e6a1c     → 橄榄辉光
       · Portfolio O 灰 #6a7378       → 冷灰辉光
     兜底: 未注入时 CSS 默认值 (255, 215, 110) 即历史"金色辉光".

   【陷阱 / Trap】
     旋转最大 25°: 严防超过 90° 触发 backface-visibility:hidden,
     否则 32% 关键帧"背面"会让整张卡变不可见 (修复前曾发生, Backlog #31
     从 40% 提前到 32%, 旋转角度同步收窄).
   ========================================================== */

/* ----------------------------------------------------------
   卡顿修复 (2026-06-23, 第二次迭代):
     (1) filter 拆分: 之前每个 keyframe 堆叠 2~3 个 drop-shadow (模糊半径
         18~82px), 动画 filter 强制每帧重栅+卷积 → 段切换处卡顿. 已将
         glow 拆到 .card-draw-wrapper::before 用 radial-gradient 画 (零
         filter), wrapper 只动 transform + opacity.
     (2) easing 修复: 原来用 cubic-bezier(0.25, 0.46, 0.45, 0.94)
         (ease-out-sine), 该曲线在 t=1 附近斜率 ≈ 0.11, 而下一段 t=0
         斜率 ≈ 1.84 — 关键帧边界处出现明显的速度跳变, 用户在 40% (放大
         到一半) 位置感到"卡一下再继续". 改成 ease-in-out
         (cubic-bezier(0.42, 0, 0.58, 1)) 后, 每段两端斜率都为 0,
         关键帧边界速度连续, 放大过程连贯.
     (3) 中间补帧: 12%→32% 是放大主轴 (位移+缩放都在这段), 单段太长
         会保留"钟形速度曲线"的可见起伏. 在 22% 处补线性插值中间帧,
         把这段切成两半, 让钟形被压扁, 视觉上几乎看不出"加速→减速→加速".
         【Backlog #31】删除 48% 补帧 — 40%→55% (104ms+91ms) 是卡顿根源
         之一, 此段本身太短 (ease-in-out 在 t=0.4-0.55 区间斜率 ≈ 0),
         浏览器在 7ms 级别反复触发样式重计算, 用户感受到"顿一下".
         简化后只有 1 个补帧 (22%), 段切换间隔均匀 (10%/10%/8%),
         主线程负担大幅降低.
     主题色注入方式不变: --card-draw-glow-r/g/b 三分量由 JS 注入,
     径向渐变里读同样的三个 CSS 变量.
   ---------------------------------------------------------- */
@keyframes card-draw {
    /* === 0%   起点: 在原卡位置, 准备开始 === */
    0% {
        transform: translate(0, 0) scale(1) rotateX(0deg) rotateY(0deg);
        opacity: 1;
    }
    /* === 12%  抽卡: 抬升 + 微缩放 + 倾斜 === 【Backlog #31 15→12% 提前】
       注意: 旋转 ≤ 25°, 严防触发 backface-visibility:hidden (临界 90°),
       否则 32% 关键帧"背面"会让整张卡变不可见 (曾发生的 bug) */
    12% {
        transform: translate(0, -28px) scale(1.06) rotateX(8deg) rotateY(20deg);
        opacity: 1;
    }
    /* === 22%  中间补帧 (12→32 段切半): 线性插值 ===
       【Backlog #31 28→22% 提前, 配合压缩 scale-up 阶段】 */
    22% {
        transform:
            translate(calc(var(--card-draw-tx, 0px) * 0.286),
                      calc(var(--card-draw-ty, 0px) * 0.286 - 14px))
            scale(1.14) rotateX(5deg) rotateY(14deg);
        opacity: 1;
    }
    /* === 32%  放大 (途中): 飞向中心 + 倾斜收敛 + 缩放加大
       【Backlog #31 40→32% 提前, scale-up 总时长 520ms → 416ms (-20%)】 */
    32% {
        transform:
            translate(calc(var(--card-draw-tx, 0px) * 0.55),
                      calc(var(--card-draw-ty, 0px) * 0.55))
            scale(1.22) rotateX(3deg) rotateY(8deg);
        opacity: 1;
    }
    /* === 40%  悬停展示 (抵达): 中心位置 + 翻回正面 + 主题色高光最强
       【Backlog #31 55→40% 提前, 让 scale-up 阶段在 40% 就结束,
         此后纯悬停展示, 用户看到"放大"的总时长从 520ms 降到 416ms,
         配合 ease-in-out cubic-bezier, 段切换不再卡顿】 */
    40% {
        transform:
            translate(var(--card-draw-tx, 0px),
                      var(--card-draw-ty, 0px))
            scale(1.4) rotateX(0deg) rotateY(0deg);
        opacity: 1;
    }
    /* === 50%  悬停展示 (呼吸脉冲): 微放大 1.42, 高光略增
       【Backlog #31 65→50% 提前, 配合 hover 阶段延长】 */
    50% {
        transform:
            translate(var(--card-draw-tx, 0px),
                      var(--card-draw-ty, 0px))
            scale(1.42) rotateX(0deg) rotateY(0deg);
        opacity: 1;
    }
    /* === 80%  悬停展示 (停留末): 回到 1.4, 准备进入消失
       【Backlog #31 75→80% 延长, hover 总时长从 260ms → 520ms (+260ms ≈ +0.3s),
         让用户有充足时间看到卡牌"显灵"的呼吸脉冲 + 主题色高光峰值】 */
    80% {
        transform:
            translate(var(--card-draw-tx, 0px),
                      var(--card-draw-ty, 0px))
            scale(1.4) rotateX(0deg) rotateY(0deg);
        opacity: 1;
    }
    /* === 100% 消失: 略微缩小 + 渐隐, 落地回调 modal.open
       【Backlog #31 75→80% 后, fade 阶段从 325ms 缩到 260ms, 仍然柔和】 */
    100% {
        transform:
            translate(var(--card-draw-tx, 0px),
                      var(--card-draw-ty, 0px))
            scale(0.92) rotateX(0deg) rotateY(0deg);
        opacity: 0;
    }
}

/* glow 同步动画: 只动 opacity + scale, 与 wrapper 主动画完全同步.
   关键帧时间点与 @keyframes card-draw 一一对齐 (Backlog #31 同步调整),
   保证光晕节奏与卡牌放大节奏一致, 不会出现"卡牌在动、光晕不动"的错位.
   【Backlog #31】删除 48% 中间补帧 (与 card-draw 同步精简), 15%→12%,
   28%→22%, 40%→32%, 55%→40%, 65%→50%, 75%→80%. */
@keyframes card-draw-glow {
    0%   { opacity: 0;    transform: scale(0.92); }
    12%  { opacity: 0.55; transform: scale(1.00); }
    22%  { opacity: 0.70; transform: scale(1.06); }
    32%  { opacity: 0.85; transform: scale(1.12); }
    40%  { opacity: 0.95; transform: scale(1.18); }
    50%  { opacity: 1.00; transform: scale(1.22); }
    80%  { opacity: 0.85; transform: scale(1.18); }
    100% { opacity: 0;    transform: scale(1.05); }
}

/* wrapper: 静态基类, 不带 transition (动画完全交给 @keyframes 控制).
   【Backlog #31-F 卡顿修复】移除 backface-visibility: hidden:
   - 旋转角度最大 √(8²+20²) ≈ 21.5°, 远低于 90° 临界
   - 没有"翻到背面"的可能, backface-visibility 强制合成层是浪费
   - 没有 backface-visibility, 浏览器不会为 wrapper 创建额外合成层,
     减少 GPU 内存占用与层合成开销 */
.card-draw-wrapper {
    /* (原 backface-visibility: hidden 已删除, Backlog #31-F) */
    /* 占位保留: wrapper 仍依赖 perspective: 1200px (JS 注入) 提供 3D 视角 */
}

/* glow 层: 绝对定位的 ::before 伪元素, 用径向渐变画主题色光晕.
   - 不走 filter (drop-shadow 模糊是卡顿根源), 用 radial-gradient 颜色衰减
     模拟柔光, GPU 直接合成, 零重栅.
   - 默认 opacity: 0, 动画期间由 card-draw-glow 推到 1 再收回.
   - 必须 z-index: -1, 否则会盖在 clone 上挡住卡面.
   - inset / 百分比基于 wrapper 自身 (position:fixed 容器内, ::before absolute
     用百分比时以 wrapper 为 containing block). */
.card-draw-wrapper::before {
    content: '';
    position: absolute;
    left: -25%;
    top: -25%;
    width: 150%;
    height: 150%;
    border-radius: 18%;
    pointer-events: none;
    z-index: -1;
    opacity: 0;
    background: radial-gradient(
        ellipse at center,
        rgba(var(--card-draw-glow-r, 255),
             var(--card-draw-glow-g, 215),
             var(--card-draw-glow-b, 110), 0.65) 0%,
        rgba(var(--card-draw-glow-r, 255),
             var(--card-draw-glow-g, 215),
             var(--card-draw-glow-b, 110), 0.30) 28%,
        rgba(var(--card-draw-glow-r, 255),
             var(--card-draw-glow-g, 215),
             var(--card-draw-glow-b, 110), 0) 62%
    );
    will-change: opacity, transform;
    transform-origin: center center;
}

/* 启动动画: .card-draw-animating 类由 JS 在下一帧注入.
   timing-function 改用 ease-in-out (cubic-bezier(0.42, 0, 0.58, 1)):
   两端斜率都为 0, 关键帧边界速度连续, 放大过程不再"顿一下". */
.card-draw-wrapper.card-draw-animating {
    animation: card-draw 1.3s ease-in-out forwards;
}

/* glow 同步动画: 与 wrapper 同一时长/同一 easing, 关键帧时间点一一对齐.
   只动 opacity + scale, 零 filter, 零重栅, 4 段切换不再卡顿. */
.card-draw-wrapper.card-draw-animating::before {
    animation: card-draw-glow 1.3s ease-in-out forwards;
}

/* clone 内层: 强制 block + 关闭交互 + 保留 clone 自带的 transform (--rand-*)
   【Backlog #31-F 卡顿修复】clone 静态规则:
   - clip-path: none + box-shadow: none (与 JS inline 同步, 防止 cascade 顺序问题)
   - will-change: transform 提示浏览器提至独立合成层
   - 不设 transition, 让 transform 完全由外层 wrapper 的 @keyframes 控制
   - background-clip / border 等保留 (clone 仍要看起来像卡牌, 只是去掉
     动画期间高开销的 clip-path 和 box-shadow 计算) */
.card-draw-clone {
    position: relative;
    display: block;
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    /* 摆脱逐帧 clip-path 计算 (由 wrapper 接管) */
    clip-path: none !important;
    /* 摆脱逐帧 box-shadow 重栅 */
    box-shadow: none !important;
    /* GPU 合成层提示 */
    will-change: transform;
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
}

/* 减弱动画偏好: 直接跳过整个抽卡过渡, JS 也走 fallback */
@media (prefers-reduced-motion: reduce) {
    .card-draw-wrapper.card-draw-animating,
    .card-draw-wrapper.card-draw-animating::before {
        animation: none;
    }
}