/* ═══ z-index band 表（2026-09-10，抄 WeebPaint styles.css 顶部：**全仓唯一允许出现 z-index 数字的地方**；test/z-index.test.mjs 机械执法，局部 ≤3 的层内小序放行）
   不变量：page（纸面内浮件：话筒/候选/锁卡/横幅）< chrome（顶栏/侧栏；侧栏遮罩 = chrome-1）< overlay（书库一屏）< drawer（设置抽屉，能开在书库之上；其遮罩 = drawer-1）
           < menu（弹出菜单，能从抽屉行弹）< popover（菜单内再弹）< toast < idle（闲置模糊）< busy < modal（sheet；解锁循环在 busy 外）< gate（冲突必 surface）< notice < error
   user 2026-09-10「麦克风按钮和边栏的 z order 乱了。z order 的问题系统解决一下」：话筒以前 7 > 侧栏 5。 ═══ */
:root {
  --z-page: 5;
  --z-chrome: 10;
  --z-overlay: 300;
  --z-drawer: 350;
  --z-menu: 400;
  --z-popover: 420;
  --z-toast: 450;
  --z-idle: 470;
  --z-busy: 490;
  --z-modal: 500;
  --z-ime: 530;      /* 内置输入法候选条：高于 sheet(500)、低于 gate/notice——名字 sheet / 侧栏检索框里打字候选也在最上（user 2026-09-10「输入法的 z order 不对」） */
  --z-gate: 540;
  --z-notice: 560;
  --z-error: 9999;
  /* 配色 token：与 WeebPaint 同名同值（user 2026-09-03「配色换 weebpaint 的，以后再加别的配色」）。
     基础 token 在下面；本仓旧变量名（--bg-0/1/2/3、--ink-muted、--accent-strong/soft、--danger-soft）改为派生，主题只换基础 token。 */
  --bg: #f6f4ef;
  --bg-soft: #efece4;
  --ink: #1b1b1b;
  --ink-soft: #555;
  --line: #d8d2c4;
  --line-strong: #b8af9c;
  --accent: #8a481e;
  --danger: #c0392b;
  --warn: #d99a20;   /* 云侧待你处理（badge conflict） */
  --enc: #c98a00;    /* 加密琥珀（锁） */
  --ok: #3a9a4a;
  --quiet: #8a8a8a;
  --info: #4c8bf5;
  --void: #e6e2d6;   /* 页面外的"桌面" */
  --shadow: 0 6px 24px rgba(0, 0, 0, 0.08);
  --radius: 12px;

  /* 派生（旧名保留，值跟基础 token 走） */
  --bg-0: var(--void);
  --bg-1: var(--bg);
  --bg-2: var(--bg-soft);
  --bg-3: var(--line);
  --ink-muted: var(--quiet);
  --accent-strong: color-mix(in srgb, var(--accent) 82%, black);
  --accent-soft: color-mix(in srgb, var(--accent) 14%, transparent);
  --danger-soft: color-mix(in srgb, var(--danger) 12%, transparent);

  --font-ui: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei",
    "Segoe UI", system-ui, sans-serif;
  /* 第一位 = 内置字体（src/fonts.ts 装进文档的 family；思源黑体全量，v2.3.15）。没装好之前自然落到后面的系统字体；韩文等它没有的字也落下去。 */
  --font-fallback: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Segoe UI", system-ui, sans-serif;
  --font-editor: "XHW Sans", var(--font-fallback);

  /* 行宽（v2.3.11，user 2026-09-30「现在阅读节奏为什么还是只有两档，而且你不是说去语义化吗」）：
     唯一 knob = 每行几个字（14 / 20 / 28，同步偏好跟人走，body[data-chars]），和导出同一套数字；
     行距跟档 1.9 / 1.75 / 1.6。旧「阅读节奏」两档（短行 / 标准）退役：classic → 28、novel → 20。
     **字号和行宽互不相干（v2.3.17，user 2026-10-01「为什么字号和行宽还是串扰。字号应该是绝对的，不是相对于行宽的」）**：
       字号 = 这台设备的基准（--editor-font-base：22px；手机 16px，见下面的 media）× 字号档（--font-scale，设置「字号（本机）」）。不看行宽、不看屏宽。
       纸宽上限 = 每行字数 × 字号 + 两边留白 → 调字号纸跟着变宽，每行字数不变。
       屏幕放不下：纸只有屏那么宽，一行少排几个字，**不缩字**。
     （v2.3.11–16 的做法是字号 = clamp(16, (屏宽 − 72) / 字数, 22) × 档、纸宽 = 字数 × 22 + 72：手机上换行宽字号跟着变，任何设备上调字号档每行字数跟着变。） */
  --target-chars: 20;
  --editor-line-height: 1.75;
  --editor-font-base: 22px;
}
/* 手机（屏的短边 < 500 CSS px）基准 16px（user 2026-10-01「16可以啊」）：看的是设备不是窗口，所以同一台设备上字号恒定；16 也是 iOS 不自动放大页面的下限。 */
@media (max-device-width: 499px), (max-device-height: 499px) { :root { --editor-font-base: 16px; } }
/* --box-max 必须定义在 body 上：自定义属性里的 var() 在定义处就代换，写在 :root 会永远按 :root 的 --target-chars（20）算，body[data-chars] 改了也不跟（ruled-audit 2026-09-30 抓到 28 字档字号变 16px） */
body {
  --editor-font: calc(var(--editor-font-base) * var(--font-scale, 1));
  --page-pad-x: clamp(18px, 3.5vw, 32px);
  --box-max: calc(var(--target-chars) * var(--editor-font) + 2 * var(--page-pad-x) + 8px);   /* 8 = 纸的边框 2 + 取整余量 6（比任何字号都小，多不出一个字） */
}

:root[data-theme="night"] {
  --bg: #121110; --bg-soft: #1c1b19; --ink: #e8e3d6; --ink-soft: #998f7a; --line: #2a2926; --line-strong: #3a3833;
  --accent: #d8b48a; --danger: #e57369; --warn: #e2ad4e; --enc: #d9a542; --ok: #5cb96b; --quiet: #857e70; --info: #4c8bf5;
  --void: #0a0a09; --shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
}
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
    --bg: #121110; --bg-soft: #1c1b19; --ink: #e8e3d6; --ink-soft: #998f7a; --line: #2a2926; --line-strong: #3a3833;
    --accent: #d8b48a; --danger: #e57369; --warn: #e2ad4e; --enc: #d9a542; --ok: #5cb96b; --quiet: #857e70; --info: #4c8bf5;
    --void: #0a0a09; --shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
  }
}

/* 编辑器字体 = 书的属性（v2.3.26，user 2026-10-01「编辑器换字体 做，书的属性，导出默认跟随」）：body[data-font]。拼音字体（萌神，12 MB）选了才取；没到之前、和它没有的字，落回黑体。
   网页正文的排字自己会按词给多音字选读音（只有 canvas 不会），所以这里不用做任何标注。 */
body[data-font="pinyin"] { --font-editor: "XHW Pinyin", "XHW Sans", var(--font-fallback); }
/* 像素字体（v2.3.27，user 的 Mono8px：8×8 的格子）：字号要是 8 的整数倍才不糊——桌面基准 24px（3 格），手机 16px（2 格）。字号档不是 1 的时候会有点糊，认了。 */
body[data-font="pixel"] { --font-editor: "XHW Pixel", "XHW Sans", var(--font-fallback); --editor-font-base: 24px; }
@media (max-device-width: 499px), (max-device-height: 499px) { body[data-font="pixel"] { --editor-font-base: 16px; } }
body[data-chars="14"] { --target-chars: 14; --editor-line-height: 1.9; }
body[data-chars="28"] { --target-chars: 28; --editor-line-height: 1.6; }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  width: 100%;
  height: 100%;
}
/* 页面外延底色（v2.3.33，user 2026-10-04 Edge iPad「最下面还是空一点」）：Edge 的 WKWebView 没关自动安全区内边距（Safari 不这样），网页铺不进底部 Home 条那 20 px，
   WebKit 用「页面根背景色」填它（截图量到 = --bg-0，2026-10-04；同类：WebKit bug 292603）。根背景色单独写在 html 上（body 照旧画自己的渐变，纸面看起来不变），
   软键盘露着时换成键盘色 → 那一截看起来是键盘自己的底边（和系统键盘的垫高一样）。 */
html { background-color: var(--bg-0); }
html:has(body.ime-docked) { background-color: var(--kb-bg); }

body {
  background: var(--bg-0);
  background-image:
    radial-gradient(80% 60% at 50% 0%, rgba(255, 250, 235, 0.45), transparent 60%),
    radial-gradient(60% 50% at 100% 100%, rgba(160, 88, 38, 0.06), transparent 65%);
  color: var(--ink);
  font-family: var(--font-ui);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  height: 100dvh;
  overflow: hidden;
}

/* 一张纸模型（v2.1.26，2026-09-30 user「对的，整张纸滚」）：main.surface 是唯一的滚动容器，纸（.page）从标题到页脚是一整张、有多少字就多高；
   正文框自己不滚（高度 = 内容高度，app.ts syncBodyHeight 量）。写字线画在 .page-body 上，字和线同一层 → 滚动时不再相对抖
   （旧做法 = textarea 内部滚动 + background-attachment: local，WebKit 上背景比合成层滚动慢一帧）。顶栏常驻一条（--sheet-top）；纸从它下面滚过。
   flex 列：滚动区末尾的 padding 在所有浏览器都算进可滚范围（块容器的 padding-bottom 历史上不可靠）。 */
:root { --sheet-top: calc(var(--top-floor) + 32px); --sheet-bottom: calc(var(--bottom-floor) + var(--kb-offset, 0px) + var(--dock-h, 0px)); --sheet-h: calc(100dvh - var(--sheet-top) - var(--sheet-bottom)); }
.surface {
  position: relative;
  height: 100dvh;
  overflow-x: hidden; overflow-y: auto;
  overscroll-behavior: contain;
  display: flex; flex-direction: column; align-items: center;
  padding: var(--sheet-top) 0 var(--sheet-bottom);
  scroll-padding-top: calc(var(--sheet-top) + 8px); scroll-padding-bottom: calc(var(--sheet-bottom) + 8px);   /* 光标跟随（scrollIntoView）别把那一行塞到顶栏 / 键盘底下 */
  scrollbar-width: thin; scrollbar-color: var(--bg-3) transparent;
}
.surface::-webkit-scrollbar { width: 6px; }
.surface::-webkit-scrollbar-track { background: transparent; }
.surface::-webkit-scrollbar-thumb { background: var(--bg-3); border-radius: 3px; }

/* Top bar — narrow, aligned with the page box */

.top-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: var(--sheet-top);
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 4px;
  padding: var(--top-floor) max(8px, calc(50% - var(--box-max) / 2)) 4px;   /* 内容与纸同宽（窄屏两边留 8px）；整条是不透明的带子，纸从底下滚过 */
  background-color: var(--bg-0);
  background-image:
    radial-gradient(80% 60% at 50% 0%, rgba(255, 250, 235, 0.45), transparent 60%),
    radial-gradient(60% 50% at 100% 100%, rgba(160, 88, 38, 0.06), transparent 65%);
  background-size: 100% 100dvh; background-repeat: no-repeat; background-position: 0 0;   /* 与 body 的渐变逐像素相同：带子的下沿没有接缝 */
  z-index: var(--z-chrome);
}

.menu-button {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border: 0;
  background: transparent;
  color: var(--ink-muted);
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 120ms ease, background 120ms ease;
}

.menu-button svg { width: 16px; height: 16px; }
.menu-button[hidden] { display: none; }

.lock-toggle {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border: 0;
  background: transparent;
  color: var(--ink-muted);
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 120ms, background 120ms;
}

.lock-toggle:hover,
.lock-toggle:focus-visible {
  color: var(--ink);
  background: var(--bg-2);
  outline: none;
}

.lock-toggle[data-locked="true"] {
  color: var(--accent);
}

.crypto-toggle {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border: 0;
  background: transparent;
  color: var(--ink-muted);
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 120ms, background 120ms;
}

.crypto-toggle:hover,
.crypto-toggle:focus-visible {
  color: var(--ink);
  background: var(--bg-2);
  outline: none;
}

.crypto-toggle[data-encrypted="true"] {
  color: var(--accent);
}

.menu-button:hover,
.menu-button:focus-visible {
  color: var(--ink);
  background: var(--bg-2);
  outline: none;
}

/* 文件名（顶栏）：管理句柄不是标题（ADR-0007）——小字、可点改名、溢出省略 */
.doc-name {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 48%;
  border: 0;
  background: transparent;
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-muted);
  height: 26px;
  line-height: 26px;
  padding: 0 6px;
  border-radius: 6px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 120ms ease, background 120ms ease;
}
.doc-name:hover,
.doc-name:focus-visible { color: var(--ink-soft); }
.doc-name.pending { opacity: 0.6; }

.status {
  flex: 0 0 auto;
  color: var(--ink-muted);
  font-size: 11px;
  letter-spacing: 0.02em;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  pointer-events: none;
  font-feature-settings: "tnum" 1;
  white-space: nowrap;
  padding-right: 4px;
}

