/* PulpTale base stylesheet — single source of visual truth. See docs/DESIGN.md.
   Rules: templates use only these tokens/classes. No hex colors, no font-family,
   no unregistered font sizes anywhere else (enforced by tests). */

:root {
  color-scheme: light;
  --bg:#F6F1E7; --surface:#FFFDF8; --ink:#1B1A17; --ink-2:#5B564C; --ink-3:#8B8578; --line:#E4DCCB;
  --accent:#B8202E; --accent-hover:#971A26; --accent-soft:#F7E3E2;
  --navy:#223049; --on-navy:#F3EEE3; --sun:#E7B23B; --ok:#2F6B4F; --warn:#A24A1F;
  --shadow-1:0 1px 2px rgba(27,26,23,.06), 0 8px 24px -14px rgba(27,26,23,.35);
  --shadow-2:0 -6px 24px -12px rgba(27,26,23,.25);

  --font-display:"DM Serif Display", Georgia, "Times New Roman", serif;
  --font-read:"Source Serif 4", Georgia, serif;
  --font-ui:"Manrope", -apple-system, "Segoe UI", Roboto, sans-serif;

  --fs-xs:.75rem; --fs-sm:.875rem; --fs-md:1rem; --fs-lg:1.125rem; --fs-xl:1.375rem; --fs-2xl:1.75rem; --fs-3xl:2.25rem;
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px; --sp-7:48px; --sp-8:64px;
  --r-btn:10px; --r-card:8px; --r-pill:999px;
  --read-size:var(--fs-lg);
  /* Cookie 同意条显示时由 pixel.js 量出真实高度写进来。默认 0:
     没有同意条(或没有 JS)时,底栏照旧贴着屏底。 */
  --consent-h:0px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="paper"]):not([data-theme="sepia"]) {
    color-scheme: dark;
    --bg:#141311; --surface:#1D1B18; --ink:#ECE6D8; --ink-2:#B6AE9E; --ink-3:#7E776B; --line:#2C2924;
    --accent:#E4505B; --accent-hover:#F06A73; --accent-soft:#3A1A1D;
    --navy:#0F1420; --on-navy:#ECE6D8; --ok:#7FC49E; --warn:#E8996F;
    --shadow-1:0 1px 2px rgba(0,0,0,.5), 0 10px 28px -16px rgba(0,0,0,.8);
    --shadow-2:0 -6px 24px -12px rgba(0,0,0,.6);
  }
}
:root[data-theme="night"] {
  color-scheme: dark;
  --bg:#141311; --surface:#1D1B18; --ink:#ECE6D8; --ink-2:#B6AE9E; --ink-3:#7E776B; --line:#2C2924;
  --accent:#E4505B; --accent-hover:#F06A73; --accent-soft:#3A1A1D;
  --navy:#0F1420; --on-navy:#ECE6D8; --ok:#7FC49E; --warn:#E8996F;
  --shadow-1:0 1px 2px rgba(0,0,0,.5), 0 10px 28px -16px rgba(0,0,0,.8);
  --shadow-2:0 -6px 24px -12px rgba(0,0,0,.6);
}
:root[data-theme="sepia"] {
  color-scheme: light;
  --bg:#F1E5CF; --surface:#F7EEDD; --ink:#3B2E1E; --ink-2:#6B5B45; --ink-3:#8F7F63; --line:#DCCBAA;
}
:root[data-theme="paper"] { color-scheme: light; }

/* ---- reset & base ---- */
*, *::before, *::after { box-sizing:border-box; }
html { -webkit-text-size-adjust:100%; }
/* 竖向 flex + main 撑开:短页面(登录、确认、空的"我的")的页脚才会落在屏底,
   而不是浮在半空、下面拖一条空白。长页面不受影响,页脚仍留 --sp-8 的间距。 */
