Files
Xiao-no-love f0fe50e484 feat: StarLight 第三方登录 + 安全加固 + 循环池 + 热配置
功能:
- StarLight Yggdrasil 第三方登录(后端中转,accessToken 不落地)
- 管理员可视化权限管理(用户列表 + 一键授予/移出)
- 设置页热配置(11 项可编辑,立即生效)
- 循环池(每流独立手动歌单,队列空时随机不重复抽取)
- 登录页统一主站浅色风格
- 全站改名「Starlight专属电台」
- 删除关于页

安全:
- 全局 CSRF 防护(双提交 Cookie)
- 登录限流防 XFF 伪造、错误信息脱敏
- 权限中间件 requireAdminForWrites(读放行/写 admin)
- 密码哈希存储(scrypt/sha256)
- SSRF 防护(封面下载域名白名单)
- 空 catch 加日志、SSE 连接上限

修复:
- 多角色账号 selectedProfile 缺失
- 普通用户越权删歌(V4)
- streams 写操作越权(V9)
- 设置页布局溢出

测试:38 项全绿(api16 / e2e17 / write5)
2026-10-06 16:37:03 +08:00

2336 lines
77 KiB
CSS

/* ==================== TOKENS ==================== */
:root {
/* 背景与表面 */
--bg: #f4f5f9;
--surface: #fff;
--surface-hover: #fafbff;
--white: #fff;
/* 强调色 */
--accent: #4f6ef7;
--accent-hover: #3d5ce0;
--accent-light: #eef1fe;
--accent-glow: rgba(79, 110, 247, 0.14);
--accent-subtle: rgba(79, 110, 247, 0.06);
/* 文字 */
--text-primary: #0f1225;
--text-secondary: #5a607f;
--text-muted: #9499b8;
/* 边框 */
--border: #e8eaf2;
--border-light: #f0f1f6;
/* 语义色 */
--danger: #ef4565;
--danger-light: #fef0f2;
--danger-hover: #fdd;
--success: #30a46c;
--success-light: #e9f9ef;
--warning: #f5a623;
--warning-light: #fff7e8;
/* 浮层与终端 */
--overlay: rgba(20, 22, 30, 0.45);
--log-bg: #1e1e28;
--log-fg: #d4d4d4;
/* 字号阶梯 */
--fs-2xs: 0.62rem;
--fs-xs: 0.7rem;
--fs-sm: 0.78rem;
--fs-base: 0.84rem;
--fs-md: 0.92rem;
--fs-lg: 1rem;
--fs-xl: 1.12rem;
--fs-2xl: 1.3rem;
--fs-3xl: 1.5rem;
--fs-4xl: 2.4rem;
/* 圆角阶梯 */
--radius-xs: 8px;
--radius-sm: 10px;
--radius: 14px;
--radius-lg: 20px;
--radius-xl: 28px;
--radius-pill: 999px;
/* 阴影 */
--shadow-xs: 0 1px 2px rgba(15, 18, 37, 0.04);
--shadow-sm: 0 2px 8px rgba(15, 18, 37, 0.06);
--shadow: 0 4px 20px rgba(15, 18, 37, 0.07);
--shadow-lg: 0 8px 40px rgba(15, 18, 37, 0.1);
--shadow-accent: 0 4px 16px rgba(79, 110, 247, 0.25);
/* 动效 */
--dur-fast: 0.15s;
--dur-base: 0.22s;
--dur-slow: 0.35s;
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
/* 间距 */
--sp-1: 4px;
--sp-2: 8px;
--sp-3: 12px;
--sp-4: 16px;
--sp-5: 20px;
--sp-6: 24px;
/* 布局 */
--content-w: 920px;
--nav-bottom: 14px;
--nav-h: 56px;
--mini-h: 0px;
--total-bottom: calc(
var(--nav-bottom) + var(--nav-h) + var(--mini-h) + 14px
);
}
.mini-player-visible {
--mini-h: 82px;
}
/* ==================== RESET ==================== */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family:
"Plus Jakarta Sans",
-apple-system,
BlinkMacSystemFont,
sans-serif;
color: var(--text-primary);
height: 100dvh;
display: flex;
flex-direction: column;
-webkit-font-smoothing: antialiased;
overflow: hidden;
}
img {
display: block;
max-width: 100%;
height: auto;
}
button {
font-family: inherit;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
input {
font-family: inherit;
}
/* ==================== BG ==================== */
.page-bg {
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
background: var(--bg);
}
.bg-shapes {
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
overflow: hidden;
}
.bg-shapes .s {
position: absolute;
border-radius: 50%;
opacity: 0.03;
animation: floatShape 22s ease-in-out infinite alternate;
}
.bg-shapes .s:nth-child(1) {
width: 520px;
height: 520px;
top: -15%;
left: -10%;
background: var(--accent);
}
.bg-shapes .s:nth-child(2) {
width: 420px;
height: 420px;
bottom: -10%;
right: -8%;
background: var(--accent);
animation-delay: -6s;
}
.bg-shapes .s:nth-child(3) {
width: 300px;
height: 300px;
top: 40%;
left: 60%;
background: var(--success);
animation-delay: -12s;
}
@keyframes floatShape {
0% {
transform: translate(0, 0) scale(1);
}
100% {
transform: translate(40px, -30px) scale(1.12);
}
}
/* ==================== HEADER ==================== */
.top-header {
flex-shrink: 0;
z-index: 100;
background: rgba(244, 245, 249, 0.78);
backdrop-filter: blur(28px) saturate(180%);
padding: 0.55rem 1rem;
animation: headerSlide 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes headerSlide {
from {
opacity: 0;
transform: translateY(-100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.header-inner {
max-width: var(--content-w);
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.5rem;
}
.brand {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 800;
font-size: var(--fs-md);;
color: var(--text-primary);
letter-spacing: -0.02em;
}
.brand-icon {
width: 30px;
height: 30px;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
box-shadow: var(--shadow-accent);
flex-shrink: 0;
}
.brand-icon img {
width: 100%;
height: 100%;
object-fit: cover;
}
.live-pill {
display: inline-flex;
align-items: center;
gap: 0.28rem;
font-size: var(--fs-2xs);;
font-weight: 700;
background: var(--success);
color: var(--white);
padding: 0.18rem 0.5rem;
border-radius: var(--radius-lg);
letter-spacing: 0.06em;
text-transform: uppercase;
animation: livePulse 2.5s ease-in-out infinite;
}
.live-dot {
width: 4px;
height: 4px;
background: var(--white);
border-radius: 50%;
animation: dotPulse 1.5s ease-in-out infinite;
}
@keyframes livePulse {
0%,
100% {
box-shadow: 0 0 0 0 rgba(48, 164, 108, 0.35);
}
50% {
box-shadow: 0 0 0 8px rgba(48, 164, 108, 0);
}
}
@keyframes dotPulse {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.3;
transform: scale(0.8);
}
}
.header-actions {
display: flex;
align-items: center;
gap: 0.45rem;
}
.cache-pill {
font-size: var(--fs-xs);;
font-weight: 500;
color: var(--text-secondary);
background: var(--surface);
padding: 0.22rem 0.55rem;
border-radius: var(--radius-lg);
display: inline-flex;
align-items: center;
gap: 0.28rem;
border: 1px solid var(--border-light);
font-family: "JetBrains Mono", monospace;
letter-spacing: -0.02em;
}
.btn-link-sm {
font-size: var(--fs-xs);;
font-weight: 600;
color: var(--accent);
text-decoration: none;
padding: 0.28rem 0.7rem;
border: none;
background: transparent;
border-radius: var(--radius-lg);
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 0.25rem;
}
.btn-link-sm:hover {
background: var(--accent-light);
}
/* ==================== PAGE CONTAINER ==================== */
.page-container {
position: relative;
z-index: 1;
max-width: var(--content-w);
margin: 0 auto;
width: 100%;
padding: 1rem 1.25rem 2rem;
padding-bottom: calc(var(--total-bottom) + 1.5rem);
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.page-container::-webkit-scrollbar {
width: 5px;
}
.page-container::-webkit-scrollbar-track {
background: transparent;
}
.page-container::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: var(--radius-sm);
}
.page-container::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
.page {
display: none;
}
.page.active {
display: flex;
flex-direction: column;
gap: var(--sp-4);
flex: 1;
min-height: 0;
overflow-y: auto;
overscroll-behavior: contain;
animation: pageEnter 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes pageEnter {
from {
opacity: 0;
transform: translateY(12px) scale(0.995);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* ==================== CARDS ==================== */
.section-card,
.search-card,
.about-card {
background: var(--surface);
border-radius: var(--radius-xl);
box-shadow: var(--shadow);
border: 1px solid var(--border-light);
animation: cardRise 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.section-card {
display: flex;
flex-direction: column;
min-height: 0;
flex-shrink: 0;
}
@keyframes cardRise {
from {
opacity: 0;
transform: translateY(18px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ==================== SEARCH ==================== */
.search-card {
display: flex;
flex-direction: column;
min-height: 0;
overflow-y: auto;
}
.search-header {
padding: 1.5rem 1.5rem 0.3rem;
}
.search-header h2 {
font-size: var(--fs-xl);;
font-weight: 800;
letter-spacing: -0.03em;
margin-bottom: 0.12rem;
}
.search-header p {
font-size: var(--fs-sm);;
color: var(--text-muted);
}
.search-input-wrap {
padding: 0.75rem 1.5rem;
}
.search-field {
display: flex;
gap: 0.5rem;
align-items: stretch;
}
.search-field input {
flex: 1;
border: 1.5px solid var(--border);
border-radius: var(--radius-sm);
padding: 0.65rem 0.85rem;
font-size: var(--fs-base);;
background: var(--bg);
color: var(--text-primary);
outline: none;
transition:
border-color 0.25s,
box-shadow 0.25s,
background 0.25s;
}
.search-field input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-glow);
background: var(--surface);
}
.search-field input::placeholder {
color: var(--text-muted);
font-size: var(--fs-sm);;
}
.btn-search {
border: none;
background: var(--accent);
color: var(--white);
padding: 0 1.2rem;
border-radius: var(--radius-sm);
font-weight: 700;
font-size: var(--fs-sm);;
transition: all 0.2s;
white-space: nowrap;
letter-spacing: -0.01em;
position: relative;
overflow: hidden;
}
.btn-search::after {
content: "";
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.2);
transform: translateX(-100%);
transition: transform 0.3s;
}
.btn-search:hover::after {
transform: translateX(0);
}
.btn-search:hover {
background: var(--accent-hover);
box-shadow: var(--shadow-accent);
}
.btn-search:active {
transform: scale(0.97);
}
.btn-search:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Parse hint */
.parse-hint {
padding: 0 1.5rem;
margin-bottom: 0.3rem;
display: flex;
align-items: center;
gap: 0.35rem;
font-size: var(--fs-xs);;
color: var(--text-muted);
max-height: 0;
opacity: 0;
overflow: hidden;
transition: all 0.3s ease;
}
.parse-hint.visible {
opacity: 1;
max-height: 36px;
padding-top: 0.1rem;
}
.parse-hint i {
color: var(--accent);
font-size: var(--fs-sm);;
}
.parse-hint code {
font-family: "JetBrains Mono", monospace;
font-size: var(--fs-xs);;
background: var(--accent-light);
color: var(--accent);
padding: 0.08rem 0.35rem;
border-radius: var(--radius-xs);
font-weight: 500;
}
/* Preview card — info only, no inline player */
.preview-card {
margin: 0 1.5rem 1.5rem;
background: var(--bg);
border-radius: var(--radius-lg);
overflow: hidden;
border: 1px solid var(--border-light);
animation: slideUp 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.preview-cover-section {
display: flex;
align-items: center;
gap: 0.9rem;
padding: 0.9rem 1.1rem;
}
.preview-cover-wrap {
position: relative;
width: 64px;
height: 64px;
flex-shrink: 0;
}
.preview-cover {
width: 64px;
height: 64px;
border-radius: var(--radius);
object-fit: cover;
box-shadow: var(--shadow-sm);
}
.preview-cover-wrap::after {
content: "";
position: absolute;
inset: 0;
border-radius: var(--radius);
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.06);
pointer-events: none;
}
.preview-info {
flex: 1;
min-width: 0;
}
.preview-title {
font-weight: 700;
font-size: var(--fs-md);;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
letter-spacing: -0.02em;
margin-bottom: 0.1rem;
}
.preview-artist {
font-size: var(--fs-sm);;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.preview-details {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
padding: 0 1.1rem 0.9rem;
}
.detail-tag {
display: inline-flex;
align-items: center;
gap: 0.25rem;
font-size: var(--fs-xs);;
font-weight: 500;
color: var(--text-secondary);
background: var(--surface);
padding: 0.24rem 0.55rem;
border-radius: var(--radius-sm);
border: 1px solid var(--border-light);
}
.detail-tag i {
font-size: var(--fs-xs);;
color: var(--text-muted);
}
.detail-tag.album-tag i {
color: var(--accent);
}
.detail-tag.quality-tag i {
color: var(--success);
}
.detail-tag.duration-tag i {
color: var(--warning);
}
.preview-actions {
display: flex;
gap: 0.5rem;
padding: 0 1.1rem 1.1rem;
}
.btn-download {
border: none;
background: var(--accent);
color: var(--white);
padding: 0.5rem 1rem;
border-radius: var(--radius-sm);
font-weight: 700;
font-size: var(--fs-sm);;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 0.35rem;
white-space: nowrap;
letter-spacing: -0.01em;
flex: 1;
justify-content: center;
position: relative;
overflow: hidden;
}
.btn-download::after {
content: "";
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.2);
transform: translateX(-100%);
transition: transform 0.3s;
}
.btn-download:hover::after {
transform: translateX(0);
}
.btn-download:hover {
background: var(--accent-hover);
box-shadow: var(--shadow-accent);
}
.btn-download:active {
transform: scale(0.97);
}
.btn-download:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Search results */
.searchResults {
margin: 0 1.5rem 1.5rem;
animation: slideUp 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.sr-head {
font-size: var(--fs-sm);;
color: var(--text-muted);
margin-bottom: 0.5rem;
padding: 0 0.25rem;
}
.sr-head b {
color: var(--accent);
font-weight: 700;
}
.sr-list {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.sr-item {
display: flex;
align-items: center;
gap: 0.65rem;
padding: 0.55rem 0.75rem;
background: var(--bg);
border: 1.5px solid transparent;
border-radius: var(--radius);
cursor: pointer;
transition: all 0.25s ease;
opacity: 0;
animation: srFadeIn 0.35s ease forwards;
}
.sr-item:hover {
border-color: var(--accent);
box-shadow: var(--shadow-sm);
transform: translateY(-1px);
}
.sr-item.active {
border-color: var(--accent);
background: var(--accent-light);
}
@keyframes srFadeIn {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.sr-main {
flex: 1;
min-width: 0;
}
.sr-name {
font-weight: 600;
font-size: var(--fs-base);;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.sr-artist {
font-size: var(--fs-xs);;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 0.06rem;
}
.sr-alias {
font-size: var(--fs-xs);;
color: var(--text-muted);
background: var(--surface);
padding: 0.15rem 0.4rem;
border-radius: var(--radius-xs);
border: 1px solid var(--border-light);
max-width: 160px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.sr-duration {
font-family: "JetBrains Mono", monospace;
font-size: var(--fs-xs);;
color: var(--text-muted);
flex-shrink: 0;
}
/* Tips — expand on tall screens */
.search-tips {
padding: 2rem 1.5rem 2.5rem;
text-align: center;
}
.tips-icon {
width: 60px;
height: 60px;
background: var(--accent-light);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 0.9rem;
font-size: var(--fs-2xl);;
color: var(--accent);
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-7px);
}
}
.tips-title {
font-weight: 700;
font-size: var(--fs-base);;
margin-bottom: 0.3rem;
letter-spacing: -0.02em;
}
.tips-desc {
font-size: var(--fs-sm);;
color: var(--text-muted);
line-height: 1.7;
}
.tips-desc code {
font-family: "JetBrains Mono", monospace;
font-size: var(--fs-xs);;
background: var(--accent-light);
color: var(--accent);
padding: 0.08rem 0.3rem;
border-radius: var(--radius-xs);
font-weight: 500;
}
/* ==================== RESOURCE / PLAYLIST ==================== */
.section-header {
padding: 1.1rem 1.1rem 0.65rem;
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.6rem;
flex-wrap: wrap;
flex-shrink: 0;
}
.section-title {
font-size: var(--fs-md);;
font-weight: 800;
display: flex;
align-items: center;
gap: 0.4rem;
letter-spacing: -0.03em;
}
.section-count {
min-width: 20px;
height: 20px;
background: var(--accent-light);
color: var(--accent);
font-size: var(--fs-2xs);;
font-weight: 700;
border-radius: var(--radius-xs);
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 5px;
}
.section-body {
padding: 0 0.5rem 0.5rem 0.5rem;
margin-bottom: 0.5rem;
}
.section-body::-webkit-scrollbar {
width: 5px;
}
.section-body::-webkit-scrollbar-track {
background: transparent;
}
.section-body::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: var(--radius-sm);
}
.section-body::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
.resource-search {
border: 1.5px solid var(--border);
border-radius: var(--radius-sm);
padding: 0.35rem 0.65rem;
font-size: var(--fs-sm);;
background: var(--bg);
color: var(--text-primary);
outline: none;
width: 135px;
transition:
border-color 0.25s,
box-shadow 0.25s,
width 0.25s;
}
.resource-search:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-glow);
width: 175px;
}
.resource-search::placeholder {
color: var(--text-muted);
}
/* Resource item */
.res-item {
position: relative;
overflow: hidden;
border-radius: var(--radius-lg);
margin: 0.2rem 0;
transition: all 0.3s cubic-bezier(0.22, 1, 0.36, 1);
cursor: default;
opacity: 0;
animation: itemFadeIn 0.4s ease forwards;
}
.res-item:hover {
box-shadow: var(--shadow-sm);
transform: translateY(-2px);
}
.res-item:active {
transform: translateY(0);
}
.res-bg {
position: absolute;
inset: -40px;
background-size: cover;
background-position: center;
filter: blur(50px) saturate(1.3);
opacity: 0.07;
z-index: 0;
transition: opacity 0.35s;
}
.res-item:hover .res-bg {
opacity: 0.15;
}
.res-row {
position: relative;
z-index: 1;
display: flex;
align-items: center;
padding: 0.6rem 0.75rem;
}
.res-cover {
width: 44px;
height: 44px;
border-radius: var(--radius-sm);
object-fit: cover;
flex-shrink: 0;
box-shadow: var(--shadow-sm);
}
.res-cover-ph {
width: 44px;
height: 44px;
border-radius: var(--radius-sm);
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
background: var(--bg);
font-size: var(--fs-lg);;
border: 1px solid var(--border-light);
}
.res-meta {
flex: 1;
min-width: 0;
margin: 0 0.65rem;
}
.res-title {
font-weight: 700;
font-size: var(--fs-base);;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
letter-spacing: -0.01em;
}
.res-artist {
font-size: var(--fs-xs);;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 0.08rem;
}
.res-actions {
display: flex;
align-items: center;
gap: 0.25rem;
flex-shrink: 0;
}
.heat-chip {
font-size: var(--fs-2xs);;
font-weight: 600;
background: var(--warning-light);
color: var(--warning);
padding: 0.14rem 0.4rem;
border-radius: var(--radius-xs);
display: inline-flex;
align-items: center;
gap: 0.18rem;
}
.btn-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border: none;
background: none;
color: var(--text-muted);
font-size: var(--fs-lg);;
border-radius: var(--radius-sm);
transition: all 0.2s;
}
.btn-icon:hover {
background: var(--accent-light);
color: var(--accent);
}
.btn-icon:active {
transform: scale(0.85);
}
.btn-icon.danger:hover {
background: var(--danger-light);
color: var(--danger);
}
.res-detail {
position: relative;
z-index: 1;
max-height: 0;
opacity: 0;
overflow: hidden;
transition:
max-height 0.35s cubic-bezier(0.22, 1, 0.36, 1),
opacity 0.3s,
padding 0.35s;
font-size: var(--fs-xs);;
color: var(--text-secondary);
padding: 0 0.75rem 0 calc(0.75rem + 44px + 0.65rem);
}
.res-item:hover .res-detail {
max-height: 36px;
opacity: 1;
padding-bottom: 0.45rem;
}
.res-detail span {
display: inline-flex;
align-items: center;
gap: 0.15rem;
}
.detail-sep {
color: var(--border);
margin: 0 0.3rem;
}
/* Playlist item */
.pl-item {
position: relative;
display: flex;
align-items: center;
gap: 0.55rem;
padding: 0.6rem 0.75rem;
border-radius: var(--radius-lg);
margin: 0.2rem 0;
transition: all 0.25s cubic-bezier(0.22, 1, 0.36, 1);
cursor: grab;
opacity: 0;
animation: itemFadeIn 0.4s ease forwards;
}
.pl-item:active {
cursor: grabbing;
}
.pl-item:hover {
background: var(--bg);
transform: translateY(-1px);
}
.pl-grip {
color: var(--text-muted);
font-size: var(--fs-sm);;
flex-shrink: 0;
opacity: 0.3;
transition: opacity 0.2s;
}
.pl-item:hover .pl-grip {
opacity: 0.7;
}
.pl-num {
width: 20px;
height: 20px;
background: var(--accent-light);
color: var(--accent);
font-size: var(--fs-2xs);;
font-weight: 700;
border-radius: var(--radius-xs);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-family: "JetBrains Mono", monospace;
}
.pl-info {
flex: 1;
min-width: 0;
}
.pl-name {
font-weight: 700;
font-size: var(--fs-sm);;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
letter-spacing: -0.01em;
}
.pl-sub {
display: flex;
align-items: center;
gap: 0.4rem;
margin-top: 0.12rem;
}
.pl-cache {
font-size: var(--fs-2xs);;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 0.18rem;
padding: 0.08rem 0.35rem;
border-radius: var(--radius-xs);
}
.pl-cache.ready {
background: var(--success-light);
color: var(--success);
}
.pl-cache.pending {
background: var(--warning-light);
color: var(--warning);
}
.pl-cache.none {
background: var(--bg);
color: var(--text-muted);
}
.pl-heat {
font-size: var(--fs-2xs);;
}
.btn-clear {
border: none;
background: var(--danger-light);
color: var(--danger);
font-size: var(--fs-xs);;
font-weight: 700;
padding: 0.3rem 0.7rem;
border-radius: var(--radius-sm);
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 0.25rem;
}
.btn-clear:hover {
background: var(--danger-hover);
box-shadow: var(--shadow-xs);
}
.btn-clear:active {
transform: scale(0.97);
}
/* Sortable */
.sortable-ghost {
opacity: 0.3;
background: var(--accent-light) !important;
border-radius: var(--radius-lg);
}
/* ==================== ABOUT PAGE ==================== */
.about-card {
display: flex;
flex-direction: column;
min-height: 0;
}
.about-hero {
text-align: center;
padding: 2.2rem 1.5rem 1.8rem;
background: var(--accent-light);
flex-shrink: 0;
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}
.about-logo {
width: 72px;
height: 72px;
border-radius: var(--radius-lg);
overflow: hidden;
margin: 0 auto 1rem;
box-shadow: var(--shadow-accent);
}
.about-logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
.about-hero h2 {
font-size: var(--fs-2xl);;
font-weight: 800;
letter-spacing: -0.03em;
margin-bottom: 0.15rem;
}
.about-hero p {
font-size: var(--fs-sm);;
color: var(--text-secondary);
}
.about-body {
padding: 0.5rem 1.5rem 1.8rem;
flex: 1;
min-height: 0;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
.about-body::-webkit-scrollbar {
width: 5px;
}
.about-body::-webkit-scrollbar-track {
background: transparent;
}
.about-body::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: var(--radius-sm);
}
.about-body::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
.about-section {
display: flex;
gap: 0.75rem;
padding: 0.9rem 0;
border-bottom: 1px solid var(--border-light);
}
.about-section:last-child {
border-bottom: none;
}
.about-section-icon {
width: 34px;
height: 34px;
background: var(--accent-light);
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
color: var(--accent);
font-size: var(--fs-md);;
flex-shrink: 0;
}
.about-section-content h3 {
font-size: var(--fs-sm);;
font-weight: 700;
margin-bottom: 0.25rem;
}
.about-section-content p {
font-size: var(--fs-sm);;
color: var(--text-secondary);
line-height: 1.7;
}
.tech-tags {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
}
.tech-tags span {
font-size: var(--fs-xs);;
font-weight: 600;
background: var(--bg);
color: var(--text-secondary);
padding: 0.2rem 0.55rem;
border-radius: var(--radius-xs);
border: 1px solid var(--border-light);
}
.disclaimer-section .about-section-icon {
background: var(--warning-light);
color: var(--warning);
}
.about-version {
text-align: center;
padding: 0.8rem;
font-size: var(--fs-xs);;
color: var(--text-muted);
font-family: "JetBrains Mono", monospace;
letter-spacing: -0.01em;
flex-shrink: 0;
}
/* ==================== ABOUT — EXTENSIONS ==================== */
/* 核心功能列表 */
.about-feature-list {
display: flex;
flex-direction: column;
gap: 0.35rem;
margin-top: 0.2rem;
}
.about-feat {
display: flex;
align-items: flex-start;
gap: 0.45rem;
font-size: var(--fs-sm);;
color: var(--text-secondary);
line-height: 1.65;
padding: 0.3rem 0;
}
.about-feat i {
font-size: var(--fs-xs);;
flex-shrink: 0;
margin-top: 0.18rem;
}
/* GitHub 链接徽章 */
.about-gh-link {
display: inline-flex;
align-items: center;
gap: 0.3rem;
padding: 0.3rem 0.7rem;
background: var(--bg);
border: 1px solid var(--border-light);
border-radius: var(--radius-sm);
font-size: var(--fs-xs);;
font-weight: 600;
color: var(--text-secondary);
text-decoration: none;
transition: all 0.2s;
font-family: "JetBrains Mono", monospace;
letter-spacing: -0.01em;
}
.about-gh-link:hover {
color: var(--accent);
border-color: var(--accent);
background: var(--accent-light);
}
.about-gh-link i:first-child {
font-size: var(--fs-base);;
}
/* ==================== MINI PLAYER ==================== */
.mini-player {
position: fixed;
bottom: calc(var(--nav-bottom) + var(--nav-h) + 12px);
left: 50%;
transform: translateX(-50%);
width: min(var(--content-w), calc(100% - 2rem));
z-index: 99;
border-radius: var(--radius-lg);
overflow: hidden;
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(28px) saturate(200%);
border: 1px solid rgba(255, 255, 255, 0.6);
box-shadow:
0 4px 24px rgba(15, 18, 37, 0.1),
0 1px 4px rgba(15, 18, 37, 0.04),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
opacity: 0;
pointer-events: none;
transform: translateX(-50%) translateY(16px) scale(0.96);
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.mini-player.show {
opacity: 1;
pointer-events: auto;
transform: translateX(-50%) translateY(0) scale(1);
}
/* progress = full background */
.mp-progress {
position: absolute;
inset: 0;
width: 0;
background: rgba(79, 110, 247, 0.10);
pointer-events: none;
z-index: 0;
transition: width 0.15s linear;
}
.mini-player:hover .mp-progress {
background: rgba(79, 110, 247, 0.16);
}
.mp-body {
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.5rem 0.9rem 0.55rem;
cursor: pointer;
user-select: none;
-webkit-user-select: none;
}
.mp-cover {
width: 40px;
height: 40px;
border-radius: var(--radius-sm);
object-fit: cover;
flex-shrink: 0;
box-shadow: var(--shadow-xs);
}
.mp-info {
flex: 1;
min-width: 0;
}
.mp-name {
font-weight: 700;
font-size: var(--fs-sm);;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
letter-spacing: -0.01em;
}
.mp-artist {
font-size: var(--fs-xs);;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 0.04rem;
}
.mp-controls {
display: flex;
align-items: center;
gap: 0.3rem;
flex-shrink: 0;
}
.mp-time {
font-family: "JetBrains Mono", monospace;
font-size: var(--fs-xs);;
color: var(--text-muted);
flex-shrink: 0;
min-width: 40px;
text-align: center;
}
.mp-play {
width: 34px;
height: 34px;
border: none;
border-radius: 50%;
background: var(--accent);
color: var(--white);
font-size: var(--fs-md);;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.mp-play:hover {
background: var(--accent-hover);
box-shadow: var(--shadow-accent);
}
.mp-play:active {
transform: scale(0.88);
}
.mp-expand {
width: 30px;
height: 30px;
border: none;
border-radius: 50%;
background: transparent;
color: var(--text-muted);
font-size: var(--fs-base);;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.mp-expand:hover {
background: var(--accent-light);
color: var(--accent);
}
/* ==================== BOTTOM NAV ==================== */
.bottom-nav {
position: fixed;
bottom: var(--nav-bottom);
left: 50%;
transform: translateX(-50%);
z-index: 100;
pointer-events: none;
animation: navSlideUp 0.5s cubic-bezier(0.22, 1, 0.36, 1) 0.15s
both;
}
@keyframes navSlideUp {
from {
opacity: 0;
transform: translateX(-50%) translateY(24px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
.bottom-nav-inner {
pointer-events: auto;
display: inline-flex;
align-items: center;
gap: 4px;
padding: 6px 8px;
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(32px) saturate(220%);
-webkit-backdrop-filter: blur(32px) saturate(220%);
border: 1px solid rgba(255, 255, 255, 0.55);
border-radius: var(--radius-lg);
box-shadow:
0 2px 12px rgba(15, 18, 37, 0.07),
0 0 0 0.5px rgba(15, 18, 37, 0.03),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
.nav-tab {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
padding: 8px 18px;
border: none;
background: none;
color: var(--text-muted);
font-size: var(--fs-2xs);;
font-weight: 600;
letter-spacing: 0.03em;
border-radius: var(--radius);
z-index: 1;
transition:
color 0.3s ease,
transform 0.2s ease;
}
.nav-tab i {
font-size: var(--fs-2xl);;
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
position: relative;
z-index: 1;
line-height: 1;
}
.nav-tab span {
position: relative;
z-index: 1;
line-height: 1;
}
/* active pill */
.nav-tab::before {
content: "";
position: absolute;
inset: 0;
background: var(--accent);
border-radius: var(--radius);
opacity: 0;
transform: scale(0.6);
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
z-index: 0;
}
.nav-tab.active {
color: var(--white);
}
.nav-tab.active::before {
opacity: 1;
transform: scale(1);
}
.nav-tab.active i {
transform: scale(1.1) translateY(-1px);
}
.nav-tab:hover:not(.active) {
color: var(--text-secondary);
}
.nav-tab:hover:not(.active)::before {
opacity: 0.04;
transform: scale(0.85);
background: var(--accent);
}
.nav-tab:active {
transform: scale(0.92);
}
/* ==================== EMPTY / SCROLL ==================== */
.empty-state {
text-align: center;
padding: 2rem 1.5rem;
color: var(--text-muted);
}
.empty-icon {
width: 48px;
height: 48px;
background: var(--bg);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 0.75rem;
font-size: var(--fs-xl);;
color: var(--text-muted);
border: 1px solid var(--border-light);
animation: float 3.5s ease-in-out infinite;
}
.empty-title {
font-weight: 700;
font-size: var(--fs-sm);;
color: var(--text-secondary);
margin-bottom: 0.15rem;
}
.empty-desc {
font-size: var(--fs-sm);;
}
/* ==================== TOAST ==================== */
.toast-container {
position: fixed;
top: 68px;
left: 50%;
transform: translateX(-50%);
z-index: 9999;
display: flex;
flex-direction: column;
gap: 0.4rem;
pointer-events: none;
}
.toast-msg {
background: var(--text-primary);
color: var(--white);
padding: 0.5rem 1.1rem;
border-radius: var(--radius-sm);
font-size: var(--fs-sm);;
font-weight: 600;
box-shadow: var(--shadow-lg);
animation:
toastIn 0.35s cubic-bezier(0.22, 1, 0.36, 1) forwards,
toastOut 0.3s ease 2.5s forwards;
pointer-events: auto;
white-space: nowrap;
letter-spacing: -0.01em;
}
.toast-msg.error {
background: var(--danger);
}
.toast-msg.success {
background: var(--success);
}
@keyframes toastIn {
from {
opacity: 0;
transform: translateY(-10px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes toastOut {
from {
opacity: 1;
}
to {
opacity: 0;
transform: translateY(-8px) scale(0.95);
}
}
/* ==================== ANIMATIONS ==================== */
@keyframes itemFadeIn {
from {
opacity: 0;
transform: translateY(7px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes itemFadeOut {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-5px);
}
}
.res-item.enter,
.pl-item.enter {
animation: itemFadeIn 0.35s ease forwards;
}
.res-item.leave,
.pl-item.leave {
animation: itemFadeOut 0.25s ease forwards;
}
.spinner-sm {
width: 15px;
height: 15px;
border: 2.5px solid rgba(255, 255, 255, 0.3);
border-top-color: var(--white);
border-radius: 50%;
animation: spin 0.6s linear infinite;
display: inline-block;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* ==================== RESPONSIVE ==================== */
/* Tablet */
@media (min-width: 768px) {
.page-container {
padding: 1.2rem 2rem 2rem;
padding-bottom: calc(var(--total-bottom) + 2rem);
}
.res-detail {
display: block;
}
.search-header {
padding: 1.8rem 2rem 0.4rem;
}
.search-input-wrap {
padding: 0.8rem 2rem;
}
.preview-card {
margin: 0 2rem 2rem;
}
.searchResults {
margin: 0 2rem 2rem;
}
.parse-hint {
padding: 0 2rem;
}
.search-tips {
padding: 2.5rem 2rem 3rem;
}
.about-body {
padding: 0.5rem 2rem 2rem;
}
}
/* Large desktop */
@media (min-width: 1200px) {
:root {
--content-w: 960px;
}
.mini-player {
width: min(960px, calc(100% - 3rem));
}
}
/* Small phones */
@media (max-width: 380px) {
:root {
--nav-bottom: 10px;
}
.nav-tab {
font-size: var(--fs-2xs);;
}
.nav-tab i {
font-size: var(--fs-lg);;
}
.cache-pill {
display: none;
}
.search-header {
padding: 1.1rem 1.1rem 0.25rem;
}
.search-input-wrap {
padding: 0.6rem 1.1rem;
}
.preview-card {
margin: 0 1.1rem 1.1rem;
}
.searchResults {
margin: 0 1.1rem 1.1rem;
}
.parse-hint {
padding: 0 1.1rem;
}
.about-body {
padding: 0.4rem 1.1rem 1.2rem;
}
}
/* Landscape phone */
@media (max-height: 500px) and (orientation: landscape) {
.top-header {
padding: 0.3rem 0.8rem;
}
.page-container {
padding: 0.5rem 1rem 1rem;
}
.search-tips {
padding: 0.8rem;
}
.tips-icon {
width: 40px;
height: 40px;
font-size: var(--fs-base);;
margin-bottom: 0.5rem;
}
.preview-cover-section {
padding: 0.6rem 0.8rem;
}
.preview-cover,
.preview-cover-wrap {
width: 48px;
height: 48px;
}
.sr-list,
.scroll-area {
max-height: 38vh;
}
.about-hero {
padding: 1.2rem 1rem 1rem;
}
.about-logo {
width: 52px;
height: 52px;
}
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* ==================== 频道选择器 / 导入卡片 / 播放中标记 ==================== */
.channel-btn {
display: inline-flex;
align-items: center;
gap: 0.3rem;
max-width: 150px;
padding: 0.3rem 0.7rem;
border: 1px solid var(--border);
border-radius: var(--radius-pill);
background: var(--surface);
color: var(--text-primary);
font-size: var(--fs-sm);
font-weight: 600;
cursor: pointer;
transition: border-color var(--dur-fast), background var(--dur-fast);
}
.channel-btn:hover { background: var(--surface-hover); border-color: var(--accent); }
.channel-btn i { color: var(--accent); font-size: var(--fs-base); }
.channel-btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.import-card {
margin-top: var(--sp-5);
padding: var(--sp-5);
background: var(--bg);
border: 1px solid var(--border-light);
border-radius: var(--radius);
display: flex;
flex-direction: column;
gap: var(--sp-3);
}
.import-header h3 {
margin: 0 0 2px;
font-size: var(--fs-md);
display: flex;
align-items: center;
gap: 6px;
color: var(--accent);
}
.import-header p { margin: 0 0 4px; font-size: var(--fs-sm); color: var(--text-muted); }
.import-input {
padding: var(--sp-3);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
font-size: var(--fs-base);
outline: none;
background: var(--surface);
transition: border-color var(--dur-fast);
}
.import-input:focus { border-color: var(--accent); }
.import-progress { font-size: var(--fs-sm); color: var(--text-muted); min-height: 16px; }
.now-badge {
display: inline-block;
margin-left: 6px;
padding: 1px 7px;
font-size: var(--fs-2xs);
border-radius: var(--radius-xs);
background: var(--accent);
color: var(--white);
vertical-align: middle;
animation: nowPulse 1.6s ease-in-out infinite;
}
@keyframes nowPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
/* ==================== 频道管理模态框 ==================== */
.modal-mask {
position: fixed;
inset: 0;
background: var(--overlay);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
display: none;
align-items: center;
justify-content: center;
z-index: 9000;
padding: var(--sp-5);
}
.modal-mask.show { display: flex; }
.modal-box {
width: 100%;
max-width: 420px;
max-height: 82vh;
background: var(--surface);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
display: flex;
flex-direction: column;
overflow: hidden;
animation: modalIn var(--dur-base) var(--ease-out);
}
@keyframes modalIn {
from { opacity: 0; transform: translateY(14px) scale(0.98); }
to { opacity: 1; transform: none; }
}
.modal-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--sp-4) var(--sp-5);
border-bottom: 1px solid var(--border-light);
}
.modal-head h3 {
margin: 0;
font-size: var(--fs-lg);
display: flex;
align-items: center;
gap: var(--sp-2);
}
.modal-close {
border: none;
background: transparent;
cursor: pointer;
font-size: var(--fs-lg);
color: var(--text-muted);
padding: 4px 8px;
border-radius: var(--radius-xs);
}
.modal-close:hover { background: var(--bg); color: var(--text-primary); }
.modal-body { padding: var(--sp-3) var(--sp-5) var(--sp-5); overflow-y: auto; }
.stream-list { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.stream-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--sp-3);
border-radius: var(--radius);
background: var(--bg);
border: 1px solid var(--border-light);
}
.stream-row-name {
font-size: var(--fs-base);
font-weight: 600;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 6px;
}
.stream-row-sub { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 2px; }
.stream-row-actions { display: flex; gap: var(--sp-1); }
.stream-create {
display: flex;
flex-direction: column;
gap: var(--sp-2);
padding-top: var(--sp-3);
border-top: 1px dashed var(--border);
}
.stream-create input {
padding: 9px 12px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
font-size: var(--fs-base);
outline: none;
background: var(--surface);
}
.stream-create input:focus { border-color: var(--accent); }
.stream-create button {
padding: var(--sp-3);
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
background: var(--accent);
color: var(--white);
font-size: var(--fs-base);
font-weight: 600;
}
.stream-create button:hover { background: var(--accent-hover); }
/* ==================== 站内收听页 ==================== */
.live-card {
background: var(--surface);
border: 1px solid var(--border-light);
border-radius: var(--radius-lg);
padding: var(--sp-6);
box-shadow: var(--shadow);
display: flex;
flex-direction: column;
gap: var(--sp-5);
}
.live-now-playing { display: flex; align-items: center; gap: var(--sp-5); }
.live-cover-wrap { position: relative; width: 108px; height: 108px; flex-shrink: 0; }
.live-cover {
width: 108px;
height: 108px;
border-radius: var(--radius);
object-fit: cover;
box-shadow: var(--shadow-sm);
}
.live-cover-ph {
width: 108px;
height: 108px;
border-radius: var(--radius);
display: flex;
align-items: center;
justify-content: center;
font-size: var(--fs-3xl);
color: var(--white);
background: var(--accent);
box-shadow: var(--shadow-sm);
}
.live-badge {
position: absolute;
top: -6px;
left: -6px;
display: inline-flex;
align-items: center;
gap: var(--sp-1);
background: var(--danger);
color: var(--white);
font-size: var(--fs-2xs);
font-weight: 700;
letter-spacing: 0.03em;
padding: 3px 8px;
border-radius: var(--radius-pill);
box-shadow: var(--shadow-xs);
}
.live-meta { min-width: 0; flex: 1; }
.live-station { font-size: var(--fs-sm); color: var(--accent); font-weight: 600; margin-bottom: 6px; }
.live-now {
font-size: var(--fs-xl);
font-weight: 700;
color: var(--text-primary);
margin-bottom: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.live-sub {
font-size: var(--fs-base);
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.live-controls { display: flex; align-items: center; gap: var(--sp-4); }
.live-play-btn {
width: 54px;
height: 54px;
flex-shrink: 0;
border: none;
border-radius: 50%;
cursor: pointer;
background: var(--accent);
color: var(--white);
font-size: var(--fs-3xl);;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow-sm);
transition: background var(--dur-fast), transform var(--dur-fast);
}
.live-play-btn:hover { background: var(--accent-hover); transform: scale(1.06); }
.live-play-btn:active { transform: scale(0.96); }
.live-progress-wrap { flex: 1; min-width: 0; }
.live-progress-bar {
height: 6px;
border-radius: var(--radius-pill);
background: var(--border);
overflow: hidden;
margin-bottom: 6px;
}
.live-progress-fill {
height: 100%;
width: 0%;
border-radius: var(--radius-pill);
background: var(--accent);
transition: width 0.4s ease;
}
.live-status { font-size: var(--fs-xs); color: var(--text-muted); }
.live-hint {
font-size: var(--fs-xs);
color: var(--text-muted);
display: flex;
align-items: center;
gap: 6px;
padding-top: var(--sp-3);
border-top: 1px dashed var(--border);
}
.live-hint b { color: var(--accent); }
@media (max-width: 480px) {
.live-cover-wrap, .live-cover, .live-cover-ph { width: 84px; height: 84px; }
.live-now { font-size: var(--fs-lg); }
}
/* ==================== 设置页 ==================== */
/* 设置页内容区左右内边距与标题对齐,增加呼吸感 */
#pageSettings .section-body {
padding: 0 1.1rem 1.1rem;
}
.cache-stat { padding: var(--sp-1) 0; }
.cache-stat-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: var(--fs-base);
color: var(--text-secondary);
margin-bottom: var(--sp-3);
}
.cache-stat-row b { color: var(--text-primary); font-size: var(--fs-md); }
.cache-bar {
height: 10px;
border-radius: var(--radius-pill);
background: var(--border);
overflow: hidden;
}
.cache-bar-fill {
height: 100%;
width: 0%;
border-radius: var(--radius-pill);
background: var(--success);
transition: width 0.4s ease, background 0.3s;
}
.cache-bar-fill.warn { background: var(--warning); }
.cache-bar-fill.danger { background: var(--danger); }
.cache-stat-note { font-size: var(--fs-xs); color: var(--text-muted); margin-top: var(--sp-2); }
.config-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: var(--sp-2);
}
.config-item {
display: flex;
flex-direction: column;
gap: var(--sp-1);
padding: var(--sp-3);
border-radius: var(--radius);
background: var(--bg);
border: 1px solid var(--border-light);
}
.config-item span { font-size: var(--fs-xs); color: var(--text-muted); }
.config-item b { font-size: var(--fs-md); color: var(--text-primary); }
.log-tail-select {
appearance: none;
-webkit-appearance: none;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 6px 12px;
font-size: var(--fs-sm);
color: var(--text-primary);
background: var(--surface);
cursor: pointer;
outline: none;
}
.log-view {
max-height: 340px;
overflow-y: auto;
margin: 0;
padding: var(--sp-3);
border-radius: var(--radius);
background: var(--log-bg);
color: var(--log-fg);
font-family: "Cascadia Code", "Consolas", monospace;
font-size: var(--fs-xs);
line-height: 1.6;
white-space: pre-wrap;
word-break: break-all;
}
/* ==================== 资源页排序 ==================== */
.resource-tools { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.resource-sort {
appearance: none;
-webkit-appearance: none;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 0.35rem 1.6rem 0.35rem 0.6rem;
font-size: var(--fs-xs);
background: var(--bg) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6'><path d='M0 0l5 6 5-6z' fill='%23888'/></svg>") no-repeat right 7px center;
color: var(--text-primary);
cursor: pointer;
outline: none;
}
.resource-sort:hover { border-color: var(--accent); }
/* StarLight 用户名显示 */
.user-name {
font-size: 13px;
color: var(--muted, #8b949e);
margin-right: 8px;
white-space: nowrap;
}
/* ==================== 设置页:热配置表单 ==================== */
.settings-form {
display: flex;
flex-direction: column;
gap: 2px;
}
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 9px 0;
border-bottom: 1px solid var(--border, #30363d);
}
.setting-row:last-child { border-bottom: none; }
.setting-row label {
font-size: var(--fs-sm, 0.85rem);
color: var(--text, #e6edf3);
flex: 0 0 auto;
}
.setting-row input,
.setting-row select {
width: 130px;
padding: 6px 10px;
background: var(--bg, #0d1117);
border: 1px solid var(--border, #30363d);
border-radius: var(--radius-sm, 6px);
color: var(--text, #e6edf3);
font-size: var(--fs-sm, 0.85rem);
outline: none;
text-align: right;
}
.setting-row select { text-align: left; }
.setting-row input:focus,
.setting-row select:focus { border-color: var(--accent, #1f6feb); }
/* ==================== 设置页:热配置表单 ==================== */
.settings-form {
display: flex;
flex-direction: column;
gap: 2px;
}
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 0;
border-bottom: 1px solid var(--border-light);
}
.setting-row:last-child { border-bottom: none; }
.setting-row label {
font-size: var(--fs-base);
color: var(--text-primary);
flex: 0 0 auto;
font-weight: 500;
}
.setting-row input,
.setting-row select {
width: 140px;
padding: 7px 10px;
background: #ffffff;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
font-size: var(--fs-base);
outline: none;
text-align: right;
font-family: inherit;
}
.setting-row select { text-align: left; }
.setting-row input:focus,
.setting-row select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-glow);
}
.setting-row input::placeholder { color: var(--text-muted); }
/* ==================== 权限管理:用户列表 ==================== */
.user-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.user-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 11px 14px;
background: var(--bg);
border: 1px solid var(--border-light);
border-radius: var(--radius-sm);
}
.user-item .user-meta {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
.user-item .user-name {
font-size: var(--fs-base);
font-weight: 600;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 6px;
}
.user-item .user-sub {
font-size: var(--fs-xs);
color: var(--text-muted);
font-family: monospace;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.admin-badge {
font-size: var(--fs-2xs);
font-weight: 700;
color: #fff;
background: var(--accent);
border-radius: var(--radius-pill);
padding: 1px 8px;
}
.btn-mini {
font-size: var(--fs-sm);
font-weight: 600;
font-family: inherit;
padding: 6px 12px;
border-radius: var(--radius-pill);
border: 1px solid var(--border);
background: var(--surface);
color: var(--text-secondary);
cursor: pointer;
transition: all .18s;
white-space: nowrap;
}
.btn-mini:hover { border-color: var(--accent); color: var(--accent); }
.btn-mini.danger:hover { border-color: var(--danger); color: var(--danger); }
.user-empty {
text-align: center;
color: var(--text-muted);
font-size: var(--fs-sm);
padding: 20px;
}
/* ==================== 设置页布局优化 v2 ==================== */
/* 让整页统一滚动,卡片更透气 */
#pageSettings {
gap: var(--sp-5, 20px);
padding-bottom: 2rem;
}
#pageSettings .section-card {
margin-bottom: 0;
}
/* 热配置表单更宽松 */
#pageSettings .settings-form {
gap: 0;
}
#pageSettings .setting-row {
padding: 12px 0;
}
#pageSettings .setting-row label {
font-size: var(--fs-md, 0.92rem);
}
#pageSettings .setting-row input,
#pageSettings .setting-row select {
width: 150px;
padding: 8px 12px;
}
/* 日志区限高,自身滚动(唯一允许内滚的区域) */
#pageSettings .log-view {
max-height: 360px;
overflow-y: auto;
}
/* 用户列表项间距 */
#pageSettings .user-list {
gap: 10px;
}
/* 循环池按钮激活态 */
.btn-icon.js-pool.active {
color: var(--accent);
background: var(--accent-light);
}