#saveStatus.error { color: var(--danger); }
#saveStatus.unsynced::before {
  content: "● ";
  color: var(--accent);
}
#saveStatus.flash {
  color: var(--accent);
  transition: color 120ms ease-out;
}

/* The page box — same max-width as top bar, thin border, fills vertically */

.page {
  --page-pad-y: clamp(18px, 3vh, 28px); --floater-band: 0px;
  width: 100%;
  max-width: var(--box-max);
  flex: none;
  margin: 0 auto;
  padding: var(--page-pad-y) var(--page-pad-x) calc(var(--page-pad-y) + var(--floater-band));
  display: flex;
  flex-direction: column;
  min-height: var(--sheet-h);   /* 短稿也是一整张纸；长稿比视口高，滚的是 .surface */
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: var(--bg-1);
  box-shadow: 0 16px 40px rgba(58, 40, 24, 0.12);
  position: relative;
}
.page:has(> .mic-button:not([hidden])) { --floater-band: 52px; }   /* 纸的末尾留一条给钉在视口角上的话筒 / 键盘钮：页脚的 › 不被它盖住 */

.ghost-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  margin-bottom: 10px;
  background: rgba(157, 46, 33, 0.08);
  border: 1px solid rgba(157, 46, 33, 0.28);
  border-radius: 8px;
  color: var(--danger);
  font-size: 12px;
}

.ghost-banner.hidden { display: none; }

.ghost-banner-text {
  flex: 1 1 auto;
  min-width: 0;
}

.ghost-banner-action {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid rgba(157, 46, 33, 0.4);
  color: var(--danger);
  font-family: inherit;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 6px;
  cursor: pointer;
}

.ghost-banner-action:hover,
.ghost-banner-action:focus-visible {
  background: rgba(157, 46, 33, 0.16);
  outline: none;
}

.ghost-banner-action.primary {
  background: rgba(157, 46, 33, 0.18);
}

.doc-row .doc-name.ghost::after {
  content: "";   /* 同步态徽标由 meta 行的文字说明承担（家规：不用 emoji 当图标） */
  color: var(--danger);
  font-size: 11px;
}

.doc-row .doc-name.stub::after {
  content: " ⋯";
  color: var(--ink-muted);
  font-size: 11px;
}

.doc-row .doc-name.local-only::after {
  content: "";
  color: var(--ink-muted);
  font-size: 11px;
}

/* Encryption indicator: small key glyph rendered as SVG prefix injected by
   renderDocList. Lives inside the same .doc-main as the name. */

.doc-row .doc-name-row {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

.doc-row .doc-crypto-prefix {
  display: inline-flex;
  align-items: center;
  color: var(--accent);
  flex: 0 0 auto;
}

.doc-row .doc-crypto-prefix svg {
  width: 12px;
  height: 12px;
}

.doc-row .doc-name.encrypted-locked {
  color: var(--ink-muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
}

.body {
  flex: none;
  display: block;
  overflow: hidden;   /* 高度 = 内容高度（app.ts 量），永远不内部滚动 */
  border: 0;
  background: transparent;
  font-family: var(--font-editor);
  font-size: var(--editor-font);
  line-height: var(--editor-line-height);
  color: var(--ink-soft);
  caret-color: var(--accent);
  outline: none;
  resize: none;
  width: 100%;
  padding: 0;
  letter-spacing: 0;
}

.body::placeholder { color: var(--ink-muted); }

.body.locked {
  caret-color: var(--accent);
}

.body::selection {
  background: rgba(160, 88, 38, 0.22);
}


/* 候选：样式全在文件末尾「输入法附件」一节（v2.1.16；悬浮条 .candidates + 手机式 .ime-dock） */

/* Voice input — floating mic button anchored to the page box */

.mic-button {
  position: fixed;   /* 钉在视口：纸整张滚，它不跟着走；横向对齐纸的右边（纸居中、宽 ≤ --box-max） */
  right: calc(max(0px, 50% - var(--box-max) / 2) + 14px);
  bottom: calc(var(--sheet-bottom) + 14px);   /* 软键盘 / iOS 键盘那一块（--dock-h / --kb-offset）之上 */
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: rgba(241, 230, 207, 0.94);
  color: var(--ink-soft);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6px 18px rgba(58, 40, 24, 0.18);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  z-index: var(--z-page);
  transition: background 140ms, color 140ms, transform 140ms, border-color 140ms;
  padding: 0;
}

.mic-button[hidden] { display: none !important; }

/* Disabled state: visible (so the user knows the feature exists) but greyed
   and click-through-blocked. Used when the active doc is encrypted and the
   chosen voice backend would ship audio to a third party — encrypting the
   draft but dictating it through cloud STT would defeat the point. */

.mic-button.disabled,
.mic-button.disabled:hover {
  opacity: 0.35;
  cursor: not-allowed;
  color: var(--ink-muted);
  background: rgba(241, 230, 207, 0.7);
  border-color: var(--line);
  transform: none;
}

.mic-button:hover,
.mic-button:focus-visible {
  color: var(--accent);
  background: var(--bg-1);
  outline: none;
}

.mic-button[data-state="listening"],
.mic-button[data-state="recording"] {
  color: #faf2e2;
  background: var(--accent);
  border-color: var(--accent);
  transform: scale(1.04);
}

.mic-button[data-state="transcribing"] {
  color: var(--accent-strong);
  background: var(--bg-1);
  border-color: var(--accent);
}

.mic-button[data-state="error"] {
  color: var(--danger);
  border-color: rgba(157, 46, 33, 0.4);
}

.mic-icon {
  position: relative;
  z-index: 1;
}

.mic-pulse {
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  opacity: 0;
  pointer-events: none;
}

.mic-button[data-state="listening"] .mic-pulse,
.mic-button[data-state="recording"] .mic-pulse {
  animation: mic-pulse 1.4s ease-out infinite;
}

.mic-button[data-state="transcribing"] .mic-pulse {
  animation: mic-pulse 2.2s ease-out infinite;
  border-color: var(--ink-muted);
}

@keyframes mic-pulse {
  0% { opacity: 0.55; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.55); }
}

@media (prefers-reduced-motion: reduce) {
  .mic-button[data-state="listening"] .mic-pulse { animation: none; }
  .mic-button[data-state="listening"] { transform: none; }
}

/* Drawer */

.backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: calc(var(--z-drawer) - 1);
}

.backdrop.hidden { display: none; }

.drawer {   /* 右侧滑出（user 2026-09-10「那么设置和三条杠也应该在右边」：与右侧栏同边） */
  position: fixed;
  top: 0;
  right: 0;
  height: 100dvh;
  width: min(320px, 86vw);
  background: var(--bg-1);
  border-left: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  z-index: var(--z-drawer);
  transition: transform 200ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.drawer.hidden {
  transform: translateX(102%);
  pointer-events: none;
}

.drawer-header {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 12px 10px;
  border-bottom: 1px solid var(--line);
}

.drawer-header h2 {
  flex: 1;
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  letter-spacing: 0.06em;
}

.settings-build {
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  font-size: 10px;
  color: var(--ink-muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.04em;
  user-select: text;
}

.settings-build:empty { display: none; }

.icon-button {
  width: 32px;
  height: 32px;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  border-radius: 7px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms, color 120ms;
}

.icon-button:hover,
.icon-button:focus-visible {
  background: var(--bg-2);
  color: var(--ink);
  outline: none;
}

.drawer-back[hidden] { display: none; }

.auth-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  font-size: 12px;
  color: var(--ink-muted);
}

.auth-row .auth-account {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink-soft);
}

.auth-action {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid var(--line-strong);
  color: var(--ink-soft);
  font-family: inherit;
  font-size: 12px;
  padding: 5px 12px;
  border-radius: 6px;
  cursor: pointer;
  transition: background 120ms, color 120ms, border-color 120ms;
}

.auth-action:hover,
.auth-action:focus-visible {
  background: var(--bg-2);
  color: var(--ink);
  outline: none;
}

.auth-action.primary {
  flex: 1 1 auto;
  background: var(--accent-soft);
  border-color: rgba(160, 88, 38, 0.4);
  color: var(--accent-strong);
  text-align: center;
}

.auth-action.primary:hover {
  background: rgba(160, 88, 38, 0.22);
}

/* Settings view (separate drawer page; hosts auth + voice config) */

.settings-view {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  padding: 0;
  scrollbar-width: thin;
  scrollbar-color: var(--bg-3) transparent;
}

.settings-view::-webkit-scrollbar { width: 6px; }
.settings-view::-webkit-scrollbar-track { background: transparent; }
.settings-view::-webkit-scrollbar-thumb {
  background: var(--bg-3);
  border-radius: 3px;
}
.settings-view::-webkit-scrollbar-thumb:hover { background: var(--ink-muted); }

.voice-config-vocab {
  scrollbar-width: thin;
  scrollbar-color: var(--bg-3) transparent;
}

.voice-config-vocab::-webkit-scrollbar { width: 6px; }
.voice-config-vocab::-webkit-scrollbar-track { background: transparent; }
.voice-config-vocab::-webkit-scrollbar-thumb {
  background: var(--bg-3);
  border-radius: 3px;
}

.settings-view[hidden] { display: none; }

.settings-section {
  padding: 12px 12px 14px;
  border-bottom: 1px solid var(--line);
}

.settings-section[hidden] { display: none; }

.settings-section-title {
  margin: 0;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-muted);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  user-select: none;
  display: flex;
  align-items: center;
  gap: 6px;
  list-style: none;
}

.settings-section-title::-webkit-details-marker { display: none; }

.settings-section-title::before {
  content: "▸";
  font-size: 10px;
  color: var(--ink-muted);
  transition: transform 120ms ease;
  display: inline-block;
}

.settings-section[open] > .settings-section-title::before {
  transform: rotate(90deg);
}

.settings-section[open] .voice-config-form {
  margin-top: 10px;
}
/* 全部节都折叠得起来（v2.3.10，user 2026-09-30「输入法等其他节能不能也折叠防误触」）：只有「账号」默认开；展开后标题和内容之间留一口气 */
.settings-section[open] > .settings-section-title + * { margin-top: 10px; }

.voice-config-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.voice-config-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  user-select: none;
  padding: 2px 0;
}

.voice-config-toggle input {
  margin: 0;
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
  cursor: pointer;
}

.voice-config-backend {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--line);
}

.voice-config-backend[hidden] { display: none; }

.voice-config-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.voice-config-field[hidden] { display: none; }

.voice-config-label {
  font-size: 11px;
  color: var(--ink-muted);
  letter-spacing: 0.02em;
}

.voice-config-select,
.voice-config-key,
.voice-config-vocab {
  border: 1px solid var(--line-strong);
  background: var(--bg-1);
  color: var(--ink);
  font-family: inherit;
  font-size: 12px;
  padding: 6px 8px;
  border-radius: 6px;
  outline: none;
  width: 100%;
}

.voice-config-key,
.voice-config-vocab {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.voice-config-vocab {
  resize: vertical;
  min-height: 36px;
  line-height: 1.4;
}

.voice-config-select:focus,
.voice-config-key:focus,
.voice-config-vocab:focus {
  border-color: var(--accent);
}

.voice-config-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.voice-config-save {
  flex: 0 0 auto;
  background: var(--accent-soft);
  border: 1px solid rgba(160, 88, 38, 0.4);
  color: var(--accent-strong);
  font-family: inherit;
  font-size: 12px;
  padding: 5px 14px;
  border-radius: 6px;
  cursor: pointer;
}

.voice-config-save:hover { background: rgba(160, 88, 38, 0.22); }
.voice-config-save:disabled { opacity: 0.5; cursor: default; }

.voice-config-hint {
  flex: 1 1 100%;
  margin: 0;
  font-size: 10px;
  color: var(--ink-muted);
  line-height: 1.4;
}

/* Reading mode — segmented picker. Same chrome as voice section but
   non-collapsible since it's just two options. */

.settings-section.is-flat {
  padding-top: 14px;
}

.settings-section.is-flat > .settings-section-title {
  cursor: default;
  margin-bottom: 8px;
}

.settings-section.is-flat > .settings-section-title::before {
  content: none;
}

.reading-mode-picker {
  display: flex;
  gap: 6px;
}

.reading-mode-option {
  flex: 1 1 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--bg-1);
  color: var(--ink-soft);
  font-family: inherit;
  font-size: 12px;
  cursor: pointer;
  user-select: none;
  transition: background 120ms ease, border-color 120ms ease;
}

.reading-mode-option input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.reading-mode-option:hover {
  background: var(--bg-2);
}

.reading-mode-option.is-selected {
  background: var(--accent-soft);
  border-color: rgba(160, 88, 38, 0.55);
  color: var(--accent-strong);
  font-weight: 600;
}

.reading-mode-hint {
  margin: 8px 0 0;
  font-size: 10px;
  color: var(--ink-muted);
  line-height: 1.4;
}

.drawer-actions {
  display: flex;
  gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
}

.drawer-actions.hidden { display: none; }

.drawer-action {
  flex: 1 1 auto;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
  padding: 7px 12px;
  border-radius: 7px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  transition: background 120ms, border-color 120ms;
}

