* { margin: 0; padding: 0; box-sizing: border-box; }
/* 100dvh 而不是 100vh：移动端 100vh 按"地址栏收起后"的高度算，比实际可视区域大，
   会把聊天输入框顶到屏幕外面去。dvh 跟随可视区域变化，iOS 15.4+ / Chrome 108+ 支持，
   前一行 100vh 是给老浏览器的兜底。 */
body { font-family: "PingFang SC", "Microsoft YaHei", sans-serif; background: #14141f; color: #eee; height: 100vh; height: 100dvh; overflow: hidden; -webkit-text-size-adjust: 100%; }
.hidden { display: none !important; }

#join-overlay { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background: #14141f; z-index: 10; padding: 16px; }
.join-card { text-align: center; padding: 40px; background: #1e1e2e; border-radius: 16px; width: 100%; max-width: 360px; }
.join-card h1 { margin-bottom: 24px; font-size: 22px; }
/* font-size 必须 >= 16px：iOS Safari 在聚焦更小的输入框时会自动放大整个页面，
   放大后不会自己缩回去，用户就被卡在一个横向滚动的页面里。 */
.join-card input { display: block; width: 100%; max-width: 260px; margin: 0 auto 16px; padding: 12px 14px; border-radius: 8px; border: 1px solid #444; background: #14141f; color: #eee; font-size: 16px; }
.join-card button { padding: 12px 32px; min-height: 44px; border: none; border-radius: 8px; background: #e8642c; color: #fff; font-size: 15px; cursor: pointer; }

#room { display: grid; grid-template-columns: 380px 1fr 320px; height: 100vh; height: 100dvh; }
.console-panel { min-height: 0; border-right: 1px solid #2a2a3e; background: #191926; }
.console-panel iframe { display: block; width: 100%; height: 100%; border: 0; }
.stage { display: flex; flex-direction: column; padding: 16px; gap: 12px; min-width: 0; min-height: 0; }
.state-bar { display: flex; gap: 16px; align-items: center; background: #1e1e2e; padding: 10px 16px; border-radius: 10px; font-size: 14px; }
#period { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.phase-badge { padding: 2px 10px; border-radius: 999px; background: #2e7d32; white-space: nowrap; flex-shrink: 0; }
.phase-badge.sealed { background: #c62828; }
.phase-badge.drawing { background: #ef6c00; }
#countdown { margin-left: auto; font-variant-numeric: tabular-nums; font-size: 18px; flex-shrink: 0; }

.avatar-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px; background: radial-gradient(circle at 50% 30%, #2a2a3e, #14141f); border-radius: 14px; }
.avatar { position: relative; }
.avatar-video { width: 240px; height: 240px; border-radius: 50%; object-fit: cover; background: #1e1e2e; display: block; }
.avatar-fallback { width: 240px; height: 240px; border-radius: 50%; background: #1e1e2e; display: flex; align-items: center; justify-content: center; font-size: 14px; color: #888; text-align: center; padding: 16px; }
.waves { display: none; justify-content: center; gap: 5px; margin-top: 14px; height: 24px; }
.avatar.speaking .waves { display: flex; }
.waves span { width: 5px; background: #e8642c; border-radius: 3px; animation: wave .6s ease-in-out infinite; }
.waves span:nth-child(2) { animation-delay: .1s; }
.waves span:nth-child(3) { animation-delay: .2s; }
.waves span:nth-child(4) { animation-delay: .3s; }
.waves span:nth-child(5) { animation-delay: .4s; }
@keyframes wave { 0%, 100% { height: 6px; } 50% { height: 24px; } }

.subtitle { min-height: 24px; max-width: 85%; text-align: center; font-size: 16px; color: #ffd9a0; }
.balls { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; min-height: 48px; }
.ball { width: 44px; height: 44px; border-radius: 50%; background: radial-gradient(circle at 35% 30%, #ff9d6c, #e8642c); display: flex; align-items: center; justify-content: center; font-size: 20px; font-weight: 700; animation: pop .4s; }
@keyframes pop { from { transform: scale(0); } }

.chat-panel { display: flex; flex-direction: column; min-height: 0; border-left: 1px solid #2a2a3e; background: #191926; }
.chat-log { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 12px; display: flex; flex-direction: column; gap: 8px; font-size: 14px; overflow-wrap: anywhere; }
.chat-log .msg .nick { color: #7fb3ff; margin-right: 6px; }
.chat-log .msg.host .nick { color: #e8642c; }
.chat-log .msg.system { color: #888; font-size: 13px; }
#chat-form { display: flex; gap: 8px; padding: 10px; border-top: 1px solid #2a2a3e; }
#chat-form input { flex: 1; min-width: 0; padding: 10px 12px; border-radius: 8px; border: 1px solid #444; background: #14141f; color: #eee; font-size: 16px; }
#chat-form button { padding: 10px 16px; min-height: 44px; flex-shrink: 0; border: none; border-radius: 8px; background: #e8642c; color: #fff; font-size: 15px; cursor: pointer; }

.join-error {
  margin: 8px 0 0;
  color: #ff6b6b;
  font-size: 14px;
  text-align: center;
}

/* ---------- 直播叠加 ---------- */
/* 底图视频加载失败时 .has-live 不会加上，下面整块不生效，页面退回数字人居中的旧样子。 */
.avatar-wrap { position: relative; overflow: hidden; }

#live-player { display: none; }
.avatar-wrap.has-live #live-player { display: block; position: absolute; inset: 0; }

/* contain 而不是 cover：实景开奖画面裁掉边缘可能把关键信息裁没了 */
.avatar-wrap.has-live #live-player video {
  display: block; width: 100%; height: 100%;
  object-fit: contain; background: #000;
}

/* 有直播垫底时，数字人不是浮在角落，而是正好盖在底图里原主播的方框上，
   底色用视频里方框周围那种深蓝，看起来就像原片把主播换掉了。
   RTMP/H.264 没有透明通道，数字人没法真的"融"进画面，盖住是成本最低的做法。 */
/* --vpad-l/r/t/b 是"画面实际显示矩形"到容器四边的距离，--vw/--vh 是该矩形的
   宽高，都由 app.js 的 syncOverlayToVideo() 用 video 的实测 bounding rect 算出来
   ——不能靠容器尺寸推，因为播放器未必撑满容器。四边分开存，不假设留白对称。
   不用 object-fit:cover 顶满，是因为 cover 会裁掉画面边缘，实景开奖机很可能
   就在被裁的那一圈里。
   下面的百分比是照 assets/1.mp4 量出来的：原主播方框在 1280×720 画面里
   占 x 970–1238、y 50–262（含一圈浅色边）。换底图视频要重新量。 */
/* 2026-09-13 按要求放大一倍：宽高各 ×2，右上角仍钉在原方框的右上角（右边缘 96.72%），
   于是左边缘从 75.78% 移到 54.84%，往左盖掉一部分游戏画面。要回到原尺寸把四个数改回
   0.7578 / 0.0694 / 0.2094 / 0.2944。 */
.avatar-wrap.has-live #avatar {
  position: absolute; z-index: 2;
  left: calc(var(--vpad-l, 0px) + var(--vw, 0px) * 0.5484);
  top: calc(var(--vpad-t, 0px) + var(--vh, 0px) * 0.0694);
  width: calc(var(--vw, 0px) * 0.4188);
  height: calc(var(--vh, 0px) * 0.5888);
  padding: 0; border-radius: 4px; overflow: hidden;
  background: #00004c;
}
/* 数字人素材是 4:3 半身（1108×828），方框约 1.26:1 略窄：cover 裁掉两侧少许，居中即可 */
.avatar-wrap.has-live #avatar .avatar-video, .avatar-wrap.has-live #avatar .avatar-fallback {
  width: 100%; height: 100%; border-radius: 0;
  object-fit: cover; object-position: 50% 50%;
}
.avatar-wrap.has-live #avatar .avatar-fallback { font-size: 11px; padding: 8px; }
.avatar-wrap.has-live #avatar .waves { position: absolute; left: 0; right: 0; bottom: 6px; margin: 0; }
/* 待机不淡出：数字人常驻画面，"正在说话"只靠声波条和字幕提示。 */

/* 字幕改成底部横幅。数字人在右上角，底部整行都是字幕的。 */
.avatar-wrap.has-live .subtitle {
  position: absolute; z-index: 3;
  left: calc(var(--vpad-l, 0px) + 16px);
  right: calc(var(--vpad-r, 0px) + 16px);
  bottom: calc(var(--vpad-b, 0px) + 22px);
  max-width: none; text-align: left;
  background: rgba(0, 0, 0, .55); padding: 8px 12px; border-radius: 10px;
}
.avatar-wrap.has-live .subtitle:empty { display: none; }

/* ---------- 手机竖屏 ---------- */
/* 桌面版是「舞台 | 320px 聊天栏」的两列。窄屏下 1fr 的自动最小尺寸是 min-content，
   舞台不肯缩到自己内容宽度以下，于是整行撑到 612px、聊天栏被挤出屏幕右侧、
   页面横向滚动 —— 手机上连输入框都够不着。这里改成上下两行。 */
@media (max-width: 768px) {
  #room { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) minmax(0, 42%); }
  .console-panel { display: none; }

  .stage { padding: 10px; gap: 8px; }
  .state-bar { gap: 8px; padding: 8px 12px; font-size: 13px; }
  #countdown { font-size: 16px; }

  .avatar-wrap { gap: 12px; }
  .avatar-video, .avatar-fallback { width: 104px; height: 104px; }
  .avatar-fallback { font-size: 11px; padding: 8px; }
  .waves { margin-top: 10px; }

  .subtitle { font-size: 15px; max-width: 92%; }
  .balls { gap: 8px; min-height: 40px; }
  .ball { width: 38px; height: 38px; font-size: 17px; }

  .chat-panel { border-left: none; border-top: 1px solid #2a2a3e; }
  .chat-log { padding: 10px; }
  /* iPhone 的home 指示条会盖住底部：safe-area 内边距把发送按钮抬上来。
     需要 index.html 的 viewport 带 viewport-fit=cover，否则 env() 恒为 0。 */
  #chat-form { padding: 8px 8px calc(8px + env(safe-area-inset-bottom)); }
  /* 数字人盖在原主播框上，随画面按比例缩放，手机上不用单独定位。 */
  .avatar-wrap.has-live #avatar .avatar-fallback { font-size: 9px; padding: 4px; }
  /* 数字人在右上角，字幕贴底走整行，两者不会打架。 */
  .avatar-wrap.has-live .subtitle {
    left: calc(var(--vpad-l, 0px) + 10px);
    right: calc(var(--vpad-r, 0px) + 10px);
    bottom: calc(var(--vpad-b, 0px) + 10px);
    font-size: 14px; padding: 6px 10px;
  }

  .join-card { padding: 28px 20px; }
  .join-card h1 { font-size: 20px; margin-bottom: 20px; }
}

/* ---------- 手机横屏 / 矮屏 ---------- */
/* 横过来只剩 ~375px 高，头像按竖屏尺寸会把号码球挤没。 */
@media (max-height: 500px) and (orientation: landscape) {
  #room { grid-template-columns: 1fr 260px; grid-template-rows: 100%; }
  .console-panel { display: none; }
  .chat-panel { border-left: 1px solid #2a2a3e; border-top: none; }
  .avatar-wrap { gap: 8px; }
  .avatar-video, .avatar-fallback { width: 72px; height: 72px; }
  .avatar-fallback { font-size: 10px; padding: 6px; }
  .waves { display: none !important; }
  .subtitle { font-size: 14px; }
  .ball { width: 34px; height: 34px; font-size: 15px; }
  .balls { min-height: 36px; }
  .join-card { padding: 20px; }
  .join-card h1 { font-size: 18px; margin-bottom: 14px; }
  .join-card input { margin-bottom: 10px; }
}

/* 系统开启「减少动态效果」时停掉循环动画（声波/号码球是无限循环的）。 */
@media (prefers-reduced-motion: reduce) {
  .waves span, .ball { animation: none !important; }
}
