/* ==========================================================================
   presenter.css —— 虚拟主播预览页（内部）
   --------------------------------------------------------------------------
   三个版本共用一套骨架：
     .pv-stage      16:9 舞台，人物层 / 嘴型层 / 眼睑层 / 字幕层都叠在里面
     .pv-sub        字幕（第一行中文，第二行英文；没中文就只有英文一行）
     .pv-ctl        控制栏
   2.5D 版只是在同一骨架上多加 perspective / 视差 / 灯光，不换结构。

   动画只用 transform 与 opacity，避免触发布局重排。
   ========================================================================== */

:root {
  --pv-bg: #07090c;
  --pv-panel: #12161c;
  --pv-line: #232a34;
  --pv-text: #e6e8ea;
  --pv-dim: #8d96a3;
  --pv-gold: #c8a96a;
  --pv-red: #e2534a;
  --pv-green: #4ab87a;
}

* { box-sizing: border-box; }

.pv-body {
  margin: 0;
  background: var(--pv-bg);
  color: var(--pv-text);
  font: 15px/1.7 -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif;
}

/* ---------------- 授权风险横幅：刻意做得刺眼，不许被忽略 ---------------- */
.pv-alert {
  padding: 12px 18px;
  font-size: 14px;
  line-height: 1.7;
  font-weight: 600;
  letter-spacing: .2px;
}
.pv-alert--risk  { background: #3a0f0d; color: #ffb4ae; border-bottom: 2px solid var(--pv-red); }
.pv-alert--fatal { background: #3a2a05; color: #ffd98a; border-bottom: 2px solid var(--pv-gold); }
/* 公开页固定说明条：当前形象是自制占位，不是正式主播。语气中性，不用红色告警。 */
.pv-alert--note  { background: #12202e; color: #cfe0f0; border-bottom: 1px solid #2a4257; }

/* ---------------- 顶栏 ---------------- */
.pv-topbar {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 10px 18px; background: var(--pv-panel);
  border-bottom: 1px solid var(--pv-line);
}
.pv-topbar__brand { color: var(--pv-gold); font-weight: 700; letter-spacing: .5px; }
.pv-topbar__tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.pv-tab {
  padding: 6px 14px; border-radius: 6px; text-decoration: none;
  color: var(--pv-dim); border: 1px solid transparent; font-size: 14px;
}
.pv-tab:hover { color: var(--pv-text); background: #1a2028; }
.pv-tab.is-active { color: #0b0d10; background: var(--pv-gold); font-weight: 700; }
.pv-setpick { margin-left: auto; color: var(--pv-dim); font-size: 13px; }
.pv-setpick select {
  margin-left: 8px; padding: 5px 8px; background: #0d1116;
  color: var(--pv-text); border: 1px solid var(--pv-line); border-radius: 5px;
}

/* ---------------- 舞台：严格 16:9 ---------------- */
.pv-wrap { max-width: 1280px; margin: 18px auto; padding: 0 14px; }

.pv-stage {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
  overflow: hidden;
  background: #000;
  border: 1px solid var(--pv-line);
  border-radius: 10px;
  /* ★ 容器查询单位的基准：字幕字号必须跟着**舞台宽度**走，不能跟视口走。
     否则比较页里三张小卡的字幕会按整屏宽度算，字大到盖满预览。 */
  container-type: inline-size;
  contain: paint;
}
.pv-stage:fullscreen { border: 0; border-radius: 0; aspect-ratio: auto; height: 100vh; }
.pv-stage:fullscreen .pv-ctl { opacity: 0; transition: opacity .3s; }
.pv-stage:fullscreen.is-uiactive .pv-ctl { opacity: 1; }

/* 人物层
   ★ 关键：.pv-figure 的宽高比由 JS 按素材清单的 canvas 设成 --pv-ar，
     再用 width:100% + max-height:100% 让它在舞台内自动留边。
     这样容器**恰好等于图片的实际显示区域**，嘴型/眼睑贴片按百分比定位才对得上。
     早期版本让 .pv-figure 铺满舞台、图片用 object-fit:contain 居中留边，
     于是贴片坐标系与图片显示区差一个 letterbox 偏移，嘴贴到了脸外面。 */
.pv-layer { position: absolute; inset: 0; }
.pv-figure {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  transform-origin: 50% 60%;
  aspect-ratio: var(--pv-ar, 16 / 9);
  width: 100%;
  max-width: 100%;
  max-height: 100%;
}
/* ★ 2026-07-31 V2：fill → contain。容器比例正确时两者渲染完全一致；
   容器比例算错时 contain 只会留透明边，绝不把人物拉宽/拉长 —— 失效安全。 */
.pv-figure__base { display: block; width: 100%; height: 100%; object-fit: contain; }

/* 嘴型 / 眼睑贴片：绝对定位到清单里的 geometry 坐标（由 JS 换算成百分比） */
.pv-patch { position: absolute; image-rendering: auto; pointer-events: none; }
.pv-patch--hidden { opacity: 0; }

/* CSS 合成眼睑：没有真实闭眼帧时用它。用 scaleY 而不是改高度，避免重排 */
.pv-eyelid {
  position: absolute; pointer-events: none;
  background: var(--pv-skin, #f2cdb4);
  transform-origin: 50% 0%;
  transform: scaleY(0);
  will-change: transform;
}
.pv-eyelid.is-blink { transform: scaleY(1); }

/* 呼吸 + 极小幅身体摆动：只在播放时加 class，停止就撤掉 */
.pv-figure.is-breathing { animation: pv-breath 4.2s ease-in-out infinite; }
@keyframes pv-breath {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  50%      { transform: translate(-50%, calc(-50% - 2px)) scale(1.006); }
}
.pv-figure.is-swaying { animation: pv-breath 4.2s ease-in-out infinite, pv-sway 9s ease-in-out infinite; }
@keyframes pv-sway {
  0%, 100% { rotate: -0.35deg; }
  50%      { rotate: 0.35deg; }
}

/* ==========================================================================
   多姿势素材集（zb-anchor-hd-v1 等）—— 双缓冲舞台交叉淡化
   --------------------------------------------------------------------------
   两个 .pv-pose-stage 严格重叠铺满 .pv-figure，切换时一个淡出、一个淡入，
   过程中始终有一层完全不透明的人物，不会出现"人物短暂消失"或闪白。
   嘴/眼用 clip-path 裁出对应区域再叠加另一张图，只有该区域跟着变化。
   ========================================================================== */
.pv-pose-stage {
  position: absolute; inset: 0;
  opacity: 1;
}
.pv-pose-stage--back { z-index: 1; }
.pv-pose-stage.is-active { z-index: 2; }
.pv-pose-base { display: block; width: 100%; height: 100%; object-fit: contain; }
.pv-pose-clip {
  position: absolute; inset: 0;
  opacity: 0;               /* 由 PoseMouth/PoseBlink 用 JS 改这个值 */
  pointer-events: none;
  transition: opacity 90ms linear;   /* 嘴/眼状态切换的短渡变，不是身体动作那个 transitionMs */
}
.pv-pose-clip img { display: block; width: 100%; height: 100%; object-fit: contain; }

/* ---------------- 字幕 ---------------- */
.pv-sub {
  position: absolute; left: 0; right: 0;
  /* ★ 留出控制栏高度，字幕不压在按钮上；全屏隐藏控制栏后仍留同样边距 */
  bottom: max(52px, 9cqw);
  padding: 0 5%;
  text-align: center;
  pointer-events: none;
  text-shadow: 0 2px 10px rgba(0, 0, 0, .95), 0 0 3px rgba(0, 0, 0, .8);
}
.pv-sub__meta {
  display: inline-flex; align-items: center; gap: 8px;
  margin-bottom: 8px; padding: 3px 10px; border-radius: 4px;
  background: rgba(0, 0, 0, .55); font-size: clamp(10px, 1.5cqw, 15px);
  color: var(--pv-gold); letter-spacing: .5px;
}
/* ★ 还没加载到播放列表时，.pv-sub__meta 里四个 span 全是空的，但它自己
   仍有 padding + rgba(0,0,0,.55) 底色，于是在人物身上留下一个 36×6 的
   小灰杠（实测 elementFromPoint 命中的就是它，位置固定在舞台 50%,81%，
   与素材集无关，中性占位集同样出现）。
   内容为空就整行不显示 —— 不动人物素材，也不用图片去盖。 */
/* 主用：JS 在四个 span 全空时打上 is-empty（兼容不支持 :has() 的旧浏览器）
   增强：:has() 作为纯 CSS 兜底，两者任一生效即可 */
.pv-sub__meta.is-empty { display: none; }
.pv-sub__meta:not(:has(> *:not(:empty))) { display: none; }
.pv-sub__kind {
  padding: 1px 7px; border-radius: 3px;
  background: var(--pv-gold); color: #0b0d10; font-weight: 700; font-size: .92em;
}
.pv-sub__zh {
  margin: 0 0 6px; font-weight: 700; line-height: 1.4;
  font-size: clamp(13px, 3.4cqw, 38px);
}
.pv-sub__en {
  margin: 0; color: #cfd6de; line-height: 1.45;
  font-size: clamp(11px, 2.4cqw, 26px);
  overflow-wrap: break-word;      /* 长英文换行，不横向溢出 */
}
/* 没有可靠中文时，英文升为主行 —— 绝不留空白中文行 */
/* ★ 单语显示：中文模式隐藏英文行，英文模式隐藏中文行。
   两行同时出现正是"中英来回闪"的观感来源之一。 */
.pv-sub:not(.is-en-only) .pv-sub__en { display: none; }
/* ★ 字幕区固定最小高度：中英文长度不同会让整块忽高忽低，画面上下跳。 */
.pv-sub { min-height: 22cqh; }
.pv-sub__zh, .pv-sub__en { min-height: 1.35em; }
.pv-sub.is-en-only .pv-sub__zh { display: none; }
.pv-sub.is-en-only .pv-sub__en {
  color: var(--pv-text); font-weight: 700;
  font-size: clamp(12px, 3.0cqw, 32px);
}

/* GIF 原版：素材自带烧录字幕，我们的字幕上移，不遮住第三方来源信息 */
.pv-sub--above-burnin { bottom: 22%; }
.pv-burnin-note {
  position: absolute; left: 0; right: 0; bottom: 1.5%;
  text-align: center; font-size: clamp(9px, 1.4cqw, 13px);
  color: #ffb4ae; background: rgba(58, 15, 13, .82);
  padding: 4px 8px; pointer-events: none;
}
/* ★ 顶部变体：底部那条会被控制栏 .pv-ctl 盖住，看不见。
   「没有可公开 GIF」这类必须让访客读到的说明一律用这个，放舞台顶部。
   语气中性不用红色 —— 它不是风险告警，是功能说明。 */
.pv-burnin-note--top {
  bottom: auto; top: 0; line-height: 1.7;
  color: #cfe0f0; background: rgba(18, 32, 46, .92);
  border-bottom: 1px solid #2a4257; padding: 8px 10px;
  /* ★ 必须给 z-index：人物层是 presenter-common.js 在这个节点**之后**插进舞台的，
     同为绝对定位又都没有 z-index 时后插入者盖在上面，提示条就看不见了。
     4 < .pv-ctl 的 5，所以控制栏仍在最上层。 */
  z-index: 4;
}

/* ---------------- 待机 ---------------- */
.pv-idle {
  position: absolute; inset: 0; display: grid; place-content: center;
  gap: 10px; text-align: center; background: rgba(4, 6, 9, .82);
}
/* ★ [hidden] 的 display:none 来自 UA 样式表，优先级低于上面的 display:grid，
   不显式写这一条待机层永远盖在画面上。 */
.pv-idle[hidden] { display: none; }
.pv-idle__t { font-size: clamp(14px, 3.6cqw, 34px); font-weight: 700; color: var(--pv-gold); }
.pv-idle__s { color: var(--pv-dim); font-size: 14px; }

/* ---------------- 控制栏 ---------------- */
.pv-ctl {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 5;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 12px;
  background: linear-gradient(to top, rgba(0, 0, 0, .88), rgba(0, 0, 0, 0));
}
.pv-btn {
  padding: 7px 13px; border-radius: 6px; cursor: pointer;
  background: #1a2028; color: var(--pv-text);
  border: 1px solid var(--pv-line); font-size: 13px; line-height: 1;
}
.pv-btn:hover { background: #232b35; }
.pv-btn:disabled { opacity: .4; cursor: not-allowed; }
.pv-btn--primary { background: var(--pv-gold); color: #0b0d10; font-weight: 700; border-color: var(--pv-gold); }
.pv-ctl select, .pv-ctl input[type=range] { accent-color: var(--pv-gold); }
.pv-ctl select {
  padding: 6px 8px; background: #0d1116; color: var(--pv-text);
  border: 1px solid var(--pv-line); border-radius: 5px; font-size: 13px;
}
.pv-ctl__spacer { flex: 1 1 auto; }
.pv-ctl__stat { font-size: 12px; color: var(--pv-dim); font-variant-numeric: tabular-nums; }

/* ---------------- 说明面板 ---------------- */
.pv-note {
  margin: 14px 0; padding: 14px 16px;
  background: var(--pv-panel); border: 1px solid var(--pv-line); border-radius: 8px;
}
.pv-note h2 { margin: 0 0 8px; font-size: 15px; color: var(--pv-gold); }
.pv-note ul { margin: 0; padding-left: 20px; color: var(--pv-dim); font-size: 13.5px; }
.pv-note code { background: #0d1116; padding: 1px 5px; border-radius: 3px; color: #d8c79a; }
.pv-kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; font-size: 13px; }
.pv-kv dt { color: var(--pv-dim); }
.pv-kv dd { margin: 0; font-variant-numeric: tabular-nums; }

/* ---------------- 2.5D：分层视差 + 灯光 ---------------- */
.pv-stage--25d { perspective: 900px; perspective-origin: 50% 45%; }
.pv-stage--25d .pv-layer--bg,
.pv-stage--25d .pv-layer--fg { transform-style: preserve-3d; will-change: transform; }
.pv-stage--25d .pv-figure { will-change: transform; }
/* 演播室灯光呼吸：只动 opacity */
.pv-light {
  position: absolute; inset: -10%;
  pointer-events: none; mix-blend-mode: screen; opacity: .28;
  background:
    radial-gradient(38% 46% at 26% 18%, rgba(96, 168, 255, .55), transparent 70%),
    radial-gradient(34% 42% at 74% 22%, rgba(255, 214, 140, .40), transparent 70%);
  animation: pv-light 7.5s ease-in-out infinite;
}
@keyframes pv-light { 0%, 100% { opacity: .20; } 50% { opacity: .40; } }
/* 细微景深：背景轻微模糊，人物清晰 */
.pv-stage--25d .pv-layer--bg { filter: blur(1.4px) saturate(1.05); }

/* ---------------- 比较页 ---------------- */
.pv-cards {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
  max-width: 1440px; margin: 18px auto; padding: 0 14px;
}
.pv-card {
  background: var(--pv-panel); border: 1px solid var(--pv-line);
  border-radius: 10px; overflow: hidden; display: flex; flex-direction: column;
}
.pv-card.is-active { border-color: var(--pv-gold); box-shadow: 0 0 0 1px var(--pv-gold) inset; }
.pv-card__hd {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 14px; border-bottom: 1px solid var(--pv-line);
}
.pv-card__hd h3 { margin: 0; font-size: 15px; }
.pv-card__badge {
  margin-left: auto; padding: 2px 8px; border-radius: 4px;
  font-size: 11.5px; font-weight: 700;
}
.pv-card__badge--off { background: #33230a; color: #e8c374; }
.pv-card__badge--on  { background: #10331f; color: #7fd6a3; }
.pv-card__stage { position: relative; }
.pv-card__body { padding: 12px 14px; }
.pv-card__body .pv-kv { margin-bottom: 10px; }
.pv-card__acts { display: flex; gap: 8px; padding: 0 14px 14px; flex-wrap: wrap; }
.pv-warn { color: #ffb4ae; font-size: 12.5px; margin: 6px 0 0; }
.pv-ok { color: #7fd6a3; }

/* 比较页里的预览默认暂停，只有激活的那张跑动画 —— 避免比较页自己吃 CPU */
.pv-card:not(.is-active) .pv-figure { animation: none !important; }
.pv-card:not(.is-active) .pv-light  { animation: none !important; }

@media (max-width: 640px) {
  .pv-topbar { gap: 10px; }
  .pv-setpick { margin-left: 0; width: 100%; }
  .pv-ctl { gap: 6px; padding: 8px; }
  .pv-btn { padding: 6px 10px; font-size: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .pv-figure, .pv-light { animation: none !important; }
}

/* ---------------- 2.5D 前景视差（变量由 JS 每帧写入）---------------- */
/* 与 is-breathing / is-swaying 的 animation 共存：动画改的是 translate/rotate，
   这里用独立的 translate/rotate 属性，互不覆盖。 */
.pv-stage--25d .pv-figure {
  translate: var(--pv-px, 0) var(--pv-py, 0);
  rotate: y var(--pv-ry, 0deg);
}