.drawer-action:hover,
.drawer-action:focus-visible {
  background: var(--bg-2);
  border-color: var(--line-strong);
  outline: none;
}

.drawer-action.danger {
  color: var(--danger);
  border-color: rgba(248, 113, 113, 0.3);
}

.drawer-action.danger:hover { background: var(--danger-soft); }

.doc-list {
  list-style: none;
  margin: 0;
  padding: 4px 6px 12px;
  overflow-y: auto;
  flex: 1;
  scrollbar-width: thin;
  scrollbar-color: var(--bg-3) transparent;
}

.doc-list::-webkit-scrollbar { width: 6px; }
.doc-list::-webkit-scrollbar-thumb {
  background: var(--bg-3);
  border-radius: 3px;
}

.doc-list-empty {
  padding: 28px 16px;
  text-align: center;
  color: var(--ink-muted);
  font-size: 12px;
}

.doc-list-empty.hidden { display: none; }

.doc-row {
  display: flex;
  align-items: stretch;
  gap: 2px;
  border-radius: 7px;
  margin: 1px 0;
  position: relative;
}

.doc-row:hover { background: var(--bg-2); }
.doc-row.active { background: var(--accent-soft); }

.doc-main {
  flex: 1;
  min-width: 0;
  text-align: left;
  background: transparent;
  border: 0;
  color: var(--ink);
  font-family: inherit;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  cursor: pointer;
  border-radius: 6px;
}

.doc-main:disabled { cursor: default; }

.doc-main:focus-visible {
  outline: 1px solid var(--line-strong);
  outline-offset: -1px;
}

.doc-name {
  font-size: 13px;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}


.doc-preview {
  font-size: 11px;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.doc-row-actions {
  display: flex;
  align-items: center;
  gap: 1px;
  padding-right: 4px;
}

.row-icon-button {
  width: 28px;
  height: 28px;
  border: 0;
  background: transparent;
  color: var(--ink-muted);
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 120ms, color 120ms, background 120ms;
}

.doc-row:hover .row-icon-button,
.doc-row:focus-within .row-icon-button {
  opacity: 1;
}

.row-icon-button:hover,
.row-icon-button:focus-visible {
  background: var(--bg-3);
  color: var(--ink);
  outline: none;
  opacity: 1;
}

.row-icon-button.danger:hover,
.row-icon-button.danger:focus-visible {
  background: var(--danger-soft);
  color: var(--danger);
}

.drawer-foot {
  border-top: 1px solid var(--line);
  padding: 6px 8px;
  display: flex;
  gap: 4px;
}

.drawer-foot-button {
  flex: 1 1 0;
  border: 0;
  background: transparent;
  color: var(--ink-muted);
  font-family: inherit;
  font-size: 12px;
  padding: 7px 10px;
  border-radius: 7px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  text-align: center;
  transition: background 120ms, color 120ms;
}

.drawer-foot-button:hover,
.drawer-foot-button:focus-visible {
  background: var(--bg-2);
  color: var(--ink);
  outline: none;
}

/* Idle overlay — half-locked state after N min of no input */

.idle-overlay {
  position: fixed;
  inset: 0;
  background: rgba(45, 27, 15, 0.5);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  z-index: var(--z-idle);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  animation: idle-fade-in 200ms ease-out;
}

.idle-overlay.hidden { display: none; }

.idle-card {
  background: rgba(241, 230, 207, 0.96);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  padding: 18px 24px;
  text-align: center;
  box-shadow: 0 16px 40px rgba(58, 40, 24, 0.3);
  max-width: 80vw;
}

.idle-title {
  margin: 0;
  font-size: 14px;
  color: var(--ink-soft);
  letter-spacing: 0.04em;
}

.idle-hint {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--ink-muted);
}

@keyframes idle-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Busy overlay — same chrome as .idle-overlay but used during synchronous
   crypto work (encrypt / decrypt / setup / unlock). Click-through is
   disabled so the user can't fire follow-up actions mid-work. */

.busy-overlay {
  cursor: default;
  z-index: var(--z-busy);
}

/* New-version toast (SW signaled an asset update) */

.update-toast {
  position: fixed;
  right: 12px;
  bottom: 12px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px 6px 14px;
  background: rgba(74, 53, 32, 0.94);
  color: #f1e6cf;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 24px;
  box-shadow: 0 10px 24px rgba(58, 40, 24, 0.32);
  font-size: 12px;
  z-index: var(--z-toast);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  animation: toast-in 220ms ease-out;
}

.update-toast.hidden { display: none; }

.update-toast .update-text {
  letter-spacing: 0.04em;
}

.update-reload {
  background: var(--accent);
  color: #faf2e2;
  border: 0;
  border-radius: 14px;
  padding: 4px 12px;
  cursor: pointer;
  font-size: 12px;
  font-family: inherit;
  transition: background 120ms;
}

.update-reload:hover,
.update-reload:focus-visible {
  background: var(--accent-strong);
  outline: none;
}

.update-dismiss {
  background: transparent;
  color: rgba(241, 230, 207, 0.6);
  border: 0;
  border-radius: 12px;
  width: 22px;
  height: 22px;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 0;
}

.update-dismiss:hover,
.update-dismiss:focus-visible {
  color: #f1e6cf;
  outline: none;
}

