Files
ONE/frontend/src/styles.css
T

1589 lines
56 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:root {/* 皮肤:米白纸感 */
--paper: #faf7f1;
--card: #fffdf8;
--paper-sunken: #f3efe6;
/* 墨色:正文暖灰,不用纯黑 */
--ink: #33302b;
--ink-soft: #5c564d;
--muted: #8a8378;
--faint: #a9a297;
/* 强调色:降饱和蓝(只出现在少数几处) */
--accent: #3d7f9c;
--accent-soft: rgba(61, 127, 156, 0.1);
--accent-line: rgba(61, 127, 156, 0.35);
/* 细线:纸感的分割靠 1px 线,不靠阴影 */
--line: #e6e0d4;
--line-soft: #efe9dd;
/* 字体:标题衬线,正文黑体 */
--serif: Georgia, "Times New Roman", "Songti SC", "Noto Serif SC", "Source Han Serif SC", serif;
--sans: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
--mono: "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
--lh: 1.95;
/* 三栏骨架 */
--col-left: 236px;
--col-main: 640px;
--col-right: 264px;
--gutter: 28px;}
:root[data-theme='ink'] {/* 墨:暖炭底、暖白字、鎏金点缀。比旧版更中性(褪掉黄褐相),
层次靠三档灰阶拉开;强调金提亮提纯,暗底上对比更足 */
--paper: #191817;
--card: #201e1c;
--paper-sunken: #262420;
--ink: #e7e3da;
--ink-soft: #b8b2a6;
--muted: #867f73;
--faint: #5f594f;
--line: #34302b;
--line-soft: #292623;
--accent: #d9b97c;
--accent-soft: rgba(217, 185, 124, 0.12);
--accent-line: rgba(217, 185, 124, 0.4);}
:root[data-admin-theme='dark'] {--admin-bg: #191817;
--admin-paper: var(--admin-bg);
--admin-card: #201e1c;
--admin-paper-sunken: #262420;
--admin-ink: #e7e3da;
--admin-ink-soft: #b8b2a6;
--admin-muted: #948c7f;
--admin-faint: #635c51;
--admin-line: #34302b;
--admin-line-soft: #292623;
--admin-accent: #d9b97c;
--admin-accent-soft: rgba(217, 185, 124, 0.16);
--admin-accent-line: rgba(217, 185, 124, 0.42);
--admin-on-accent: #191817;
--admin-danger: #d68d75;
--admin-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
--admin-overlay: rgba(0, 0, 0, 0.6);}
body {transition: background-color 0.2s ease, color 0.2s ease;}
a, .btn, .tag-chip, .pill, .editor-mode button, .chip, input.input, input.select, textarea.textarea {transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;}
* {box-sizing: border-box;}
html, body {margin: 0;
padding: 0;}
body {background: var(--paper);
color: var(--ink);
font-family: var(--sans);
font-size: 16px;
line-height: var(--lh);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
/* 移动端不留横向滚动。用 clip 不用 hidden:hidden 会把 body 变成滚动容器
(overflow-y 坍缩成 auto),页内所有 position:sticky 就此失明——
首页筛选条吸顶失效就是它干的。clip 只裁剪、不建滚动容器。 */
overflow-x: clip;}
a {color: inherit;
text-decoration: none;}
@media (hover: hover) and (pointer: fine) {
a:hover {color: var(--accent);}
}
img {max-width: 100%;}
button, input, textarea, select {font-family: inherit;
font-size: inherit;
line-height: inherit;
color: inherit;}
h1, h2, h3, h4 {font-family: var(--serif);
font-weight: 600;
line-height: 1.4;
margin: 0;
letter-spacing: 0.01em;}
::selection {background: rgba(61, 127, 156, 0.18);}
.shell {max-width: 1238px;
margin: 0 auto;
padding: 0 20px;}
.layout {display: grid;
grid-template-columns: var(--col-left) minmax(0, var(--col-main)) var(--col-right);
gap: var(--gutter);
align-items: start;}
@media (max-width: 1080px) {
.layout {grid-template-columns: var(--col-left) minmax(0, 1fr);}
.layout > .rail-right {display: none;}
}
@media (max-width: 780px) {
.layout {grid-template-columns: minmax(0, 1fr);}
.layout > .rail-left {display: none;}
}
@media (max-width: 640px) {
/* 手机页边距收到 12px:与评论区(.cm-sec 手机断点)和余白主题的文章列对齐,
正文和评论共用同一条左缘(文章 .wrap 的侧边距在 PostView.vue 里归零) */
.shell {padding: 0 12px;}
}
/* 顶栏实际高度(9+9 内边距 + 汉堡 40/44 + 1px 边框),首页 .sticky-head 的
吸顶偏移用它;TopBar 隐藏时把 html.topbar-hidden 翻上来,偏移归 0 跟着滑走 */
:root {--topbar-h: 59px;}
@media (pointer: coarse) {
:root {--topbar-h: 63px;}
}
html.topbar-hidden {--topbar-h: 0px;}
.eyebrow {font-size: 12px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);}
.divider {height: 1px;
background: var(--line);
border: 0;
margin: 0;}
.muted {color: var(--muted);}
.mono {font-family: var(--mono);
font-size: 13px;}
.tag-chip {display: inline-block;
padding: 1px 8px;
border: 1px solid var(--line);
border-radius: 999px;
font-size: 12px;
line-height: 1.8;
color: var(--ink-soft);
background: transparent;}
@media (hover: hover) and (pointer: fine) {
.tag-chip:hover {border-color: var(--accent-line);
color: var(--accent);}
}
.pill {display: inline-block;
padding: 7px 18px;
border-radius: 999px;
background: var(--accent);
color: #fff;
font-size: 14px;
border: 1px solid var(--accent);
cursor: pointer;}
@media (hover: hover) and (pointer: fine) {
.pill:hover {background: var(--accent-soft);
color: var(--accent);
border-color: var(--accent-line);}
}
.pill-ghost {background: transparent;
color: var(--accent);}
@media (hover: hover) and (pointer: fine) {
.pill-ghost:hover {background: var(--accent-soft);
color: var(--accent);}
}
.field {margin-bottom: 18px;}
.field > label {display: block;
font-size: 13px;
color: var(--muted);
margin-bottom: 6px;}
.input, .textarea, .select {width: 100%;
padding: 9px 12px;
background: var(--card);
border: 1px solid var(--line);
border-radius: 3px;
outline: none;}
.input:focus, .textarea:focus, .select:focus {border-color: var(--accent-line);}
.textarea {resize: vertical;}
.btn {padding: 8px 18px;
border: 1px solid var(--line);
background: var(--card);
border-radius: 3px;
cursor: pointer;}
@media (hover: hover) and (pointer: fine) {
.btn:hover {border-color: var(--accent-line);
color: var(--accent);}
}
.btn-primary {background: var(--accent);
border-color: var(--accent);
color: #fff;}
@media (hover: hover) and (pointer: fine) {
.btn-primary:hover {background: var(--accent-soft);
color: var(--accent);
border-color: var(--accent-line);}
}
@media (hover: hover) and (pointer: fine) {
.btn-danger:hover {border-color: #b4553f;
color: #b4553f;}
}
.btn:disabled {opacity: 0.5;
cursor: not-allowed;}
/* 编辑器封面:URL 输入框 + 上传按钮同行 */
.cover-row {display: flex;
gap: 6px;
align-items: center;}
.cover-row > input {flex: 1;
min-width: 0;}
.prose {font-size: 16.5px;
line-height: var(--lh);
color: var(--ink);
/* anywhere 而非 break-word:长 URL/长 token 要参与 min-content 计算,
否则时间线正文里的裸链接会把列撑破(真机上浏览器缩放适配后整页缩小+右侧留白) */
overflow-wrap: anywhere;}
.prose > :first-child {margin-top: 22px;}
.prose p {margin: 0 0 1.1em;}
.prose > p:first-of-type::first-letter {float: left;
font-family: var(--serif);
font-size: 52px;
line-height: 1;
margin: 6px 10px 0 0;
color: var(--accent);}
.prose h1, .prose h2, .prose h3, .prose h4 {font-family: var(--serif);
color: var(--ink);
margin: 1.8em 0 0.6em;}
.prose h1 {font-size: 26px;}
.prose h2 {font-size: 22px;
padding-bottom: 6px;
border-bottom: 1px solid var(--line);}
.prose h3 {font-size: 18px;}
.prose h3::after {content: '';
display: block;
width: 28px;
height: 2px;
margin-top: 8px;
background: var(--accent);}
.prose h4 {font-size: 16.5px;}
/* 悬停浮现的标题锚点(enhanceProse 注入,指向 heading id) */
.prose .h-anchor {opacity: 0;
margin-left: 8px;
font-family: var(--mono);
font-size: 0.72em;
color: var(--faint);
text-decoration: none;
border-bottom: 0;
transition: opacity .2s;}
.prose h2:hover .h-anchor, .prose h3:hover .h-anchor, .prose h4:hover .h-anchor {opacity: 1;}
.prose .h-anchor:hover {color: var(--accent);}
.prose blockquote {margin: 1.4em 0;
padding: 2px 0 2px 18px;
border-left: 2px solid var(--accent-line);
font-family: var(--serif);
color: var(--ink-soft);}
.prose blockquote p:last-child {margin-bottom: 0;}
.prose ul, .prose ol {margin: 0 0 1.1em;
padding-left: 1.4em;}
.prose li {margin-bottom: 0.35em;}
.prose a {color: var(--accent);
border-bottom: 1px solid var(--accent-line);}
@media (hover: hover) and (pointer: fine) {
.prose a:hover {border-bottom-color: var(--accent);}
}
.prose code {font-family: var(--mono);
font-size: 13.5px;
background: var(--paper-sunken);
padding: 1px 5px;
border-radius: 3px;}
.prose .pre-wrap {position: relative;}
.pre-copy {position: absolute;
top: 8px;
right: 8px;
z-index: 1;
padding: 3px 10px;
border: 0;
border-radius: 5px;
font-size: 12px;
color: inherit;
background: color-mix(in srgb, var(--ink) 14%, transparent);
opacity: 0;
cursor: pointer;
transition: opacity .15s, transform .08s;}
.pre-copy:hover {opacity: 1;}
.pre-wrap:hover .pre-copy {opacity: 0.85;}
.pre-copy:active {transform: translateY(2px);}
.pre-copy.done {opacity: 1;
color: var(--accent);}
.prose pre {background: var(--paper-sunken);
border: 1px solid var(--line-soft);
border-radius: 10px;
padding: 16px 18px;
overflow: auto;
line-height: 1.7;
max-height: 30.5em;}
.prose pre code {background: none;
padding: 0;
font-size: 13.5px;}
.prose img {border-radius: 3px;
display: block;}
.prose hr {height: 1px;
border: 0;
background: var(--line);
margin: 2em 0;}
.prose table {width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 1.6em 0;
font-size: 14.5px;
border: 1px solid var(--line);
border-radius: 10px;
overflow: hidden;}
.prose th, .prose td {border: 0;
border-bottom: 1px solid var(--line-soft);
padding: 11px 16px;
text-align: left;}
.prose tr > * + * {border-left: 1px solid var(--line-soft);}
.prose tbody tr:last-child td {border-bottom: 0;}
.prose th {background: var(--paper-sunken);
font-weight: 600;}
.prose tbody tr:nth-child(even) {background: color-mix(in srgb, var(--paper-sunken) 55%, transparent);}
.prose-short {font-size: 20px;
line-height: 1.9;}
.prose-short > p:first-of-type::first-letter {float: none;
font-size: inherit;
line-height: inherit;
margin: 0;
color: inherit;}
/* 时间线里的短文同样不放大首字:一句话的纸条压个大字像截断
(详情页 .prose-short 同此;长文详情的首字下沉保留) */
.prose.short-body > p:first-of-type::first-letter {float: none;
font-size: inherit;
line-height: inherit;
font-family: inherit;
margin: 0;
color: inherit;}
/* 短文配图:Twitter 式网格(快发盒上传,≤4 张;详情页用) */
.post-imgs {display: grid;
gap: 3px;
margin-top: 18px;
border-radius: 6px;
overflow: hidden;}
.post-imgs img {display: block;
width: 100%;
height: 100%;
object-fit: cover;}
.post-imgs.n2, .post-imgs.n4 {grid-template-columns: 1fr 1fr;}
.post-imgs.n3 {grid-template-columns: 1fr 1fr;}
.post-imgs.n2 img, .post-imgs.n4 img {aspect-ratio: 1;}
.post-imgs.n3 img {aspect-ratio: 1;}
.post-imgs.n3 img:first-child {grid-row: span 2;
aspect-ratio: auto;
height: 100%;}
.post-imgs.n1 img {max-height: 560px;}
.empty {padding: 48px 0;
text-align: center;
color: var(--muted);
font-size: 14px;}
.loading {padding: 48px 0;
text-align: center;
color: var(--muted);
font-size: 14px;}
:root {--admin-sidebar: 224px;
--admin-topbar-h: 56px;
--admin-bg: #f6f2ea;
--admin-paper: var(--admin-bg);
--admin-card: #fffdf8;
--admin-paper-sunken: #f0ebde;
--admin-ink: #33302b;
--admin-ink-soft: #5c564d;
--admin-muted: #8a8378;
--admin-faint: #a9a297;
--admin-line: #ebe4d5;
--admin-line-soft: #f3eedf;
--admin-accent: #2f6f88;
--admin-accent-soft: rgba(47, 111, 136, 0.1);
--admin-accent-line: rgba(47, 111, 136, 0.35);
--admin-on-accent: #ffffff;
--admin-danger: #b4553f;
--admin-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
--admin-overlay: rgba(0, 0, 0, 0.4);}
.admin-shell {display: grid;
grid-template-columns: var(--admin-sidebar) minmax(0, 1fr);
min-height: 100vh;
min-height: 100dvh;
background: var(--admin-bg);}
.admin-side {background: var(--admin-card);
border-right: 1px solid var(--admin-line);
display: flex;
flex-direction: column;
position: sticky;
top: 0;
height: 100vh;
height: 100dvh;}
.admin-side .brand {padding: 18px 20px 16px;
border-bottom: 1px solid var(--admin-line);
display: flex;
align-items: baseline;
gap: 6px;}
.admin-side .brand .name {font-family: var(--serif);
font-size: 18px;
font-weight: 600;
color: var(--admin-ink);}
.admin-side .brand .label {font-size: 12px;
color: var(--admin-muted);}
.admin-side .nav {padding: 14px 10px;
display: flex;
flex-direction: column;
gap: 2px;
flex: 1;
overflow-y: auto;}
.admin-side .group {font-size: 11px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--admin-muted);
padding: 14px 12px 6px;}
.admin-side .item {display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 6px;
font-size: 14px;
color: var(--admin-ink-soft);
border: 1px solid transparent;
cursor: pointer;}
@media (hover: hover) and (pointer: fine) {
.admin-side .item:hover {background: var(--admin-paper-sunken);
color: var(--admin-ink);}
}
.admin-side .item.router-link-exact-active {background: var(--admin-accent-soft);
color: var(--admin-accent);
font-weight: 500;}
.admin-side .item .ic {width: 18px;
display: inline-flex;
justify-content: center;
font-size: 16px;
line-height: 1;}
.admin-side .item .badge {margin-left: auto;
font-size: 11px;
color: var(--admin-muted);
background: var(--admin-paper-sunken);
padding: 1px 7px;
border-radius: 999px;
min-width: 22px;
text-align: center;}
.admin-side .footer {border-top: 1px solid var(--admin-line);
padding: 14px 18px;
font-size: 12.5px;
color: var(--admin-muted);
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;}
.admin-side .footer .meta {min-width: 0;}
.admin-side .footer .who {color: var(--admin-ink-soft);
font-weight: 500;}
.admin-side .footer .leave {display: block;
margin-top: 6px;
color: var(--admin-muted);
background: none;
border: 0;
padding: 0;
cursor: pointer;
font-size: 12px;}
@media (hover: hover) and (pointer: fine) {
.admin-side .footer .leave:hover {color: var(--admin-accent);}
}
.admin-main {display: flex;
flex-direction: column;
min-width: 0;}
.admin-topbar {height: var(--admin-topbar-h);
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 0 28px;
background: var(--admin-card);
border-bottom: 1px solid var(--admin-line);
position: sticky;
top: 0;
z-index: 5;}
.admin-topbar .crumbs {font-size: 14px;
color: var(--admin-ink-soft);}
.admin-topbar .crumbs strong {color: var(--admin-ink);
font-weight: 600;}
.admin-topbar .right {display: flex;
align-items: center;
gap: 10px;}
.admin-topbar .cmd-hint {font-size: 12px;
color: var(--admin-muted);
border: 1px solid var(--admin-line);
padding: 4px 10px;
border-radius: 6px;
cursor: pointer;
background: var(--admin-paper);}
.admin-topbar .cmd-hint kbd {font-family: var(--mono);
font-size: 11px;
border: 1px solid var(--admin-line);
padding: 1px 5px;
border-radius: 3px;
background: var(--admin-card);}
.admin-content {padding: 26px 28px 80px;
max-width: 1180px;
width: 100%;
margin: 0 auto;}
@media (max-width: 860px) {
/* minmax(0,1fr) 而非裸 1fr:裸 1fr 带 min-content 下限,内容宽的页面会把
整条轨道连页面一起撑出横向滚动。align-content:start:shell 的
min-height:100dvh 会把 auto 行拉伸填满屏幕,行高回到内容高度。
侧栏本身在 ≤860px 变成抽屉(规则在文件末尾「后台 · 手机端适配」段)。 */
.admin-shell {grid-template-columns: minmax(0, 1fr);
align-content: start;}
.admin-content {padding: 20px 16px 60px;}
}
@media (max-width: 780px) {
.admin-side .footer {display: none;}
}
.panel {background: var(--admin-card);
border: 1px solid var(--admin-line);
border-radius: 8px;
padding: 18px 20px;}
.panel + .panel {margin-top: 16px;}
.panel-title {display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;}
.panel-title h2 {font-family: var(--sans);
font-size: 14px;
font-weight: 600;
color: var(--admin-ink);}
.panel-title .meta {font-size: 12px;
color: var(--admin-muted);}
.stat-grid {display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 14px;
margin-bottom: 24px;}
.stat {background: var(--admin-card);
border: 1px solid var(--admin-line);
border-radius: 8px;
padding: 16px 18px;
display: flex;
flex-direction: column;
gap: 6px;
position: relative;
overflow: hidden;}
.stat .label {font-size: 12px;
color: var(--admin-muted);
letter-spacing: 0.06em;}
.stat .value {font-family: var(--serif);
font-size: 28px;
font-weight: 600;
color: var(--admin-ink);
line-height: 1;}
.stat .hint {font-size: 12px;
color: var(--admin-muted);}
.stat.accent {background: var(--admin-accent);
border-color: var(--admin-accent);
color: var(--admin-on-accent);}
.stat.accent .label, .stat.accent .hint {color: rgba(255, 255, 255, 0.78);}
.stat.accent .value {color: var(--admin-on-accent);}
.dash-chart {display: flex;
align-items: flex-end;
gap: 6px;
height: 140px;
padding: 14px 4px 6px;
border-bottom: 1px solid var(--admin-line);
margin-bottom: 8px;}
.dash-chart .bar {flex: 1;
background: var(--admin-accent-soft);
border-top: 2px solid var(--admin-accent);
border-radius: 3px 3px 0 0;
position: relative;
min-height: 2px;
display: flex;
align-items: flex-end;
justify-content: center;}
@media (hover: hover) and (pointer: fine) {
.dash-chart .bar:hover {background: var(--admin-accent);}
}
.dash-chart .bar .v {position: absolute;
top: -18px;
font-size: 11px;
color: var(--admin-muted);}
.dash-chart .labels {display: flex;
gap: 6px;}
.dash-chart .labels span {flex: 1;
font-size: 10px;
color: var(--admin-muted);
text-align: center;}
.recent-list {display: flex;
flex-direction: column;}
.recent-item {display: flex;
align-items: center;
gap: 12px;
padding: 10px 4px;
border-bottom: 1px solid var(--admin-line);}
.recent-item:last-child {border-bottom: 0;}
.recent-item .title {flex: 1;
font-size: 14px;
color: var(--admin-ink);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;}
@media (hover: hover) and (pointer: fine) {
.recent-item .title:hover {color: var(--admin-accent);}
}
.recent-item .meta {font-size: 12px;
color: var(--admin-muted);
white-space: nowrap;}
.chip {display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 10px;
border: 1px solid var(--admin-line);
border-radius: 999px;
font-size: 12px;
color: var(--admin-ink-soft);
background: var(--admin-paper);
cursor: pointer;
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;}
@media (hover: hover) and (pointer: fine) {
.chip:hover {border-color: var(--admin-accent);
color: var(--admin-accent);}
}
.chip.on {background: var(--admin-accent);
border-color: var(--admin-accent);
color: var(--admin-on-accent);}
.chip .x {border: 0;
background: none;
cursor: pointer;
color: inherit;
padding: 0 0 0 2px;
font-size: 14px;
line-height: 1;}
.chip-static {display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 10px;
border: 1px solid var(--admin-line);
border-radius: 999px;
font-size: 12px;
color: var(--admin-ink-soft);
background: transparent;}
.tag-dot {width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;}
.posts-toolbar {display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: center;
margin-bottom: 14px;}
.posts-toolbar .grow {flex: 1;
min-width: 180px;}
.post-card {display: grid;
grid-template-columns: 96px minmax(0, 1fr) 110px 90px;
gap: 16px;
align-items: center;
padding: 16px 18px;
background: var(--admin-card);
border: 1px solid var(--admin-line);
border-radius: 8px;
margin-bottom: 10px;
cursor: pointer;
transition: border-color 0.15s;}
@media (hover: hover) and (pointer: fine) {
.post-card:hover {border-color: var(--admin-accent);}
}
.post-card.selected {border-color: var(--admin-accent);
background: var(--admin-accent-soft);}
.post-card .cover {width: 96px;
height: 64px;
background: var(--admin-paper-sunken);
border-radius: 4px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;}
.post-card .cover img {width: 100%;
height: 100%;
object-fit: cover;}
.post-card .cover .ph {font-size: 11px;
color: var(--admin-muted);
letter-spacing: 0.1em;}
.post-card .body {min-width: 0;}
.post-card .title {font-family: var(--serif);
font-size: 16px;
color: var(--admin-ink);
font-weight: 600;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;}
.post-card .summary {font-size: 13px;
color: var(--admin-muted);
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
margin-bottom: 6px;}
.post-card .meta {display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
font-size: 11.5px;
color: var(--admin-muted);}
.post-card .meta .dot {width: 6px;
height: 6px;
border-radius: 50%;
background: var(--admin-faint);
display: inline-block;
margin-right: 4px;}
.post-card .meta .dot.published {background: var(--admin-accent);}
.post-card .stats {font-size: 12px;
color: var(--admin-muted);
text-align: center;
font-family: var(--mono);}
.post-card .stats div {line-height: 1.4;}
.post-card .actions {display: flex;
flex-direction: column;
gap: 6px;
align-items: stretch;}
.post-card .actions button, .post-card .actions a {font-size: 12.5px;
padding: 4px 0;
background: none;
border: 0;
color: var(--admin-accent);
cursor: pointer;
text-align: right;
text-decoration: none;}
.post-card .actions .danger {color: #b4553f;}
.post-card .check {width: 18px;
height: 18px;
accent-color: var(--admin-accent);
cursor: pointer;}
.post-card.has-check {grid-template-columns: 24px 96px minmax(0, 1fr) 110px 90px;}
@media (max-width: 760px) {
/* has-check 必须一起覆写:它(0,2,0)写在上面,单列 .post-card(0,1,0) 压不过它,
5 列会被 390px 挤成一字一行。操作链接保留(藏掉就删不了文章),
挪到正文下面一行;字数/时长列是次要信息,手机上藏掉。 */
.post-card {grid-template-columns: 64px minmax(0, 1fr);}
.post-card.has-check {grid-template-columns: 24px 64px minmax(0, 1fr);}
.post-card .cover {width: 64px;
height: 64px;}
.post-card .stats {display: none;}
.post-card .actions {grid-column: 2 / -1;
grid-row: 2;
flex-direction: row;
gap: 14px;}
}
.bulk-bar {display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
padding: 10px 14px;
background: var(--admin-accent);
color: var(--admin-on-accent);
border-radius: 8px;
margin-bottom: 12px;
font-size: 13px;}
.bulk-bar button {background: rgba(255, 255, 255, 0.18);
color: var(--admin-on-accent);
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 4px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 13px;}
@media (hover: hover) and (pointer: fine) {
.bulk-bar button:hover {background: rgba(255, 255, 255, 0.28);}
}
.bulk-bar .grow {flex: 1;}
.bulk-bar .x {background: none;
border: 0;
color: var(--admin-on-accent);
font-size: 16px;
cursor: pointer;}
.editor-grid {display: grid;
grid-template-columns: minmax(0, 1fr) 280px;
gap: 20px;
align-items: start;}
@media (max-width: 920px) {
.editor-grid {grid-template-columns: minmax(0, 1fr);}
}
.cover-block {margin-bottom: 16px;
border: 1px dashed var(--admin-line);
border-radius: 6px;
overflow: hidden;
background: var(--admin-card);}
.cover-block.has {border-style: solid;}
.cover-block .preview {position: relative;
height: 160px;
background: var(--admin-paper-sunken);
background-size: cover;
background-position: center;}
.cover-block .preview .remove {position: absolute;
top: 8px;
right: 8px;
background: rgba(0, 0, 0, 0.6);
color: #fff;
border: 0;
border-radius: 4px;
padding: 4px 10px;
cursor: pointer;
font-size: 12px;}
.cover-block .input-row {display: flex;
gap: 6px;
padding: 10px 12px;}
.cover-block .input-row input {flex: 1;
border: 0;
background: transparent;
outline: none;
font-size: 13px;
color: var(--admin-ink-soft);}
.cover-block .input-row button {border: 0;
background: var(--admin-accent);
color: var(--admin-on-accent);
padding: 4px 12px;
border-radius: 3px;
cursor: pointer;
font-size: 12px;}
.cover-block .placeholder {padding: 26px 18px;
text-align: center;
font-size: 13px;
color: var(--admin-muted);}
.editor-mode {display: inline-flex;
border: 1px solid var(--admin-line);
border-radius: 6px;
overflow: hidden;
background: var(--admin-card);}
.editor-mode button {padding: 4px 12px;
font-size: 12.5px;
border: 0;
background: transparent;
cursor: pointer;
color: var(--admin-ink-soft);}
.editor-mode button.on {background: var(--admin-accent);
color: var(--admin-on-accent);}
.editor-stats {display: flex;
gap: 14px;
align-items: center;
font-size: 12px;
color: var(--admin-muted);
padding: 8px 4px 0;}
.editor-stats strong {color: var(--admin-ink);
font-weight: 600;
font-family: var(--mono);}
.save-bar {display: flex;
align-items: center;
gap: 12px;
font-size: 12.5px;
color: var(--admin-muted);
margin-bottom: 8px;}
.save-bar .dot {width: 8px;
height: 8px;
border-radius: 50%;
background: var(--admin-faint);}
.save-bar.saving .dot {background: #d39e2f;
animation: pulse 1.2s ease-in-out infinite;}
.save-bar.saved .dot {background: #6c8a4f;}
.save-bar.dirty .dot {background: #b4553f;}
@keyframes pulse {
0%, 100% {opacity: 1;}
50% {opacity: 0.3;}
}
/* md 页签容器现在是 CodeMirror 挂载点(原为 textarea) */
.md-pane {width: 100%;
min-height: 460px;
border: 1px solid var(--admin-line);
border-radius: 3px;
background: var(--admin-card);
padding: 6px 14px;
color: var(--admin-ink);
overflow: hidden;}
.md-pane:focus-within {border-color: var(--admin-accent);}
/* 轻提示栈:上传进度 / 转存结果 / 校验失败 */
.toast-stack {position: fixed;
right: 18px;
bottom: 18px;
z-index: 90;
display: flex;
flex-direction: column;
gap: 8px;
max-width: 360px;}
.toast {padding: 9px 14px;
border-radius: 6px;
font-size: 13px;
line-height: 1.6;
color: var(--admin-ink);
background: var(--admin-card);
border: 1px solid var(--admin-line);
box-shadow: 0 6px 24px rgba(0,0,0,.12);
overflow-wrap: anywhere;}
.toast.ok {border-color: color-mix(in srgb, var(--admin-accent) 55%, transparent);}
.toast.err {border-color: #c26a55;
color: #b4553f;
outline: none;}
.md-pane:focus {border-color: var(--admin-accent);}
.toolbar {display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0 10px;
padding: 4px 6px;
border: 1px solid var(--admin-line);
border-bottom: 0;
border-radius: 3px 3px 0 0;
background: var(--admin-paper-sunken);}
.tb-group {display: inline-flex;
align-items: center;
gap: 2px;}
.tb-group + .tb-group {border-left: 1px solid var(--admin-line);
padding-left: 10px;
margin-left: 2px;}
.tb-label {font-size: 11px;
letter-spacing: 0.06em;
color: var(--admin-muted);
margin-right: 4px;
text-transform: uppercase;}
/* xLog 式工具栏按钮:方形圆角、线性图标、悬停出底框 */
.tb-btn {width: 32px;
height: 32px;
border: 1px solid transparent;
border-radius: 6px;
background: transparent;
color: var(--admin-muted);
cursor: pointer;
font-size: 12.5px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;}
@media (hover: hover) and (pointer: fine) {
.tb-btn:hover {background: var(--admin-card);
border-color: var(--admin-line);
color: var(--admin-ink);}
}
.tb-btn svg {width: 17px;
height: 17px;}
.tb-text {font-family: var(--mono);
font-size: 11.5px;
font-weight: 700;
color: inherit;}
.tb-popover {display: flex;
gap: 6px;
padding: 8px;
border: 1px solid var(--admin-line);
border-top: 0;
background: var(--admin-paper-sunken);}
.tb-popover .input {flex: 1;
font-size: 13px;}
.outline {position: sticky;
top: 84px;
max-height: calc(100vh - 120px);
max-height: calc(100dvh - 120px);
overflow-y: auto;
border-left: 1px solid var(--admin-line);
padding: 6px 0 6px 14px;
font-size: 12.5px;}
.outline .ttl {font-size: 11px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--admin-muted);
margin-bottom: 6px;}
.outline ul {list-style: none;
margin: 0;
padding: 0;}
.outline li {padding: 3px 0;
color: var(--admin-ink-soft);
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;}
@media (hover: hover) and (pointer: fine) {
.outline li:hover {color: var(--admin-accent);}
}
.outline li.h3 {padding-left: 12px;
font-size: 12px;}
.outline li.active {color: var(--admin-accent);
font-weight: 500;}
.tag-row {display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border: 1px solid var(--admin-line);
border-radius: 6px;
margin-bottom: 8px;
background: var(--admin-card);}
.tag-row .swatch {width: 28px;
height: 28px;
border-radius: 50%;
border: 1px solid var(--admin-line);
flex-shrink: 0;
cursor: pointer;
position: relative;
overflow: hidden;}
.tag-row .swatch input[type=color] {position: absolute;
inset: 0;
opacity: 0;
cursor: pointer;}
.tag-row .name {flex: 1;
font-size: 14px;
border: 0;
background: transparent;
outline: none;}
.tag-row .count {font-size: 12px;
color: var(--admin-muted);
font-family: var(--mono);}
.tag-row .acts {display: flex;
gap: 6px;}
.tag-row .acts button {border: 0;
background: none;
font-size: 12.5px;
cursor: pointer;
color: var(--admin-accent);
padding: 2px 6px;}
.tag-row .acts .danger {color: #b4553f;}
.settings-grid {display: grid;
grid-template-columns: 200px minmax(0, 1fr);
gap: 24px;
align-items: start;}
@media (max-width: 760px) {
.settings-grid {grid-template-columns: 1fr;}
}
.settings-tabs {display: flex;
flex-direction: column;
gap: 2px;
position: sticky;
top: 84px;}
.settings-tabs button {text-align: left;
padding: 8px 14px;
border: 0;
background: transparent;
cursor: pointer;
font-size: 14px;
color: var(--admin-ink-soft);
border-radius: 6px;
border-left: 2px solid transparent;}
@media (hover: hover) and (pointer: fine) {
.settings-tabs button:hover {background: var(--admin-paper-sunken);}
}
.settings-tabs button.on {background: var(--admin-accent-soft);
color: var(--admin-accent);
border-left-color: var(--admin-accent);
font-weight: 500;}
/* 窄屏下 tab 列表收成可换行的横排:竖排 + sticky 是桌面左栏的写法,
单列布局里它会粘在屏幕上盖住下面的面板。必须放在基础规则之后,
否则同优先级会被上面的 column/sticky 盖掉。 */
@media (max-width: 760px) {
.settings-tabs {position: static;
flex-direction: row;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 10px;}
.settings-tabs button {padding: 6px 12px;
border-left: 0;
border-radius: 999px;
background: var(--admin-paper-sunken, #f3efe6);}
.settings-tabs button.on {background: var(--admin-accent-soft);}
}
.theme-swatches {display: flex;
gap: 10px;
flex-wrap: wrap;}
.theme-swatches .sw {width: 64px;
height: 64px;
border-radius: 8px;
border: 2px solid var(--admin-line);
cursor: pointer;
position: relative;
overflow: hidden;}
.theme-swatches .sw.on {border-color: var(--admin-accent);}
.theme-swatches .sw span {position: absolute;
bottom: 4px;
left: 4px;
font-size: 10px;
color: var(--admin-on-accent);
background: rgba(0, 0, 0, 0.4);
padding: 1px 5px;
border-radius: 3px;}
.cmd-overlay {position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
display: flex;
align-items: flex-start;
justify-content: center;
z-index: 100;
padding-top: 14vh;}
.cmd-panel {width: 560px;
max-width: 90vw;
background: var(--admin-card);
border: 1px solid var(--admin-line);
border-radius: 10px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
overflow: hidden;}
.cmd-panel input {width: 100%;
padding: 16px 18px;
border: 0;
border-bottom: 1px solid var(--admin-line);
outline: none;
font-size: 16px;
background: var(--admin-card);}
.cmd-list {max-height: 360px;
overflow-y: auto;}
.cmd-list .row {padding: 10px 18px;
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
border-bottom: 1px solid var(--admin-line);}
.cmd-list .row:last-child {border-bottom: 0;}
.cmd-list .row.on {background: var(--admin-accent-soft);}
@media (hover: hover) and (pointer: fine) {
.cmd-list .row:hover {background: var(--admin-accent-soft);}
}
.cmd-list .row .label {flex: 1;
font-size: 14px;}
.cmd-list .row .hint {font-size: 12px;
color: var(--admin-muted);}
.cmd-list .row kbd {font-family: var(--mono);
font-size: 11px;
border: 1px solid var(--admin-line);
padding: 1px 5px;
border-radius: 3px;}
.cmd-list .empty {padding: 24px;
text-align: center;
color: var(--admin-muted);
font-size: 13px;}
.shortcut-grid {display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px;}
.shortcut-grid .row {display: flex;
align-items: center;
gap: 8px;
padding: 8px 0;
font-size: 13px;}
.shortcut-grid .row .lab {flex: 1;
color: var(--admin-ink-soft);}
.shortcut-grid kbd {font-family: var(--mono);
font-size: 12px;
border: 1px solid var(--admin-line);
background: var(--admin-paper);
padding: 2px 7px;
border-radius: 4px;
min-width: 22px;
text-align: center;}
:root {color-scheme: light;}
:root[data-theme='ink'], :root[data-admin-theme='dark'] {color-scheme: dark;}
:where(a, button, input, select, textarea, [tabindex]):focus-visible {outline: 2px solid var(--admin-accent);
outline-offset: 2px;
border-radius: 3px;}
body:not(.admin-shell-active) :where(a, button, input, select, textarea, [tabindex]):focus-visible {outline-color: var(--accent);}
.crepe-host :focus-visible {outline: none !important;}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;}
body {transition: none !important;}
}
.cmd-overlay {overscroll-behavior: contain;}
button:active:not(:disabled), a.btn:active, a.pill:active, .tab:active, .tag-chip:active, .rail-left .item:active, .admin-side .item:active, .cmd-panel .row:active {transform: scale(0.97);}
button, a.btn, a.pill, .tab, .tag-chip, .rail-left .item, .admin-side .item {touch-action: manipulation;}
html.theme-anim, html.theme-anim * {transition: background-color 250ms ease, color 250ms ease, border-color 250ms ease !important;}
.cmd-panel {transform-origin: top center;}
.pop-enter-active {transition: opacity 160ms ease-out;}
.pop-enter-active .cmd-panel {transition: transform 240ms cubic-bezier(0.2, 0.8, 0.25, 1), opacity 160ms ease-out;}
.pop-enter-from {opacity: 0;}
.pop-enter-from .cmd-panel {transform: scale(0.96) translateY(-10px);
opacity: 0;}
.pop-leave-active {transition: opacity 130ms ease-in;}
.pop-leave-to {opacity: 0;}
@media (prefers-reduced-motion: reduce) {
.pop-enter-active, .pop-leave-active {transition: opacity 150ms ease !important;}
.pop-enter-active .cmd-panel {transition: opacity 150ms ease !important;}
.pop-enter-from .cmd-panel {transform: none;}
html.theme-anim, html.theme-anim * {transition: none !important;}
}
/* ==========================================================================
移动端基线(mobile-native)
--------------------------------------------------------------------------
逐条理由:
· tap-highlight:触屏点按有系统灰/蓝闪,是最大的「这是网页」信号;
盖掉之后每个可点元素都有自己的 :active 反馈,不会失去点按反馈
· text-size-adjust:横屏时 iOS 会自作主张放大字体
· touch-action: manipulation:跳过双击缩放等待,点按即时触发
· user-select 只给控件:正文要能复制(地址/报错/订单号都是内容)
· input 16px:iOS 聚焦字号 <16px 的输入框会缩放页面,且失焦不回弹
(overscroll 不设 none:本站是滚动文档,下拉刷新是欢迎的)
========================================================================== */
html {
-webkit-tap-highlight-color: transparent;
-webkit-text-size-adjust: 100%;
}
button,
a,
[role='button'] {
touch-action: manipulation;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
}
@media (pointer: coarse) {
input,
textarea,
select {
font-size: 16px;
}
}
/* ==========================================================================
评论区
--------------------------------------------------------------------------
classic 和 vivid 共用同一份 DOM(components/comments/)。
视觉语言与去卡片后的文章页一致:评论正文直接落在纸面上,
不再套带边框的气泡盒 —— 层级靠头像 / 名字行 / 留白撑起来。
这里只写「两边都要」的基础,用经典那套 token —— vivid 把那批名字
(--paper/--card/--ink/--accent/--line/--paper-sunken…)都别名到了 --v-*,
所以同一份规则在两套 UI 下都成立。
vivid 的差异(头像配色、代码块、弹簧手感、触屏尺寸)在
ui/yohaku/comments.css,那边特指度更高,会盖住这里。
========================================================================== */
/* 头部只放计数 + 排序。刻意不写「评论」二字标题:靠计数和排序控件
就能认出这是评论区,多一行标题只是噪音。 */
.cm-sec {margin-top: 28px;padding: 22px 26px 0;border-top: 1px solid var(--line);}
.cm-head {display: flex;align-items: center;justify-content: space-between;
gap: 12px;flex-wrap: wrap;margin-bottom: 24px;}
.cm-count {font-size: 13px;font-weight: 600;color: var(--muted);
font-variant-numeric: tabular-nums;}
/* classic 下是一排文字钮;vivid 里这个元素还带 .seg,会被 pill 化 */
.cm-sort {display: inline-flex;align-items: center;gap: 2px;}
.cm-sort button {font-size: 13px;font-weight: 600;color: var(--muted);
padding: 5px 10px;border: 0;border-radius: 999px;background: none;cursor: pointer;
transition: color .2s,background-color .2s;}
.cm-sort button.on {color: var(--accent);background: var(--accent-soft);}
/* 未登录:不画卡片,一行引导 + 一排登录方式,直接落在纸面上 */
.cm-gate {padding: 26px 0 4px;text-align: center;}
.cm-gate-skel {height: 96px;background: var(--paper-sunken);border-radius: 12px;}
.cm-gate-t {margin: 0 0 16px;font-size: 13px;color: var(--muted);}
.cm-gate-hint {margin: 0;font-size: 13px;color: var(--muted);}
.cm-providers {display: flex;justify-content: center;gap: 14px;
margin: 0;padding: 0;list-style: none;}
.cm-provider {display: grid;place-items: center;width: 42px;height: 42px;
border: 1px solid var(--line);border-radius: 50%;background: none;color: var(--muted);
cursor: pointer;transition: border-color .2s,color .2s;}
.cm-provider:hover {border-color: var(--accent);color: var(--accent);}
.cm-brand {width: 18px;height: 18px;fill: currentColor;}
.cm-brand-t {font-size: 15px;font-weight: 700;}
.cm-tg {margin-top: 14px;}
/* 输入区:没有盒子,只有 textarea 底下的一条「书写线」,聚焦时变强调色。
名字 / 计数 / 发送排在线下方,像在纸上落款。 */
.cm-compose {display: flex;gap: 12px;align-items: flex-start;}
.cm-av-me {width: 36px;height: 36px;}
.cm-field {flex: 1;min-width: 0;}
.cm-field-inline {margin-top: 12px;}
.cm-input {display: block;width: 100%;min-height: 68px;padding: 4px 0 10px;
border: 0;border-bottom: 1px solid var(--line);background: none;resize: vertical;
font: inherit;font-size: 14.5px;line-height: 1.75;color: var(--ink);
transition: border-color .2s;}
.cm-input:focus {outline: none;}
.cm-field:focus-within .cm-input {border-bottom-color: var(--accent);}
.cm-bar {position: relative;display: flex;align-items: center;justify-content: space-between;gap: 10px;
padding: 8px 0 0;font-size: 12.5px;color: var(--muted);}
.cm-bar-l {display: flex;align-items: center;gap: 8px;min-width: 0;
overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
.cm-bar-l b {font-weight: 600;color: var(--ink-soft);}
.cm-bar-r {display: flex;flex: none;align-items: center;gap: 12px;}
.cm-len {font-size: 12px;font-variant-numeric: tabular-nums;}
.cm-len.over {color: #a64953;}
.cm-link {padding: 4px 2px;border: 0;background: none;color: var(--muted);
font-size: 12.5px;cursor: pointer;transition: color .2s;}
.cm-link:hover {color: var(--accent);}
.cm-send {padding: 6px 18px;border: 0;border-radius: 999px;cursor: pointer;
background: var(--accent);color: #fff;font-size: 13px;font-weight: 600;
transition: opacity .2s;}
.cm-send:disabled {opacity: .4;cursor: default;}
.cm-err {margin: 14px 0 0;font-size: 13px;color: #a64953;}
/* 表情 / 颜文字:触发钮在工具条上,面板从条上方弹出、盖住输入框。
右锚点 —— 工具条右缘离屏边最近,面板往左伸才不出屏(手机上也成立) */
.cm-emoji-btn {display: grid;place-items: center;width: 30px;height: 30px;
border: 0;border-radius: 8px;background: none;font-size: 17px;line-height: 1;
cursor: pointer;opacity: .72;transition: opacity .2s,background-color .2s;}
.cm-emoji-btn:hover, .cm-emoji-btn.on {opacity: 1;background: var(--accent-soft);}
.cm-emoji {position: absolute;right: 0;bottom: calc(100% + 8px);z-index: 30;
width: min(320px, calc(100vw - 24px));
padding: 10px;border: 1px solid var(--line);border-radius: 12px;
background: var(--card);color: var(--ink);
box-shadow: 0 12px 40px -16px rgba(28, 28, 30, .3);
animation: cmEmojiIn .18s cubic-bezier(.22,.61,.36,1) both;}
/* 上方放不下(打开时量过)翻到工具条下方 */
.cm-emoji.is-below {bottom: auto;top: calc(100% + 8px);}
@keyframes cmEmojiIn {from {opacity: 0;transform: translateY(4px)}}
@media (prefers-reduced-motion: reduce) {
.cm-emoji {animation: none;}
}
.cm-emoji-tabs {display: flex;gap: 2px;margin-bottom: 8px;}
.cm-emoji-tabs button {padding: 3px 10px;border: 0;border-radius: 999px;background: none;
color: var(--muted);font-size: 12px;font-weight: 600;cursor: pointer;}
.cm-emoji-tabs button.on {color: var(--accent);background: var(--accent-soft);}
/* 网格自动填列:面板宽度随屏变化时列数自己跟 */
.cm-emoji-grid, .cm-emoji-list {max-height: 224px;overflow-y: auto;
scrollbar-width: thin;scrollbar-color: var(--line) transparent;}
.cm-emoji-grid {display: grid;grid-template-columns: repeat(auto-fill, minmax(34px, 1fr));gap: 2px;}
.cm-emoji-cat {grid-column: 1 / -1;padding: 5px 2px 1px;font-size: 11px;color: var(--faint);}
.cm-emoji-item {display: grid;place-items: center;border: 0;border-radius: 8px;
background: none;color: var(--ink);font-size: 19px;line-height: 1;cursor: pointer;
transition: background-color .15s,transform .3s;}
.cm-emoji-item:hover {background: var(--accent-soft);}
.cm-emoji-item:active {transform: scale(.88);}
/* 颜文字是文本:不进方块格子,一行一个胶囊 */
.cm-emoji-list {display: flex;flex-wrap: wrap;gap: 4px;align-content: flex-start;}
.cm-kao {width: auto;aspect-ratio: auto;padding: 5px 9px;font-size: 13px;font-weight: 600;}
/* 列表:条与条之间靠留白分隔,不画线不画盒 */
.cm-list {display: flex;flex-direction: column;gap: 28px;
margin: 26px 0 0;padding: 0;list-style: none;}
.cm-item {display: flex;gap: 12px;align-items: flex-start;}
.cm-av {position: relative;display: grid;place-items: center;flex: none;
width: 36px;height: 36px;border-radius: 50%;
background: var(--paper-sunken);color: var(--muted);
font-size: 14px;font-weight: 700;}
.cm-av img {display: block;width: 100%;height: 100%;border-radius: 50%;object-fit: cover;}
/* 头像角上的登录方式小标:回复的 26px 头像戴不下,只在顶层显示 */
.cm-pv {position: absolute;right: -3px;bottom: -3px;
display: grid;place-items: center;width: 15px;height: 15px;border-radius: 50%;
background: var(--card);color: var(--muted);font-style: normal;
box-shadow: 0 0 0 1.5px var(--line);}
.cm-pv svg {width: 8px;height: 8px;fill: currentColor;}
.cm-replies .cm-pv {display: none;}
.cm-main {flex: 1;min-width: 0;}
/* 名字行:名字、徽标、时间共用一条基线 */
.cm-name {display: flex;align-items: baseline;gap: 8px;flex-wrap: wrap;
row-gap: 2px;margin-bottom: 6px;}
.cm-name-a {font-size: 14px;font-weight: 600;color: var(--ink);}
a.cm-name-a:hover {color: var(--accent);}
.cm-owner {padding: 1px 7px;border-radius: 999px;background: var(--accent-soft);
color: var(--accent);font-size: 11px;font-weight: 600;}
.cm-time {font-size: 12px;color: var(--faint);font-variant-numeric: tabular-nums;}
.cm-pending {padding: 1px 7px;border-radius: 999px;background: var(--paper-sunken);
color: var(--muted);font-size: 11px;font-weight: 600;}
.cm-edited-tip {font-size: 11.5px;color: var(--faint);}
/* 正文:类名保留 .cm-bubble(DOM 不动),盒子拆掉 —— 和文章正文同一种「纸」。
删除态不给盒子,靠变灰变斜认。 */
.cm-bubble {display: block;padding: 0;border: 0;background: none;color: var(--ink);}
.cm-item.is-deleted .cm-bubble {color: var(--muted);}
.cm-gone {font-size: 13.5px;color: var(--muted);font-style: italic;}
/* 正文(走 CommentMarkdown,后端已渲染 + 前端 DOMPurify 再洗一遍)。
@提及 和 .cm-md 共用字号行高:float 贴首行时两者行盒必须一样高,
三处成对存在(基础 / 手机断点 / vivid),改字号要一起改。 */
.cm-md, .cm-at {font-size: 14.5px;line-height: 1.75;}
.cm-md {
/* 评论是用户生成内容,长链接/长代码串必须在行宽内断行 */
overflow-wrap: break-word;}
/* 回复回复时的 @对方:浮在正文首行行首(和首字下沉同一个技巧),
不占一整行;正文以代码块/列表开头时由 .cm-at-block 退成独立一行 */
.cm-at {float: left;margin: 0 .45em 0 0;color: var(--accent);font-weight: 600;}
.cm-at-block {float: none;margin: 0 0 4px;}
.cm-md > :first-child {margin-top: 0;}
.cm-md > :last-child {margin-bottom: 0;}
.cm-md p {margin: 0 0 .6em;}
.cm-md a {color: var(--accent);text-underline-offset: 2px;}
.cm-md code {padding: 1px 5px;border-radius: 5px;font-family: var(--mono);font-size: .92em;
background: color-mix(in srgb, var(--ink) 6%, transparent);}
.cm-md pre {padding: 10px 12px;border-radius: 8px;overflow-x: auto;font-size: 12.5px;
background: var(--paper-sunken);}
.cm-md pre code {padding: 0;background: none;}
.cm-md ul, .cm-md ol {margin: .5em 0;padding-left: 1.4em;}
.cm-md blockquote {margin: .6em 0;padding-left: 12px;border-left: 2px solid var(--line);
color: var(--muted);}
.cm-md img {max-width: 100%;border-radius: 6px;}
/* 操作:一行安静的文字钮(回复 / 编辑 / 删除)。桌面 hover 或键盘聚焦
才浮现,位置预留、不跳版;触屏常显、加高可点面积。删除悬停给红。 */
.cm-acts {display: flex;gap: 2px;margin-top: 2px;
opacity: 0;transition: opacity .2s;}
.cm-item:hover .cm-acts, .cm-item:focus-within .cm-acts {opacity: 1;}
@media (hover: none) {
.cm-acts {opacity: 1;}
}
.cm-act {padding: 5px 8px;border: 0;background: none;color: var(--muted);
font-size: 12.5px;font-weight: 600;cursor: pointer;transition: color .2s;}
.cm-act:first-child {margin-left: -8px;}
.cm-act:hover {color: var(--accent);}
.cm-act.is-danger:hover {color: #a64953;}
/* 键盘可达:文字钮没有边框,聚焦环自己画(鼠标点击不触发 focus-visible) */
.cm-act:focus-visible, .cm-link:focus-visible, .cm-more-r:focus-visible,
.cm-sort button:focus-visible, .cm-provider:focus-visible {
outline: 2px solid var(--accent-line);outline-offset: 2px;border-radius: 6px;}
.cm-send:focus-visible {outline: 2px solid var(--accent-line);outline-offset: 2px;}
/* 回复:只有一层。左侧一条发丝线把回复串起来(和左栏时间线同一语言),
线从第一条回复头顶到末条脚下,比空白缩进更有「同属一条」的指向。 */
.cm-replies {display: flex;flex-direction: column;gap: 18px;
margin: 14px 0 0;padding: 0 0 2px 18px;list-style: none;
border-left: 1px solid var(--line);}
.cm-replies .cm-av {width: 26px;height: 26px;font-size: 11.5px;}
.cm-more-r {margin: 12px 0 0;padding: 5px 0;border: 0;background: none;
color: var(--muted);font-size: 12.5px;font-weight: 600;cursor: pointer;
transition: color .2s;}
.cm-more-r:hover {color: var(--accent);}
/* 分页 / 空态 */
.cm-more {display: flex;justify-content: center;margin-top: 26px;}
.cm-empty {margin-top: 26px;}
/* 骨架:和真实列表同一副骨架(头像 + 两行),落位一致才不闪 */
.cm-skel-row {display: flex;gap: 12px;align-items: flex-start;}
.cm-skel-av {flex: none;width: 36px;height: 36px;border-radius: 50%;background: var(--paper-sunken);}
.cm-skel-lines {display: flex;flex: 1;flex-direction: column;gap: 8px;padding-top: 3px;}
.cm-skel-lines i {display: block;height: 12px;border-radius: 6px;background: var(--paper-sunken);}
.cm-skel-lines i:last-child {width: 55%;}
/* --------------------------------------------------------------------------
手机(≤640):评论是纯阅读 + 触控场景 —— 缩头像、收紧缩进、
可点目标加高、输入 16px 防 iOS 聚焦缩放。
页边距与 .article-layout 的手机页边距(12px,见 mobile.css)对齐,
正文和评论共用同一条左缘,头像离屏幕边 12px。
-------------------------------------------------------------------------- */
@media (max-width: 640px) {
.cm-sec {padding: 20px 0 0;}
.cm-head {margin-bottom: 18px;}
.cm-list {gap: 22px;margin-top: 20px;}
.cm-item {gap: 10px;}
.cm-av, .cm-av-me {width: 28px;height: 28px;font-size: 12px;}
.cm-name-a {font-size: 13.5px;}
.cm-md, .cm-at {font-size: 14px;line-height: 1.7;}
.cm-replies {padding-left: 14px;gap: 14px;}
.cm-replies .cm-av {width: 24px;height: 24px;font-size: 10.5px;}
.cm-act {padding: 8px 10px;}
.cm-act:first-child {margin-left: -10px;}
.cm-emoji-btn {width: 36px;height: 36px;}
.cm-sort button {min-height: 44px;}
.cm-provider {width: 44px;height: 44px;}
.cm-compose {gap: 10px;}
.cm-input {font-size: 16px;}
.cm-send {padding: 9px 18px;}
.cm-link {padding: 8px 2px;}
}
/* 触屏(可能是平板/桌面触屏,不限于窄屏):可点目标给足 */
@media (pointer: coarse) {
.cm-provider {width: 44px;height: 44px;}
.cm-act {padding: 8px 10px;}
.cm-emoji-btn {width: 36px;height: 36px;}
.cm-link {padding: 8px 2px;}
.cm-more-r {padding: 10px 0;}
.cm-input {font-size: 16px;}
}
/* 编辑器图片缩放把手:Milkdown 图片块 hover 底部出现拖拽条(上下拖调高度,
比例存进 markdown 的 alt)。默认半透明让访客知道它在那儿。 */
.admin-shell .image-resize-handle {opacity: 0.45;}
.admin-shell .image-wrapper:hover .image-resize-handle {opacity: 1;}
/* lightbox 打开时藏掉吸顶/固定栏:一是全屏观感,二是绕开真机上
backdrop-filter 元素合成层盖过遮罩的 Chromium 绘制问题 */
body.lb-open .sticky-head,
body.lb-open .topbar,
body.lb-open .reading-bar {visibility: hidden;}
/* classic 文章页阅读进度条:视口顶部细线,vivid 的同款能力 */
.reading-bar {position: fixed;
top: 0;
left: 0;
height: 2px;
width: 0;
background: var(--accent);
z-index: 60;
transition: width .1s linear;
pointer-events: none;}
/* ==========================================================================
后台 · 手机端适配
--------------------------------------------------------------------------
放在文件末尾:上面的 .editor-mode / .tb-btn / .tb-popover .input / .chip
等基础定义同特指度、位置更靠后,手机端覆写写在前面会被反压(chrome.css
同款老坑)。
========================================================================== */
/* 顶栏退出按钮:桌面隐藏,退出走侧栏 footer;≤860px 侧栏进抽屉后
退出固定在顶栏(显示规则在下面的 ≤860px 块)。 */
.admin-topbar .leave-m {display: none;
padding: 5px 12px;
font-size: 12.5px;
color: var(--admin-muted);
border: 1px solid var(--admin-line);
background: var(--admin-paper);
border-radius: 6px;
cursor: pointer;}
/* 顶栏 ☰:唤出侧栏抽屉,桌面隐藏。 */
.admin-topbar .menu-btn {display: none;
width: 36px;
height: 36px;
align-items: center;
justify-content: center;
padding: 0;
border: 1px solid var(--admin-line);
border-radius: 6px;
background: var(--admin-paper);
color: var(--admin-ink);
font-size: 15px;
cursor: pointer;}
.admin-topbar .crumbs {display: flex;
align-items: center;
gap: 10px;}
/* ≤860px:侧栏整体收成抽屉。横铺一行要横向滚、换行三行吃掉 230px+ 才见
内容,竖排抽屉两头都不占——☰ 唤出、点遮罩或 Esc 或跳转后收回。
宽度 264px 比桌面 224px 略宽:手机上项的可点区域更足。 */
@media (max-width: 860px) {
.admin-side {position: fixed;
top: 0;
bottom: 0;
left: 0;
width: 264px;
height: auto;
z-index: 120;
border-right: 1px solid var(--admin-line);
border-bottom: 0;
transform: translateX(-105%);
transition: transform 0.28s ease;}
.admin-side.open {transform: none;
box-shadow: var(--admin-shadow);}
.admin-side .group {display: block;}
.side-scrim {position: fixed;
inset: 0;
z-index: 110;
background: var(--admin-overlay);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease;}
.side-scrim.on {opacity: 1;
pointer-events: auto;}
.admin-topbar {padding: 0 14px;}
.admin-topbar .menu-btn {display: inline-flex;}
.admin-topbar .leave-m {display: inline-block;}
.admin-content {padding: 16px 12px 60px;}
}
/* 手机端(≤720px)编辑器细节:⌘K 对触屏无意义藏掉 kbd;类型/模式切换条
收满一行,避免「转存外链图」被压成一字一行。 */
@media (max-width: 720px) {
.admin-topbar .cmd-hint kbd {display: none;}
/* Crepe 主题给左侧 +/拖拽手柄留了 120px 槽位,手机上吃掉三分之一宽度;
上下也各留 60px,第一行文字离头顶太远。触屏上把手柄藏掉(插块走 /
斜杠菜单),四边收紧到 16px,正文贴边、第一行上移;特指度压过 crepe
frame 主题的 .milkdown .editor。手柄的 display:flex 是插件运行时写在
内联样式上的,必须 !important 才压得住。 */
.admin-content .milkdown .ProseMirror {padding: 16px;}
.admin-content .milkdown-block-handle {display: none !important;}
.editor-top-row .kind-hint,
.editor-top-row .spacer {display: none;}
.editor-mode {display: flex;
width: 100%;}
.editor-mode button {flex: 1;
white-space: nowrap;}
/* 链接/图片输入条:一行塞不下两组输入框,改上下堆叠 */
.tb-popover {flex-wrap: wrap;}
.tb-popover .input {flex: 1 1 100%;}
.tb-popover > .btn {flex: 1;}
/* 字数统计条放得下、不挤爆 */
.editor-stats {flex-wrap: wrap;
row-gap: 4px;}
}
/* 触屏:iOS 聚焦时字号 <16px 的输入框会触发页面自动放大——
CodeMirror 正文与链接/图片输入条都显式压过字号,这里抬回 16px。
顺带把工具栏按钮提到 40px,够得上指腹。 */
@media (pointer: coarse) {
.tb-btn {width: 38px;
height: 36px;}
.tb-btn svg {width: 18px;
height: 18px;}
.tb-popover .input {font-size: 16px;}
.md-pane .cm-content {font-size: 16px;}
.chip {padding: 8px 14px;}
}
/* 编辑器顶部行:改用类而不是行内样式,手机端才有得调(上面的 ≤720px 规则) */
.editor-top-row {display: flex;
gap: 6px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 10px;}
.editor-top-row .kind-hint {font-size: 12px;
color: var(--admin-muted);
margin-left: 4px;}
.editor-top-row .spacer {flex: 1;}
/* 富文本空态只有 64px 高,跟 Markdown 页签(.md-pane min-height:460px)差一截;
对齐到 460px,白底画在 .milkdown 上,min-height 就够撑起整块。 */
.admin-content .milkdown {min-height: 460px;}