body { margin:0; background:var(--bg); color:var(--ink); font-family:var(--font-ui); font-size:var(--fs-md); line-height:1.55; -webkit-font-smoothing:antialiased; min-height:100dvh; display:flex; flex-direction:column; }
main { flex:1 0 auto; }
img, svg { display:block; max-width:100%; }
/* width/height 属性是给浏览器占位用的,别让它压过 aspect-ratio(封面会被拉成条) */
img { height:auto; }
a { color:inherit; text-decoration:none; }
/* button.link 也在里面:页脚的 "Cookie settings" 不去任何地方(它只是把同意
   选择清掉再问一次),所以它是 button 而不是 a——但读者要认得出它可以点。 */
a.link, button.link { text-decoration:underline; text-decoration-thickness:1px; text-underline-offset:3px; }
button { font:inherit; color:inherit; background:none; border:0; padding:0; cursor:pointer; }
:focus-visible { outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-btn); }
h1, h2, h3 { margin:0; font-weight:400; text-wrap:balance; }
p { margin:0; }
ul { margin:0; padding:0; list-style:none; }
.num { font-variant-numeric:tabular-nums; }
.visually-hidden { position:absolute !important; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }

/* ---- type roles ---- */
.display { font-family:var(--font-display); font-weight:400; line-height:1.15; }
.read { font-family:var(--font-read); }
.eyebrow { font-size:var(--fs-xs); letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3); font-weight:700; }
.muted { color:var(--ink-2); } .faint { color:var(--ink-3); }
.t-xs { font-size:var(--fs-xs); } .t-sm { font-size:var(--fs-sm); } .t-md { font-size:var(--fs-md); } .t-lg { font-size:var(--fs-lg); }
.t-xl { font-size:var(--fs-xl); line-height:1.3; } .t-2xl { font-size:var(--fs-2xl); line-height:1.2; } .t-3xl { font-size:var(--fs-3xl); line-height:1.1; }

/* ---- layout ---- */
.wrap { width:100%; max-width:720px; margin:0 auto; padding-left:var(--sp-4); padding-right:var(--sp-4); }
/* 后台是唯一在桌面上看的页面:720px 的读者栏宽放不下六列表格。 */
.wrap-wide { width:100%; max-width:1080px; margin:0 auto; padding-left:var(--sp-4); padding-right:var(--sp-4); }
.stack { display:flex; flex-direction:column; } .row { display:flex; align-items:center; }
/* `hidden` 属性必须赢过任何布局类。浏览器自带的 [hidden]{display:none} 是 UA 样式,
   作者样式里随便一个 display 都能压过它——于是 .stack / .row 一挂上 hidden 就照样显示
   (D7 实弹:推送软提示里 iOS 那两步说明在 Chrome 上一直露着)。下面 .aa-panel[hidden]、
   .consent[hidden] 两条是这条之前各修各的,留着不碍事。 */
[hidden] { display:none !important; }
.gap-1 { gap:var(--sp-1); } .gap-2 { gap:var(--sp-2); } .gap-3 { gap:var(--sp-3); } .gap-4 { gap:var(--sp-4); } .gap-5 { gap:var(--sp-5); } .gap-6 { gap:var(--sp-6); }
.mt-2 { margin-top:var(--sp-2); } .mt-3 { margin-top:var(--sp-3); } .mt-4 { margin-top:var(--sp-4); } .mt-5 { margin-top:var(--sp-5); } .mt-6 { margin-top:var(--sp-6); } .mt-7 { margin-top:var(--sp-7); }
.pb-6 { padding-bottom:var(--sp-6); } .pb-8 { padding-bottom:var(--sp-8); }
.between { justify-content:space-between; } .center { text-align:center; }
.hr { height:1px; background:var(--line); border:0; margin:0; }
.grid-books { display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:var(--sp-5) var(--sp-4); }
@media (min-width:640px) { .grid-books { grid-template-columns:repeat(3, minmax(0,1fr)); } }
/* 横滑区出血到屏幕两边(负 margin + 同样大的 padding),这样卡片能滑到边缘,
   静止时又和正文对齐。scroll-padding 不能少:scroll-snap 对齐的是滚动口的边,
   不看 padding——不写这一行,浏览器一进页面就把 16px 的左内边距滚掉,首卡贴边。 */