@keyframes toast-in {
  from { transform: translateY(10px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

@media (max-width: 560px) {
  .status { font-size: 10px; gap: 4px; }
  .doc-name { font-size: 11px; }
}

@media (hover: none) {
  .row-icon-button { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .drawer { transition: none; }
}

/* Crypto modal — centred card for setup / unlock / decrypt-confirm */

.crypto-modal {
  position: fixed;
  inset: 0;
  background: rgba(45, 27, 15, 0.5);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}

.crypto-modal.hidden { display: none; }

.crypto-card {
  background: var(--bg-1);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  padding: 20px;
  width: min(360px, 100%);
  box-shadow: 0 16px 40px rgba(58, 40, 24, 0.28);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.crypto-title {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

.crypto-hint {
  margin: 0;
  font-size: 12px;
  color: var(--ink-muted);
  line-height: 1.5;
}

.crypto-hint.warning {
  color: var(--danger);
}

.crypto-input {
  width: 100%;
  border: 1px solid var(--line-strong);
  background: var(--bg-1);
  color: var(--ink);
  font-family: inherit;
  font-size: 14px;
  padding: 8px 10px;
  border-radius: 7px;
  outline: none;
}

.crypto-input:focus {
  border-color: var(--accent);
}

/* sheets.ts 用 class="hidden" 切换各部件（2026-09-03 修：之前只有 [hidden] 属性规则，导致「强制更新？」确认框也露出两个密码输入框） */
.crypto-input.hidden, .crypto-hint.hidden, .crypto-error.hidden, .sheet-choices.hidden { display: none; }
.crypto-input[hidden] {
  display: none;
}

.crypto-error {
  margin: 0;
  font-size: 12px;
  color: var(--danger);
  min-height: 16px;
}

.crypto-error.hidden {
  display: none;
}

.crypto-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 4px;
}

.crypto-button {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid var(--line-strong);
  color: var(--ink-soft);
  font-family: inherit;
  font-size: 13px;
  padding: 7px 16px;
  border-radius: 7px;
  cursor: pointer;
  transition: background 120ms, color 120ms, border-color 120ms;
}

.crypto-button:hover,
.crypto-button:focus-visible {
  background: var(--bg-2);
  color: var(--ink);
  outline: none;
}

.crypto-button.primary {
  background: var(--accent-soft);
  border-color: rgba(160, 88, 38, 0.4);
  color: var(--accent-strong);
}

.crypto-button.primary:hover {
  background: rgba(160, 88, 38, 0.22);
}

.crypto-button.danger {
  color: var(--danger);
  border-color: rgba(157, 46, 33, 0.4);
}

.crypto-button.danger:hover-dead-rule {   /* 旧淡粉 hover 让白字不可读（审计 UI-9）；下面的 .danger 规则统一管 */
  background: var(--danger-soft);
}

/* ── v2 追加（2026-09-03，Claude Fable 5.1）：共享 sprite 图标 / 通用 sheet / sync gate / 错误横幅 / 设置页零件 ── */
.ico { width: 18px; height: 18px; display: inline-block; vertical-align: middle; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.menu-button .ico, .icon-button .ico { width: 18px; height: 18px; }
.crypto-toggle .ico, .lock-toggle .ico, .doc-crypto-prefix .ico { width: 14px; height: 14px; }
.drawer-action .ico, .drawer-foot-button .ico { width: 14px; height: 14px; margin-right: 6px; }
.row-icon-button .ico { width: 15px; height: 15px; }
.mic-button .ico { width: 22px; height: 22px; }
.update-dismiss .ico { width: 14px; height: 14px; }
.doc-main.static { cursor: default; }
.sheet-choices { display: flex; flex-direction: column; gap: 8px; margin: 12px 0 4px; }
.sheet-choices.hidden { display: none; }
/* pick sheet 的自绘列表（sheets.ts openPickSheet；user 2026-09-10「弹一个对话框，搜索，下拉，选中」「不用原生 select」）：封顶后内滚（Quest 竖向空间紧） */
.sheet-pick { list-style: none; margin: 0; padding: 0; max-height: min(40vh, 264px); overflow-y: auto; border: 1px solid var(--line-strong, #c9b89a); border-radius: 10px; background: var(--bg-1); }
.sheet-pick.hidden { display: none; }
.sheet-pick li { display: block; }
.sheet-pick li.empty { padding: 9px 12px; color: var(--ink-muted); font-size: 12px; }
.sheet-pick li > button { display: flex; align-items: center; gap: 8px; width: 100%; padding: 9px 12px; border: 0; border-bottom: 1px solid var(--line); background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.sheet-pick li:last-child > button { border-bottom: 0; }
.sheet-pick li > button .ico { width: 16px; height: 16px; flex: 0 0 auto; color: var(--ink-muted); }
.sheet-pick li > button span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sheet-pick li[aria-selected="true"] > button { background: var(--paper, #f1e6cf); font-weight: 600; }
.sheet-choice { padding: 10px 14px; border-radius: 10px; border: 1px solid var(--line-strong, #c9b89a); background: var(--paper, #f1e6cf); color: inherit; font: inherit; text-align: left; cursor: pointer; }
.sheet-choice.primary { background: var(--accent, #a05826); color: #fff; border-color: transparent; }
.sheet-choice.danger { color: var(--danger, #b0413e); }
/* 「范围 + 动作」sheet（sheets.ts openScopedChoiceSheet；首用 = 导出）：一条段选定范围、一行小字报这个范围有多少、一排动作钮 */
.sheet-seg { display: flex; border: 1px solid var(--line-strong, #c9b89a); border-radius: 10px; overflow: hidden; }
.sheet-seg-btn { flex: 1 1 0; min-width: 0; padding: 9px 6px; border: 0; border-left: 1px solid var(--line-strong, #c9b89a); background: transparent; color: inherit; font: inherit; cursor: pointer; }
.sheet-seg-btn:first-child { border-left: 0; }
.sheet-seg-btn[aria-checked="true"] { background: var(--ink, #1b1b1b); color: var(--paper, #f1e6cf); }
.sheet-seg-line { display: flex; align-items: center; gap: 10px; }
.sheet-seg-line .sheet-seg { flex: 1 1 auto; }
.sheet-seg-line .sheet-seg-btn { padding: 6px 6px; line-height: 1.5; }   /* 数字和汉字的行盒不一样高：钉住行高，几条段选才一样高 */
.sheet-seg-label { flex: none; font-size: 13px; color: var(--ink-muted); }
.sheet-aux { align-self: flex-start; padding: 5px 10px; border: 1px dashed var(--line-strong, #c9b89a); border-radius: 8px; background: transparent; color: var(--ink-soft); font: inherit; font-size: 13px; cursor: pointer; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sheet-aux.hidden { display: none; }
.sheet-check-inline { margin: 0; font-size: 13px; color: var(--ink-soft); }
@media (max-height: 460px) {   /* 手机横屏：导出面板行多，收紧一点（放不下时整个遮罩可以滚） */
  .sheet-choices { gap: 5px; margin: 6px 0 2px; }
  .sheet-seg-btn, .sheet-seg-line .sheet-seg-btn { padding: 4px 6px; }
  .sheet-choice-row .sheet-choice { padding: 7px 6px; }
}
.sheet-seg-note { margin: 2px 0; font-size: 13px; line-height: 1.5; color: var(--ink-muted); white-space: pre-line; }
.sheet-choice-row { display: flex; gap: 8px; }
.sheet-choice-row .sheet-choice { flex: 1 1 0; min-width: 0; text-align: center; padding: 10px 6px; }
.crypto-button.danger { background: var(--danger, #b0413e); color: #fff; border-color: transparent; }
.crypto-button.danger:hover { background: var(--accent-strong, #8f2f2c); color: #fff; }
.crypto-button.hidden { display: none; }   /* choice sheet 不该有「确定」（审计 UI-5） */
.crypto-hint.warning { color: var(--danger, #b0413e); }
.gate-sheet { z-index: var(--z-gate); }
.gate-card { max-width: 420px; }
.gate-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 14px; }
.gate-actions button { padding: 8px 14px; border-radius: 10px; border: 1px solid var(--line-strong, #c9b89a); background: var(--paper, #f1e6cf); color: inherit; font: inherit; cursor: pointer; }
.gate-actions button.primary { background: var(--accent, #a05826); color: #fff; border-color: transparent; }
.gate-note { margin-top: 10px; font-size: 12px; opacity: .75; }
.gate-note.hidden, .gate-spinner.hidden { display: none; }
.gate-spinner { width: 22px; height: 22px; margin: 12px auto 0; border: 2px solid rgba(0,0,0,.15); border-top-color: var(--accent, #a05826); border-radius: 50%; animation: xhw-spin .9s linear infinite; }
@keyframes xhw-spin { to { transform: rotate(360deg); } }
.err-banner { position: fixed; left: 50%; transform: translateX(-50%); bottom: max(env(safe-area-inset-bottom, 0px), 12px); z-index: var(--z-error); width: calc(100% - 24px); max-width: 640px; box-sizing: border-box; padding: 10px 14px; border-radius: 10px; box-shadow: 0 4px 16px rgba(0,0,0,.35); background: #c0392b; color: #fff; font: 13px/1.4 system-ui, sans-serif; white-space: pre-wrap; word-break: break-word; max-height: 50vh; overflow: auto; cursor: pointer; }
.err-banner.warning { background: #b7791f; }
.err-banner.hidden { display: none; }
.settings-row { display: flex; flex-wrap: wrap; gap: 8px; }
.diag-log { font: 11px/1.4 ui-monospace, monospace; white-space: pre-wrap; word-break: break-all; max-height: 40vh; overflow: auto; background: rgba(0,0,0,.05); padding: 8px; border-radius: 8px; margin: 8px 0 0; }
#langSelect { max-width: 200px; }


/* 语音包（离线模型）行：状态 + 进度 + 三个动作钮 */
.voice-pack-row { display: flex; flex-direction: column; gap: 8px; }
.voice-pack-progress { width: 100%; height: 8px; accent-color: var(--accent, #6aa8ff); }
.voice-pack-progress[hidden] { display: none; }
.voice-config-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.voice-config-save[hidden] { display: none; }
.voice-config-save.is-danger { border-color: rgba(255, 96, 96, 0.5); color: #ff8a8a; }
.voice-config-hint { font-size: 0.85em; opacity: 0.8; line-height: 1.4; }

/* 「这篇用的不是当前密码」横幅（复用 ghost-banner 视觉；[hidden] 必须真隐藏） */
.key-banner[hidden] { display: none; }
.key-banner { justify-content: space-between; }

/* ── 审计修补（2026-09-03）── */
.crypto-toggle[hidden], .lock-toggle[hidden] { display: none !important; }   /* 作者层 display 压掉了 UA 的 [hidden]（审计 UI-2） */
.key-banner { position: fixed; top: calc(var(--sheet-top) + 4px); left: 50%; transform: translateX(-50%); width: min(100% - 16px, var(--box-max, 720px)); z-index: var(--z-page); margin: 0; box-sizing: border-box; }   /* 不挤走 .page（审计 UI-7） */
.status { min-width: 0; overflow: hidden; text-overflow: ellipsis; }   /* 长状态别把标题挤没（审计 UI-11） */
.voice-config-save.is-danger { color: var(--danger, #b0413e); }   /* 暗色遗留的 #ff8a8a 在淡底上看不见（审计 UI-17） */
.drawer.hidden { visibility: hidden; transition: transform 0.25s ease, visibility 0s linear 0.25s; }   /* 屏外按钮不可 Tab 到（审计 UI-18） */
.drawer-status { margin: 6px 12px 0; font-size: 12px; opacity: 0.85; }
.drawer-status.error { color: var(--danger, #b0413e); opacity: 1; }
.drawer-status[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .mic-button[data-state="recording"] .mic-pulse { animation: none; } .mic-button[data-state="recording"] { transform: none; } }

/* ── 多文件夹（ADR-0006）── */
.doc-breadcrumb { display: flex; align-items: center; gap: 6px; padding: 6px 14px 2px; font-size: 13px; opacity: 0.9; }
.doc-breadcrumb[hidden] { display: none; }
.crumb-link { background: none; border: 0; padding: 0; font: inherit; color: var(--accent, #6aa8ff); cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.crumb-sep { opacity: 0.5; }
.crumb-current { display: inline-flex; align-items: center; gap: 4px; font-weight: 600; }
.folder-row .doc-name { font-weight: 600; }
.drawer-action[hidden] { display: none; }

/* ── 对齐 WeebPaint（2026-09-03）：紧凑单行 + 云状态 badge + ⋯ 弹出菜单 + 抽屉头云钮 ── */
.doc-main { flex-direction: row; align-items: center; gap: 8px; padding: 6px 10px; min-height: 34px; }
.doc-enc { display: inline-flex; flex: none; color: var(--enc); }
.doc-enc svg, .doc-enc .ico { width: 13px; height: 13px; }
.doc-folder-ic { color: var(--ink-soft); }
.doc-badge { margin-left: auto; display: inline-flex; flex: none; color: var(--ink-soft); }
.doc-badge svg { width: 12px; height: 12px; display: block; }
.doc-badge.b-conflict { color: var(--warn); }
.doc-badge.b-newer { color: var(--accent); }
.doc-more { align-self: center; }
.doc-more .ico { width: 16px; height: 16px; }
.doc-row .row-icon-button { opacity: 0.55; }
.doc-row:hover .row-icon-button, .doc-row:focus-within .row-icon-button { opacity: 1; }
.menu-panel { background: var(--bg); border: 1px solid var(--line-strong); border-radius: 10px; box-shadow: var(--shadow); padding: 6px; min-width: 180px; max-width: min(92vw, 320px); }
.popup-menu { z-index: var(--z-menu); }   /* 抽屉 11 之上、sheet 40 之下 */
.menu-item { width: 100%; display: flex; align-items: center; gap: 10px; padding: 9px 12px; background: transparent; border: 0; border-radius: 6px; font: inherit; font-size: 14px; color: var(--ink); cursor: pointer; text-align: left; }
.menu-item:hover { background: color-mix(in srgb, var(--ink) 8%, transparent); }
.menu-item:disabled { opacity: 0.6; cursor: default; }
.menu-item:disabled:hover { background: transparent; }
.menu-item.danger { color: var(--danger); }
.menu-item svg { width: 18px; height: 18px; flex: none; }
.menu-item-icon-blank { width: 18px; height: 18px; flex: none; }
.menu-item-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.popup-menu-sep { border: 0; border-top: 1px solid var(--line); margin: 4px 0; }
.cloud-btn[data-cloud-state="signedin"] { color: var(--info); }
.cloud-btn[data-cloud-state="offline"] { color: var(--quiet); }
.cloud-btn[data-cloud-state="out"] { color: var(--ink-soft); opacity: 0.7; }

/* ── zen 顶栏 + 写字线（2026-09-04 user「帮我 zen mode 一点」「显示一条条的写字线」）：字数撤、干净态状态留白、IME 只剩「中/英」一字。
   标题框 2026-09-04 撤（ADR-0007：文件名是管理句柄不是标题，回顶栏 .doc-name；编辑器里没有标题） ── */
.top-spacer { flex: 1 1 0; min-width: 0; }
/* --editor-font 定义在 body（见 :root 里的行宽注释）：字号 = 设备基准 × 字号档，和行宽无关 */
.body { font-size: var(--editor-font); }
/* 章节名框（v0.2.15 的 #titleInput 捞回来当节点名；user 2026-09-10）：只在工程模式显示；正文字号 ×1.25、居中、无边框 */
/* 章节名行 = ⟨ 章节名 ⟩：上一页 / 下一页 chevron 住章节名两侧，不再占页脚（user 2026-09-10「上一页和下一页可以放在标题行，用 ⟨ ⟩ 的 svg…quest 屏幕 vertical space is scarcity」） */
.node-title-row { display: none; flex: none; align-items: center; gap: 4px; margin: 0 0 calc(var(--editor-font) * 0.5); }
body[data-project] .node-title-row { display: flex; }
/* 章节名框 = 单行起步的 textarea，随内容长高（user 2026-09-30「不过对于标题栏怎么办。也自动加行？」）：Enter 仍 = 提交（text-field 拦下），不出现换行 */
.node-title { flex: 1 1 auto; min-width: 0; width: auto; box-sizing: border-box; border: 0; background: transparent; outline: none; text-align: center; line-height: 1.4; resize: none; overflow: hidden; display: block; white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--font-editor); font-size: calc(var(--editor-font) * 1.25); font-weight: 600; color: var(--ink); padding: 0; margin: 0; caret-color: var(--accent); }
.node-title::placeholder { color: var(--ink-muted); font-weight: 400; opacity: 0.7; }
/* 页脚（v2.1.26）：‹ 上一页 · 字数 · 下一页 ›，印在纸的末尾（user 2026-09-30「同意也加导航，和页头一样」；字数统计 = user 2026-09-10「页脚可以开一个字数统计，xx 字 xx 词，可设置里面 toggle 关」）。
   margin-top: auto = 短稿时页脚也在纸的底端；三件都藏着时整行不占地。 */
.page-foot-row { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 4px; margin-top: auto; padding-top: calc(var(--editor-font) * 0.5); }
.page-foot-row:not(:has(> :not([hidden]))) { display: none; }
.page-foot { flex: 1 1 auto; min-width: 0; text-align: center; font-family: var(--font-ui); font-size: 12px; line-height: 32px; color: var(--ink-muted); font-variant-numeric: tabular-nums; user-select: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.page-foot[hidden] { display: none; }
/* 图片页（2.1，ADR-0012/0013）：textarea 让位给 img；contain 不放大（user「如果超框就缩小 to fit…小图不要放大，不然 jumpscale」）；点击切 1:1（滚动看细节）。 */
body[data-page-kind="image"] #editor, body[data-page-kind="image"] .page-foot, body[data-page-kind="image"] .candidates { display: none; }
body[data-page-kind="image"] .page { height: var(--sheet-h); }   /* 图片页没有正文可长：纸就是一屏，图 contain 进去 */
body[data-page-kind="image"] .page-body { flex: none; }
.page-image { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; overflow: auto; }
.page-image[hidden] { display: none; }
.page-image img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; display: block; cursor: zoom-in; flex: 0 1 auto; min-height: 0; }
.page-image.natural { justify-content: flex-start; align-items: flex-start; }
.page-image.natural img { max-width: none; max-height: none; cursor: zoom-out; }
.page-image-bar { flex: none; display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--ink-soft); }
.page-image-meta { font-variant-numeric: tabular-nums; }
.edge-kind { flex: none; width: 14px; height: 14px; color: var(--ink-soft); }
.sheet-check { display: flex; align-items: center; gap: 8px; margin: 10px 0 2px; font-size: 14px; cursor: pointer; }
.sheet-check.hidden { display: none; }
.node-title.locked { color: var(--ink-muted); }
.node-title::selection { background: rgba(160, 88, 38, 0.22); }
#saveStatus:empty + .sep { display: none; }



/* 写字线：样式在文件末尾「稿纸几何」一节（v2.1.17；行高 / 线位由 src/ui/paper.ts 量了写） */
/* 列表行：名字左缘对齐（锁挪到云图标左边；有夹时正文行补一个夹图标宽的留白——user 2026-09-04「锁放在云图标左边…看起来对齐」） */
.doc-main .doc-name { flex: 1 1 auto; min-width: 0; }
.doc-badge { margin-left: 0; }
.doc-lead { width: 13px; flex: none; }
.toast {
  position: fixed; left: 50%; bottom: calc(var(--sheet-bottom) + 14px); transform: translateX(-50%); z-index: var(--z-toast);   /* 图库屏（8）之上：图库里的状态提示也得看见 */
  max-width: min(86%, 560px); padding: 6px 12px; border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 86%, transparent); color: var(--bg);
  font-family: var(--font-ui); font-size: 12px; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  box-shadow: var(--shadow); pointer-events: none; opacity: 0; visibility: hidden; transition: opacity 180ms ease, visibility 0s linear 180ms;
}
.toast.show { opacity: 1; visibility: visible; transition: opacity 120ms ease; }
.toast.error { background: var(--danger); color: #fff; }
body:has(.candidates:not(.hidden)) .toast { bottom: calc(var(--sheet-bottom) + 64px); }   /* 候选条已不在 .page 里：兄弟选择器换成 body:has */
/* 语音模式退格钮（话筒左邻，只在纯口述时出现）。Quest「键盘不在本页」提示条 + 压暗 2026-09-04 撤（user：光标失踪本身看得见，没有煤气灯疑惑）；焦点找回逻辑留在 app.ts */
.mic-side { right: calc(max(0px, 50% - var(--box-max) / 2) + 70px); width: 40px; height: 40px; bottom: calc(var(--sheet-bottom) + 18px); }
.mic-side .ico { width: 18px; height: 18px; }
/* 话筒悬浮在纸面右下、不占版面；页脚字数统计露着时往上让一让（user 2026-09-10「麦克风也是悬浮的，如果和字数统计挡了可以稍微往上挪一点」）。#wordCount 在 DOM 里排在话筒前面，兄弟选择器够用 */
/* ── 纸面亲缘（v2.1.9，user 2026-09-26「父亲页面拉到最下面可以显示孩子页面的目录列表，然后标题栏也有回到上一级的链接，这样导航就舒服」）──
   回上一级：章节名上方一行 `.. 父页名`（UI 小字；与侧栏 `..` 行同语汇）；顶层页 hidden（顶栏已是书名）。 */
.parent-link { display: none; align-self: center; align-items: center; gap: 6px; max-width: 100%; margin: 0 0 4px; padding: 2px 10px; border: 0; border-radius: 8px; background: transparent; color: var(--ink-soft); font-family: var(--font-ui); font-size: 12px; line-height: 1.4; cursor: pointer; }
body[data-project] .parent-link:not([hidden]) { display: inline-flex; }
.parent-link .edge-dots { flex: none; letter-spacing: 1px; }
.parent-link-name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.parent-link:hover, .parent-link:focus-visible { background: color-mix(in srgb, var(--ink) 8%, transparent); color: var(--ink); outline: none; }
/* 子节目录：正文之下、页脚之上，有子节就露（v2.1.26 起纸整张滚，「滚到底才露」的迟滞退役）。 */
.page-body { flex: 1 1 auto; display: flex; flex-direction: column; }   /* 正文 + 目录的容器：正文框 = 内容高度，目录紧跟其后（user 2026-09-26「取决于父节点输入了多少行…相当于嵌入了」）；剩下的纸面空白也画线（点了照样能写） */

/* 子节目录：样式在文件末尾「稿纸几何」一节（v2.1.17） */
/* 锁卡（0.2 护栏，2026-09-04）：锁着 / 不可用的稿不再假装是编辑器 */
.lock-card { position: fixed; top: var(--sheet-top); bottom: var(--sheet-bottom); left: max(0px, 50% - var(--box-max) / 2); right: max(0px, 50% - var(--box-max) / 2); z-index: var(--z-page); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 24px; text-align: center; background: var(--bg-1); border-radius: 12px; }
.lock-card[hidden] { display: none; }
.lock-card-ico { width: 28px; height: 28px; color: var(--enc); }
.lock-card-text { margin: 0; font-family: var(--font-ui); font-size: 14px; color: var(--ink-soft); max-width: 36em; }
.lock-card-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }

/* ── 贴边件的地板（移植 WeebPaint ADR-0010「浮窗拖把地板从顶栏派生，不手填常数」；user 2026-09-04「weebpaint 内耗了三个月刚解决的 ios 出血线…也修」）──
   iPhone/iPad standalone：顶部状态栏 + 系统死区（iOS 18+ 横屏顶边、iPadOS 26 窗口控制钮）没有任何 web API 能查；官方约定只有
   safe-area（viewport-fit=cover + env()）。所以地板 = max(env(safe-area-inset-top), 20px 社区缓冲)，只在 standalone/fullscreen 生效
   （浏览器标签页自己有 chrome，没有死区）；底部同理 max(env(bottom), 原值)。全仓唯一出处 = 这四个 token，禁再写贴边常数。
   :root[data-standalone] 由 app 按 display-mode / navigator.standalone 打（媒体查询没法在无头测试里模拟，attribute 可测）。 */
:root { --top-floor: 4px; --bottom-floor: 14px; --sa-left: 0px; --sa-right: 0px; }
:root[data-standalone] { --top-floor: max(env(safe-area-inset-top, 0px), 20px); --bottom-floor: max(env(safe-area-inset-bottom, 0px), 14px); --sa-left: env(safe-area-inset-left, 0px); --sa-right: env(safe-area-inset-right, 0px); }

/* 纸的上下留白 = .surface 的 padding（--sheet-top / --sheet-bottom，见文件头「一张纸模型」） */
.drawer { box-sizing: border-box; padding-top: calc(var(--top-floor) - 4px); padding-bottom: calc(var(--bottom-floor) - 14px); padding-right: var(--sa-right); }
.update-toast { bottom: calc(var(--bottom-floor) - 2px); right: calc(12px + var(--sa-right)); }

/* ── 双击放大 / 长按菜单（移植 WeebPaint 20260530 iPad 双击误触四层防御的兜底层；user 2026-09-04「触屏会不小心双击放大」）──
   通配而不是逐元素：动态生成的 DOM（列表行、弹出菜单、候选条）逐个标注必漏。manipulation = 允许 pan/pinch、禁双击 zoom；
   配 viewport user-scalable=no。编辑器里长按选字/复制菜单保留（只压 chrome 上的 callout）。 */
* { touch-action: manipulation; }
html, body { -webkit-tap-highlight-color: transparent; }
button, .top-bar, .drawer, .menu-panel, .mic-button, .lock-card, .candidates { -webkit-touch-callout: none; }

/* ── smart save 钮（顶栏，触屏的 Ctrl+S；状态色同抽屉 badge）── */
.save-btn.s-clean { color: var(--info); }
.save-btn.s-unsynced { color: var(--accent); }
.save-btn.s-local, .save-btn.s-offline { color: var(--quiet); }
.save-btn.s-pending { color: var(--warn); }
.save-btn.flash { color: var(--ok); }
/* ── iOS 软键盘：visualViewport → --kb-offset，进 --sheet-bottom（.surface 底部让位；光标 / 候选 / 话筒都在可见区，user 2026-09-04「屏幕没跟上，被键盘遮挡，候选也看不到」）── */


/* ═══ 2.0 侧栏（☰ 唯一入口；user 2026-09-10「只有一个三条杠」「侧栏不应默认开」「侧栏不平衡，放在右边试试看」）：顶部 书库/设置 两入口 + 工程内导航（当前节点的出边，ADR-0008）。
   开关 = body[data-edges]（默认 0）。永远是纸面之上的一层浮层、不推纸面（user 2026-09-10「windows 上面弹侧栏的时候写作区不应该位移，而是侧栏在上面一个图层里」）；窄屏（≤900px）加遮罩。零全图、零计数。 ═══ */
.edge-sidebar {
  position: fixed; top: 52px; bottom: 12px; right: 12px; width: 240px;
  display: none; flex-direction: column; gap: 8px; padding: 10px;
  border: 1px solid var(--line-strong); border-radius: 12px; background: var(--bg-1); color: var(--ink);
  font-family: var(--font-ui); font-size: 13px; z-index: var(--z-chrome); box-sizing: border-box; box-shadow: var(--shadow);
}
body[data-edges="1"] .edge-sidebar { display: flex; }
.edge-backdrop { display: none; position: fixed; inset: 0; z-index: calc(var(--z-chrome) - 1); background: rgba(58, 40, 24, 0.22); }   /* 顶栏（5）之下：☰ 仍可点 */
.edge-entries { display: flex; gap: 6px; }
/* 四个入口只留图标（user 2026-09-30「也许就 icon 就够了」：四个带字挤成两行）；文字留给读屏（.edge-entry span 视觉隐藏）+ title 悬停 */
.edge-entry { flex: 1 1 0; min-width: 0; display: inline-flex; align-items: center; justify-content: center; gap: 0; padding: 9px 6px; border: 1px solid var(--line); border-radius: 8px; background: var(--bg-0); color: var(--ink); font: inherit; cursor: pointer; }
.edge-entry:hover, .edge-entry:focus-visible { background: color-mix(in srgb, var(--ink) 8%, var(--bg-0)); }
.edge-entry .ico { width: 18px; height: 18px; flex: none; }
.edge-entry span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.edge-entry[hidden] { display: none; }
/* 孤儿页入口（2026-09-30）：导航底部一行小字钮，有孤儿才露 */
.edge-orphans { align-self: flex-start; margin: 2px 0 0; padding: 4px 8px; border: 0; border-radius: 6px; background: transparent; color: var(--ink-soft); font: inherit; font-size: 12px; cursor: pointer; }
.edge-orphans:hover, .edge-orphans:focus-visible { background: color-mix(in srgb, var(--ink) 8%, var(--bg-0)); color: var(--ink); }
.edge-orphans[hidden] { display: none; }   /* 参考窗入口在 txt 稿上藏（display 规则会盖过 hidden 属性，得点名） */
.edge-txt { display: flex; }
.edge-txt[hidden] { display: none; }
.edge-pane { display: flex; flex-direction: column; gap: 8px; min-height: 0; flex: 1 1 auto; }
.edge-pane[hidden] { display: none; }
.edge-head { display: flex; align-items: center; gap: 6px; }
.edge-titles { min-width: 0; flex: 1; }
.edge-node { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.edge-project { color: var(--ink-soft); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.edge-search { width: 100%; box-sizing: border-box; padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--bg-0); color: var(--ink); font: inherit; }
.edge-list { list-style: none; margin: 0; padding: 0; flex: 1 1 auto; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.edge-row { display: flex; align-items: center; gap: 4px; }
.edge-row.header { color: var(--ink-soft); font-size: 11px; text-transform: none; padding: 6px 4px 2px; }
.edge-row.empty { color: var(--ink-soft); padding: 8px 4px; line-height: 1.5; }
.edge-main { flex: 1; min-width: 0; text-align: left; background: transparent; border: none; border-radius: 6px; padding: 7px 8px; color: var(--ink); font: inherit; cursor: pointer; display: flex; align-items: center; gap: 6px; }
.edge-main:hover, .edge-main:focus-visible { background: color-mix(in srgb, var(--ink) 8%, transparent); }
.edge-row.current .edge-main { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.edge-name { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.edge-sub { flex: none; color: var(--ink-soft); font-size: 11px; font-variant-numeric: tabular-nums; }   /* 同一行小字：修改时间 */
.edge-row.add .edge-main { color: var(--ink-soft); border: 1px dashed var(--line); justify-content: center; margin-top: 4px; }   /* 列表末尾的「+」 */
.edge-row.add .edge-main .ico { width: 14px; height: 14px; }
.edge-more { flex: none; }
.edge-foot { display: flex; flex-wrap: wrap; gap: 4px; border-top: 1px solid var(--line); padding-top: 8px; }
/* v2 邻域（ADR-0014 §7）：`..` 行 = 父页（顶层 = 书，不可点）；块头 = 兄弟 / 子节 / 链接 / 谁指向这里；没有第三层。 */
.edge-row.parent .edge-main { font-weight: 600; }
/* hidden（2026-09-30，Unity Hierarchy 同款）：藏着的行只变灰、不画图标（user「都改颜色了那么眼睛图标就不要了吧」） */
.edge-row.hidden-self .edge-name, .edge-row.hidden-inherited .edge-name { opacity: 0.5; }
.page-hidden-badge { color: var(--ink-muted); opacity: 0.8; }
.edge-row.parent .edge-dots { flex: none; color: var(--ink-soft); font-family: var(--font-ui); letter-spacing: 1px; }
.edge-row.parent.root .edge-main { color: var(--ink-soft); font-weight: 500; cursor: default; }
.edge-row.parent.root .edge-main:hover { background: transparent; }
/* 纸面页脚：上一页 / 下一页（全树前序 DFS；首尾灰、散页灰）。书模式才显示；图片页也要有（DFS 会经过图片页）。 */
/* 上一页 / 下一页 = 章节名两侧的 chevron 钮（页脚 .page-nav 2026-09-10 撤）；锁着 / 无书时整颗 hidden，两端到头只是灰（仍占位让章节名居中） */
.page-nav-btn { flex: none; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; background: transparent; border: 0; border-radius: 8px; padding: 0; color: var(--ink-soft); cursor: pointer; }
.page-nav-btn .ico { width: 20px; height: 20px; }
.page-nav-btn[hidden] { display: none; }
.page-nav-btn:hover:not(:disabled), .page-nav-btn:focus-visible:not(:disabled) { background: color-mix(in srgb, var(--ink) 8%, transparent); color: var(--ink); }
.page-nav-btn:disabled { opacity: .3; cursor: default; }
@media (max-width: 900px) {
  .edge-sidebar { width: min(82vw, 320px); }
  body[data-edges="1"] .edge-backdrop { display: block; }
}


/* ═══ 2.0 图库屏（@internal/gallery card view，独立一屏替代抽屉列表；网格/tile 皮在 vendor/internal-css/gallery.css）═══ */
.gallery-full { position: fixed; inset: 0; z-index: var(--z-overlay);   /* 包内菜单/弹层用 :root 的 --z-menu/--z-popover/--z-modal/--z-toast/--z-notice（与 WeebPaint 同表） */ display: flex; flex-direction: column; background: var(--bg-0); color: var(--ink); font-family: var(--font-ui); }
.gallery-full.hidden { display: none; }
.gallery-chrome { display: flex; align-items: center; gap: 8px; padding: max(env(safe-area-inset-top, 0px), 8px) 14px 8px; border-bottom: 1px solid var(--line); }
.gallery-chrome-title { font-weight: 600; }
.gallery-chrome .auth-action .ico, .gallery-asidebar .auth-action .ico { width: 14px; height: 14px; margin-right: 4px; }
/* 回收站 / 备份箱共用的一条栏：回到文件 · 两个页签 · 清空 */
.gallery-asidebar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 8px 14px; border-bottom: 1px solid var(--line); }
.gallery-asidebar.hidden { display: none; }
.gallery-aside-tabs { display: inline-flex; border: 1px solid var(--line-strong); border-radius: 8px; overflow: hidden; }
.gallery-aside-tab { display: inline-flex; align-items: center; gap: 5px; min-height: 32px; padding: 0 11px; border: 0; background: transparent; color: var(--ink-soft); font: inherit; font-size: 13px; cursor: pointer; }
.gallery-aside-tab + .gallery-aside-tab { border-left: 1px solid var(--line-strong); }
.gallery-aside-tab .ico { width: 15px; height: 15px; }
.gallery-aside-tab.is-on { background: color-mix(in srgb, var(--ink) 9%, transparent); color: var(--ink); font-weight: 600; }
.gallery-mount { flex: 1 1 auto; overflow: auto; padding: 0 0 12px; }   /* 横向 padding 归包内 .gallery-grid（16px）；宿主再叠 14px 曾让 375px 宽只剩 315px、排不下两列（user 2026-09-10 iPhone SE2 打回） */
.gallery-mount .sheet-action { background: transparent; border: 1px solid var(--line); border-radius: 8px; padding: 6px 10px; color: var(--ink); font: inherit; cursor: pointer; }

/* ═══ 输入法附件（v2.1.16，2026-09-29；src/input/）：app 内软键盘 + 常驻候选（手机式）/ 悬浮候选条（PC 式，实体键盘）═══
   user 2026-09-29「加软键盘，以后不用触屏手机的自带键盘了」「软键盘的时候候选框和软键盘应该在一起，就和手机一样，然后常驻」「物理键盘的时候是这种 pc 式的悬浮的」。
   手机式那一块住 body 层贴底、z = --z-ime（sheet / 锁屏之上：sheet 里的输入框也要用它打字）；纸面 / 侧栏 / sheet / 抽屉让出它的高度（:root --dock-h，dock.ts 量了写）。
   拼音单独一行、候选另起一行：长拼音再也挤不走首选（以前拼音芯片排在候选前面，375 宽的屏上首选只露 30px）。 */
:root { --dock-h: 0px; --key-bg: color-mix(in srgb, var(--bg) 92%, white); --key-fn-bg: color-mix(in srgb, var(--line) 70%, var(--bg)); --key-pressed: color-mix(in srgb, var(--accent) 22%, var(--bg)); --kb-bg: color-mix(in srgb, var(--void) 78%, var(--line-strong)); }

body.ime-docked { --bottom-floor: 6px; }   /* 底部安全区由键盘那一块自己垫；纸面贴着它 */
body.ime-docked .edge-sidebar { bottom: calc(12px + var(--dock-h, 0px)); }
.crypto-modal { align-items: flex-start; overflow-y: auto; box-sizing: border-box; }   /* 卡片比可用高度高时能滚（手机横屏 + 键盘只剩 170px）；margin:auto = 放得下就居中、放不下从顶排起 */
.crypto-card { margin: auto; }
body.ime-docked .crypto-modal { padding-bottom: calc(var(--dock-h, 0px) + 6px); }
@media (max-height: 460px) {
  body.ime-docked .crypto-modal { padding-top: 4px; padding-bottom: calc(var(--dock-h, 0px) + 4px); }
  body.ime-docked .crypto-card { padding: 8px 14px; gap: 4px; }
  body.ime-docked .crypto-hint { line-height: 1.35; }
  body.ime-docked .crypto-input { padding: 5px 10px; }
  body.ime-docked .crypto-button { padding: 4px 14px; }
  body.ime-docked .crypto-actions { margin-top: 0; }
}
body.ime-docked .drawer { padding-bottom: var(--dock-h, 0px); }
body.ime-docked .gallery-full { bottom: var(--dock-h, 0px); }

.ime-dock {
  position: fixed; left: 0; right: 0; bottom: var(--kb-offset, 0px); z-index: var(--z-ime);   /* 贴 visual viewport 底（iPad mini 竖屏真机「软键盘没有贴到屏底部」2026-09-30：fixed 钉的是 layout viewport，iOS 把视口顶起 / 留一条系统附件栏时会脱开；--kb-offset = 两个视口的差） */
  background: var(--kb-bg); border-top: 1px solid var(--line-strong);
  padding: 0 max(3px, var(--sa-left)) max(env(safe-area-inset-bottom, 0px), 4px) max(3px, var(--sa-right));
  font-family: var(--font-ui); user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  --key-h: 46px; --strip-cand-h: calc(36px + var(--cand-grow));   /* v2.1.24：候选条只剩候选行、36px（原拼音 16 + 候选 40 = 56）；v2.3.20 起随字号档长高 */
}
/* 候选字号跟字号档（v2.3.20，user 2026-10-01「输入候选要不要字大小怎么跟？」「候选条跟会有什么坏处吗」「好，那么做」）：
   候选字号 = 它自己的现值 × 字号档（手机式 19px、悬浮条 15px / 触屏 19px），**不是**等于正文字号——标准档下一切照旧，只有调大字号的设备上候选才变大。
   调小不跟着缩（下限 1）；封顶 1.5（19 → 28.5px，再大一屏只剩三个候选）。条高按字号的增量长（--cand-grow），键帽不变（那是手指的尺寸）。
   键盘那一块的高度由 dock.ts 的 ResizeObserver 重量、写回 --dock-h，纸面跟着让。 */
:root { --cand-scale: clamp(1, var(--font-scale, 1), 1.5); --cand-grow: calc(17px * (var(--cand-scale) - 1)); }
.ime-dock.hidden { display: none; }
/* 键盘底色往下多铺一截（v2.3.31，iPad 真机「键盘最下面没贴屏幕底部」）：iPadOS 给的视口比屏幕矮时，视口外那一条也是键盘的颜色，不露纸面背景 */
.ime-dock::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 120px; background: var(--kb-bg); pointer-events: none; }
.ime-strip { position: relative; display: flex; align-items: stretch; max-width: 1000px; margin: 0 auto; border-bottom: 1px solid color-mix(in srgb, var(--line-strong) 50%, transparent); }
/* 手机式候选条上的拼音小字（v2.1.33）：绝对定位在条的左上，候选行照旧 36px、只把候选钮往下压 3px 给它让位 */
.ime-strip .ime-preedit-small { position: absolute; left: 10px; top: 0; height: calc(12px * var(--cand-scale)); line-height: calc(12px * var(--cand-scale)); padding: 0; font-size: calc(10px * var(--cand-scale)); letter-spacing: 0.03em; color: var(--ink-muted); pointer-events: none; max-width: 60%; overflow: hidden; white-space: nowrap; }
.ime-strip:has(.ime-preedit-small:not(:empty)) .ime-cands { padding-top: calc(6px * var(--cand-scale)); }
.ime-strip:has(.ime-preedit-small:not(:empty)) .cand { height: calc(var(--strip-cand-h) - 8px * var(--cand-scale)); }
.ime-strip-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.ime-preedit { flex: none; height: 16px; line-height: 16px; padding: 0 10px; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 12px; letter-spacing: 0.04em; color: var(--ink-soft); white-space: nowrap; overflow: hidden; }
.ime-cands { flex: none; height: var(--strip-cand-h); display: flex; align-items: center; gap: 2px; padding: 0 4px; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; touch-action: pan-x; overscroll-behavior: contain; }
.ime-cands::-webkit-scrollbar { display: none; }
.cand { flex: 0 0 auto; min-width: 40px; height: calc(var(--strip-cand-h) - 4px); padding: 0 12px; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; color: var(--ink); font-size: calc(19px * var(--cand-scale)); white-space: nowrap; cursor: pointer; }
.cand.first { background: var(--accent-soft); color: var(--accent-strong); }
.cand.nav { min-width: 36px; padding: 0 6px; color: var(--ink-soft); }
.cand .ico { width: 16px; height: 16px; }
.cand .index { color: var(--ink-muted); margin-right: 5px; font-size: calc(11px * var(--cand-scale)); font-family: ui-monospace, monospace; }
.ime-key.hide .ico { width: 20px; height: 20px; }
/* 手机候选条右端的「收起」（v2.3.31，照 iPhone：一道竖线 + ⌄）；候选行吃掉剩下的宽、自己横滑 */
.ime-strip .ime-cands { flex: 1 1 0; min-width: 0; }
.ime-strip-hide { flex: none; width: 44px; margin: 6px 0; padding: 0; border: 0; border-left: 1px solid color-mix(in srgb, var(--line-strong) 60%, transparent); background: transparent; color: var(--ink-soft); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.ime-strip-hide[hidden] { display: none; }
.ime-strip-hide .ico { width: 20px; height: 20px; pointer-events: none; }
/* 键盘里的话筒键（手机底排，v2.3.31）：录音中 = 实心强调色；转写中 = 强调色字 */
.ime-key.mic.mic-recording, .ime-key.mic.mic-listening { background: var(--accent); color: #faf2e2; }
.ime-key.mic.mic-transcribing { color: var(--accent); }

/* 键位几何照 iOS（v2.1.33，user 2026-09-30「ios 的键位会宽一点，然后 s 和 z 对齐的」）：字母层所有字母键等宽 = (行宽 − 9 缝) / 10，
   第二排 9 键居中（两端各空半键），第三排 shift / 退格 = 1.5 键 + 半缝 → z 正落在 s 下面；键之间用 gap 不用 margin，缝 5px、外边 3px。
   符号层 / 最下一排仍按权重 flex-grow 铺满。 */
.ime-keys { --kg: 5px; max-width: 1000px; margin: 0 auto; padding: 3px 3px 2px; touch-action: none; }
/* 「格」（v2.3.29，user 2026-10-04 键位对齐肌肉记忆）：一格 = 键宽 + 缝；占 n 格的键宽 = n × --pitch − --kg（soft-keyboard.ts 行内 style 用）。
   百分比在用到的地方才解析（= 那一排的宽）。手机：一排 10 个字母键 → (100% + 缝) / 10；平板（iPad）：一排 10 字母 + 退格 = 11 格。
   iPad 横屏键盘是全宽的 → 平板放宽到 1400（盖住 iPad Pro 13 横屏 1376）。 */
.ime-keys[data-form="phone"] { --pitch: calc((100% + var(--kg)) / 10); }
.ime-keys[data-form="tablet"] { --pitch: calc(100% / 11); max-width: 1400px; }
.ime-row { display: flex; justify-content: center; gap: var(--kg); }
.ime-row[data-letters][data-n="9"] .ime-key { flex: 0 0 calc((100% - 9 * var(--kg)) / 10); flex-grow: 0 !important; }   /* 行内 style 写的是 flex-grow，得压过它 */
.ime-row[data-letters][data-n="9"] .ime-key.fn { flex-basis: calc(1.5 * (100% - 9 * var(--kg)) / 10 + 0.5 * var(--kg)); }
.ime-key {
  flex: 1 1 0; min-width: 0; height: var(--key-h); margin: 3px 0; box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 7px; background: var(--key-bg); color: var(--ink);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--ink) 28%, transparent);
  font-size: 21px; line-height: 1; cursor: pointer; overflow: hidden; white-space: nowrap;
}
.ime-key.small { font-size: 14px; }
/* iPad 的「！，」「？。」：上档字小字在上、主字在下；按了上档时上面那个变亮 */
.ime-key.dual { flex-direction: column; gap: 3px; font-size: 19px; }
.ime-key.dual .alt { font-size: 13px; color: var(--ink-muted); }
.ime-key.dual.alt-on .alt { color: var(--accent); }
.ime-key.space { font-size: 13px; color: var(--ink-muted); }
.ime-key.fn { background: var(--key-fn-bg); color: var(--ink-soft); }
.ime-key.accent { background: var(--accent); color: #faf2e2; }
.ime-key.disabled { opacity: 0.35; }
.ime-key .ico { width: 22px; height: 22px; pointer-events: none; }
.ime-key.on { color: var(--accent); }
.ime-key.locked { color: var(--accent); }
.ime-key.locked .ico { fill: currentColor; }
.ime-key.pressed { background: var(--key-pressed); }
.ime-key.accent.pressed { background: var(--accent-strong); }
@media (hover: hover) {   /* 鼠标 / VR 手柄射线：hover 高亮帮瞄准 */
  .ime-key:hover { background: color-mix(in srgb, var(--accent) 10%, var(--key-bg)); }
  .ime-key.fn:hover { background: color-mix(in srgb, var(--accent) 12%, var(--key-fn-bg)); }
  .ime-key.accent:hover { background: var(--accent-strong); }
  .cand:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
}
/* 平板 = iPad 系统键盘的尺寸（v2.3.32，user 2026-10-04「ipad上面键盘是不是键太矮了而且候选字也太矮了？量一下截图」「截图里面键盘有刻意垫高，但这个是键盘自己的布局」；
   iPad mini 竖屏系统键盘截图逐像素量的，CSS px）：键高 55.5、行距 64.5（上下缝各 4.5）、候选栏到第一排键顶 63（候选条 56 + 分隔线 1 + 1.5 + 4.5）、候选字墨迹 24 ≈ 27px 字号、
   字母墨迹 16.5 ≈ 23px、空格字 ≈ 18px；最后一排键底到屏幕底 28（安全区 20 + 4.5 + 2 + 1.5）。以前 50 / 56 / 38 / 19px / 21px / 13px，整块矮 50、第一排键顶离屏底差 34。横屏没量，先用同一套。 */
.ime-keys[data-form="tablet"] { padding-top: 1.5px; }
.ime-dock:has(.ime-keys[data-form="tablet"]) { --key-h: 55.5px; --strip-cand-h: calc(56px + var(--cand-grow)); padding-bottom: max(calc(env(safe-area-inset-bottom, 0px) + 1.5px), 8px); }
.ime-keys[data-form="tablet"] .ime-key { margin: 4.5px 0; font-size: 23px; }
.ime-keys[data-form="tablet"] .ime-key.space, .ime-keys[data-form="tablet"] .ime-key.small { font-size: 18px; }
.ime-keys[data-form="tablet"] .ime-key .ico { width: 25px; height: 25px; }
.ime-dock:has(.ime-keys[data-form="tablet"]) .cand { font-size: calc(27px * var(--cand-scale)); padding: 0 16px; }
.ime-dock:has(.ime-keys[data-form="tablet"]) .ime-strip .ime-preedit-small { font-size: calc(12px * var(--cand-scale)); height: calc(15px * var(--cand-scale)); line-height: calc(15px * var(--cand-scale)); }
@media (max-height: 460px) {   /* 手机横屏：纵向空间最紧，键矮一档 */
  .ime-dock { --key-h: 33px; --strip-cand-h: calc(32px + var(--cand-grow)); }
  .ime-key { margin: 2px 0; font-size: 18px; }   /* 横向的缝走 .ime-row 的 gap（v2.1.33） */
  .cand { font-size: calc(17px * var(--cand-scale)); }
  .ime-preedit { height: 14px; line-height: 14px; font-size: 11px; }
}

/* PC 式悬浮候选条（实体键盘）：两行——拼音在上、候选在下；首选永远从最左开始 */
.candidates {
  position: fixed; left: 50%; bottom: calc(var(--bottom-floor, 14px) + 8px + var(--kb-offset, 0px)); transform: translateX(-50%); z-index: var(--z-ime);
  display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-rows: auto auto; align-items: center; column-gap: 6px; row-gap: 1px; padding: 3px 6px 4px; box-sizing: border-box;
  width: max-content; max-width: min(calc(100% - 24px), 640px); overflow: hidden;
  border: 1px solid var(--line-strong); border-radius: 10px; background: rgba(241, 230, 207, 0.96);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); box-shadow: 0 6px 18px rgba(58, 40, 24, 0.2);
  user-select: none; -webkit-user-select: none;
}
.candidates.hidden { display: none; }
/* 中 / 英 芯片：顶栏的那一字 v2.1.26 搬到这里（悬浮条只在组字时出现，切中英时空着也闪一下让人看见）；点 = 切中 / 英（同 Shift） */
.candidates .ime-mode { grid-row: 1 / 3; grid-column: 1; align-self: center; min-width: 26px; height: 26px; padding: 0 6px; border-radius: 7px; display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-ui); font-size: 13px; font-weight: 600; color: var(--ink-soft); background: color-mix(in srgb, var(--ink) 8%, transparent); cursor: pointer; user-select: none; }
.candidates .ime-mode:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 14%, transparent); }
.candidates .ime-preedit { grid-column: 2; height: calc(15px * var(--cand-scale)); line-height: calc(15px * var(--cand-scale)); font-size: calc(12px * var(--cand-scale)); padding: 0 4px; }
.candidates .ime-cands { grid-column: 2; }
.candidates .ime-preedit:empty { display: none; }   /* 只闪芯片时没有拼音行：条就一行高 */
.candidates .ime-cands { height: auto; min-height: calc(28px + 15px * (var(--cand-scale) - 1)); padding: 0; touch-action: pan-x; }
.candidates .cand { height: calc(28px + 15px * (var(--cand-scale) - 1)); min-width: 0; padding: 0 8px; font-size: calc(15px * var(--cand-scale)); border-radius: 6px; }
.candidates.lingering { visibility: hidden; }   /* 触屏点过候选：空着多留一拍再收（占着位置、看不见），等手指抬起 */
@media (pointer: coarse) {
  .candidates { padding: 5px 8px 6px; border-radius: 14px; }
  .candidates .cand { height: calc(44px + 19px * (var(--cand-scale) - 1)); padding: 0 14px; font-size: calc(19px * var(--cand-scale)); border-radius: 10px; }
  .candidates .cand .index { font-size: calc(12px * var(--cand-scale)); }
}


/* 键盘钮：纸面左下角（与右下角的话筒对称） */
.mic-button.kb-toggle { right: auto; left: calc(max(0px, 50% - var(--box-max) / 2) + 14px); }
.kb-toggle .ico { width: 22px; height: 22px; }

/* ═══ 稿纸几何（v2.1.17，2026-09-29；src/ui/paper.ts）═══
   user 2026-09-29「现在稿纸的线其实还是和文字没对齐的」「章后面的超链接我也想做成就像文字一样就在线上的」「小横屏适配（看一下 gpd win mini…）」。
   --editor-lh = 一行的高度（对齐设备像素）；--rule-y / --rule-w = 线在一行里的位置与粗细（实测基线之下，对齐设备像素）。三个数都由 paper.ts 写在 .page 上。
   正文、子节目录、纸面空白处用同一块平铺 → 线从头到尾是一套格子，字坐在线上。 */
.page { --editor-lh: calc(var(--editor-font) * var(--editor-line-height)); --rule-y: calc(var(--editor-lh) - 0.4em); --rule-w: 1px; }   /* paper.ts 还没量之前的近似值 */
.body { line-height: var(--editor-lh); }
.body-mirror { position: absolute; left: 0; top: 0; height: 0; min-height: 0; flex: none; visibility: hidden; pointer-events: none; overflow: hidden; }
body.ruled-lines .page-body {   /* 线只画在这一层（正文框 / 目录都透明）：字和线同层，整张纸一起滚，不会相对抖（v2.1.26） */
  /* 一张「会重复的渐变」铺满整个内容区，不是一块一块平铺：第 k 条线的位置 = k × 行高 + 线位，是算出来的，不会逐块取整累积漂移 */
  background-image: repeating-linear-gradient(to bottom, transparent 0, transparent var(--rule-y), var(--line) var(--rule-y), var(--line) calc(var(--rule-y) + var(--rule-w)), transparent calc(var(--rule-y) + var(--rule-w)), transparent var(--editor-lh));
  background-repeat: no-repeat;
  background-position: 0 0;
}

body.ruled-lines .body-mirror { background-image: none; }

/* 有子节的页：正文框高度 = 内容行数 × 行高，子节目录紧跟其后；都是整行，所以和容器的格子对得上（目录透明，坐在容器的线上）。 */
.child-toc {
  flex: none; margin: 0; padding: var(--editor-lh) 0 0; border: 0; box-sizing: border-box;   /* 顶上空一行 = 正文与目录之间的那一行空白 */
  background: transparent; font-family: var(--font-editor); font-size: var(--editor-font); line-height: var(--editor-lh);
}
.child-toc[hidden] { display: none; }
.child-toc-head { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }   /* 只给读屏：行本身看得出是链接 */
.child-toc-list { list-style: none; margin: 0; padding: 0; }
/* 目录行可换行（user 2026-09-30「碰到长的英文标题…对于子节目录来说可以换行吧」）：最多两行，每行仍 = --editor-lh，行高整数倍 → 仍坐在线上 */
.child-toc-list li { min-height: var(--editor-lh); }
.child-toc-row {
  width: 100%; min-height: var(--editor-lh); display: flex; align-items: flex-start; gap: 0.4em; margin: 0; padding: 0; border: 0; border-radius: 0;
  background: transparent; color: var(--accent-strong); font: inherit; line-height: var(--editor-lh); text-align: left; cursor: pointer;
}
.child-toc-row:hover .child-toc-name, .child-toc-row:focus-visible .child-toc-name { text-decoration: underline; text-underline-offset: 0.2em; }
.child-toc-row:focus-visible { outline: none; }
.child-toc-row .ico { flex: none; align-self: flex-start; width: 0.7em; height: 0.7em; margin-top: calc((var(--editor-lh) - 0.7em) / 2); color: var(--ink-muted); }
.child-toc-name { flex: 0 1 auto; min-width: 0; line-height: var(--editor-lh); white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.child-toc-sub { flex: none; margin-left: auto; padding-left: 0.6em; line-height: var(--editor-lh); font-family: var(--font-ui); font-size: 11px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
/* hidden（2026-09-30 user「hidden 的话正文里的链接也应该有颜色变化」「都改颜色了那么眼睛图标就不要了吧」）：目录行与侧栏同一套只变灰；`..` 指向的上级本身不出门也灰 */
.child-toc-row.hidden-self .child-toc-name, .child-toc-row.hidden-inherited .child-toc-name { opacity: 0.5; }
.kin-hidden { opacity: 0.55; }

/* 「宽稿纸」档（body.paper-wide）2026-09-30 撤了（user「win mini 现在的情况不需要加宽，加宽可以撤了」）；矮屏档 body.paper-short 仍在。 */
/* 矮纸（body.paper-short）：可用高度 ≤ 300（手机横屏 + 软键盘只剩一百多像素）→ 页脚字数统计让位，纸边再收 */
body.paper-short .page { --page-pad-y: 6px; }
body.paper-short .page:has(> .mic-button:not([hidden])) { --floater-band: 44px; }
body.paper-short .mic-button { width: 40px; height: 40px; bottom: calc(var(--sheet-bottom) + 8px); }

/* ═══ 设置住在侧栏里（v2.1.18，user 2026-09-29「不如设置和侧条都是同一个侧条里面？」）═══
   侧栏 data-view="settings"：导航各块让位，设置面板占满；面板加宽到抽屉那么宽（设置里的下拉和说明文字是按 320 排的）。 */
.edge-settings { display: none; flex-direction: column; min-height: 0; flex: 1 1 auto; margin: -10px; }   /* 吃掉侧栏的内边距：设置各节自己有分隔线和留白 */
.edge-sidebar[data-view="settings"] { width: min(320px, 86vw); }
.edge-sidebar[data-view="settings"] > * { display: none; }
.edge-sidebar[data-view="settings"] > .edge-settings { display: flex; }
.edge-settings-head { flex: none; display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-bottom: 1px solid var(--line); }
.edge-settings-title { font-size: 13px; font-weight: 600; color: var(--ink-soft); letter-spacing: 0.06em; }
.edge-settings-head .row-icon-button { opacity: 1; color: var(--ink-soft); }   /* 这颗返回钮是导航，不是「悬停才露的行内钮」 */
.edge-settings-head .row-icon-button .ico { width: 18px; height: 18px; }
.edge-settings-mount { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; border-radius: 0 0 12px 12px; overflow: hidden; }
.edge-settings-mount > .settings-view { flex: 1 1 auto; min-height: 0; }

/* ═══ 书库封面（v2.1.19 立，v2.1.21 改成两层；2026-09-29；排版规则在 src/ui/book-cover.ts）═══
   user 2026-09-29「封面上印书名…配色用花璃同人的那个。但是更加哑一点，不要渐变，扁平，不过花璃那本旁边的装订线很漂亮，保留。然后最好下面的日期 大小也收上来？」
   user 2026-09-29（同日稍后）「带图的能不能不要做底栏而就是图是fit all然后字还是一样的，就是字的逻辑一样，无视是否有图，图只是背景」
   卡片 = 一本书的封面，两层：
     · 底：封面图铺满整张封面；没有图就是一张纸（.xhw-paper，图库包的占位槽）。锁着 / 还在云端时，纸上多一枚小图标。
     · 面：书名、日期、装订线（.xhw-cover，图库包的覆盖层槽 .gallery-tile-overlay）——有没有图都一样印。底下是图时字带一圈纸色描边。
   图库包画的名字行（名字 + 同步徽章 + 时间 · 大小）挪到封面下沿、没有底色；名字已经印在封面上了，名字行里的那份藏起来。
   全是宿主侧的排版覆盖，图库包（vendor/internal-css/gallery.css）一个字不动。只管卡片视图（.gallery-grid:not(.list)）。 */
:root {
  --cover-paper: #e9e3d5; --cover-spine: #d2c9b5; --cover-spine-edge: #c2b8a2; --cover-ink: #43382c; --cover-ink-soft: #857b6a;
  --cover-sheet: #f4f2ec; --cover-sheet-ink: #4d4a44;
}
:root[data-theme="night"] {
  --cover-paper: #3b372f; --cover-spine: #2f2c26; --cover-spine-edge: #26231f; --cover-ink: #e4dccb; --cover-ink-soft: #a0968a;
  --cover-sheet: #2b2a27; --cover-sheet-ink: #cfc9bc;
}
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
    --cover-paper: #3b372f; --cover-spine: #2f2c26; --cover-spine-edge: #26231f; --cover-ink: #e4dccb; --cover-ink-soft: #a0968a;
    --cover-sheet: #2b2a27; --cover-sheet-ink: #cfc9bc;
  }
}
#galleryMount .gallery-grid:not(.list) .gallery-tile { background: transparent; border-color: var(--line-strong); border-radius: 4px 7px 7px 4px; }
#galleryMount .gallery-grid:not(.list) .gallery-tile-thumb { border-radius: 3px 6px 6px 3px; }
/* 底：封面图铺满（裁边，不留白边）；没有图 = 纸 */
#galleryMount .gallery-grid:not(.list) img.gallery-tile-thumb { object-fit: cover; background: var(--cover-paper); }
#galleryMount .gallery-grid:not(.list) .gallery-tile-thumb.placeholder { position: relative; align-items: stretch; justify-content: stretch; background: var(--cover-paper); color: var(--cover-ink-soft); }
#galleryMount .gallery-grid:not(.list) .gallery-tile-ph-icon { display: block; width: 100%; height: 100%; color: inherit; }
.xhw-paper { display: block; width: 100%; height: 100%; background: var(--cover-paper); border-radius: inherit; }
.xhw-paper.draft { background: var(--cover-sheet); }
/* 锁着 / 还在云端：纸上一枚小图标，放在封面左下、日期上方，不和书名抢地方 */
#galleryMount .gallery-grid:not(.list) .gallery-tile-thumb.placeholder.locked { background: var(--cover-paper); }
#galleryMount .gallery-grid:not(.list) .gallery-tile-thumb.placeholder > span:not(.gallery-tile-ph-icon) { display: none !important; }   /* 2026-09-30 user「只需要左上的那个锁」：锁 / 云一律在左上（大锁 + 同步徽章），纸上不再另印。!important：包给这些 span 的是行内 display（加密占位的 42px 锁、云端占位的 48px 云） */

/* 面：印在封面上的那一层 */
#galleryMount .gallery-grid:not(.list) .gallery-tile-overlay { border-radius: 3px 6px 6px 3px; container-type: inline-size; }
.xhw-cover {
  --spine-w: 7px;
  position: relative; display: block; width: 100%; height: 100%; box-sizing: border-box; overflow: hidden;
  padding: 34px 9px 40px calc(var(--spine-w) + 9px);   /* 上：让开右上角的 ⋯ 钮；下：让开日期与名字行 */
  border-radius: 3px 6px 6px 3px;
  background: transparent; color: var(--cover-ink); text-align: left; font-size: 15px; line-height: 1.25;
}
.xhw-cover.book::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: var(--spine-w); background: var(--cover-spine); border-right: 1px solid var(--cover-spine-edge); }   /* 装订线 */
.xhw-cover.draft { --spine-w: 0px; color: var(--cover-sheet-ink); }
/* 加密件的大锁（gallery 0.6.1 `.gallery-tile-lockmark`；user 2026-09-30「gallery里面加密和不加密也需要有辨识度。左上角加一把大锁？」）：
   住封面左上角、书名之上的那一条空带里；书的让开装订线；光晕用纸色（包默认白色，在深色封面图上才对，纸面上要用纸色） */
#galleryMount .gallery-grid:not(.list) .gallery-tile-lockmark { left: 9px; top: 6px; width: 24px; height: 24px; color: var(--enc); }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .gallery-tile-lockmark { filter: drop-shadow(0 0 1px #fff) drop-shadow(0 0 1px #fff); }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(.xhw-cover.book) .gallery-tile-lockmark { left: 15px; }
/* v2.3.26 书架封面向导出封面对齐（user 2026-10-01「书架对齐。不过书架用内置黑体这点小divergent没事吧」）：比例 1:√2（gallery-host tile.aspect）、
   字体 = 内置黑体常规体（导出嵌的就是它；书架不知道每本书选了什么字体——要解开 zip 才知道——所以拼音的书在书架上也是黑体）、书名压在封面图上时垫半透明纸（不描边）。 */
.xhw-cover-title { display: block; max-width: 100%; max-height: 100%; overflow: hidden; font-weight: 400; letter-spacing: 0.04em; font-family: "XHW Sans", var(--font-fallback); }
.xhw-cover.v .xhw-cover-title { writing-mode: vertical-rl; text-orientation: mixed; height: 100%; margin-left: auto; letter-spacing: 0.08em; }
.xhw-cover.v .tcy { text-combine-upright: all; -webkit-text-combine: horizontal; letter-spacing: 0; }
.xhw-cover.h .xhw-cover-title { letter-spacing: 0.01em; overflow-wrap: anywhere; hyphens: auto; -webkit-hyphens: auto; line-height: 1.18; }
.xhw-cover.draft .xhw-cover-title { font-weight: 400; }
.xhw-cover.coded .xhw-cover-title { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-weight: 400; letter-spacing: 0; color: var(--cover-ink-soft); }
/* 字号四档：卡片宽度的百分比（cqw），两头夹住 */
.xhw-cover.v.sz-xl .xhw-cover-title { font-size: clamp(15px, 17cqw, 30px); }
.xhw-cover.v.sz-l  .xhw-cover-title { font-size: clamp(14px, 14cqw, 25px); }
.xhw-cover.v.sz-m  .xhw-cover-title { font-size: clamp(13px, 12cqw, 21px); }
.xhw-cover.v.sz-s  .xhw-cover-title { font-size: clamp(12px, 10cqw, 17px); }
.xhw-cover.h.sz-xl .xhw-cover-title { font-size: clamp(15px, 16cqw, 28px); }
.xhw-cover.h.sz-l  .xhw-cover-title { font-size: clamp(13px, 13cqw, 23px); }
.xhw-cover.h.sz-m  .xhw-cover-title { font-size: clamp(12px, 11cqw, 19px); }
.xhw-cover.h.sz-s  .xhw-cover-title { font-size: clamp(11px, 9.5cqw, 16px); }
.xhw-cover.coded .xhw-cover-title { font-size: clamp(13px, 13cqw, 22px); letter-spacing: 0.04em; }   /* 没起名的稿：四位消歧码 */
/* 底栏（user 2026-09-30「左边是 yyyymmdd，右边是 size」「半透明背景垫底…很窄的一条贴着底部」）：17px 一条贴底、纸色 78% 垫底、不描边 */
.xhw-cover-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 17px; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 0 6px 0 calc(var(--spine-w) + 8px); font-family: var(--font-ui); font-size: 9px; line-height: 17px; letter-spacing: 0.02em; color: var(--cover-ink-soft); background: color-mix(in srgb, var(--cover-paper) 78%, transparent); font-variant-numeric: tabular-nums; white-space: nowrap; }
.xhw-cover-size { margin-left: auto; }
.xhw-cover-date { flex: none; }

/* 底下是封面图：书名底下垫一块半透明的纸（和导出封面同一个做法，user 2026-10-01「标题的字下面还是垫一个半透明吧」；09-30 的白描边退役）。
   竖排的书名块只有字那么长（不再撑满整列高），垫的就是字占的那一块。 */
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .xhw-cover-title { color: var(--cover-ink); background: color-mix(in srgb, var(--cover-paper) 78%, transparent); padding: 0.18em; border-radius: 0.22em; box-sizing: border-box; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .xhw-cover.v .xhw-cover-title { height: fit-content; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .xhw-cover.h .xhw-cover-title { width: fit-content; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .xhw-cover-bar,
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .gallery-tile-meta { color: var(--cover-ink); }
/* 有封面图的书不画装订线（user 2026-09-30「有封面的时候左边不应该有 binding 的那一条」）：--spine-w 归零，书名 / 日期 / 锁标的让位一起收回 */
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .xhw-cover.book { --spine-w: 0px; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .xhw-cover.book::before { display: none; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb):has(.xhw-cover.book) .gallery-tile-lockmark { left: 9px; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .gallery-tile-meta .gallery-tile-state-icon svg { filter: drop-shadow(1px 0 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 1px 0 #fff) drop-shadow(0 -1px 0 #fff); }

/* 图库包的名字行：挪到封面下沿，没有底色（字直接落在封面上），让开装订线 */
#galleryMount .gallery-grid:not(.list) .gallery-tile-name-row {
  position: absolute; left: 0; right: 0; top: 0; bottom: 0; box-sizing: border-box; padding: 4px 8px 5px 16px;
  display: flex; flex-direction: column; justify-content: flex-end;   /* 铺满整张卡（同步徽章要用卡的坐标去左上），内容仍贴底 */
  background: transparent; color: var(--cover-ink); border-radius: 0 0 6px 3px;
}
#galleryMount .gallery-grid:not(.list) .gallery-tile-name { font-size: 12.5px; line-height: 1.3; color: inherit; white-space: normal; overflow: hidden; text-overflow: clip; overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
#galleryMount .gallery-grid:not(.list) .gallery-tile-meta { position: absolute; right: 6px; bottom: 0; height: 17px; line-height: 17px; margin: 0; max-width: 60%; font-size: 9px; letter-spacing: -0.01em; color: var(--cover-ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }   /* 包的「时间 · 大小」顶在底栏右侧；gallery ≥ 0.6.4 把 size 交给覆盖层后整行藏掉（user「上次编辑…可以做 tooltip」） */
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(.xhw-cover-bar.has-size) .gallery-tile-meta { position: static; width: 0; height: 0; margin: 0; overflow: visible; }   /* 只藏文字；同步徽章住在它里面（绝对定位到左上，参照系 = 铺满卡的名字行），不能整块 display:none、也不能让它自己当参照系 */
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(.xhw-cover-bar.has-size) .gallery-tile-meta > span:not(.gallery-tile-state-icon) { display: none; }
/* 左上角：先云（同步徽章）再锁（user 2026-09-30「云图标也放在左上，先云再锁」）；名字行里那把小锁是重复的，藏掉 */
#galleryMount .gallery-grid:not(.list) .gallery-tile-meta .gallery-tile-state-icon.enc { display: none; }
#galleryMount .gallery-grid:not(.list) .gallery-tile-meta .gallery-tile-state-icon[class*="b-"] { position: absolute; left: 9px; top: 8px; width: 20px; height: 20px; color: var(--cover-ink); }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb) .gallery-tile-meta .gallery-tile-state-icon[class*="b-"] { filter: drop-shadow(0 0 1px #fff) drop-shadow(0 0 1px #fff); }
#galleryMount .gallery-grid:not(.list) .gallery-tile-meta .gallery-tile-state-icon[class*="b-"] svg { width: 20px; height: 20px; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(.xhw-cover.book) .gallery-tile-meta .gallery-tile-state-icon[class*="b-"] { left: 15px; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb):has(.xhw-cover.book) .gallery-tile-meta .gallery-tile-state-icon[class*="b-"] { left: 9px; }
/* 徽章有图时大锁往右让 24px（徽章 v-html 可能为空 → :empty 不让） */
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(.gallery-tile-meta .gallery-tile-state-icon[class*="b-"]:not(:empty)) .gallery-tile-lockmark { left: 33px; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(.xhw-cover.book):has(.gallery-tile-meta .gallery-tile-state-icon[class*="b-"]:not(:empty)) .gallery-tile-lockmark { left: 39px; }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(img.gallery-tile-thumb):has(.xhw-cover.book):has(.gallery-tile-meta .gallery-tile-state-icon[class*="b-"]:not(:empty)) .gallery-tile-lockmark { left: 33px; }
#galleryMount .gallery-grid:not(.list) .gallery-tile-active-tag { position: absolute; left: auto; right: 6px; top: auto; bottom: 22px; margin: 0; background: var(--cover-paper); border: 1px solid var(--cover-spine-edge); }   /* 「打开中」：和日期同一行的右端（时间大小那一行窄卡片上放不下两样） */
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(.xhw-cover.draft) .gallery-tile-name-row { padding-left: 9px; color: var(--cover-sheet-ink); }
#galleryMount .gallery-grid:not(.list) .gallery-tile:has(.xhw-cover) .gallery-tile-name { display: none; }   /* 名字已经印在封面上 */
/* 文件夹：不是书，保持图标；名字行同样收到下沿 */
#galleryMount .gallery-grid:not(.list) .gallery-tile.folder .gallery-tile-name-row { background: transparent; border-top: 0; color: var(--ink); }
#galleryMount .gallery-grid:not(.list) .gallery-tile.folder .gallery-tile-meta { color: var(--ink-soft); }
#galleryMount .gallery-grid:not(.list) .gallery-tile.folder .gallery-tile-thumb svg { width: 46%; height: auto; margin-bottom: 18%; }


/* ═══ 参考窗（ADR-0016，2026-09-29；本体 = @internal/reference-window，chrome / 手势 / 排版全在 shadow 里；宿主接线 src/reference-host.ts）═══
   宿主只管三件：① 层级（band 表里的 --z-popover，压在纸面 / 侧栏之上、toast / 锁屏之下）；② define 前不闪 light DOM；③ 书库那一屏盖住时不露。
   颜色 / 圆角 / 阴影它读的就是本仓 :root 的 --bg / --ink / --ink-soft / --line / --shadow / --radius / --void，不用另配。 */
wp-reference-window { --z-window: calc(var(--z-chrome) - 2); }   /* 侧栏 / 设置（chrome − 1）之下、纸面之上（user 2026-09-30「参考窗应该在设置和侧栏的下面」） */
wp-reference-window:not(:defined) { display: none; }
body[data-mode="gallery"] wp-reference-window { display: none !important; }
