/* 由 build.sh 从 web/src/ 生成，别直接改这个文件 */
* { box-sizing: border-box; }
html { color-scheme: dark; }          /* 不声明的话 select 展开的原生弹层是白底 */
body { font-family: -apple-system, "PingFang SC", sans-serif; margin: 0;
       background: #0b0d12; color: #e8eaed; }
header { border-bottom: 1px solid #171b23; }
/* max-width/padding 跟 .home 保持一致，logo 才会跟下面的海报栅格左对齐 */
.hwrap { max-width: 980px; margin: 0 auto; padding: 14px 16px;
         display: flex; align-items: center; gap: 18px; }
.brand { display: inline-flex; align-items: center; text-decoration: none; color: inherit; }
/* 品牌名不再用 h1：h1 留给首页 hero（SSR 页则是各自的页面标题），一页只留一个 */
.brand-name { font-size: 20px; font-weight: 700; display: inline-flex; align-items: center; gap: 8px; }
.brand:hover { opacity: .85; }
/* margin-left:auto 让导航靠右。原来这条挂在语言胶囊上，胶囊下线后移到这儿 */
.hnav { display: flex; gap: 14px; font-size: 13px; margin-left: auto; }
.hnav a { color: #8b909a; text-decoration: none; }
.hnav a:hover { color: #e8eaed; }
/* 会员徽章。快到期时补一句剩余天数——续费提醒放在这儿比弹窗体面。 */
.hnav a.nav-vip { color: #8b7dff; font-weight: 600; letter-spacing: .3px; }
.hnav a.nav-vip:hover { color: #a99cff; }
.hnav a.nav-vip.soon { color: #e8b04b; }
.hero { text-align: center; padding: 34px 16px 0; }
.brand-name img { border-radius: 6px; display: block; }
.hero h1 { font-size: 30px; line-height: 1.22; letter-spacing: -.02em; color: #fff;
           max-width: 620px; margin: 0 auto 10px; }
.hero .sub { font-size: 14px; max-width: 560px; margin: 0 auto; }
.sub { color: #8b909a; font-size: 13px; }
/* 上下 28 而不是 16：SSR 页把这块夹在内容中间（详情页在选集和推荐之间、
   标签页在置顶卡和卡片之间），16px 会跟上下两块糊成一坨。首页同样受益。 */
.searchbar { display: flex; gap: 8px; max-width: 640px; margin: 28px auto; padding: 0 16px; }
/* 这块必须排在上面 .hero h1 / .hero .sub / .searchbar 的基础定义「之后」：
   同为一级选择器时后来居上，放在前面的话 font-size 和 margin 会被基础值盖掉
   （2026-08-27 踩过：h1 实测仍是 30px 两行、searchbar 仍是 28px margin）。
   跟下面 .home .grid-home 那条注释是同一个坑。 */
/* 移动端首屏预算（390pt 宽 / 约 660pt 可视高）：改之前 header 48 + hero 110 +
   搜索框 102 + 区块标题 73 = 333pt 才轮到第一张卡，卡片本身 219pt 高，
   于是第二行只露出半个封面。埋点里首页卡片的位置衰减几乎是平的
   （pos0 37 次 vs pos11 24 次，最后一行反而比中间行多），说明用户是滑到底
   逐个翻找而不是被首屏抓住——那就把能露出来的行数做够。
   下面四条一共提上来约 100pt，正好让第二行完整进屏。桌面端不受影响。 */
@media (max-width: 560px) {
  .hwrap { gap: 12px; padding: 12px; }
  .hnav { gap: 10px; }
  .brand-name { font-size: 17px; }
  .hero { padding-top: 16px; }
  .hero h1 { font-size: 20px; margin-bottom: 6px; }
  /* 这句是纯自我介绍，没有一个用户是因为它留下的（首页访客 63% 进来就直接
     摸搜索框）。移动端首屏寸土寸金，让位给海报；桌面端还留着。
     用 display:none 不影响 SEO——元素仍在 HTML 里，爬虫读得到。 */
  .hero .sub { display: none; }
  .searchbar { margin: 14px auto; }
  /* 只压第一块（Trending）的上边距，下面几块保持 34px 的呼吸感 */
  .home .home-sec:first-child { margin-top: 16px; }
}
.searchbar input { flex: 1; padding: 15px 18px; border-radius: 12px; border: 1px solid #2a2f3a;
       background: #151922; color: #e8eaed; font-size: 16px; }
.searchbar input:focus { outline: none; border-color: #6c5ce7; }
.searchbar button { padding: 15px 26px; border: 0; border-radius: 10px; background: #6c5ce7;
       color: #fff; font-size: 15px; cursor: pointer; }
/* 搜索中按钮置灰：不给视觉反馈的话用户只会以为没点上，接着猛点 */
.searchbar button:disabled { opacity: .5; cursor: default; }
#homeblocks { max-width: 640px; margin: 4px auto 0; padding: 0 16px 8px; }
/* ---- 首页 ---- */
.home { max-width: 980px; margin: 0 auto; padding: 8px 16px 40px; }
.home-sec { margin: 34px 0 0; }
.sec-hd { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 14px; }
.home-sec h2 { font-size: 17px; margin: 0; font-weight: 600; letter-spacing: -.01em; }
.sec-more { color: #8b909a; font-size: 13px; text-decoration: none; white-space: nowrap; }
.sec-more:hover { color: #6c5ce7; }
/* 首页海报比搜索结果密一档：这里要的是"一眼看出是个短剧站"，不是让人逐个读标题 */
/* 固定 6 列：首页正好放 12 张，2 行填满。用 auto-fill 会排成 5+5+2，尾巴很难看 */
/* 必须用 .home .grid-home 两级：.grid 的定义排在本段之后，同权重会后来居上，
   单写 .grid-home 会被它的 auto-fill 覆盖掉 */
.home .grid-home, .home .grid-new { grid-template-columns: repeat(6, 1fr); gap: 12px; padding: 0; max-width: none; }
@media (max-width: 900px) { .home .grid-home, .home .grid-new { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 560px) { .home .grid-home { grid-template-columns: repeat(3, 1fr); } }

/* 首页上新那一行。桌面跟 Trending 一样 6 列对齐；手机改横滑而不是继续 3 列——
   3 列时每张卡只有 106px 宽，是全站最小的一批（浏览页和搜索推荐位都是 2 列）。
   定宽 40% 是为了露出第三张的一半：那半张就是「可以滑」的提示，不用额外加箭头。
   负 margin 抵掉 .home 的 16px 内边距，让卡片能滑到贴边，否则右侧会留一条死白。 */
@media (max-width: 560px) {
  .home .grid-new { display: flex; overflow-x: auto; scroll-snap-type: x proximity;
                    -webkit-overflow-scrolling: touch; scrollbar-width: none;
                    margin: 0 -16px; padding: 0 16px; }
  .home .grid-new::-webkit-scrollbar { display: none; }
  .home .grid-new > .card { flex: 0 0 40%; scroll-snap-align: start; }
}
/* SSR 卡片的这几条原本只写在各 SSR 页自己注入的 head 里；首页走 SSR_HOME 区段拿不到，
   不补的话标题会退回成默认的蓝色下划线链接 */
.ssr-card { text-decoration: none; color: inherit; display: block; }
/* 服务端按 30 字符截了一道（见 pages.js 的 short()），但长词换不动行，
   BILLIONAIRE'S 这种照样撑成三行，于是第三行被横切、露出半截字。
   关键在底部 padding：盒子高 = 8(上) + 两行 + 8(下)，那 8px 下边距正好
   漏出第三行的头顶。所以底部 padding 归零、高度卡成"上 padding + 整两行"，
   多出来的行整行落在盒子外，一个像素都不露；间距挪给下面的 .m。
   line-clamp 一并留着：它生效时能补个省略号，不生效也不会再切出半截字。 */
.home .grid-home .t, .home .grid-new .t { height: calc(2.6em + 8px); padding-bottom: 0; overflow: hidden;
                      display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.home .grid-home .m, .home .grid-new .m { padding-top: 8px; }
/* ---- 首页「继续观看」（history.js 填） ----
   默认 hidden：首页 HTML 是全站共用的边缘缓存，这块只有回头客该看见，必须客户端出。
   卡片是横版小卡不是海报格：它排在搜索框上面，用海报会把全站主入口顶下去一整屏。
   宽度卡死成「两张卡 + 间距 + 左右内边距」，标题行才跟卡片左右对齐；
   展开时放宽到四张一排（4*210 + 3*10 + 32）。 */
/* 上边距交给 .searchbar 自己的 28px，这里不再叠加。
   宽度跟搜索框同为 640：收起时两张卡各占一半、正好铺满，标题行、卡片和上面的
   搜索框左右对齐；窄一档的话这一小块会比搜索框缩进一截，很显眼。桌面端不跟
   下面 980 宽的首页区块对齐是有意的——这块属于上面那个居中的入口区。
   展开时放宽到四张一排（4*210 + 3*10 + 32），卡片回到定宽居中——
   让它继续拉伸的话，末行剩几张就会被撑成比上面几行宽一大截。 */
#cw { max-width: 640px; margin: 0 auto; padding: 0 16px; }
#cw.all { max-width: 902px; }
/* min-width: 0 少不了：flex 项的自动最小尺寸是它的 min-content，而卡片里那行标题
   是 nowrap，于是每张卡的最小宽度 = 整句标题的宽度，两张放不下就各占一行。
   .cw-tx 上那个 min-width 只管它自己，管不到外层这张卡。 */
#cw:not(.all) .cw-card { flex: 1 1 0; width: auto; min-width: 0; }
/* 标题行跟首页各区块同一个写法（.sec-hd）：左边标签、右边入口。
   展开键单独占一行的话，两张卡的这一小块会被撑成三行高 */
.cw-hd { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 8px; }
/* 不用全大写：12px 的全大写英文在手机上最难认，而这行只是句提示，
   不需要抢「New this week」那几个区块标题的视觉重量 */
.cw-hd b { font-size: 12px; font-weight: 400; color: #7a839a; }
.cw-more { background: none; border: 0; padding: 0; color: #8b909a; font-size: 12px;
           font-family: inherit; cursor: pointer; white-space: nowrap; }
.cw-more:hover { color: #6c5ce7; }
.cw-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.cw-card { display: flex; align-items: center; gap: 10px; width: 210px; padding: 8px;
           background: #151922; border: 1px solid #252b38; border-radius: 12px;
           color: inherit; text-decoration: none; }
.cw-card:hover { border-color: #6c5ce7; }
.cw-card img { width: 40px; height: 54px; flex: 0 0 40px; border-radius: 6px; object-fit: cover; background: #232733; }
.cw-tx { min-width: 0; }
/* 标题只给一行。这块是「回到上次那部」的快捷入口，认出是哪部剧靠封面和开头几个字就够，
   两行会把这一小块撑高、也跟「别在这儿摆太多信息」的定位相反 */
.cw-tx b { display: block; font-size: 13px; font-weight: 500; line-height: 1.3;
           overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cw-tx i { display: block; margin-top: 3px; font-style: normal; font-size: 11px; color: #6c5ce7; }
/* 展开态在手机上定宽 210 会掉成一列，白白多占一屏高度。改成两等分 */
@media (max-width: 560px) { .cw-card { width: calc(50% - 5px); } }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip { display: inline-flex; align-items: center; gap: 6px; padding: 8px 13px; border-radius: 999px;
        background: #151922; border: 1px solid #252b38; color: #e8eaed; font-size: 13px; text-decoration: none; }
.chip span { color: #6b7080; font-size: 11px; }
.chip:hover { border-color: #6c5ce7; background: #1d222d; }
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px; }
.step { background: #151922; border: 1px solid #252b38; border-radius: 12px; padding: 16px; }
.step-ic { width: 26px; height: 26px; border-radius: 8px; margin-bottom: 10px;
           background: linear-gradient(135deg, #6c5ce7, #e9548c); color: #fff;
           font-size: 13px; font-weight: 600; display: flex; align-items: center; justify-content: center; }
.step b { display: block; font-size: 14px; margin-bottom: 3px; }
.step span { color: #8b909a; font-size: 13px; line-height: 1.45; }
/* ---- 广告位 ---- */
/* 首页位和搜索位的样式只能落在这儿：这两个位所在的页面用的是外壳的 app.css，
   没有自己的 head。SSR 那几个页（详情页、列表页）另有一份写在各自 head 里。 */
.site-foot { display: flex; flex-wrap: wrap; gap: 18px; align-items: center; justify-content: center;
             max-width: 980px; margin: 0 auto; padding: 22px 16px 40px; border-top: 1px solid #1a1e28;
             color: #6b7080; font-size: 13px; }
.site-foot a { color: #8b909a; text-decoration: none; }
.site-foot a:hover { color: #6c5ce7; }
.how-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.how-list li { background: #1a1d24; border: 1px solid #232733; border-radius: 10px; padding: 10px 12px; font-size: 13px; color: #8b909a; }
.how-list b { color: #e8eaed; }
.trend, .explore { display: flex; flex-wrap: wrap; gap: 8px; }
.trend a, .explore a { display: inline-block; padding: 8px 12px; border-radius: 999px; border: 1px solid #2a2f3a; background: #1a1d24; color: #e8eaed; text-decoration: none; font-size: 13px; }
.trend a:hover, .explore a:hover { border-color: #6c5ce7; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
        gap: 14px; max-width: 980px; margin: 0 auto; padding: 16px; }
/* 推荐区必须跟结果区断开：不断开就等于把「猜的」当「搜到的」端给用户 */
/* 列表页/标签页第 1 页顶部的上新行，样子跟首页上新区块一致 */
.new-row { max-width: 980px; margin: 0 auto; padding: 12px 16px 0; }
.new-row h2 { font-size: 17px; margin: 0; font-weight: 600; letter-spacing: -.01em; }
.new-row .grid-new { grid-template-columns: repeat(6, 1fr); gap: 12px; padding: 0; max-width: none; }
.new-row .grid-new .t { height: calc(2.6em + 8px); padding-bottom: 0; overflow: hidden;
                        display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.new-row .grid-new .m { padding-top: 8px; }
@media (max-width: 900px) {
  .new-row .grid-new { display: flex; overflow-x: auto; scroll-snap-type: x proximity;
                       -webkit-overflow-scrolling: touch; scrollbar-width: none;
                       margin: 0 -16px; padding: 0 16px; }
  .new-row .grid-new::-webkit-scrollbar { display: none; }
  .new-row .grid-new > .card { flex: 0 0 23%; scroll-snap-align: start; }
}
@media (max-width: 560px) { .new-row .grid-new > .card { flex-basis: 40%; } }
#suggest { max-width: 980px; margin: 8px auto 0; }
.suggest-hd { border-top: 1px solid #252b38; margin: 0 16px; padding-top: 16px;
              font-size: 12px; color: #7a839a; letter-spacing: .04em; text-transform: uppercase; }
/* 固定列数而不是 auto-fill：6 条在 auto-fill 下桌面排成 5+1，末行吊一张孤卡很难看。
   断点跟首页 .grid-home 对齐，6 被 6/3/2 整除，每档都刚好铺满。 */
#suggest-grid { padding-top: 10px; grid-template-columns: repeat(6, 1fr); }
@media (max-width: 900px) { #suggest-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 560px) { #suggest-grid { grid-template-columns: repeat(2, 1fr); } }
/* 卡片一律是 <a>（SSR 页和搜索结果都是），链接三件套直接挂在 .card 上。
   搜索卡不带 .ssr-card：track.js 的文档级委托按 a.ssr-card 认卡发 drama_open，
   带上就会跟搜索自己那条埋点同一次点击发两遍。 */
.card { background: #151922; border-radius: 12px; overflow: hidden; cursor: pointer;
        text-decoration: none; color: inherit; display: block;
        border: 1px solid #252b38; transition: transform .12s, border-color .12s; }
.card:hover { transform: translateY(-2px); border-color: #6c5ce7; }
/* height: auto 不能省：SSR 卡片带 width/height 属性，属性会当成 height:400px 生效，
   把 aspect-ratio 顶掉（aspect-ratio 只在某一维是 auto 时才算数），卡片会被拉成长条 */
.card img { width: 100%; height: auto; aspect-ratio: 3/4; object-fit: cover; display: block; background:#232733; }
/* 封面占位。上游图床有一部分图会被它自己的 CDN 挡掉（浏览器侧是 ERR_BLOCKED_BY_ORB，
   不是 404），原来只留一个空灰框。图挂了就露出底下这张剧名字卡。
   .ph > img 和 .card img 特异性相同，靠源码顺序压过去，所以这段必须排在 .card img 之后。 */
.ph { position: relative; display: block; aspect-ratio: 3/4; background: #232733; overflow: hidden; }
.ph > img { position: absolute; inset: 0; width: 100%; height: 100%;
            object-fit: cover; display: block; aspect-ratio: auto; }
.ph.bad > img { display: none; }
.ph::after { content: attr(data-t); position: absolute; inset: 0; display: none;
             align-items: center; justify-content: center; padding: 14px;
             background: linear-gradient(150deg, #2b2447, #1a1d24);
             color: #b9a6f5; font-size: 13px; font-weight: 600; line-height: 1.4;
             text-align: center; overflow: hidden; }
.ph.bad::after { display: flex; }
/* NEW 角标。压在封面上而不是占一行文字：手机 3 列时卡片内容宽只有 86px，
   下面那行 11px 的灰字已经被集数占满，塞不下日期。也不写绝对日期——它会变旧，
   一部剧过一个月还挂着上线日，本来要传递新鲜感的元素就反过来了。
   z-index 必须有：封面挂图时 .ph.bad::after 会铺满整层，不压一下角标会被盖掉。 */
.nb { position: absolute; top: 6px; left: 6px; z-index: 1;
      background: #6c5ce7; color: #fff; font-size: 10px; font-weight: 700;
      letter-spacing: .04em; line-height: 1.4; padding: 2px 6px;
      border-radius: 4px; pointer-events: none; }

.card .t { padding: 8px 10px; font-size: 13px; line-height: 1.3; }
.card .m { padding: 0 10px 10px; font-size: 11px; color: #8b909a; }
.status { text-align: center; color: #8b909a; padding: 20px; }
/* 这两个盒子现在排在搜索框正下方，空着也带 padding，会在搜索框和首页内容之间顶出一条缝 */
#status:empty, #grid:empty { display: none; }
/* 集数弹层 */
.modal { position: fixed; inset: 0; background: rgba(0,0,0,.7); display: none;
         align-items: center; justify-content: center; padding: 16px; z-index: 10; }
.modal.on { display: flex; }
.box { background: #1a1d24; border-radius: 16px; max-width: 560px; width: 100%;
       max-height: 84vh; overflow: auto; padding: 20px; }
.box h2 { margin: 0 0 4px; font-size: 17px; }
.box .meta { color: #8b909a; font-size: 12px; margin-bottom: 14px; }
.eps { display: grid; grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)); gap: 8px; }
/* 状态文字必须跨满整行：.eps 是 grid，裸文本会被按单词拆成一个个 grid item
   塞进 52px 的列里竖排，加载中和加载失败长得一模一样，用户看不出出错了 */
.eps-status { grid-column: 1 / -1; color: #8b909a; font-size: 14px; padding: 12px 0; }
/* 边框跟 SSR 详情页的 .ssr-ep 保持一致（那边一直有，SPA 这两个网格漏了）：
   一片同色方块只靠数字区分，扫视时没有落点，补一圈描边就有格子感了。
   box-sizing 是 border-box，加边框不改按钮高度。 */
.eps button { padding: 10px 0; background: #232733; border: 1px solid #2a2f3a; border-radius: 8px;
         color: #e8eaed; font-size: 14px; cursor: pointer; font-family: inherit;
         transition: background .15s, border-color .15s; }
/* 紫色留给"当前集"，hover 的底色用中性色，否则两者在桌面端撞脸；只有描边提亮到紫 */
.eps button:hover { background: #343a4a; border-color: #6c5ce7; }
.close { float: right; cursor: pointer; color: #8b909a; font-size: 20px; }
.eps button.cur { background: #6c5ce7; border-color: #6c5ce7; color: #fff; }
.hint { font-size: 12px; color: #8b909a; margin: 10px 0 0; }
/* 播放器 */
#player { position: fixed; inset: 0; background: #000; display: none; z-index: 20;
          flex-direction: column; align-items: center; justify-content: center; }
#player.on { display: flex; }
#video { width: 100%; height: 100%; object-fit: contain; background: #000; }
/* ---------- 播放器内层叠总表（改任何一层前先看这里）----------
     1  #tapzone / #ploading      铺满全屏的点击热区、转圈
     2  #tapplay / #autonext      ▶ 降级层、连播倒计时卡
     3  #pg / #unmute             进度条、开声提示
     4  #perr / #pvip             错误卡片、VIP 闸门（必须高于热区，否则卡片上的按钮点不动；
                                  外层 pointer-events:none，卡片以外照旧能点到底部导航）
     5  .pbar                     顶栏 ✕（必须最高）

   #perr 原来没写 z-index（auto）。CSS 规定同一层叠上下文里 z-index>0 的定位元素
   永远画在 auto 之上，跟 DOM 顺序无关——所以给 #tapzone 加上 z-index:1 之后，
   热区就盖死了它：用户看得见重试按钮，点下去却全被热区当成"暂停/开声"吃掉。
   这个坑在 TG 版的付费墙上真实发生过一次，代价是 VIP 收入静默归零三天。 */
.pbar { position: absolute; top: 0; left: 0; right: 0; z-index: 5; display: flex; align-items: center;
        gap: 12px; padding: 14px 16px; font-size: 14px; pointer-events: none;
        background: linear-gradient(rgba(0,0,0,.75), transparent); }
.pbar .ptitle { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pbar span[role=button] { cursor: pointer; padding: 4px 8px; pointer-events: auto; }
/* pointer-events 只给按钮：这条横条铺满整宽，实心的话点它下面的画面想暂停会落空。
   bottom 抬到进度条上沿：那条线自己占着屏幕最底下一格。 */
/* 播完那一刻盖上来的自动连播卡。刻意不铺满全屏：底部 .nav 要露在外面，
   用户想自己挑集照样点得到，而且那条不用为这个卡片改任何逻辑。 */
#autonext { position: absolute; inset: 0; z-index: 2; display: none;
            align-items: center; justify-content: center; }
#autonext.on { display: flex; }
#autonext .box { background: rgba(18,20,26,.9); backdrop-filter: blur(10px);
                 border-radius: 16px; padding: 22px 26px; text-align: center;
                 /* 桌面上视频是竖屏居中的，只给百分比会横跨整个播放器宽度，很突兀 */
                 max-width: min(78%, 320px); }
#autonext .lb { font-size: 14px; color: #c6cad3; }
#autonext .n { margin-top: 6px; font-size: 44px; font-weight: 700; line-height: 1; color: #fff;
               font-variant-numeric: tabular-nums; }
#autonext .go { margin-top: 16px; padding: 12px 28px; border: 0; border-radius: 999px;
                background: #fff; color: #14161c; font-size: 15px; font-weight: 600;
                font-family: inherit; cursor: pointer; }
/* 剧末连播。跟 #autonext 共用一个定时器，两者互斥，不会同时挂着 */
#finish { position: absolute; inset: 0; z-index: 2; display: none;
          align-items: center; justify-content: center; }
#finish.on { display: flex; }
#finish .box { background: rgba(18,20,26,.94); backdrop-filter: blur(10px);
               border-radius: 16px; padding: 18px 12px 12px; text-align: center;
               /* 卡片宽度是这一屏的主体，内边距和外边距都压到够用就行 */
               width: min(94%, 400px); }
#finish .done { font-size: 15px; font-weight: 600; color: #fff; }
#finish .cards { display: flex; gap: 8px; justify-content: center; margin: 14px 0 12px; }
/* 不给 max-width：三张平分剩下的宽度，390 的机器上每张能到 ~110px */
#finish .fc { flex: 1; min-width: 0; max-width: calc((100% - 16px) / 3);
              text-decoration: none; color: inherit; }
#finish .fc img { width: 100%; aspect-ratio: 2/3; object-fit: cover; border-radius: 8px;
                  background: #2a2f3a; display: block; }
/* 第一张是倒计时到点要自动播的那部，给个白框让用户看得出「说的是它」 */
#finish .fc.lead img { outline: 2px solid #fff; outline-offset: 2px; }
#finish .fc .ft { margin-top: 6px; font-size: 12px; line-height: 1.3; color: #c6cad3;
                  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
                  overflow: hidden; }
#finish .lb { font-size: 13px; color: #c6cad3; display: flex; align-items: center;
              justify-content: center; gap: 8px; }
/* 数字裹进一个圆里：13px 正文旁边挂个 17px 的粗体，扫一眼根本注意不到在倒数 */
#finish .lb b { display: inline-flex; align-items: center; justify-content: center;
                min-width: 30px; height: 30px; border-radius: 50%; background: #fff;
                color: #14161c; font-size: 16px; font-weight: 700;
                font-variant-numeric: tabular-nums; }
#finish .cancel { margin-top: 10px; padding: 7px 18px; border: 0; background: none;
                  color: #8b909a; font-size: 13px; font-family: inherit; cursor: pointer; }
.nav { position: absolute; bottom: calc(26px + env(safe-area-inset-bottom)); left: 0; right: 0;
       z-index: 1; display: flex; gap: 10px;
       justify-content: center; padding: 18px; pointer-events: none;
       transition: opacity .25s, visibility 0s;
       background: linear-gradient(transparent, rgba(0,0,0,.75)); }
.nav button { padding: 10px 18px; border: 0; border-radius: 999px; background: rgba(255,255,255,.15);
       color: #fff; font-size: 14px; cursor: pointer; font-family: inherit; backdrop-filter: blur(6px);
       pointer-events: auto; }
.nav button:disabled { opacity: .35; cursor: default; }
/* 播放中把这条收起来让画面干净。
   用 visibility 而不是只调 opacity：opacity:0 的元素照样接点击，
   按钮会变成一块看不见的雷区，点画面想唤出反而直接跳集。 */
#player.navhide .nav { opacity: 0; visibility: hidden;
                       transition: opacity .25s, visibility 0s .25s; }
/* 加载转圈：纯黑跟"页面崩了"长得一样，得让用户看出来是在加载。
   pointer-events:none —— 它是整屏铺开的，不能挡住顶栏的 ✕ 和底部导航 */
#ploading { position: absolute; inset: 0; z-index: 1; display: none; align-items: center;
            justify-content: center; pointer-events: none; }
#ploading.on { display: flex; }
#ploading i { width: 34px; height: 34px; border-radius: 50%; border: 3px solid rgba(255,255,255,.25);
              border-top-color: #fff; animation: spin .8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.perr-btn { padding: 10px 22px; border: 0; border-radius: 999px; background: #6c5ce7;
            color: #fff; font-size: 14px; cursor: pointer; font-family: inherit; }
/* 点画面的热区。<video> 不带 controls 了（原生控件的 shadow DOM 会把画面上的点击
   整个吞掉——CDP 打真实触摸实测：捕获、目标、冒泡一个 click 都收不到），所以整块画面
   铺满即可，不用再给原生控制条留 56px/60px 那两个猜出来的边。
   z-index 跟 .pbar/.nav 同为 1，靠 DOM 顺序在前保证那两层盖在它上面。 */
#tapzone { position: absolute; inset: 0; z-index: 1; }
/* 极简进度条。占屏幕最底下一格，safe-area 让开 iPhone 的 Home 条——不用再猜几何值。
   播放中（navhide）缩成一条淡细线常驻、不吃点击，画面干净又始终看得见进度；
   导航亮着时变粗、可点可拖。touch-action:none 是拖拽的前提，否则手指一动就变成页面滚动。 */
#pg { position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; padding: 9px 14px;
      padding-bottom: calc(11px + env(safe-area-inset-bottom));
      background: rgba(0,0,0,.75); touch-action: none; cursor: pointer;
      transition: background .25s; }
#pg.off { display: none; }          /* duration 拿不到（HLS 直播/还没就绪）：不画永远不动的线 */
#pg .track { position: relative; height: 6px; border-radius: 6px; background: rgba(255,255,255,.4);
             transition: height .2s, background .2s; }
#pg .fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; border-radius: 6px;
            background: #6c5ce7; }
#pg .knob { position: absolute; top: 50%; left: 0; width: 14px; height: 14px; margin: -7px 0 0 -7px;
            border-radius: 50%; background: #fff; opacity: 0; transition: opacity .15s; }
/* 暂停态就把圆点亮出来：手机上没有 hover，不给这个暗示用户不知道这条线能抓，
   实测会把它当成浏览器边框。播放态（navhide）仍然藏着，画面要干净。 */
#player:not(.navhide) #pg .knob, #pg.drag .knob { opacity: 1; }
#player.navhide #pg { background: transparent; pointer-events: none; }
#player.navhide #pg .track { height: 2px; background: rgba(255,255,255,.16); }
#player.navhide #pg .fill { background: rgba(255,255,255,.5); }
/* 自动播放被系统拦掉时的降级画面（iOS 低电量模式最典型，连静音 autoplay 都拦）。
   原来是 rgba(0,0,0,.4) 的黑纱 + 一颗 60px 半透明 ▶，一个字说明都没有：底下挂着的
   R2 封面被压得看不清，整屏跟"视频加载失败"长得一模一样。实测被拦的人只有 68.2%
   最终播上（未被拦 89.0%），三成看一眼就走——他们不是不想点，是不知道点了有反应。
   所以黑纱降到 .12 让封面透出来，▶ 做成实心圆按钮，底下补一句明确的文案。 */
/* 这一层身兼两职，样式要分开：
   ① 无 .blocked：用户自己按的暂停。他知道刚才发生了什么，一颗 ▶ 就够，别加文案。
   ② 有 .blocked：自动播放被系统拦掉（iOS 低电量模式最典型，连静音 autoplay 都拦）。
      原来两种状态共用一套：rgba(0,0,0,.4) 黑纱 + 60px 半透明 ▶，一个字说明都没有，
      底下挂着的 R2 封面被压得看不清，整屏跟"视频加载失败"长得一模一样。实测被拦的人
      只有 68.2% 最终播上（未被拦 89.0%），三成看一眼就走——不是不想点，是不知道
      点了有反应。所以这一档把黑纱降到 .12 让封面透出来，再补一句明确的文案。 */
#tapplay { position: absolute; inset: 0; z-index: 2; display: none; flex-direction: column;
           align-items: center; justify-content: center; gap: 14px;
           background: rgba(0,0,0,.32); cursor: pointer; }
#tapplay.on { display: flex; }
#tapplay.blocked { background: rgba(0,0,0,.12); }
/* text-indent 补 5px：▶ 这个字形的视觉重心偏左，不推一下在圆里看着是歪的 */
#tapplay i { width: 84px; height: 84px; border-radius: 50%; background: rgba(255,255,255,.95);
             color: #12141a; font-size: 38px; line-height: 84px; font-style: normal;
             text-align: center; text-indent: 5px; box-shadow: 0 6px 30px rgba(0,0,0,.55); }
#tapplay b { display: none; font-size: 15px; font-weight: 600; color: #fff; white-space: nowrap;
             padding: 7px 16px; border-radius: 999px; background: rgba(0,0,0,.6); }
#tapplay.blocked b { display: block; }
/* 静音提示不自动隐藏：画面在动 + 明确写着"点一下有声音" = 用户知道这是设计；
   提示消失了就只剩一个没声音的视频，那就是"坏了"的样子。z-index 压在 pbar 下面，
   位置避开画面正中，别挡住剧情。 */
/* 开声提示分两态。big：落地头几秒摆在画面正中，做成一颗大按钮——开了声的人完播率
   64.4%，一直静音的只有 32.2%（08-31 首小时，104 人），把这一下点出来是当前最值钱的
   一步。原来那颗 13px 的小胶囊缩在 top:72px，在 600px 高的竖屏里等于不存在。
   之后缩回顶部小胶囊常驻不消失：一直挡着画面正中就成了牛皮癣。 */
#unmute { position: absolute; z-index: 3; display: none; align-items: center; justify-content: center;
          gap: 6px; background: rgba(0,0,0,.62); color: #fff; font-weight: 600;
          white-space: nowrap; cursor: pointer; border-radius: 999px;
          left: 50%; top: 72px; transform: translateX(-50%);
          padding: 8px 14px; font-size: 13px;
          transition: top .3s, padding .3s, font-size .3s, background .3s; }
#unmute.on { display: flex; }
#unmute.big { top: 50%; transform: translate(-50%, -50%); flex-direction: column; gap: 10px;
              padding: 22px 28px; font-size: 15px; background: rgba(0,0,0,.72);
              box-shadow: 0 6px 28px rgba(0,0,0,.5); }
#unmute span { font-size: 15px; }
#unmute.big span { font-size: 44px; line-height: 1; }
/* 报错层是一张居中的卡片，不是铺满全屏的一整块。
   外层 pointer-events:none —— 卡片以外的区域照旧能点：底部选集/上一集/下一集
   都在它下面（z-index 1），卡片亮着时也够得着，用户不用退出播放器就能换一集。 */
#perr { position: absolute; inset: 0; z-index: 4; display: none;
        align-items: center; justify-content: center; padding: 24px;
        pointer-events: none; }
#perr.on { display: flex; }
.perr-card { pointer-events: auto; position: relative; width: 100%; max-width: 340px;
             display: flex; flex-direction: column; align-items: center; gap: 12px;
             padding: 26px 20px 20px; border-radius: 14px; text-align: center;
             background: rgba(16,19,26,.95); box-shadow: 0 12px 40px rgba(0,0,0,.55);
             color: #e8eaed; font-size: 14px; line-height: 1.6; }
.perr-x { position: absolute; top: 4px; right: 6px; padding: 8px 10px; border: 0;
          background: none; color: #8b909a; font-size: 16px; line-height: 1;
          cursor: pointer; font-family: inherit; }
.perr-t { font-size: 15px; }
#perr a { color: #6c5ce7; }

/* VIP 闸门。跟错误卡片同一层、同一套排版：对用户来说这两张卡都是"画面停下来了，
   这里告诉你为什么"。底色压深一点是因为它下面那一帧是真画面（地址已经挂上、
   缓冲也开始了），不压的话封面透上来会让人以为还在加载。 */
#pvip { position: absolute; inset: 0; z-index: 4; display: none;
        align-items: center; justify-content: center; padding: 24px;
        background: rgba(0,0,0,.72); pointer-events: none; }
#pvip.on { display: flex; }
.pvip-card { pointer-events: auto; width: 100%; max-width: 340px;
             display: flex; flex-direction: column; align-items: center; gap: 10px;
             padding: 26px 22px 18px; border-radius: 14px; text-align: center;
             background: rgba(16,19,26,.97); box-shadow: 0 12px 40px rgba(0,0,0,.55);
             color: #e8eaed; }
.pvip-card b { font-size: 16px; font-weight: 600; color: #fff; }
.pvip-card p { margin: 0; font-size: 13px; line-height: 1.6; color: #a0a6b2; }
.pvip-go { width: 100%; margin-top: 6px; padding: 12px; border: 0; border-radius: 8px;
           background: #6c5ce7; color: #fff; font-size: 15px; font-weight: 600;
           cursor: pointer; font-family: inherit; }
.pvip-go:hover { background: #7d6ef0; }
/* 次要出口也得看得出是个按钮：纯文字色那版在深色卡片上跟正文一个样，
   不想买的人找不到出去的路，只能去够顶栏那个 ✕。 */
.pvip-x { width: 100%; padding: 10px; border: 1px solid #343a47; border-radius: 8px;
          background: #232733; color: #c8ccd4; font-size: 14px; cursor: pointer; font-family: inherit; }
.pvip-x:hover { background: #2c313d; color: #fff; }
/* 选集抽屉：从底部升起半屏，视频继续播，不打断 */
.epbtn { min-width: 108px; }
#sheetmask { position: absolute; inset: 0; background: rgba(0,0,0,.45); display: none; z-index: 2; }
#sheetmask.on { display: block; }
#epsheet { position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; display: flex;
           flex-direction: column; max-height: 64%; background: #12151c;
           border-radius: 16px 16px 0 0; transform: translateY(101%);
           transition: transform .22s ease;
           /* 没有安全区的设备（多数安卓和桌面浏览器）env() 取到 0，最后一行会贴着抽屉
              下沿，所以兜一个 10px 的底 */
           padding-bottom: max(10px, env(safe-area-inset-bottom)); }
#epsheet.on { transform: translateY(0); }
.sheet-hd { display: flex; align-items: center; justify-content: space-between;
            padding: 14px 16px 12px; font-size: 14px; border-bottom: 1px solid #232733; }
.sheet-hd span[role=button] { cursor: pointer; color: #8b909a; font-size: 18px; padding: 0 4px; }
/* min-height:0 是必须的：flex item 默认 min-height:auto，不写这条 overflow-y 不生效，
   网格会把 #epsheet 的 max-height 顶破。分段条加进来之前只有它一个伸缩项，碰巧看不出来。 */
.sheet-bd { position: relative; flex: 1; min-height: 0; overflow-y: auto; padding: 14px 16px 24px; }
/* 分段条：跟 .eps button 同一套灰底圆角，选中态也用紫色——它和网格里的"当前集"是同一个语义。
   横向可滚是给 200 集以上的剧留的（7 段起排不下），滚动条本身藏掉。 */
/* 452 = 420 网格 + 左右各 16 padding，跟 #sheeteps 左右对齐 */
.sheet-seg { display: flex; gap: 8px; overflow-x: auto; padding: 12px 16px 0;
             scrollbar-width: none; -ms-overflow-style: none; }
.sheet-seg::-webkit-scrollbar { display: none; }
.sheet-seg[hidden] { display: none; }
.sheet-seg button { flex: 0 0 auto; padding: 7px 14px; background: #232733; border: 0;
                    border-radius: 8px; color: #c6cad3; font-size: 13px; cursor: pointer;
                    font-family: inherit; white-space: nowrap; }
.sheet-seg button:hover { background: #343a4a; }
.sheet-seg button.cur { background: #6c5ce7; color: #fff; }
/* 末段的占位格。列数是 auto-fill 自适应的（窄屏 5 列、宽屏十几列都可能），
   写死 min-height 换个宽度就不准，所以改成把末段的格子补满到整段：格子数一样多，
   行数就一定一样，抽屉高度天然不会在切段时塌一截。resize 改了列数也自动跟着变。
   没有 data-i，两个 click 委托的守卫本来就会把它拦下。
   类名别叫 .ph——那是封面图占位（见上面 aspect-ratio:3/4 那条），撞上了会把
   53px 宽的按钮拉成 70px 高，末段反而比满段更高。 */
#sheeteps button.eps-ph { visibility: hidden; pointer-events: none; }
/* 44px 是给原生控制条让的位，不让按钮压着进度条 */
.nav  { padding-bottom: calc(18px + 44px + env(safe-area-inset-bottom)); }