.hscroll { display:flex; gap:var(--sp-4); overflow-x:auto; scroll-snap-type:x mandatory; padding-bottom:var(--sp-2); margin-left:calc(-1 * var(--sp-4)); margin-right:calc(-1 * var(--sp-4)); padding-left:var(--sp-4); padding-right:var(--sp-4); scroll-padding-left:var(--sp-4); scroll-padding-right:var(--sp-4); scrollbar-width:none; }
.hscroll::-webkit-scrollbar { display:none; }
.hscroll > * { flex:0 0 38%; scroll-snap-align:start; max-width:180px; }

/* ---- topbar / footer ---- */
.topbar { height:56px; background:var(--navy); color:var(--on-navy); position:sticky; top:0; z-index:20; }
.topbar .wrap { height:100%; display:flex; align-items:center; justify-content:space-between; }
.brand { font-family:var(--font-display); font-size:var(--fs-xl); letter-spacing:.01em; color:var(--on-navy); }
.brand .dot { color:var(--sun); }
.icon-btn { width:44px; height:44px; display:inline-flex; align-items:center; justify-content:center; border-radius:var(--r-pill); color:inherit; }
.icon-btn svg { width:22px; height:22px; stroke:currentColor; fill:none; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
.footer { background:var(--navy); color:var(--on-navy); padding:var(--sp-6) 0 calc(var(--sp-6) + env(safe-area-inset-bottom)); margin-top:var(--sp-8); font-size:var(--fs-sm); }
.footer a, .footer button.link { opacity:.85; }
.footer a:hover, .footer button.link:hover { opacity:1; text-decoration:underline; }
.footer .links { display:flex; flex-wrap:wrap; gap:var(--sp-2) var(--sp-4); }
/* 显式声明界面字体,和 .book-row 同一个理由:这个组件也出现在阅读页里
   (第 1 章正文上方),而 .reader 把容器字体设成了阅读衬线。不写这一行,
   同一处披露在两个页面上是两种字体。 */
/* display:block 而不是 inline-flex:阅读页那一处是一句会折行的话,flex 会把
   "Learn more" 当成第二个 flex item 甩到行尾,两行之间还留一条 gap。 */
.ai-note { font-size:var(--fs-xs); line-height:1.5; color:var(--ink-3); font-family:var(--font-ui); display:block; }
/* 改 block 之后 "Learn more" 只剩一个词间空格。页脚与书页上它是一枚跟在后面的
   入口,要看得出是分开的两截。 */
.ai-note a { margin-left:var(--sp-2); }
/* 阅读页那一处是一句完整的话,句号后面本来就有空格;再加 8px 会像句子中间
   断了一下。 */
.reader .ai-note a { margin-left:0; }
.footer .ai-note { color:var(--on-navy); opacity:.7; }

/* ---- buttons ---- */
.btn { display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-2); height:44px; padding:0 var(--sp-5); border-radius:var(--r-btn); font-weight:700; font-size:var(--fs-md); line-height:1; border:1px solid transparent; transition:background-color .12s, transform .06s; white-space:nowrap; }
.btn:active { transform:translateY(1px); }
.btn[disabled], .btn[aria-disabled="true"] { opacity:.5; pointer-events:none; }
.btn-primary { background:var(--accent); color:#fff; }
.btn-primary:hover { background:var(--accent-hover); }
.btn-ghost { background:transparent; border-color:var(--line); color:var(--ink); }
.btn-ghost:hover { background:var(--surface); }
.btn-lg { height:52px; font-size:var(--fs-lg); }
.btn-block { width:100%; }
/* 一行放不下的长文案按钮(还没开卖时那颗 "Membership opens soon — leave your email")。
   .btn 默认 nowrap 是对的:绝大多数按钮两三个词,断行只会让它们看起来像出了错;
   所以这里给一个显式的修饰类,而不是把 nowrap 从所有按钮上拿掉。高度改成
   min-height,文字撑几行就多高。 */
.btn-wrap { white-space:normal; height:auto; min-height:52px; padding-top:var(--sp-3); padding-bottom:var(--sp-3); line-height:1.3; text-align:center; }
.btn svg { width:18px; height:18px; stroke:currentColor; fill:none; stroke-width:2; }

/* ---- chips ---- */
.chip { display:inline-flex; align-items:center; height:24px; padding:0 var(--sp-3); border-radius:var(--r-pill); font-size:var(--fs-xs); font-weight:700; letter-spacing:.06em; text-transform:uppercase; background:var(--surface); border:1px solid var(--line); color:var(--ink-2); }
.chip-sun { background:var(--sun); border-color:var(--sun); color:var(--navy); }
.chips { display:flex; flex-wrap:wrap; gap:var(--sp-2); }

/* ---- covers & cards ---- */
.cover { aspect-ratio:3/4; width:100%; object-fit:cover; border-radius:var(--r-card); background:var(--line); box-shadow:var(--shadow-1); }
.cover-lg { width:60vw; max-width:240px; margin:0 auto; }
.cover-sm { width:72px; flex:0 0 72px; box-shadow:none; }
.book-card { display:flex; flex-direction:column; gap:var(--sp-2); color:inherit; }
.book-card .title { font-family:var(--font-display); font-size:var(--fs-xl); line-height:1.2; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.book-card .by { font-size:var(--fs-sm); color:var(--ink-3); }
.book-card .hook { font-size:var(--fs-sm); color:var(--ink-2); display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
/* 显式声明界面字体:这个组件也出现在阅读页里(章末"接着读"),而 .reader 把
   容器字体设成了阅读衬线。不写这一行,同一个组件在两个页面上是两种字体。 */
.book-row { display:flex; gap:var(--sp-4); align-items:flex-start; padding:var(--sp-3) 0; border-bottom:1px solid var(--line); color:inherit; font-family:var(--font-ui); }
.book-row:last-child { border-bottom:0; }
.book-row .rank { font-family:var(--font-display); font-size:var(--fs-2xl); color:var(--ink-3); width:32px; flex:0 0 32px; line-height:1; padding-top:2px; }
.book-row .title { font-family:var(--font-display); font-size:var(--fs-lg); line-height:1.25; }
.book-row .meta { font-size:var(--fs-sm); color:var(--ink-3); margin-top:2px; }
.card { background:var(--surface); border:1px solid var(--line); border-radius:var(--r-card); padding:var(--sp-4); }
.card-raised { box-shadow:var(--shadow-1); border-color:transparent; }

/* ---- featured (home hero) ---- */
/* 5:4 = 精选封面约占屏宽 55%。3:4 时它比下面网格里的卡片封面还小,主次就倒了。 */
.featured { display:grid; grid-template-columns:minmax(0,5fr) minmax(0,4fr); gap:var(--sp-4); align-items:center; padding:var(--sp-5) 0; }
.featured .title { font-family:var(--font-display); font-size:var(--fs-3xl); line-height:1.08; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
.featured .cover { box-shadow:var(--shadow-1); }
@media (max-width:420px) { .featured .title { font-size:var(--fs-2xl); } } /* 窄屏右栏只有 44%,36px 一行放不下两个词,书名读不全比字小一号更伤 */
.section-title { font-family:var(--font-display); font-size:var(--fs-xl); }
.section-head { display:flex; align-items:baseline; justify-content:space-between; margin-bottom:var(--sp-3); }
.section-head a { font-size:var(--fs-sm); color:var(--ink-2); text-decoration:underline; text-underline-offset:3px; }

/* ---- book page ---- */
.book-title { font-family:var(--font-display); font-size:var(--fs-2xl); line-height:1.15; text-align:center; }
.book-meta { text-align:center; color:var(--ink-2); font-size:var(--fs-sm); }
.blurb { font-family:var(--font-read); font-size:var(--fs-lg); line-height:1.6; color:var(--ink); }
.blurb.clamped { display:-webkit-box; -webkit-line-clamp:6; -webkit-box-orient:vertical; overflow:hidden; }
/* 简介展开:一个视觉隐藏的 checkbox + label,不用 JS。焦点环落在 label 上。 */
.more-label { align-self:flex-start; cursor:pointer; }
.more-toggle:checked ~ .blurb.clamped { display:block; overflow:visible; }
.more-toggle:checked ~ .more-label { display:none; }
.more-toggle:focus-visible ~ .more-label { outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-btn); }
.toc-list li { display:flex; align-items:center; justify-content:space-between; padding:var(--sp-3) 0; border-bottom:1px solid var(--line); font-size:var(--fs-md); }
.toc-list li.locked { color:var(--ink-3); }
.toc-list svg { width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.8; }

/* ---- reader ---- */
.progress { position:fixed; top:0; left:0; height:2px; width:0; background:var(--accent); z-index:30; transition:width .1s linear; }
.minibar { height:42px; display:flex; align-items:center; gap:var(--sp-3); font-size:var(--fs-sm); color:var(--ink-2); }
.minibar .bt { flex:1; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
.reader { max-width:34em; margin:0 auto; padding:var(--sp-5) 20px var(--sp-8); font-family:var(--font-read); font-size:var(--read-size); line-height:1.75; color:var(--ink); }
.reader-head { border-top:1px solid var(--line); padding-top:var(--sp-4); margin-bottom:var(--sp-5); }
.reader-head .chap { font-family:var(--font-display); font-size:var(--fs-sm); letter-spacing:.14em; text-transform:uppercase; color:var(--ink-3); }
.reader-head .book { font-family:var(--font-ui); font-size:var(--fs-sm); color:var(--ink-2); margin-top:var(--sp-1); }
.reader p { margin:0 0 1em; }
.reader p:last-child { margin-bottom:0; }
.reader-next { margin-top:var(--sp-6); }
.reader-bar { position:fixed; left:0; right:0; bottom:0; height:calc(56px + env(safe-area-inset-bottom)); padding-bottom:env(safe-area-inset-bottom); background:var(--surface); box-shadow:var(--shadow-2); display:flex; align-items:center; justify-content:space-between; z-index:20; transition:transform .16s ease, opacity .16s ease; }
.reader-bar.hidden-bar { transform:translateY(100%); opacity:0; }
.reader-bar .wrap { display:flex; align-items:center; justify-content:space-between; }
.reader-bar .btn-ghost { border-color:transparent; }
.aa-panel { position:fixed; left:0; right:0; bottom:calc(56px + env(safe-area-inset-bottom)); background:var(--surface); border-top:1px solid var(--line); padding:var(--sp-4); z-index:21; }
.aa-panel[hidden] { display:none; }
.seg { display:flex; border:1px solid var(--line); border-radius:var(--r-btn); overflow:hidden; }
/* 分段控件的两种成员:按钮(阅读页字号)与链接(题材页排序)。排序必须是链接
   ——它要能被分享、被收藏、被爬虫读到,一个 button 做不到这三件事。 */
.seg button { flex:1; height:44px; font-weight:700; }
.seg a { flex:1; height:44px; display:flex; align-items:center; justify-content:center; font-weight:700; font-size:var(--fs-sm); color:var(--ink-2); }
.seg button[aria-pressed="true"], .seg a[aria-current="page"] { background:var(--accent-soft); color:var(--accent); }
.theme-dots { display:flex; gap:var(--sp-3); }
.theme-dot { width:44px; height:44px; border-radius:var(--r-pill); border:2px solid var(--line); }
.theme-dot[aria-pressed="true"] { border-color:var(--accent); }
.theme-dot.paper { background:#F6F1E7; } .theme-dot.sepia { background:#F1E5CF; } .theme-dot.night { background:#141311; }

/* ---- paywall ---- */
.paywall-fade { position:relative; max-height:6.5em; overflow:hidden; }
.paywall-fade::after { content:""; position:absolute; inset:0; background:linear-gradient(to bottom, transparent 0%, var(--bg) 85%); }
.paywall { background:var(--navy); color:var(--on-navy); border-radius:var(--r-card); padding:var(--sp-5) var(--sp-4); margin-top:var(--sp-4); font-family:var(--font-ui); animation:rise .24s ease-out both; }
.paywall .title { font-family:var(--font-display); font-size:var(--fs-2xl); line-height:1.15; }
.paywall .sub { font-size:var(--fs-md); opacity:.85; margin-top:var(--sp-2); }
.paywall .fine { font-size:var(--fs-xs); opacity:.7; margin-top:var(--sp-3); line-height:1.5; }
.paywall .fine a { text-decoration:underline; }
.plans { display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-3); margin-top:var(--sp-4); }
/* 竖排:名称 / 价格 / 原价 / 续费说明各占一行。inline 排会把四段挤成一句话。 */
.plan { position:relative; display:flex; flex-direction:column; align-items:flex-start; background:var(--surface); color:var(--ink); border:1px solid var(--line); border-radius:var(--r-card); padding:var(--sp-5) var(--sp-3) var(--sp-4); cursor:pointer; }
.plan[aria-checked="true"], .plan:has(input:checked) { border:2px solid var(--accent); background:var(--accent-soft); }
.plan input { position:absolute; opacity:0; pointer-events:none; }
.plan .name { font-size:var(--fs-sm); font-weight:700; color:var(--ink-2); }
.plan .price { font-size:var(--fs-2xl); font-weight:700; line-height:1.1; margin-top:var(--sp-1); font-variant-numeric:tabular-nums; }
.plan .price small { display:block; font-size:var(--fs-sm); font-weight:500; color:var(--ink-2); line-height:1.3; }
.plan .was { font-size:var(--fs-sm); color:var(--ink-3); text-decoration:line-through; }
.plan .then { font-size:var(--fs-xs); color:var(--ink-2); margin-top:var(--sp-1); }
.plan .chip-sun { position:absolute; top:-12px; left:var(--sp-3); }
.paywall .btn-primary { margin-top:var(--sp-4); }
@keyframes rise { from { opacity:0; transform:translateY(12px); } to { opacity:1; transform:none; } }

/* ---- forms ---- */
.field { display:flex; flex-direction:column; gap:var(--sp-1); }
.field label { font-size:var(--fs-sm); font-weight:700; color:var(--ink-2); }
.field input { height:48px; padding:0 var(--sp-4); border-radius:var(--r-btn); border:1px solid var(--line); background:var(--surface); color:var(--ink); font:inherit; font-size:var(--fs-md); }
.field input:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.field .err { font-size:var(--fs-sm); color:var(--warn); }
/* 同意勾选。方框 20px 但整行是触控目标(44px 高的 label),手机上没人点得中
   一个 20px 的方块;文字本身就是标签,点哪儿都算勾。 */
.check { display:flex; align-items:flex-start; gap:var(--sp-3); min-height:44px; padding:var(--sp-2) 0; font-size:var(--fs-sm); color:var(--ink-2); cursor:pointer; }
.check input { flex:0 0 20px; width:20px; height:20px; margin:2px 0 0; accent-color:var(--accent); }
/* .notice 显式声明 --font-ui(同 .book-row / .ai-note):它也出现在阅读页里(推送软提示),
   .reader 把容器字体设成了阅读衬线,不写这一行,同一个组件在两个页面上是两种字体。 */
.notice { border-left:3px solid var(--accent); background:var(--surface); padding:var(--sp-3) var(--sp-4); border-radius:0 var(--r-card) var(--r-card) 0; font-size:var(--fs-sm); color:var(--ink-2); font-family:var(--font-ui); }
.notice.ok { border-left-color:var(--ok); }
/* Cookie 同意条:.notice 的一个变体,粘在屏底。**只在像素启用时才存在**——
   没开投放的站不该拿一条同意条挡住读者。按钮 36px 而不是 44px:它压在正文
   上面,不该比正文里的主按钮还显眼(它不是读者来这儿要做的事)。 */
.consent { position:fixed; left:0; right:0; bottom:0; z-index:35; display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:var(--sp-3); border-radius:0; box-shadow:var(--shadow-2); padding:var(--sp-3) var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom)); }
.consent[hidden] { display:none; }
.consent .btn { height:36px; }
/* **同意条不许压住阅读页的 Prev / Aa / Next**。两者都 fixed 在屏底,同意条
   z-index 更高,不让路就是一整条按钮点不动(BLOCKER-1)。--consent-h 由
   pixel.js 量出真实高度写在 :root 上(文案折成两行时它更高),这里只负责让位。
   用 body 的类做开关:没有 JS 时这一段完全不生效,底栏保持原样。 */
body.consent-open .reader-bar { bottom:var(--consent-h); }
body.consent-open .aa-panel { bottom:calc(56px + env(safe-area-inset-bottom) + var(--consent-h)); }
body.consent-open .toast { bottom:calc(72px + env(safe-area-inset-bottom) + var(--consent-h)); }

/* ---- table (admin only) ---- */
/* 紧凑 14px、斑马纹用 --surface、表头是 .eyebrow。后台是一张给管理员看的密表,
   读者侧的间距在这里只会让人一屏看不见五行。横向滚动交给 .table-wrap,页面
   本身永远不横滚。 */
.table-wrap { overflow-x:auto; -webkit-overflow-scrolling:touch; }
.table { width:100%; border-collapse:collapse; font-size:var(--fs-sm); }
.table th, .table td { padding:var(--sp-2) var(--sp-3); text-align:left; vertical-align:middle; border-bottom:1px solid var(--line); }
.table thead th { font-size:var(--fs-xs); letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3); font-weight:700; white-space:nowrap; }
.table tbody tr:nth-child(even) { background:var(--surface); }
.table .right { text-align:right; }
.table input[type="number"], .table input[type="text"] { height:36px; width:72px; padding:0 var(--sp-2); border-radius:var(--r-btn); border:1px solid var(--line); background:var(--surface); color:var(--ink); font:inherit; font-size:var(--fs-sm); }
.table select { height:36px; border-radius:var(--r-btn); border:1px solid var(--line); background:var(--surface); color:var(--ink); font:inherit; font-size:var(--fs-sm); }
.table .btn { height:36px; padding:0 var(--sp-3); font-size:var(--fs-sm); }

/* ---- misc ---- */
.toast { position:fixed; left:50%; bottom:calc(72px + env(safe-area-inset-bottom)); transform:translateX(-50%); background:var(--navy); color:var(--on-navy); padding:var(--sp-3) var(--sp-4); border-radius:var(--r-btn); font-size:var(--fs-sm); z-index:40; box-shadow:var(--shadow-1); }
.empty { text-align:center; color:var(--ink-2); padding:var(--sp-7) 0; display:flex; flex-direction:column; gap:var(--sp-4); align-items:center; }
.legal { max-width:40em; margin:0 auto; font-family:var(--font-read); font-size:var(--fs-md); line-height:1.7; }
.legal h1 { font-family:var(--font-display); font-size:var(--fs-2xl); margin-bottom:var(--sp-4); }
.legal h2 { font-family:var(--font-ui); font-weight:700; font-size:var(--fs-lg); margin:var(--sp-6) 0 var(--sp-2); }
.legal h3 { font-family:var(--font-ui); font-weight:700; font-size:var(--fs-md); margin:var(--sp-5) 0 var(--sp-2); }
.legal p, .legal li { margin-bottom:var(--sp-3); }
.legal ul { list-style:disc; padding-left:1.2em; }
/* 没填的法务占位符。**只可能出现在 dev**:ENV=prod 时 app/legal.py 的 preflight
   已经拒绝启动了。醒目是它的全部职责——一份写着 [COMPANY_LEGAL_NAME] 的条款
   要一眼看得出来,而不是混在正文里发出去。 */
.legal .todo { font-family:var(--font-ui); font-size:var(--fs-sm); font-weight:700; background:var(--accent-soft); color:var(--accent); padding:0 var(--sp-1); border-radius:var(--r-btn); }
.status-card { background:var(--surface); border:1px solid var(--line); border-radius:var(--r-card); padding:var(--sp-4); display:flex; flex-direction:column; gap:var(--sp-2); }
.status-card .k { font-size:var(--fs-xs); letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3); font-weight:700; }
.pill-ok { color:var(--ok); font-weight:700; } .pill-warn { color:var(--warn); font-weight:700; }

@media (prefers-reduced-motion: reduce) {
  .paywall { animation:none; }
  .reader-bar { transition:opacity .16s ease; }
  .reader-bar.hidden-bar { transform:none; }
  .btn:active { transform:none; }
}
