/* ==========================================================================
   videoPlay 设计系统 —— 框架层
   风格：影院暗房（cinematic dark）。近黑底 + 琥珀金强调，海报优先。
   这里只放 token 和公共组件；页面专属样式写到各自的 page-*.css。
   ========================================================================== */

:root {
	/* ---------- 色板 ---------- */
	--bg:            #0a0a0c;   /* 页面底色，带一点冷调的近黑 */
	--bg-elev:       #121216;   /* 抬起一层：卡片、输入框 */
	--bg-elev-2:     #1a1a20;   /* 再抬一层：弹窗、下拉 */
	--bg-hover:      #22222a;

	--line:          #26262e;   /* 分隔线 */
	--line-strong:   #35353f;

	--text:          #f2f2f5;   /* 主文字 */
	--text-2:        #a8a8b3;   /* 次要文字 */
	--text-3:        #6e6e7a;   /* 弱化：时间戳、占位 */

	--accent:        #f5a524;   /* 琥珀金：主强调 */
	--accent-hot:    #ffc14d;   /* hover 更亮 */
	--accent-dim:    rgba(245, 165, 36, .14);
	--accent-line:   rgba(245, 165, 36, .35);

	--teal:          #2dd4bf;   /* 次强调：已看 / 成功 / 更新标记 */
	--danger:        #f45b5b;
	--warn:          #f5a524;

	/* ---------- 间距（4 的倍数，全站只用这些） ---------- */
	--s-1: 4px;
	--s-2: 8px;
	--s-3: 12px;
	--s-4: 16px;
	--s-5: 24px;
	--s-6: 32px;
	--s-7: 48px;
	--s-8: 64px;

	/* ---------- 圆角 ---------- */
	--r-sm: 4px;
	--r:    8px;
	--r-lg: 14px;
	--r-pill: 999px;

	/* ---------- 字体 ----------
	   中文站 + 国内部署：CJK 网络字体 5~10MB 且 Google Fonts 不通，
	   所以用系统 CJK 栈，靠字重反差和字距做辨识度。 */
	--font: -apple-system, BlinkMacSystemFont, "PingFang SC", "HarmonyOS Sans SC",
	        "Microsoft YaHei UI", "Microsoft YaHei", "Source Han Sans SC",
	        "Noto Sans CJK SC", sans-serif;
	/* 数字/英文用等宽，集号、时长、播放量对齐好看 */
	--font-mono: "SF Mono", "JetBrains Mono", Consolas, "Liberation Mono", monospace;

	/* ---------- 阴影 ---------- */
	--sh-1: 0 1px 2px rgba(0, 0, 0, .5);
	--sh-2: 0 4px 16px rgba(0, 0, 0, .55);
	--sh-3: 0 12px 40px rgba(0, 0, 0, .65);
	--sh-accent: 0 6px 24px rgba(245, 165, 36, .22);

	/* ---------- 动效 ---------- */
	--ease: cubic-bezier(.2, .7, .3, 1);
	--fast: .16s;
	--slow: .32s;

	/* ---------- 布局 ---------- */
	--max-w: 1880px;
	/* .vp-wrap 的左右内边距。窄屏在文件末尾的媒体查询里逐档调小。
	   要靠负边距顶出内边距的元素（播放器）也读它，别自己写死数字 */
	--wrap-pad: var(--s-5);
	--nav-h: 60px;
}

/* ==========================================================================
   基础
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font-family: var(--font);
	font-size: 14px;
	line-height: 1.6;
	/* 移动端横向不允许出现滚动条 */
	overflow-x: hidden;
	-webkit-font-smoothing: antialiased;
}

/* 页面顶部一层极淡的暖色光晕，避免大面积纯黑发闷 */
body::before {
	content: "";
	position: fixed;
	top: calc(var(--nav-h) * -1);
	left: 50%;
	width: 900px;
	height: 480px;
	transform: translateX(-50%);
	background: radial-gradient(ellipse at center, rgba(245, 165, 36, .07), transparent 70%);
	pointer-events: none;
	z-index: 0;
}

a { color: inherit; text-decoration: none; }
a:hover { color: var(--accent); }

img { max-width: 100%; display: block; }

h1, h2, h3, h4 { margin: 0; font-weight: 700; letter-spacing: -.01em; }
h1 { font-size: 26px; }
h2 { font-size: 20px; }
h3 { font-size: 16px; }

