/* dance.html deltas on top of live.css — same comic-panel visual language,
loaded after live.css so these rules win. Only what dance.html actually
needs differently: a boxed 9:16 player (T13) and the wardrobe grid (T16).
Everything else (topbar, clip-row, qrow/up-btn, nickname modal, mobile
sub-tabs) is reused as-is from live.css. */
/* dance.html's video is a fixed 480x832 (9:16) product, not the arbitrary
aspect ratio live.html's text-prompt clips can end up at — box it to that
ratio and center it instead of letting it stretch edge-to-edge on a wide
desktop viewport. */
.player-wrap {
aspect-ratio: 9 / 16;
max-width: 100%;
max-height: 100%;
margin: auto;
width: auto;
height: 100%;
}
/* T43: 排查移动端"待播放"列表为什么被挤没了才发现的——这条 `width:100%;
height:auto` 规则从 T13 就在,从来没真正被撑爆过是因为老版本"选衣服"
在右侧栏,移动端靠 tab 切换跟"待播放"互斥、不会同屏叠加;T42 把选衣服
横条挪进 .player-col 之后,视频(9:16 满宽=接近满屏高)+ 横条 + 下面
还要露出的"待播放"列表三个一起挤在 `.channel{height:100vh}` 这个硬高度
里,"待播放"被压成不到 30px 的一条缝——所以这里必须反过来,改回"由
高度决定宽度"(跟桌面端同一个逻辑,只是矮一些),把视频还给剩下两块。 */
@media (max-width: 768px) {
/* `flex: none` 是关键——.player-wrap 的 flex:1(live.css 基础规则,桌面端
靠它撑满 .player-col 剩余空间)在这里必须关掉,否则 flex-grow 会无视
下面这行 `height:40vh`,把它撑到吃掉 .player-col 里所有能占的空间,
跟桌面端一样把横条和"待播放"挤没——调了半天才找到这个根因。 */
.player-wrap { flex: none; height: 40vh; width: auto; max-width: 100%; }
.player-col { flex: 0 0 auto; }
}
/* T40: 居中"正在换装"胶囊,挂在 .live-pill(直播中)正下方;空态消息(没有
clip 时)复用同一个胶囊,只是不带 .active-label 前缀。 */
.active-pill {
position: absolute;
left: 50%;
top: 56px;
transform: translateX(-50%);
z-index: 2;
display: inline-flex;
align-items: center;
gap: 6px;
max-width: calc(100% - 28px);
font: bold 12px Tahoma, sans-serif;
color: var(--ink);
background: #fff;
border: 3px solid #000;
border-radius: 999px;
padding: 7px 14px;
box-shadow: var(--shadow);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.active-pill .active-label { opacity: .55; }
.active-pill .active-nick { color: var(--accent-text); }
/* T40: 排队头像条——每人一个昵称首字哈希色块头像 + 右下角迷你服装缩略图
角标,按 state.queue 已有的票数排序展示;横向可滚动,不换行(同时满足
T43 移动端窄屏的收纳需求,没必要另外写一套移动端样式)。 */
.queue-strip {
position: absolute;
left: 50%;
top: 98px;
transform: translateX(-50%);
z-index: 2;
display: flex;
gap: 10px;
max-width: calc(100% - 28px);
overflow-x: auto;
padding: 2px 2px 8px;
scrollbar-width: none;
}
.queue-strip::-webkit-scrollbar { display: none; }
.queue-avatar { position: relative; flex: none; width: 38px; height: 38px; }
.queue-avatar-badge {
width: 38px;
height: 38px;
border-radius: 50%;
border: 3px solid #000;
box-shadow: var(--shadow-sm);
display: flex;
align-items: center;
justify-content: center;
font: bold 14px Tahoma, sans-serif;
color: var(--ink);
}
.queue-avatar-thumb {
position: absolute;
right: -5px;
bottom: -5px;
width: 20px;
height: 20px;
border-radius: 6px;
border: 2px solid #000;
object-fit: cover;
background: #fff;
}
.wardrobe-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.wardrobe-card {
color: var(--ink);
background: #fff;
border: 3px solid #000;
border-radius: 14px;
padding: 6px;
box-shadow: var(--shadow);
cursor: pointer;
text-align: center;
}
.wardrobe-card:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 #000; }
.wardrobe-card img { width: 100%; height: 90px; object-fit: cover; border-radius: 8px; margin-bottom: 4px; display: block; }
.wardrobe-card .name { font-size: 12px; font-weight: bold; line-height: 1.3; }
.wardrobe-card.disabled { opacity: .5; cursor: default; }
/* T41: 缩略图默认只露出图+名字,hover(桌面)/ .revealed(移动端点一下,见
dance.html renderPickPanel 的 wiring)才淡入蒙层+"发送"按钮。 */
.wardrobe-thumb { position: relative; }
.wardrobe-send-overlay {
position: absolute;
inset: 0 0 4px 0; /* 4px 对应 img 的 margin-bottom,让蒙层刚好盖住图片本身 */
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, .55);
border-radius: 8px;
opacity: 0;
pointer-events: none;
transition: opacity .15s ease;
}
@media (hover: hover) {
.wardrobe-card:hover .wardrobe-send-overlay { opacity: 1; pointer-events: auto; }
}
.wardrobe-card.revealed .wardrobe-send-overlay { opacity: 1; pointer-events: auto; }
.wardrobe-card.disabled .wardrobe-send-overlay { display: none; }
.wardrobe-send-btn {
font: bold 12px Tahoma, sans-serif;
color: #fff;
background: transparent;
border: 2px solid #fff;
border-radius: 10px;
padding: 5px 14px;
cursor: pointer;
}
.wardrobe-send-btn:active { transform: translate(1px, 1px); }
/* T42: 选衣服从右侧栏网格改成贴视频底部的横条。.wardrobe-strip 是外层
(竖向堆叠:提示条 / 横向卡片行 / 生成中 / 生成失败),.wardrobe-strip-row
才是真正横向可滚动的那一条卡片。 */
.wardrobe-strip-wrap {
flex: 0 0 auto;
padding: 10px 14px 12px;
border-top: 1px solid var(--divider);
}
.wardrobe-strip-title {
font: bold 13px Tahoma, sans-serif;
color: #fff;
text-shadow: 0 1px 3px #000;
letter-spacing: .5px;
text-transform: uppercase;
margin: 0 2px 8px;
}
.wardrobe-strip {
display: flex;
flex-direction: column;
gap: 8px;
max-height: 220px;
overflow-y: auto;
}
.wardrobe-strip-row {
display: flex;
flex-direction: row;
gap: 8px;
overflow-x: auto;
padding-bottom: 4px;
}
.wardrobe-strip-row .wardrobe-card { flex: 0 0 104px; width: 104px; }
.wardrobe-strip-row .wardrobe-card .name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* T43: 移动端下 .player-col/.left-col 已经是整页竖向堆叠(视频 → 选衣服
横条 → 待播放列表),不需要额外的 tab 切换;这里只调两处触摸体验——
卡片略缩小让一屏能多露半张出来提示"这里能横滑",去掉横条自己的竖向
滚动上限(改成跟着整页一起滚),避免在已经要整页滚动的手机页面上再嵌
一层"内部滚动区域",大拇指容易被困在里面出不来。 */
@media (max-width: 768px) {
.wardrobe-strip { max-height: none; overflow-y: visible; }
.wardrobe-strip-row .wardrobe-card { flex: 0 0 92px; width: 92px; }
.wardrobe-strip-row .wardrobe-card img { height: 78px; }
/* 上面把视频从"满屏宽"改成"限高居中"(见 .player-wrap 那条 40vh 规则)
之后,视频本身变窄了,左上角的点赞按钮(.now-playing-box)跟顶部居中
的"直播中"胶囊在窄视频上会撞在一起——挪到视频左下角,那里在手机上
是空的。live.html 自己的 now-playing-box(昵称+文字,不是点赞按钮)
不用这条,它的视频从来不会被限宽,不受影响。 */
.now-playing-box { top: auto; bottom: 10px; left: 10px; }
}
/* T42: DIY 卡片——横条最后一张,虚线描边跟普通换装卡片区分开,点开是
#diyModal 的二选一(传衣服 vs 传人),不是 hover/发送那一套。 */
.wardrobe-diy { border-style: dashed; }
.wardrobe-diy-thumb {
height: 90px;
margin-bottom: 4px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, .04);
border-radius: 8px;
}
.wardrobe-diy-icon { font-size: 26px; }
/* T42: DIY 弹窗——复用 .nickname-modal/.nickname-box 的壳子,按钮沿用
`.nickname-box button` 那套已有的粉色描边样式(跟聊天发送/确定按钮同一
语言),这里只加两条子样式:文字改左对齐、按钮下面塞一行说明小字。
.diy-box 内容比昵称输入框宽,需要单独放宽一点。 */
.diy-box { width: 320px; max-width: calc(100vw - 32px); }
.diy-choice-btn {
display: block;
width: 100%;
text-align: left;
padding: 10px 12px;
margin-bottom: 10px;
}
.diy-choice-btn:last-child { margin-bottom: 0; }
.diy-choice-hint {
display: block;
font: normal 11px Tahoma, sans-serif;
color: var(--ink);
opacity: .65;
margin-top: 3px;
}
.coming-soon { color: var(--muted); text-align: center; margin-top: 40px; font-size: 13px; font-weight: bold; padding: 0 12px; }
/* T48: shown above the wardrobe grid while this uid has a free pick still
generating — explains why every card just went grey instead of leaving
it a silent no-op on click. */
.pending-hint {
font: bold 12px Tahoma, sans-serif;
color: var(--ink);
background: var(--gen-bg);
border: 3px solid #000;
border-radius: 12px;
padding: 8px 10px;
margin-bottom: 8px;
box-shadow: var(--shadow-sm);
}
/* T28: 传衣服 (mode 2) upload panel — same comic-panel language as
.wardrobe-card, laid out as a simple vertical form instead of a grid. */
.upload-panel { padding: 8px 4px; }
/* T42: 这个面板原来住在左栏(深色底),`--muted` 是给深色底配的浅白字;
现在整个搬进 #diyForm(白色弹窗),改用深色低透明度,否则几乎看不清。 */
.upload-panel .hint { font-size: 12px; color: var(--ink); opacity: .65; margin-bottom: 10px; line-height: 1.5; }
.upload-panel input[type="file"] { width: 100%; font-size: 12px; margin-bottom: 10px; }
.upload-panel .price-line { font: bold 13px Tahoma, sans-serif; margin-bottom: 10px; }
.upload-panel button {
width: 100%;
font: bold 13px Tahoma, sans-serif;
background: var(--accent);
border: 3px solid #000;
color: var(--ink);
border-radius: 12px;
padding: 8px 4px;
box-shadow: var(--shadow-sm);
cursor: pointer;
}
.upload-panel button:disabled { opacity: .5; cursor: default; }
/* T44: 伪进度+真进度混合的进度条——宽度用 CSS 变量 `--pct` 驱动
(createUploadProgress 直接 style.setProperty,不用 JS 拼 innerHTML)。 */
.upload-progress {
height: 10px;
margin-top: 8px;
background: #eee;
border: 2px solid #000;
border-radius: 8px;
overflow: hidden;
}
.upload-progress-bar {
height: 100%;
width: var(--pct, 0%);
background: var(--accent);
transition: width .15s linear;
}
.upload-panel .upload-error { color: #c0392b; font: bold 12px Tahoma, sans-serif; margin-top: 8px; }
.upload-panel .upload-status { font: bold 12px Tahoma, sans-serif; margin-top: 8px; }
/* T37: paid_boost 插队播放跟模式一/二的"生成中"要有视觉区别 */
.upload-panel .upload-status.boosted { color: #b8860b; background: #fff7d6; border: 2px solid #000; border-radius: 8px; padding: 6px 8px; }
.upload-panel select { width: 100%; font-size: 12px; margin-bottom: 10px; padding: 4px; }
.upload-panel .field-label { font: bold 12px Tahoma, sans-serif; margin-bottom: 4px; display: block; }
/* T29: balance pill + topup tier picker (reuses .nickname-modal/.nickname-box) */
.credit-pill { font: bold 12px Tahoma, sans-serif; }
.topup-tier {
display: flex;
justify-content: space-between;
align-items: center;
border: 3px solid #000;
border-radius: 12px;
padding: 8px 10px;
margin-bottom: 8px;
box-shadow: var(--shadow-sm);
}
.topup-tier .credits { font: bold 14px Tahoma, sans-serif; }
.topup-tier button { font: bold 12px Tahoma, sans-serif; background: var(--accent); border: 3px solid #000; border-radius: 10px; padding: 5px 10px; cursor: pointer; }
.topup-banner { text-align: center; font: bold 13px Tahoma, sans-serif; padding: 8px; margin: 0 0 10px; border-radius: 10px; }
.topup-banner.success { background: #d4f7d4; }
.topup-banner.cancelled { background: #f7e4d4; }
.age-box .age-disclaimer { font-size: 12px; line-height: 1.6; color: #444; font-weight: normal; }
/* Curtain transition between clips (谢幕式切换): two velvet halves slide in to
meet at center, hiding the src swap, then slide back out once the new
clip has an actual frame ready. Sits above the video and the live-pill/
now-playing overlays (z-index 3) so it hides everything during the swap. */
.curtain { position: absolute; inset: 0; z-index: 3; pointer-events: none; overflow: hidden; }
.curtain-half {
position: absolute;
top: 0;
bottom: 0;
width: 52%;
background:
repeating-linear-gradient(90deg, rgba(0,0,0,.18) 0 8px, transparent 8px 24px),
linear-gradient(180deg, #7a0f1c, #4a0710 90%);
box-shadow: inset 0 0 40px rgba(0,0,0,.6);
transition: transform .5s cubic-bezier(.65,0,.35,1);
}
.curtain-half.left { left: 0; transform: translateX(-100%); border-right: 3px solid #2a0308; }
.curtain-half.right { right: 0; transform: translateX(100%); border-left: 3px solid #2a0308; }
.curtain.closed .curtain-half.left { transform: translateX(0); }
.curtain.closed .curtain-half.right { transform: translateX(0); }