p { margin: 0 0 var(--s-3); }

::selection { background: var(--accent); color: #14140c; }

/* 滚动条：细、暗、hover 才明显 */
* { scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: var(--r-pill); }
*::-webkit-scrollbar-thumb:hover { background: var(--text-3); }

/* ==========================================================================
   布局
   ========================================================================== */

.vp-wrap {
	position: relative;
	z-index: 1;
	max-width: var(--max-w);
	margin: 0 auto;
	/* 🔴 左右内边距走 --wrap-pad，**不要**直接写 var(--s-5)。
	   有元素要靠负边距顶出这层内边距去铺满（播放器就是），
	   两边各写各的数一旦对不上，窄屏就会横向溢出几个像素 —— 2026-08-26 踩过。
	   分档的值在文件末尾的媒体查询里改这个变量，不改这条规则。 */
	padding: 0 var(--wrap-pad);
}

/* 区块标题：左侧一道琥珀色竖条，是全站的视觉签名 */
.vp-sec-title {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	margin: var(--s-6) 0 var(--s-4);
	font-size: 18px;
	font-weight: 700;
}
.vp-sec-title::before {
	content: "";
	width: 3px;
	height: 18px;
	background: var(--accent);
	border-radius: 2px;
}
.vp-sec-title .vp-sec-more {
	margin-left: auto;
	font-size: 13px;
	font-weight: 400;
	color: var(--text-3);
}

/* ==========================================================================
   按钮
   ========================================================================== */

.vp-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--s-2);
	height: 36px;
	padding: 0 var(--s-4);
	border: 1px solid var(--line-strong);
	border-radius: var(--r);
	background: var(--bg-elev);
	color: var(--text);
	font-family: inherit;
	font-size: 14px;
	cursor: pointer;
	white-space: nowrap;
	transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease),
	            transform var(--fast) var(--ease);
}
.vp-btn:hover { background: var(--bg-hover); border-color: var(--text-3); color: var(--text); }
.vp-btn:active { transform: translateY(1px); }
.vp-btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }

.vp-btn-primary {
	background: var(--accent);
	border-color: var(--accent);
	color: #14140c;
	font-weight: 600;
}
.vp-btn-primary:hover {
	background: var(--accent-hot);
	border-color: var(--accent-hot);
	color: #14140c;
	box-shadow: var(--sh-accent);
}

.vp-btn-ghost { background: transparent; border-color: transparent; color: var(--text-2); }
.vp-btn-ghost:hover { background: var(--bg-hover); color: var(--text); }

.vp-btn-danger { border-color: rgba(244, 91, 91, .4); color: var(--danger); }
.vp-btn-danger:hover { background: rgba(244, 91, 91, .12); border-color: var(--danger); color: var(--danger); }

.vp-btn-sm { height: 28px; padding: 0 var(--s-3); font-size: 13px; }
.vp-btn-lg { height: 44px; padding: 0 var(--s-5); font-size: 15px; }
.vp-btn-block { width: 100%; }

/* 图标按钮 */
.vp-btn-icon { width: 36px; padding: 0; }
.vp-btn-icon.vp-btn-sm { width: 28px; }

/* 激活态（收藏/点赞已选中） */
.vp-btn.is-on {
	background: var(--accent-dim);
	border-color: var(--accent-line);
	color: var(--accent);
}

/* ==========================================================================
   表单
   ========================================================================== */

.vp-field { margin-bottom: var(--s-4); }
.vp-label {
	display: block;
	margin-bottom: var(--s-2);
	font-size: 13px;
	color: var(--text-2);
}
.vp-input, .vp-select, .vp-textarea {
	width: 100%;
	height: 38px;
	padding: 0 var(--s-3);
	border: 1px solid var(--line-strong);
	border-radius: var(--r);
	background: var(--bg-elev);
	color: var(--text);
	font-family: inherit;
	font-size: 14px;
	transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.vp-textarea { height: auto; min-height: 88px; padding: var(--s-3); resize: vertical; line-height: 1.6; }
.vp-input:focus, .vp-select:focus, .vp-textarea:focus {
	outline: none;
	border-color: var(--accent);
	box-shadow: 0 0 0 3px var(--accent-dim);
}
.vp-input::placeholder, .vp-textarea::placeholder { color: var(--text-3); }
.vp-field-err { margin-top: var(--s-1); font-size: 12px; color: var(--danger); min-height: 16px; }

/* ==========================================================================
   标签 / 徽标
   ========================================================================== */

.vp-tag {
	display: inline-flex;
	align-items: center;
	height: 24px;
	padding: 0 var(--s-3);
	border: 1px solid var(--line-strong);
	border-radius: var(--r-pill);
	background: var(--bg-elev);
	color: var(--text-2);
	font-size: 12px;
	cursor: pointer;
	transition: all var(--fast) var(--ease);
}
.vp-tag:hover { border-color: var(--accent-line); color: var(--accent); }
.vp-tag.is-on { background: var(--accent-dim); border-color: var(--accent-line); color: var(--accent); }

/* 海报角标 */
.vp-badge {
	display: inline-flex;
	align-items: center;
	height: 20px;
	padding: 0 6px;
	border-radius: var(--r-sm);
	background: rgba(10, 10, 12, .82);
	backdrop-filter: blur(6px);
	color: var(--text);
	font-family: var(--font-mono);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: .02em;
}
.vp-badge-accent { background: var(--accent); color: #14140c; }
.vp-badge-teal { background: var(--teal); color: #06231f; }

/* ==========================================================================
   海报网格 + 卡片（全站最核心的组件）
   ========================================================================== */

.vp-grid {
	display: grid;
	/* 168 是 1440 内容区时代定的；放宽到 1760 之后会排到 9 列、每张才 170 px。
	   200 起排，宽屏 8 列，和首页分类行的卡片大小不至于差一倍 */
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
	gap: var(--s-4);
}
/* 帖子用宽卡，一行少一点 */
.vp-grid-wide { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }

.vp-card {
	position: relative;
	display: block;
	border-radius: var(--r-lg);
	background: var(--bg-elev);
	overflow: hidden;
	transition: transform var(--slow) var(--ease), box-shadow var(--slow) var(--ease);
}
.vp-card:hover { transform: translateY(-4px); box-shadow: var(--sh-3); color: var(--text); }

.vp-card-poster {
	position: relative;
	aspect-ratio: 2 / 3;
	background: linear-gradient(145deg, #1c1c22, #14141a);
	overflow: hidden;
}
/* 图集帖是横版封面。16/9 把竖版手机截图裁成中间一条，16/10 留多一点 */
.vp-card-post .vp-card-poster { aspect-ratio: 16 / 10; }

.vp-card-poster img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform var(--slow) var(--ease), opacity var(--fast) var(--ease);
}
.vp-card:hover .vp-card-poster img { transform: scale(1.06); }

/* 海报底部渐变，保证白字在任何画面上都读得清 */
.vp-card-poster::after {
	content: "";
	position: absolute;
	inset: auto 0 0 0;
	height: 52%;
	background: linear-gradient(to top, rgba(8, 8, 10, .92), transparent);
	pointer-events: none;
}

.vp-card-tl { position: absolute; top: var(--s-2); left: var(--s-2); z-index: 2; display: flex; gap: 4px; }
.vp-card-tr { position: absolute; top: var(--s-2); right: var(--s-2); z-index: 2; }
.vp-card-br { position: absolute; bottom: var(--s-2); right: var(--s-2); z-index: 2; }

.vp-card-body { padding: var(--s-3); }
.vp-card-title {
	font-size: 13px;
	font-weight: 600;
	line-height: 1.4;
	/* 固定两行，网格才不会高低不齐 */
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	min-height: 2.8em;
}
.vp-card-meta {
	margin-top: var(--s-1);
	display: flex;
	align-items: center;
	gap: var(--s-2);
	color: var(--text-3);
	font-size: 12px;
}
.vp-card-meta .vp-num { font-family: var(--font-mono); }

/* 🔴 竖版卡片下面那行 meta **占位常驻**，一个字没有也留着这行高度。
   剧集有「更新至 N 集」、单片没有，不占位的话同一行里两种卡片底边参差不齐
   （用户原话：「有多集的会撑大一点，没集数的就短一截」）。
   `.vp-card-body >` 是有意的作用域 —— 封面卡（`.vp-card-cover`）的 meta 在
   `.vp-card-over` 里压着海报，那里空着就该真的不占地方。 */
/* 简介：常驻在标题下面，固定两行。
   🔴 `min-height` 和 `-webkit-line-clamp` 必须成对 —— 只截不占位的话，
   有简介的卡比没简介的高两行，一行卡片底边参差不齐（和下面 meta 同一个道理）。

   🔴 单位必须是 `em`（= 2 行 × 1.6 行高），不能换算成 px 写死：
   38 px 对不上实际的 38.4 px，空简介的卡就比满两行的矮 1 px ——
   量出来 `[555, 556]`。标题那条 `min-height: 2.8em` 也是同一个道理。 */
.vp-card-brief {
	margin: var(--s-2) 0 0;
	color: var(--text-3);
	font-size: 12px;
	line-height: 1.6;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	min-height: 3.2em;
}

.vp-card-body > .vp-card-meta {
	/* 🔴 20 不是 18：`.vp-card-ep` 是 line-height 16 + 上下 padding 1 + 上下 border 1 = 20px。
	   按 18 留位的话，有集数的卡片仍然比没集数的高 2px —— 眼睛看不出来，
	   量 getBoundingClientRect() 才会露馅。改 `.vp-card-ep` 的边框/内边距要连这里一起改。 */
	min-height: 20px;
}

/* ---------- 区块（rail.js 生成的结构） ----------
   🔴 这三块必须待在 core.css：`VP.rail.create/grid` 是**框架层**，首页、播放页
   都在用。以前它们写在 page-home.css 里，播放页侧栏的标题栏就没样式，
   「更多短剧」和「查看全部 →」掉成上下两行。 */
.vp-rail,
.vp-sec {
	margin-bottom: var(--s-6);
}

.vp-rail-head {
	display: flex;
	align-items: baseline;
	gap: var(--s-3);
	margin-bottom: var(--s-3);
}

.vp-rail-more {
	color: var(--text-3);
	font-size: 13px;
	white-space: nowrap;
}

.vp-rail-more:hover {
	color: var(--accent);
}

/* ---------- 横版小卡（播放页右栏专用） ----------
   窄侧栏里竖卡一列只放得下两三个，横条能放八个。理由和「不许把它搬回列表页」
   写在 card.js 的 mini() 头上。 */
.vp-card-mini {
	display: flex;
	gap: var(--s-3);
	padding: var(--s-2);
	border-radius: var(--r-md);
	transition: background var(--fast) var(--ease);
}

.vp-card-mini:hover {
	background: var(--bg-elev);
	color: var(--text);
}

.vp-mini-poster {
	position: relative;
	flex: 0 0 62px;
	aspect-ratio: 2 / 3;
	border-radius: var(--r-sm);
	overflow: hidden;
	background: linear-gradient(145deg, #1c1c22, #14141a);
}

.vp-mini-poster img,
.vp-mini-poster .vp-poster-ph {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.vp-mini-poster .vp-poster-ph { font-size: 20px; }

.vp-mini-body {
	/* flex 子项默认 min-width:auto，不压成 0 的话长标题会把侧栏顶宽 */
	min-width: 0;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 6px;
}

.vp-mini-title {
	font-size: 13px;
	font-weight: 600;
	line-height: 1.4;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.vp-mini-meta {
	color: var(--text-3);
	font-size: 12px;
	font-family: var(--font-mono);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* ---------- 图集帖卡片 ----------
   图集的卖点是**图多**，所以卡片不只给一张封面：大图下面挂一条缩略图
   （第 2~5 张，最后一格叠「+N」），再加两行摘要。
   只放封面 + 标题的话，一屏帖子长得都一样，点开才知道里面是什么。 */
.vp-card-post .vp-card-body {
	padding: var(--s-4);
}

.vp-card-post .vp-card-title {
	font-size: 16px;
	line-height: 1.45;
	min-height: 0;
}

.vp-card-sum {
	margin: var(--s-2) 0 0;
	color: var(--text-2);
	font-size: 12px;
	line-height: 1.75;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* 缩略图条。固定高度而不是正方形 —— 卡片宽度会随断点变，
   正方形的话宽屏上每格能有 140 px，一条缩略图比封面还抢眼 */
.vp-thumbs {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: var(--s-1);
	margin-top: var(--s-3);
}

.vp-thumb {
	position: relative;
	height: 76px;
	border-radius: var(--r-sm);
	overflow: hidden;
	background: var(--bg-elev-2);
}

.vp-thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	opacity: .82;
	transition: opacity var(--slow) var(--ease);
}

.vp-card:hover .vp-thumb img {
	opacity: 1;
}

/* 最后一格压「+N」：压暗底图，让数字读得清 */
.vp-thumb.is-more img {
	opacity: .28;
}

.vp-card:hover .vp-thumb.is-more img {
	opacity: .38;
}

.vp-thumb-more {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	color: var(--text);
	font-family: var(--font-mono);
	font-size: 15px;
	font-weight: 700;
}

.vp-card-post .vp-card-meta {
	margin-top: var(--s-3);
}

/* ---------- 封面卡（整卡一张海报，字压在上面） ----------
   分类浏览页用。没有下面那块 body —— 一屏排开就是一面封面墙。
   简介和标签平时藏着，hover 才滑出来。 */
.vp-card-cover {
	background: none;
}

.vp-card-cover .vp-card-poster {
	border-radius: var(--r-lg);
}

/* 压字的那层渐变。比普通卡的更深更高一点 —— 这里要压住的是 17 px 的标题，
   不是 13 px 的小字，浅了在亮色封面上根本读不清 */
.vp-card-cover .vp-card-poster::after {
	height: 62%;
	/* 🔴 底部这一段是**纯不透明**的，不是 .96 —— 本站封面大多是浅色手机截图，
	   留 4% 透光的话键盘字母会从标题底下透出来，两层字叠在一起谁也读不清 */
	background: linear-gradient(to top,
		rgb(6, 6, 8) 0%,
		rgba(6, 6, 8, .92) 26%,
		rgba(6, 6, 8, .58) 55%,
		transparent 100%);
	transition: height var(--slow) var(--ease);
}

.vp-card-over {
	position: absolute;
	inset: auto 0 0 0;
	z-index: 2;
	padding: var(--s-4);
	/* 图片 hover 会放大 1.06，字跟着一起动会糊，这里单独提一层不受影响 */
	pointer-events: none;
}

.vp-card-cover .vp-card-title {
	font-size: 17px;
	font-weight: 700;
	letter-spacing: -.01em;
	-webkit-line-clamp: 2;
	min-height: 0;
	text-shadow: 0 2px 12px rgba(0, 0, 0, .8);
}

.vp-card-cover .vp-card-meta {
	margin-top: var(--s-2);
	color: var(--text-2);
}

/* 简介：hover 才展开。**三层**结构，每层都是必须的：

     .vp-card-more      grid，0fr → 1fr，负责动画
     .vp-card-more-in   overflow:hidden + min-height:0，负责裁掉
     .vp-card-desc      真正的内容，间距写在**它**身上

   为什么用 0fr→1fr 而不是 max-height：max-height 得猜一个够大的值，
   猜大了过渡前半段是空跑（看着像延迟），猜小了内容直接被截。

   为什么中间那层不能省：`grid-template-rows: 0fr` 只定义**第一行**，
   `.vp-card-more` 里放两个子元素的话第二个落进 auto 的隐式行，永远收不起来。

   为什么间距不能写在中间那层上：收起时行高是 0，但 margin 不受行高约束、
   padding 在 border-box 下也压不掉（`height:0` 的盒子实际高度仍是 padding+border）——
   两种写法都会在收起状态留一条 8 px 的空档。间距必须写在**被裁掉的内容**上。 */
.vp-card-more {
	display: grid;
	grid-template-rows: 0fr;
	transition: grid-template-rows var(--slow) var(--ease);
}

.vp-card-more-in {
	overflow: hidden;
	min-height: 0;
}

.vp-card:hover .vp-card-more,
.vp-card:focus-visible .vp-card-more {
	grid-template-rows: 1fr;
}

.vp-card-desc {
	/* 间距写在这里（最内层），不写在 .vp-card-more-in 上 —— 理由见上面那段 */
	margin: var(--s-2) 0 0;
	color: var(--text-2);
	font-size: 12px;
	line-height: 1.7;
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.vp-card-tags {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-1);
	margin-top: var(--s-2);
	/* 一行放不下就整行藏掉，宁可不显示也不要半截标签换行 */
	max-height: 20px;
	overflow: hidden;
	position: relative;
	z-index: 1;
}

.vp-card-tag {
	padding: 1px 8px;
	border-radius: var(--r-pill);
	background: rgba(255, 255, 255, .12);
	color: var(--text);
	font-size: 11px;
	line-height: 18px;
	white-space: nowrap;
	backdrop-filter: blur(4px);
}

/* hover：整卡抬起 + 琥珀描边发光，渐变同时长高，给滑出来的简介垫底。
   translateY 那 4 px 对 660 px 高的大卡太含蓄了，这里改成缩放。
   🔴 长到 78% 就够，不要 100% —— 盖满整张等于把封面糊掉，
   而 hover 的那一下正是用户想看清这张封面的时候 */
.vp-card-cover:hover {
	transform: scale(1.025);
	box-shadow: 0 22px 60px rgba(0, 0, 0, .72), 0 0 0 2px var(--accent-line);
}

.vp-card-cover:hover .vp-card-poster::after {
	height: 78%;
}

/* 系统开了「减少动态效果」就只留描边，不做缩放和展开 */
@media (prefers-reduced-motion: reduce) {
	.vp-card-cover:hover { transform: none; }
	.vp-card-more { transition: none; }
}

/* 集数标。以前压在海报左上角，太抢封面，挪到标题下面这行来了。
   连载 = 琥珀细边框，完结 = 灰边框，不填色，避免和海报角标撞在一起。 */
.vp-card-ep {
	flex: 0 0 auto;
	padding: 1px 6px;
	border: 1px solid var(--accent-line);
	border-radius: var(--r-sm);
	color: var(--accent);
	font-family: var(--font-mono);
	font-size: 11px;
	line-height: 16px;
	white-space: nowrap;
}
.vp-card-ep.is-fin {
	border-color: var(--line);
	color: var(--text-3);
}
/* 分类名 / 「看到第 N 集」：次要信息，装不下就省略，不许换行把卡片撑高 */
.vp-card-sub {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* 无封面占位：纯 CSS，不发图片请求 */
.vp-poster-ph {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	background: linear-gradient(145deg, #23232c, #14141a);
	color: var(--text-3);
	font-size: 34px;
	font-weight: 700;
	user-select: none;
}

/* 卡片上的移除按钮（收藏/历史列表）：默认藏起来，hover 才出现 */
.vp-card-x {
	opacity: 0;
	background: rgba(10, 10, 12, .8);
	backdrop-filter: blur(4px);
	border-color: transparent;
	transition: opacity var(--fast) var(--ease), background var(--fast) var(--ease);
}
.vp-card:hover .vp-card-x { opacity: 1; }
.vp-card-x:hover { background: var(--danger); border-color: var(--danger); color: #fff; }
/* 触屏没有 hover，直接常显 */
@media (hover: none) {
	.vp-card-x { opacity: .9; }
}

/* 续播进度条：贴在海报底边 */
.vp-card-progress {
	position: absolute;
	bottom: 0;
	left: 0;
	right: 0;
	height: 3px;
	background: rgba(255, 255, 255, .16);
	z-index: 2;
}
.vp-card-progress > i {
	display: block;
	height: 100%;
	background: var(--accent);
}

/* 入场：错峰淡入上移。--i 由 JS 按索引写入 */
@media (prefers-reduced-motion: no-preference) {
	.vp-card-in {
		animation: vpCardIn .42s var(--ease) both;
		animation-delay: calc(var(--i, 0) * 28ms);
	}
}
@keyframes vpCardIn {
	from { opacity: 0; transform: translateY(10px); }
	to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   骨架屏
   ========================================================================== */

.vp-skel {
	position: relative;
	border-radius: var(--r);
	background: var(--bg-elev);
	overflow: hidden;
}
.vp-skel::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .05), transparent);
	animation: vpShimmer 1.3s infinite;
}
@keyframes vpShimmer {
	from { transform: translateX(-100%); }
	to   { transform: translateX(100%); }
}
.vp-skel-poster { aspect-ratio: 2 / 3; border-radius: var(--r-lg); }
.vp-skel-line { height: 12px; margin-top: var(--s-2); }
.vp-skel-line.w60 { width: 60%; }

/* ==========================================================================
   空态 / 加载
   ========================================================================== */

.vp-empty {
	padding: var(--s-8) var(--s-4);
	text-align: center;
	color: var(--text-3);
}

/* 🔴 空态和「加载失败」都是往列表**同一个容器**里塞的（见 core/list.js），
   而那个容器多半是 .vp-grid —— 不横跨整行的话它只占第一格（宽 200 多 px），
   文字在那一格里居中，看着就是「贴着左边」。骨架屏没这问题：它塞的是一排卡片。 */
.vp-grid > .vp-empty {
	grid-column: 1 / -1;
}
.vp-empty-icon { font-size: 40px; opacity: .35; margin-bottom: var(--s-3); }
.vp-empty-text { font-size: 14px; }
.vp-empty-sub { margin-top: var(--s-2); font-size: 13px; color: var(--text-3); }

.vp-spinner {
	width: 18px;
	height: 18px;
	border: 2px solid var(--line-strong);
	border-top-color: var(--accent);
	border-radius: 50%;
	animation: vpSpin .7s linear infinite;
}
@keyframes vpSpin { to { transform: rotate(360deg); } }

/* ==========================================================================
   分页器（框架层组件，所有列表页共用）
   ========================================================================== */

.vp-pager {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--s-2);
	margin: var(--s-6) 0;
	flex-wrap: wrap;
}
.vp-pager-btn {
	min-width: 34px;
	height: 34px;
	padding: 0 var(--s-2);
	border: 1px solid var(--line-strong);
	border-radius: var(--r);
	background: var(--bg-elev);
	color: var(--text-2);
	font-family: var(--font-mono);
	font-size: 13px;
	cursor: pointer;
	transition: all var(--fast) var(--ease);
}
.vp-pager-btn:hover:not(:disabled) { border-color: var(--accent-line); color: var(--accent); }
.vp-pager-btn:disabled { opacity: .35; cursor: not-allowed; }
.vp-pager-btn.is-on {
	background: var(--accent);
	border-color: var(--accent);
	color: #14140c;
	font-weight: 700;
}
.vp-pager-gap { color: var(--text-3); padding: 0 var(--s-1); }
.vp-pager-info { margin-left: var(--s-3); color: var(--text-3); font-size: 12px; }
.vp-pager-jump { display: flex; align-items: center; gap: var(--s-2); color: var(--text-3); font-size: 12px; }
.vp-pager-jump input {
	width: 52px;
	height: 34px;
	text-align: center;
	border: 1px solid var(--line-strong);
	border-radius: var(--r);
	background: var(--bg-elev);
	color: var(--text);
	font-family: var(--font-mono);
	font-size: 13px;
}
.vp-pager-jump input:focus { outline: none; border-color: var(--accent); }

/* ==========================================================================
   Toast
   ========================================================================== */

.vp-toasts {
	position: fixed;
	top: calc(var(--nav-h) + var(--s-3));
	left: 50%;
	transform: translateX(-50%);
	z-index: 9000;
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
	pointer-events: none;
}
.vp-toast {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	max-width: min(90vw, 420px);
	padding: var(--s-3) var(--s-4);
	border: 1px solid var(--line-strong);
	border-radius: var(--r);
	background: var(--bg-elev-2);
	box-shadow: var(--sh-3);
	font-size: 14px;
	animation: vpToastIn .22s var(--ease) both;
}
.vp-toast.out { animation: vpToastOut .18s var(--ease) both; }
.vp-toast-ok { border-color: rgba(45, 212, 191, .4); }
.vp-toast-ok .vp-toast-dot { background: var(--teal); }
.vp-toast-err { border-color: rgba(244, 91, 91, .4); }
.vp-toast-err .vp-toast-dot { background: var(--danger); }
.vp-toast-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex: none; }
@keyframes vpToastIn  { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: none; } }
@keyframes vpToastOut { to   { opacity: 0; transform: translateY(-8px); } }

/* ==========================================================================
   Modal
   ========================================================================== */

.vp-mask {
	position: fixed;
	inset: 0;
	z-index: 8000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--s-4);
	background: rgba(4, 4, 6, .74);
	backdrop-filter: blur(3px);
	animation: vpFade .18s var(--ease) both;
}
@keyframes vpFade { from { opacity: 0; } to { opacity: 1; } }

.vp-modal {
	width: 100%;
	/* 460 装不下后台那些字段名 + 长内容，底下会顶出一条横向滚动条。
	   两个宽度都用 min(px, vw)：窄屏上不会宽过屏幕 */
	max-width: min(600px, 94vw);
	max-height: 86vh;
	display: flex;
	flex-direction: column;
	border: 1px solid var(--line-strong);
	border-radius: var(--r-lg);
	background: var(--bg-elev-2);
	box-shadow: var(--sh-3);
	animation: vpModalIn .22s var(--ease) both;
}
.vp-modal-lg { max-width: min(1180px, 94vw); }
@keyframes vpModalIn { from { opacity: 0; transform: translateY(12px) scale(.98); } to { opacity: 1; transform: none; } }

.vp-modal-head {
	display: flex;
	align-items: center;
	padding: var(--s-4);
	border-bottom: 1px solid var(--line);
	font-size: 15px;
	font-weight: 600;
}
.vp-modal-head .vp-modal-x { margin-left: auto; }
.vp-modal-body { padding: var(--s-4); overflow-y: auto; }
.vp-modal-foot {
	display: flex;
	justify-content: flex-end;
	gap: var(--s-2);
	padding: var(--s-4);
	border-top: 1px solid var(--line);
}

/* 确认框里的附加勾选项（如「一并删除磁盘文件」）。默认不勾，勾了才做那件不可回滚的事 */
.vp-confirm-check {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	margin-top: var(--s-3);
	cursor: pointer;
	font-size: 13px;
	color: var(--text-2);
}
.vp-confirm-check input { flex: none; }

/* 勾选框：默认那个太小，点起来费劲。`confirmCheck`（core/ui.js）唯一用它，
   但它是框架层组件，前台页面也可能用到 confirm 弹窗里的勾选项，样式必须待在 core.css。 */
.vp-check {
	width: 18px;
	height: 18px;
	accent-color: var(--accent);
	cursor: pointer;
}

/* ==========================================================================
   表格（后台用）
   ========================================================================== */

.vp-table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-lg); }
.vp-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.vp-table th, .vp-table td {
	padding: var(--s-3);
	text-align: left;
	border-bottom: 1px solid var(--line);
	white-space: nowrap;
}
.vp-table th { background: var(--bg-elev); color: var(--text-2); font-weight: 600; }
.vp-table tbody tr:hover { background: var(--bg-elev); }
.vp-table tbody tr:last-child td { border-bottom: none; }
.vp-table .vp-td-wrap { white-space: normal; min-width: 220px; }
.vp-table .vp-num { font-family: var(--font-mono); }

/* ==========================================================================
   工具类
   ========================================================================== */

.vp-row { display: flex; align-items: center; gap: var(--s-3); }
.vp-row-wrap { flex-wrap: wrap; }
.vp-spacer { flex: 1; }
.vp-muted { color: var(--text-2); }
.vp-dim { color: var(--text-3); }
.vp-num { font-family: var(--font-mono); }
.vp-hide { display: none !important; }
.vp-clamp-1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vp-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.vp-clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.vp-sr {
	position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
	overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ==========================================================================
   响应式 —— PC 和手机都要能用
   ========================================================================== */

@media (max-width: 1024px) {
	:root { --max-w: 100%; }
	:root { --wrap-pad: var(--s-4); }
}

@media (max-width: 768px) {
	body { font-size: 13px; }
	h1 { font-size: 21px; }
	h2 { font-size: 17px; }
	:root { --wrap-pad: var(--s-3); }
	.vp-grid { grid-template-columns: repeat(3, 1fr); gap: var(--s-2); }
	.vp-grid-wide { grid-template-columns: repeat(1, 1fr); }
	.vp-card-body { padding: var(--s-2); }
	.vp-card-title { font-size: 12px; }
	.vp-sec-title { margin: var(--s-5) 0 var(--s-3); font-size: 16px; }
	/* 移动端隐藏页码明细，只留上下页 + 当前页 */
	.vp-pager-num-lg { display: none; }
	.vp-pager-info { display: none; }
}

@media (max-width: 420px) {
	.vp-grid { grid-template-columns: repeat(2, 1fr); }
}

/* 尊重系统的减弱动效设置 */
@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
	}
}
