﻿/* ========== 全局变量 ========== */
:root {
  --bg-color: #F5F5F7;
  --panel-bg: #FFFFFF;
  --hover-bg: #F3F4F6;
  --text-color: #111827;
  --text-muted: #6B7280;
  --text-placeholder: #9CA3AF;
  --border-color: rgba(0, 0, 0, 0.06);
  --primary-color: #111827;
  --primary-hover: #374151;
  --accent-color: #6B7280;
  --success-color: #10B981;
  --warning-color: #F59E0B;
  --danger-color: #EF4444;
  /* —— 角色定位语义色（替代 tag-role / char-role 写死色） —— */
  --role-protagonist: #3b82f6;   /* 主角：蓝 */
  --role-antagonist: #ef4444;    /* 反派：红 */
  --role-mentor: #8b5cf6;        /* 导师：紫 */
  --role-supporting: #10b981;     /* 配角：绿 */
  --role-extra-bg: #d1d5db;      /* 龙套：灰底 */
  --role-extra-fg: #374151;      /* 龙套：深字 */
  /* —— 状态徽标语义色（替代 status-badge 写死色） —— */
  --status-draft: #4E7A9B;
  --status-revise: #A8722E;
  /* —— 角色动向徽标语义色（替代 char-move 写死色） —— */
  --move-dead-bg: #e5e7eb; --move-dead-fg: #6b7280; --move-dead-bd: #d1d5db;
  --move-dark-bg: #7c3aed; --move-dark-fg: #fff;
  --move-awake-bg: #f59e0b; --move-awake-fg: #3b2f0b;
  /* —— 浮层 / 胶囊统一令牌 —— */
  --popover-radius: var(--radius-md);
  --popover-shadow: var(--shadow-popover);
  --radius-pill: 999px;
  /* 彩色/实色底上的恒定字色：白字压在 primary/danger/角色标识色上，与主题反转无关，跨双模恒定 */
  --on-accent: #FFFFFF;
  --overlay-color: rgba(17, 24, 39, 0.04);   /* 画布底纹：极淡，只作层次 */
  --scrim-color: rgba(17, 24, 39, 0.20);      /* 遮罩：浅色 20% 冷灰 */
  --memo-bg: var(--panel-bg);
  --memo-done-bg: #E5E7EB;
  /* —— 档案纸张令牌：保留独立命名，但配色完全跟随全局灰白体系 ——
     不再另起米黄/牛皮纸色板：浅色=干净高级灰白，暗色由全局令牌自动翻转。 */
  --paper-bg: var(--panel-bg);
  --paper-bg-2: var(--bg-color);          /* 抬升纸面（导航 / 弹层 / 胶囊底） */
  --paper-ink: var(--text-color);
  --paper-ink-soft: var(--text-muted);
  --paper-ink-mute: var(--text-placeholder);
  --paper-line: var(--border-color);
  --paper-line-soft: var(--border-color);
  --paper-line-strong: var(--border-strong);
  --paper-seal: var(--danger-color);
  --paper-gold: var(--primary-color);
  --paper-hover: var(--hover-bg);
  --paper-surface-hover: var(--hover-bg);
  /* 脱敏遮挡条：跨双模式恒为"深色条 + 浅色揭示字"，不走纸张翻转（否则浅色模式遮挡条会变浅失效） */
  --paper-censor: #1F2937;
  --paper-censor-fg: #EADFC4;
  /* —— 弥散阴影（UI 重构 Step 1）：多层大模糊、低浓度，层级越高越虚 —— */
  --shadow-sm: 0 1px 2px rgba(17, 24, 39, 0.04), 0 2px 6px rgba(17, 24, 39, 0.04);
  --shadow-md: 0 2px 6px rgba(17, 24, 39, 0.05), 0 8px 18px rgba(17, 24, 39, 0.06);
  --shadow-lg: 0 4px 12px rgba(17, 24, 39, 0.06), 0 16px 36px rgba(17, 24, 39, 0.08);
  /* 弹窗：大范围、低浓度弥散阴影（设计总监规范 0 20px 40px / 0.1） */
  --shadow-pop: 0 20px 40px rgba(0, 0, 0, 0.10);
  /* 浮层：略深弥散阴影，配清晰 1px 实线边框切割空间 */
  --shadow-popover: 0 8px 24px rgba(0, 0, 0, 0.12);
  --shadow-drawer: -8px 0 32px rgba(17, 24, 39, 0.12), 0 0 1px rgba(17, 24, 39, 0.10);
  --shadow-float: 0 2px 8px rgba(17, 24, 39, 0.06), 0 12px 28px rgba(17, 24, 39, 0.10);
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-btn: 6px;
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --font-serif: 'Crimson Text', serif;
  --font-sans: 'Inter', sans-serif;
  --header-height: 55px;
  --sidebar-width: 320px;

  /* ===== 思维导图 Design Tokens =====
     这些变量是导图渲染的唯一视觉来源：js/ui/mindmap.js 只写"数据里明确指定"的值，
     其余一律由这里的变量兜底；换肤时由 js/theme.js 整组替换。
     新增一套主题 = 在 js/theme.js 的主题表里补同名键，无需改渲染代码。
     能用 var(--*) 引用的都指向全局令牌，主题改一处、导图自动跟随；
     需要主题"特调"的（如深色的连线更暗、阴影更深）才在主题表里显式覆盖。 */
  --mm-line-color: var(--border-color);
  --mm-line-width: 2px;
  --mm-conn-label-bg: var(--panel-bg);
  --mm-conn-label-border: var(--border-color);
  --mm-conn-label-text: var(--text-color);
  --mm-conn-btn-bg: var(--panel-bg);
  --mm-conn-btn-border: var(--accent-color);
  --mm-conn-btn-text: var(--accent-color);
  --mm-node-bg: var(--panel-bg);
  --mm-node-radius: 12px;
  --mm-node-border-width: 2px;
  --mm-node-border-color: var(--primary-color);
  --mm-node-title-color: var(--text-color);
  --mm-node-body-color: var(--text-muted);
  --mm-node-pad: 12px;
  --mm-node-min-w: 170px;
  --mm-node-min-h: 85px;
  --mm-node-max-w: 300px;
  /* 节点图片的高度上限：与 js/data-model.js 的 MM_IMAGE_MAX_H 同值，改一处要改两处。
     封顶它是为了守住"卡片布局盒不许被图撑大"这条底线——巨盒尺寸会让 getNodeSize()
     量到虚拟高度，连线锚点跟着飞出画布。 */
  --mm-img-max-h: 300px;
  --mm-node-shadow: 0 2px 4px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.03);
  --mm-node-shadow-hover: 0 8px 20px rgba(0, 0, 0, 0.15);
  --mm-node-ring: 0 0 0 3px rgba(139, 92, 246, 0.3);
  /* 异形（clip-path）节点用的阴影：box-shadow 会被裁剪吃掉，只能换成 drop-shadow 写法 */
  --mm-node-drop: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.10));
  --mm-node-drop-hover: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.22));
  --mm-ball-size: 22px;
  --mm-ball-bg: rgba(127, 127, 127, 0.22);
  --mm-ball-color: var(--text-muted);
  --mm-ball-bg-hover: var(--accent-color);
  --mm-panel-bg: var(--panel-bg);
  --mm-panel-border: var(--border-color);
  --mm-panel-radius: 10px;
  --mm-panel-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  --mm-ph-color: var(--text-muted);
  /* 智能对齐辅助线：拖拽吸附时显示的浅蓝虚线 */
  --mm-guide-color: rgba(59, 130, 246, 0.65);

  /* ===== 设计令牌 v2（UI 重构 Step 1）：层级 / 缓动 / 表面 ===== */
  /* —— Z-index 七级标尺：取代散落的 4000 与 2147483000 魔法值 —— */
  --z-canvas: 0;
  --z-content: 1;
  --z-sticky: 50;
  --z-bar: 100;
  --z-drawer: 200;
  --z-popover: 1000;
  --z-modal: 2000;
  --z-toast: 3000;
  --z-overlay-tool: 9000;   /* 拖拽指示线 / 日历等须永远压顶的工具层 */

  /* —— 统一缓动与时长 —— */
  --ease-standard: cubic-bezier(0.34, 0.12, 0.18, 1);  /* 带微回弹 */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);    /* 强调动作轻微 overshoot */
  --dur-fast: 140ms;
  --dur-base: 220ms;
  --dur-slow: 320ms;

  /* —— 表面与可见描边（弹层 / 浮层复用，替代各自硬编码底色） —— */
  --surface-raised: var(--panel-bg);
  --border-strong: #E5E7EB;
  /* —— 输入框专用令牌（控件质感收敛：不引入新灰阶，全部引用既有令牌） —— */
  --input-sunken: var(--bg-color);                                      /* 凹陷井：复用底纸色，浮板上的控件像"陷进去" */
  --input-radius: 8px;
  --input-pad: 6px 10px;   /* 高密度表单：去掉肥厚内边距，控件收敛到 32-36px */
  --input-line-height: 1.65;
  --input-focus-ring: 0 0 0 3px color-mix(in srgb, var(--primary-color) 18%, transparent);
  /* —— 全站统一悬停提示：亮色深底浅字，95% 不透明；暗色在下方整组翻转 —— */
  --tooltip-bg: rgba(30, 41, 59, 0.95);
  --tooltip-fg: #FFFFFF;
  --tooltip-border: rgba(255, 255, 255, 0.10);
  --tooltip-track: var(--border-strong);
}

[data-theme="dark"] {
  /* 暗色绝不能还是黑底白字：反转为浅灰底 + 深字，才不会和深色页面融在一起 */
  --tooltip-bg: rgba(226, 232, 240, 0.95);
  --tooltip-fg: #0F172A;
  --tooltip-border: rgba(15, 23, 42, 0.10);
  --tooltip-track: color-mix(in srgb, var(--text-muted) 55%, transparent);
  --bg-color: #121212;
  --panel-bg: #1E1E24;
  --hover-bg: #374151;
  --text-color: #F9FAFB;
  --text-muted: #9CA3AF;
  --text-placeholder: #6B7280;
  --border-color: rgba(255, 255, 255, 0.08);
  --primary-color: #F9FAFB;
  --primary-hover: #E5E7EB;
  --accent-color: #9CA3AF;
  --overlay-color: rgba(0, 0, 0, 0.3);        /* 画布底纹：深色仍作极淡层 */
  --scrim-color: rgba(0, 0, 0, 0.5);          /* 遮罩：深色 50% 黑 */
  /* 异形节点（clip-path）的阴影：box-shadow 会被裁掉，只能换成 drop-shadow */
  --mm-node-drop: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.55));
  --mm-node-drop-hover: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.65));

  /* —— 设计令牌 v2 暗色覆盖：表面 / 描边 / 弥散阴影（UI 重构 Step 1） —— */
  --border-strong: #334155;
  --surface-raised: var(--panel-bg);
  /* 深色弹窗：同底色，仅加大扩散 + 极弱白色提亮层（不换灰阶层级） */
  --shadow-pop: 0 24px 48px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.02);
  --shadow-drawer: -8px 0 32px rgba(0, 0, 0, 0.45), 0 0 1px rgba(17, 24, 39, 0.10);
  --shadow-float: 0 2px 8px rgba(0, 0, 0, 0.30), 0 12px 28px rgba(0, 0, 0, 0.40);
  /* 深色浮层：同底色 + 更大扩散 + 微弱白色提亮层 */
  --shadow-popover: 0 10px 28px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 255, 255, 0.02);
  --vd-hover: rgba(241, 245, 249, 0.04);
  /* —— 暗色下语义色覆盖 —— */
  --role-extra-bg: #334155;
  --role-extra-fg: #cbd5e1;
  --status-draft: #8FB8DC;
  --status-revise: #E0AE72;
  --move-dead-bg: #334155; --move-dead-fg: #94a3b8; --move-dead-bd: #475569;
  --move-dark-bg: #a78bfa; --move-dark-fg: #1e1b4b;
  --move-awake-bg: #fbbf24; --move-awake-fg: #422006;
  /* —— 档案纸张暗色：不再单独配暖色板 ——
     :root 里的 --paper-* 已全部锚定全局令牌，这里随 [data-theme="dark"] 自动翻转，
     档案页与全站保持同一套灰白/深色表面，不做特殊化。 */
  /* 脱敏遮挡条：跨双模式恒为常量（见浅色块注释） */
  --paper-censor: #1F2937;
  --paper-censor-fg: #EADFC4;
}

/* ===================================================================
   UI 高级化重构 · Step 2：全站统一组件原语（Badge / Pill / Popover / Menu / Tooltip）
   —— 仅定义"词汇"层，零侵入；具体组件在各自区块引用这些令牌 / 基类即实现大一统。
   =================================================================== */
.badge { display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; border-radius: var(--radius-pill); font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; vertical-align: middle; flex-shrink: 0; }
.pill  { display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; border-radius: var(--radius-pill); font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; vertical-align: middle; flex-shrink: 0; }
.pop-layer { background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: var(--popover-radius); box-shadow: var(--popover-shadow); }
.menu-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 10px; box-sizing: border-box; border: none; border-radius: var(--radius-sm); background: transparent; color: var(--text-color); font-family: inherit; font-size: .8rem; text-align: left; white-space: nowrap; cursor: pointer; transition: background .15s ease, color .15s ease; }
.menu-item:hover { background: var(--hover-bg); }
.menu-item.danger { color: var(--danger-color); }
.menu-item.danger:hover { background: color-mix(in srgb, var(--danger-color) 12%, transparent); }
/* 危险菜单项：把各组件自写的危险态收编到同一套视觉 */
.tl-lane-menu-danger, .tl-quick-menu-item.is-confirm, .tl-qm-del, .mm-node-menu-item.danger, .menu-item.danger { color: var(--danger-color); }
.tl-lane-menu-danger:hover, .tl-quick-menu-item.is-confirm:hover, .tl-qm-del:hover, .mm-node-menu-item.danger:hover, .menu-item.danger:hover { background: color-mix(in srgb, var(--danger-color) 12%, transparent); }
/* 全站统一悬停提示：原生 title 由 js/ui/tooltip.js 的全局委托升级为 data-tip，
   再统一渲染成 .app-tooltip。双模配色走 --tooltip-* 令牌，不随页面底色翻转。
   visibility 而非 display 控制显隐：元素常驻 DOM 才能先量尺寸再做防溢出定位。 */
.app-tooltip {
  position: fixed; left: 0; top: 0; box-sizing: border-box;
  max-width: 240px; padding: 6px 10px;
  border: 1px solid var(--tooltip-border); border-radius: 8px;
  background: var(--tooltip-bg); color: var(--tooltip-fg);
  font-family: inherit; font-size: 12px; line-height: 1.5; font-weight: 500;
  white-space: pre-line; overflow-wrap: anywhere; word-break: break-word; text-align: left;
  box-shadow: var(--shadow-popover);
  pointer-events: none; opacity: 0; visibility: hidden;
  transition: opacity 120ms ease, visibility 120ms ease;
  z-index: calc(var(--z-popover) + 50);
}
.app-tooltip.is-visible { opacity: 1; visibility: visible; }
.app-tooltip.is-long { white-space: pre-line; }

/* ===================================================================
   UI 高级化重构 · Step 1：层级基类与统一动效
   这些基类当前仅定义"词汇"，尚未挂到任何组件上（对现有界面零侵入）；
   Step 2 把 Modal / Drawer / Popover 改为继承它们后，视觉与层级才生效。
   =================================================================== */
/* —— 统一遮罩：Modal / Drawer 共用，毛玻璃 + 半透明 —— */
.overlay-scrim {
  position: fixed; inset: 0;
  background: var(--scrim-color);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  z-index: var(--z-modal);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard);
  pointer-events: none;
}
.overlay-scrim.is-open { opacity: 1; pointer-events: auto; }

/* —— 统一浮层：Popover / 菜单 / 提示共用 —— */
.pop-layer {
  position: fixed;
  background: var(--surface-raised);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-popover);
  z-index: var(--z-popover);
}

/* —— 统一进出场动效（Step 2 组件将引用这些 keyframes / 工具类） —— */
@keyframes layer-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes layer-pop-in {
  from { opacity: 0; transform: scale(0.96) translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
@keyframes layer-drawer-in {
  from { opacity: 0.4; transform: translateX(24px); }
  to   { opacity: 1; transform: none; }
}
.layer-anim-pop { animation: layer-pop-in var(--dur-base) var(--ease-standard); }
.layer-anim-fade { animation: layer-fade var(--dur-fast) var(--ease-standard); }
.layer-anim-drawer { animation: layer-drawer-in var(--dur-base) var(--ease-standard); }

/* 导出安全：html-to-image 拍 PNG/SVG 前给捕获根加 .export-clean，
   切除弥散阴影 / 滤镜 / 毛玻璃，避免阴影溢出边界或透明残留 */
.export-clean,
.export-clean * {
  box-shadow: none !important;
  filter: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* —— Step 2：统一浮层 / 菜单进出场（替代各自无动画或生硬动画） —— */
.tl-filter-pop, .tl-search-drop, .tl-settings-pop, .tl-pace-view-pop,
.tl-lane-menu, .tl-quick-menu, .ol-links-pop.tl-char-pop,
.relation-detail-popup, .topbar-more-menu, .vd-pop, .legend-more-menu {
  animation: layer-pop-in var(--dur-base) var(--ease-standard);
}

/* —— 无痕滚动 · 暴力兜底（UI 重构 Step 2 补强）——
   凡类名含 modal / pop / drawer / cp- / cdp / dossier / menu / inspector /
   overlay / scrim 的元素【及其所有后代】，强制隐藏滚动条（!important），
   但保留滚轮 / 触控 / 键盘滑动；保留 scrollbar-gutter: stable 防横向跳动。
   这样即使“外壳不滚、真正滚动的是内层节点”（如 .timeline-inspector 内层、
   抽屉内层 .outline）也能一网打尽。
   白名单（绝不波及）：画布 mm-/tl-/timeline-canvas 等、宽屏大纲 .sidebar 宽屏态
   —— 这些类名不含上述关键字，天然被排除。 */
[class*="modal"], [class*="pop"], [class*="drawer"], [class*="cp-"], [class*="cdp"],
[class*="dossier"], [class*="menu"], [class*="inspector"], [class*="overlay"], [class*="scrim"],
[class*="modal"] *, [class*="pop"] *, [class*="drawer"] *, [class*="cp-"] *, [class*="cdp"] *,
[class*="dossier"] *, [class*="menu"] *, [class*="inspector"] *, [class*="overlay"] *, [class*="scrim"] * {
  scrollbar-width: none !important;          /* Firefox */
  -ms-overflow-style: none !important;       /* IE 10+ */
  scrollbar-gutter: stable;                  /* 防跳动；非滚动容器自动失效 */
}
[class*="modal"]::-webkit-scrollbar, [class*="pop"]::-webkit-scrollbar,
[class*="drawer"]::-webkit-scrollbar, [class*="cp-"]::-webkit-scrollbar, [class*="cdp"]::-webkit-scrollbar,
[class*="dossier"]::-webkit-scrollbar, [class*="menu"]::-webkit-scrollbar, [class*="inspector"]::-webkit-scrollbar,
[class*="overlay"]::-webkit-scrollbar, [class*="scrim"]::-webkit-scrollbar,
[class*="modal"] *::-webkit-scrollbar, [class*="pop"] *::-webkit-scrollbar,
[class*="drawer"] *::-webkit-scrollbar, [class*="cp-"] *::-webkit-scrollbar, [class*="cdp"] *::-webkit-scrollbar,
[class*="dossier"] *::-webkit-scrollbar, [class*="menu"] *::-webkit-scrollbar, [class*="inspector"] *::-webkit-scrollbar,
[class*="overlay"] *::-webkit-scrollbar, [class*="scrim"] *::-webkit-scrollbar {
  display: none !important;                  /* Chrome / Safari / Webkit */
}

/* 补充：角色筛选条等内联横向滚动条（非弹窗/浮层，但属“全站其他 UI”同样需无痕） */
.char-filter-controls, .char-filter-controls * {
  scrollbar-width: none !important; -ms-overflow-style: none !important; scrollbar-gutter: stable;
}
.char-filter-controls::-webkit-scrollbar, .char-filter-controls *::-webkit-scrollbar { display: none !important; }

[data-theme="dark"] .sidebar {
  background: color-mix(in srgb, var(--panel-bg) 70%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-color: color-mix(in srgb, var(--border-color) 50%, transparent);
}
[data-theme="dark"] .chapter-item,
[data-theme="dark"] .scene-item,
[data-theme="dark"] .character-card,
[data-theme="dark"] .timeline-event,
[data-theme="dark"] .memo-item {
  background: color-mix(in srgb, var(--panel-bg) 82%, transparent);
  color: var(--text-color);
  text-shadow: 0 1px 2px rgba(0,0,0,0.5);
}
[data-theme="dark"] .mm-node-body,
[data-theme="dark"] .timeline-event div,
[data-theme="dark"] .character-card div {
  color: var(--text-color);
}
/* 深色下角色卡片的层级不能被上一条"div 全刷白"抹平：小字、输入框各自回自己的颜色。
   卡片单独吃实心抬升面（--panel-bg 暗色 #1E1E24），叠在半透明组规则之后，
   与描边 --border-strong（暗色 #334155）、画布 --bg-color（暗色 #121212）形成三级层次，绝不糊在一起。 */
[data-theme="dark"] .character-card { background: var(--panel-bg); border-color: var(--border-strong); }
[data-theme="dark"] .character-card .char-alias,
[data-theme="dark"] .character-card .char-appearance { color: var(--text-muted); }
[data-theme="dark"] .character-card .cc-line--background { color: var(--text-muted); }
/* 索引速览新增部件的深色适配：仍是黑白灰，只是把灰阶反过来 */
[data-theme="dark"] .character-card .cc-basic-badge,
[data-theme="dark"] .character-card .char-head-badge { color: var(--text-muted); }
[data-theme="dark"] .character-card .character-name { color: var(--text-color); }
/* 深色主题下仍保留角色专属标识色与白字，只把白边调成深灰 */
[data-theme="dark"] .character-avatar { border-color: var(--border-strong); color: var(--on-accent); }
[data-theme="dark"] .cc-footer { border-top-color: var(--border-strong); }
[data-theme="dark"] .cc-jump-btn, [data-theme="dark"] .cc-more-btn { color: var(--text-muted); }
[data-theme="dark"] .cc-jump-btn:hover, [data-theme="dark"] .cc-more-btn:hover { color: var(--text-color); background: var(--hover-bg); }
[data-theme="dark"] .character-card .char-faction { background: var(--role-extra-bg); border-color: var(--border-strong); color: var(--role-extra-fg); }
[data-theme="dark"] .character-card .char-inline { color: var(--text-color); }
/* 属性面板新增部件的深色适配：灰阶反过来，层级依旧是"浅灰标签 + 深灰数值" */
[data-theme="dark"] .character-card .info-row .label { color: var(--text-muted); }
[data-theme="dark"] .character-card .info-row .cc-value { color: var(--text-color); }
[data-theme="dark"] .character-card .cc-value.value-name { color: var(--text-color); }
[data-theme="dark"] .character-card .tag-pill { background: var(--role-extra-bg); border-color: var(--border-strong); color: var(--role-extra-fg); }
[data-theme="dark"] .character-card .tag-role--extra { background: var(--role-extra-bg); color: var(--role-extra-fg); }
[data-theme="dark"] .character-card .char-appearance { color: var(--text-muted); }
[data-theme="dark"] .btn {
  background: var(--hover-bg);
  color: var(--text-color);
  border-color: transparent;
}
[data-theme="dark"] .memo-item {
  background: var(--panel-bg);
  border-left-color: var(--border-color);
}
[data-theme="dark"] .memo-item.done {
  background: var(--bg-color);
  border-left-color: var(--border-color);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
/* ========== 极简滚动条（全局） ========== */
html { scrollbar-width: thin; scrollbar-color: rgba(0,0,0,0.15) transparent; }
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.28); }
[data-theme="dark"] html { scrollbar-color: rgba(255,255,255,0.2) transparent; }
[data-theme="dark"] ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 10px; }
[data-theme="dark"] ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.35); }
body {
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--text-color);
  background: var(--bg-color);
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
  height: 100vh;
  overflow: hidden;
}
body::before {
  content: "";
  position: fixed;
  top: 0; left: 0; width: 100%; height: 100%;
  pointer-events: none;
  background-image: none;
  opacity: 0.2;
  z-index: -1;
}
/* 首页 */
.home-page { display: flex; min-height: 100vh; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px; }
.home-page.hidden { display: none; }
.home-logo { font-size: 2.5rem; font-weight: 700; color: var(--primary-color); margin-bottom: 1rem; display: flex; align-items: center; gap: 12px; }
.home-logo i { align-self: center; }
.home-logo-title { color: var(--primary-color); }
.home-logo-en { font-size: 1.05rem; font-weight: 300; color: var(--text-muted); letter-spacing: 3px; text-transform: lowercase; font-family: var(--font-sans); margin-left: 2px; }
.project-list-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
.btn-import { border: 1px solid var(--border-strong); background: transparent; color: var(--text-color); padding: 6px 12px; border-radius: var(--radius-btn); cursor: pointer; display: inline-flex; align-items: center; gap: 6px; font-size: 0.8rem; transition: background 0.18s ease, border-color 0.18s ease; }
.btn-import:hover { background: var(--hover-bg); }
.btn-import:active { transform: scale(0.98); }
.home-desc { color: var(--text-muted); margin-bottom: 2rem; text-align: center; max-width: 500px; }
.home-btn { padding: 12px 28px; border-radius: var(--radius-btn); border: none; background: var(--primary-color); color: var(--panel-bg); font-size: 1rem; cursor: pointer; margin: 10px; transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.18s ease; }
.home-btn:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.home-btn:active { transform: scale(0.98); }
.home-btn.secondary { background: transparent; color: var(--text-color); border: 1px solid var(--border-strong); }
.home-btn.secondary:hover { background: var(--hover-bg); transform: none; box-shadow: none; }
/* 设置面板下拉框：改为统一定义（见上方 select 收编规则），此处不再单独写皮 */
.project-list { width: 100%; max-width: 700px; background: var(--panel-bg); border-radius: var(--radius-lg); padding: 20px; box-shadow: var(--shadow-sm); display: none; margin-top: 20px; max-height: 60vh; overflow-y: auto; }
.project-list.active { display: block; }
.project-item { display: flex; justify-content: space-between; align-items: center; padding: 15px; border-bottom: 1px solid var(--border-color); transition: 0.2s; }
.project-item:hover { background: var(--bg-color); }
.project-item:last-child { border-bottom: none; }
.project-actions { display: flex; gap: 5px; }
/* 编辑器 */
.editor-page { display: none; height: 100vh; flex-direction: column; }
.editor-page.active { display: flex; }
.top-bar { height: var(--header-height); background: var(--panel-bg); border-bottom: 1px solid var(--border-color); display: flex; align-items: center; justify-content: space-between; padding: 0 20px; flex-shrink: 0; z-index: var(--z-bar); }
.top-bar-left { display: flex; align-items: center; gap: 15px; }
.top-bar-center { display: flex; gap: 8px; }
.top-bar-right { display: flex; gap: 8px; align-items: center; }
/* 顶部栏按钮：完全扁平、无边框、无底色，纯图标/文本，hover 微弱浅灰圆角底 */
.top-bar-left .btn, .top-bar-right .btn {
  border: none;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
  color: var(--text-muted);
}
.top-bar-left .btn:hover, .top-bar-right .btn:hover {
  background: color-mix(in srgb, var(--text-color) 8%, transparent);
  border-color: transparent;
  color: var(--text-color);
}
.view-tabs { display: flex; background: var(--hover-bg); border-radius: 8px; padding: 4px; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border-color) 55%, transparent); }
.view-tab { padding: 6px 14px; border-radius: 6px; cursor: pointer; transition: 0.2s; font-size: 0.85rem; display: flex; align-items: center; gap: 6px; color: var(--text-muted); }
.view-tab:hover { background: color-mix(in srgb, var(--text-color) 6%, transparent); }
.view-tab.active { background: var(--panel-bg); color: var(--primary-color); box-shadow: var(--shadow-sm); }
[data-theme="dark"] .view-tab.active { background: var(--panel-bg); color: var(--primary-color); }
.main-container { flex: 1; display: flex; overflow: hidden; position: relative; }
.sidebar { width: var(--sidebar-width); background: color-mix(in srgb, var(--panel-bg) 70%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-right: 1px solid color-mix(in srgb, var(--border-color) 50%, transparent); display: flex; flex-direction: column; transition: width 0.25s ease-out, transform 0.25s ease-out; flex-shrink: 0; box-shadow: var(--shadow-sm); }
.sidebar.right { border-right: none; border-left: 1px solid color-mix(in srgb, var(--border-color) 50%, transparent); }
.sidebar.collapsed { width: 0 !important; overflow: hidden; border: none; }
.sidebar.right { border-right: none; border-left: 1px solid color-mix(in srgb, var(--border-color) 50%, transparent); }
.panel-header { padding: var(--space-md); border-bottom: 1px solid transparent; font-weight: 600; display: flex; justify-content: space-between; align-items: center; font-family: var(--font-sans); font-size: 1.05rem; color: var(--text-color); }
.panel-content { flex: 1; overflow-y: auto; overflow-x: hidden; padding: var(--space-md); }
.canvas-area { flex: 1; position: relative; background-color: var(--bg-color); overflow: auto; }
.canvas-area::after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: var(--overlay-color); pointer-events: none; z-index: 0; }
.canvas-wrapper, #svg-connections, #nodes-container { position: relative; z-index: 1; }
/* ===== 层叠契约（改这里之前先读完，它决定"线会不会盖住节点"）=====
   .canvas-wrapper 是唯一的定位父级，内部只有三个固定层：
     0 层 #svg-connections —— 所有连线、箭头、标签、删除按钮
     1 层 #nodes-container —— 所有节点卡片（.mm-node 自带 z-index:1，拖拽时临时提到 100）
     2 层 #selection-box   —— 框选矩形
   两个兄弟都带 position+z-index，各自成栈，因此卡片永远画在连线之上；节点底色是实色，
   粗线走到边缘锚点就停（终点回缩一个箭头长度），既不会穿进卡片，也不会从卡片里透出来。
   任何新增的画布覆盖层都必须显式登记在这个序列里，不要靠 DOM 顺序赌运气。 */
.canvas-wrapper { position: relative; min-width: 2000px; min-height: 2000px; transform-origin: 0 0; }
#svg-connections { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; overflow: visible; }
#nodes-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; }
.view-container { display: none; width: 100%; height: 100%; overflow: auto; }
#timeline-container { position: relative; }
#timeline-container .timeline-content { height: calc(100vh - var(--header-height) - 56px); overflow: auto; }
.view-container.active { display: block; }
/* 时间线：工具栏 + 模式切换 */
.timeline-controls {
  position: sticky; top: 0; z-index: 50; height: 56px;
  background: var(--panel-bg); border-bottom: 1px solid var(--border-color);
  display: flex; justify-content: space-between; align-items: center; padding: 0 20px; gap: 12px;
}
/* 模式切换 / 视图切换：Xmind 式分段控制器（Segmented Control）。浅灰槽 + 选中白胶囊，图标 + 文字，无描边 */
.tl-mode-toggle { display: inline-flex; align-items: center; gap: 2px; padding: 3px; background: var(--hover-bg); border-radius: 9px; }
[data-theme="dark"] .tl-mode-toggle { background: var(--hover-bg); }
[data-theme="dark"] .tl-mode-toggle button:not(.active):hover,
[data-theme="dark"] .timeline-actions .btn:not(.btn-primary):hover { background: var(--hover-bg); }
.tl-mode-toggle button {
  border: none; background: transparent; color: var(--text-muted); cursor: pointer;
  height: 28px; padding: 0 12px; border-radius: 6px; font-size: 13px; font-family: inherit; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; transition: color .15s, background .15s;
}
.tl-mode-toggle button i { font-size: 12px; }
.tl-mode-toggle button:hover { color: var(--text-color); }
.tl-mode-toggle button:not(.active):hover { background: var(--hover-bg); }
.tl-mode-toggle button.active { color: var(--text-color); background: var(--panel-bg); box-shadow: 0 1px 2px rgba(0,0,0,.05); }
.tl-mode-toggle .tl-btn-label { font-size: 13px; }
/* 工具栏三分区：左（模式/画布列表/筛选）｜中（视图设置/搜索）｜右（添加轨道/添加事件）。
   controls 是 space-between，三个区天然落成 左｜中｜右；区内用 gap 隔开各按钮。 */
.tl-bar-left, .tl-bar-center, .tl-bar-right { display: flex; align-items: center; }
.tl-bar-left { gap: 24px; }
.tl-bar-center { gap: 18px; }
.tl-bar-right { gap: 12px; }
.tl-btn-label { font-size: 13px; line-height: 1; white-space: nowrap; }
.timeline-actions { display: flex; align-items: center; gap: 12px; }
/* 核心操作：添加轨道走幽灵按钮（灰字无底无框），添加事件是全界面唯一纯黑实心主按钮 */
.timeline-actions .btn { border-radius: 8px; font-size: 13px; padding: 7px 14px; }
.timeline-actions .btn:not(.btn-primary) { border: none; background: transparent; color: var(--text-muted); }
.timeline-actions .btn:not(.btn-primary):hover { color: var(--text-color); background: var(--hover-bg); border-color: transparent; }
.timeline-actions .btn-primary { background: var(--primary-color); border-color: var(--primary-color); color: var(--panel-bg); }
.timeline-actions .btn-primary:hover { background: color-mix(in srgb, var(--primary-color), var(--panel-bg) 12%); border-color: color-mix(in srgb, var(--primary-color), var(--panel-bg) 12%); }
/* 工具栏按钮：轨道筛选 / 视图设置共用，图标 + 文字 + 下拉小箭头，无框无阴影，hover 才浮出浅底 */
.tl-tool-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 32px; padding: 0 12px; border: none; background: transparent;
  color: var(--text-muted); cursor: pointer; border-radius: var(--radius-btn); font-family: inherit; font-size: 13px;
  transition: color .15s, background .15s;
}
.tl-tool-btn:hover { color: var(--text-color); background: var(--bg-color); }
.tl-tool-btn.active { color: var(--text-color); background: transparent; }
.tl-tool-btn.hidden { display: none; }
.tl-tool-btn svg { width: 15px; height: 15px; }
/* 角色投影：工具栏直 toggle 按钮——关：透明底线框灰像灰字；开：浅灰圆角底+实心黑像黑字 */
.tl-proj-toggle { background: transparent; color: var(--text-muted); border: none; }
.tl-proj-toggle:hover { background: transparent; color: var(--text-color); }
.tl-proj-toggle.on { background: var(--hover-bg); color: var(--text-color); }
.tl-proj-toggle.on:hover { background: color-mix(in srgb, var(--hover-bg), var(--text-color) 8%); }
[data-theme="dark"] .tl-proj-toggle.on { background: var(--hover-bg); }
[data-theme="dark"] .tl-proj-toggle.on:hover { background: color-mix(in srgb, var(--hover-bg), var(--text-color) 8%); }
.tl-proj-ic { display: inline-flex; }
.tl-proj-ic svg { width: 14px; height: 14px; }
.tl-dropdown-btn { padding: 0 10px; }
.tl-caret { display: inline-flex; opacity: .5; }
.tl-caret svg { width: 9px; height: 9px; }
.tl-f-dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto; }
/* 轨道筛选下拉浮层：全部 / 主线 / 支线 / 角色线 */
.tl-filter-pop {
  display: flex; flex-direction: column; gap: 1px; min-width: 148px; padding: 4px;
  background: var(--panel-bg); border: 1px solid var(--border-color);
  border-radius: 4px; box-shadow: var(--shadow-md); z-index: var(--z-popover);
}
.tl-filter-opt { display: flex; align-items: center; gap: 7px; width: 100%; padding: 6px 8px; border: none; background: transparent; color: var(--text-color); font-family: inherit; font-size: 0.78rem; text-align: left; border-radius: 2px; cursor: pointer; }
.tl-filter-opt:hover { background: var(--hover-bg); }
.tl-filter-opt.on { color: var(--primary-color); }
.tl-filter-check { flex: 0 0 auto; width: 13px; display: inline-flex; }
.tl-filter-opt-name { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 时间线搜索：常态只是一颗放大镜图标，点击向右展开输入框；失焦且无查询时收回。搜索只改视线，不改缩放/滚动/筛选 */
.tl-search { position: relative; display: inline-flex; align-items: center; margin-left: 4px; }
.tl-search-toggle {
  display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px;
  padding: 0; border: none; background: transparent; color: var(--text-muted); cursor: pointer;
  border-radius: 2px; transition: color .15s, background .15s; flex: 0 0 auto;
}
.tl-search-toggle:hover { color: var(--text-color); background: transparent; }
.tl-search-toggle svg { width: 15px; height: 15px; }
.tl-search.tl-search--active .tl-search-toggle { color: var(--primary-color); }
.tl-search-field {
  display: inline-flex; align-items: center; gap: 6px; overflow: hidden;
  max-width: 0; opacity: 0; padding: 0; border-bottom: 1px solid transparent;
  transition: max-width .2s ease, opacity .2s ease, padding .2s ease;
}
.tl-search.tl-search--open .tl-search-field { max-width: 260px; opacity: 1; padding: 0 4px 0 8px; border-bottom-color: var(--border-color); }
.tl-search.tl-search--open .tl-search-field:focus-within { border-bottom-color: var(--primary-color); }
.tl-search-input { flex: 1 1 auto; min-width: 0; width: 150px; border: none; background: transparent; color: var(--text-color); font-family: inherit; font-size: 0.78rem; outline: none; padding: 4px 0; }
.tl-search-input::placeholder { color: var(--text-muted); opacity: .55; }
.tl-search-count { flex: 0 0 auto; font-size: 0.68rem; color: var(--text-muted); white-space: nowrap; }
.tl-search-count.hidden, .tl-search-clear.hidden { display: none; }
.tl-search-clear { flex: 0 0 auto; width: 16px; height: 16px; padding: 0; border: none; background: none; color: var(--text-muted); cursor: pointer; border-radius: 2px; display: inline-flex; align-items: center; justify-content: center; }
.tl-search-clear svg { width: 11px; height: 11px; }
.tl-search-clear:hover { background: var(--hover-bg); color: var(--text-color); }
.tl-search-drop {
  position: absolute; top: calc(100% + 6px); right: 0; width: 320px; max-height: 264px; overflow-y: auto;
  background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: 4px;
  box-shadow: var(--shadow-md); padding: 4px; z-index: var(--z-popover); display: none;
}
.tl-search-drop.open { display: block; }
.tl-search-item { display: flex; flex-direction: row; align-items: center; gap: 8px; width: 100%; padding: 5px 8px; border: none; background: transparent; color: var(--text-color); font-family: inherit; text-align: left; border-radius: 2px; cursor: pointer; }
/* 结果行：左 POV 头像 + 右两行文字。头像单独可点，开角色档案中枢；文字区留着跳事件 */
.tl-search-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.tl-search-ava { flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%; overflow: hidden; display: inline-flex; cursor: pointer; }
.tl-search-ava--empty { cursor: inherit; opacity: .5; }
.tl-search-ava .ol-lp-ava { width: 22px; height: 22px; }
.tl-search-item:hover, .tl-search-item.active { background: var(--hover-bg); }
.tl-search-item.active { box-shadow: inset 0 0 0 1px var(--primary-color); }
.tl-search-name { font-size: 0.78rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-search-note { font-size: 0.66rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-search-item.off .tl-search-name { color: var(--text-muted); }
/* 迷你导航雷达：搜索有结果时挂在画布右下角。可交互（红点可点、视口框可拖），所以要收鼠标事件。面板开着时让到它左边，不压在表单上 */
.tl-minimap { position: absolute; right: 16px; bottom: 16px; z-index: 55; padding: 4px; background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: var(--radius-sm); box-shadow: var(--shadow-md); pointer-events: auto; }
#timeline-container .timeline-inspector:not(.hidden) ~ .tl-minimap { right: 524px; }
.tl-mm-base { fill: var(--bg-color); }
.tl-mm-hit { fill: transparent; cursor: pointer; pointer-events: auto; }
.tl-mm-dot { fill: var(--danger-color); pointer-events: none; }
.tl-mm-view { fill: none; stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 3 2; cursor: grab; pointer-events: stroke; }
.tl-mm-view:active { cursor: grabbing; }
/* 拖拽态 */
.tl-point-card.tl-dragging, .tl-bar.tl-dragging { opacity: 0.35; }
/* 搜索明暗：未命中留 0.4 的结构感（不全透明），并且不可点击；命中加一圈淡蓝描边 */
.tl-point-card.tl-search-dim, .tl-bar.tl-search-dim { opacity: 0.15; pointer-events: none; }
.tl-list-row.tl-search-dim { opacity: 0.15; pointer-events: none; }
.tl-point-card.tl-search-hit, .tl-bar.tl-search-hit { box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.55); }
.tl-list-row.tl-search-hit > td { background: rgba(59, 130, 246, 0.10); }
.tl-list-row.tl-link-flash > td { animation: tl-link-flash .5s ease-out 4; }
.tl-drag-ghost { box-shadow: var(--shadow-sm); }
.tl-lane.tl-drop-hover { background: rgba(59, 130, 246, 0.08); outline: 2px solid var(--primary-color); outline-offset: -2px; }
.tl-drop-indicator { position: absolute; top: 0; width: 2px; background: var(--primary-color); pointer-events: none; z-index: var(--z-overlay-tool); display: none; }
/* 拖拽日期提示：只保留自身定位/显隐，视觉全部继承 .app-tooltip 令牌（旧 .tl-drop-tooltip 已废弃） */
.app-tooltip--drop { position: absolute; top: 6px; left: 8px; display: none; visibility: visible; opacity: 1; white-space: nowrap; }
/* 泳道 + Excel 式冻结框架 */
.timeline-track { position: relative; background: var(--panel-bg); transition: grid-template-rows .18s ease, height .18s ease; }
.tl-corner, .tl-top, .tl-left, .tl-pace, .tl-pace-label { background: var(--bg-color); }
.tl-corner { position: sticky; top: 0; left: 0; z-index: 40; padding: 0 12px; display: flex; align-items: center; gap: 8px; border-bottom: 1px solid rgba(0,0,0,0.05) !important; border-right: 1px solid rgba(0,0,0,0.05) !important; }
.tl-corner-clock { font-size: 0.9rem; color: var(--text-muted); }
.tl-pace-fold-corner { display: inline-flex; align-items: center; gap: 3px; background: none; border: none; cursor: pointer; color: var(--text-muted); padding: 4px 6px; border-radius: 6px; }
.tl-pace-fold-corner:hover { background: var(--hover-bg); color: var(--text-color); }
.tl-pace-fold-corner .fa-chart-line { font-size: 0.85rem; }
.tl-corner-chev { font-size: 0.62rem; transition: transform .15s ease; }
.tl-corner-chev.folded { transform: rotate(-90deg); }
.tl-top { position: sticky; top: 0; z-index: 30; border-bottom: 1px solid rgba(0,0,0,0.05) !important; overflow: hidden; }
.tl-left { position: sticky; left: 0; z-index: 20; border-right: 1px solid rgba(0,0,0,0.05) !important; }
.tl-body { z-index: 1; overflow: hidden; } /* 画布裁切：拖影越过冻结轨道头/表头时被直接切断，不再盖住左侧冻结区 */
/* 故事节奏轨：冻结表头正下方一条贯穿全局的专属轨道，纵向冻住——往下滚多少条泳道，曲线都在眼前。
   带子本身不透明，卡片滚到它下面就被"切断"（和左侧冻结列同一只手）；轨内接手拖拽/框选/双击。 */
.tl-pace { position: sticky; top: 46px; z-index: 25; border-bottom: 1px solid rgba(0,0,0,0.05) !important; overflow: hidden; transition: height .18s ease; cursor: crosshair; }
.tl-pace-label {
  position: sticky; top: 46px; left: 0; z-index: 35; border-right: 1px solid rgba(0,0,0,0.05) !important;
  border-bottom: 1px solid rgba(0,0,0,0.05) !important; display: flex; align-items: center; gap: 6px; padding: 0 4px 0 8px;
  font-size: 0.72rem; color: var(--text-muted); letter-spacing: .02em; overflow: hidden; transition: height .18s ease;
}
/* 折叠态：高度/最小高度/边框/padding 一律归零 + 溢出裁切，SVG 图表与彩色数据点一像素都不露 */
.tl-pace.tl-pace--folded, .tl-pace-label.tl-pace-label--folded {
  height: 0 !important; min-height: 0 !important; border-width: 0 !important; padding: 0 !important; overflow: hidden !important;
}
.tl-pace-fold {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0; border: none; background: transparent;
  color: var(--text-muted); cursor: pointer; border-radius: 6px;
  transition: background .15s ease, color .15s ease;
}
.tl-pace-fold:hover { background: var(--hover-bg); color: var(--text-color); }
.tl-pace-fold-chevron { font-size: 0.62rem; transition: transform .18s ease; }
.tl-pace-fold:not(.open) .tl-pace-fold-chevron { transform: rotate(-90deg); }
/* 「故事节奏 ▾」视角下拉：无边框浅底胶囊，hover 微微染主色；单击弹 openPaceViewPop */
.tl-pace-view-btn {
  flex: 0 1 auto; min-width: 0; display: inline-flex; align-items: center; gap: 4px;
  height: 24px; padding: 0 8px; border: none; background: var(--bg-color);
  color: var(--text-color); cursor: pointer; border-radius: 8px;
  font-size: 0.72rem; font-family: inherit; line-height: 1;
  transition: background .15s ease, color .15s ease;
}
.tl-pace-view-btn:hover { background: color-mix(in srgb, var(--primary-color) 10%, var(--bg-color)); }
.tl-pace-view-btn-name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-pace-view-btn .tl-caret { flex: 0 0 auto; }
.tl-pace-name { flex: 0 0 auto; white-space: nowrap; }
.tl-pace-name--vertical {
  writing-mode: vertical-rl; text-orientation: upright; letter-spacing: .02em;
  font-size: 0.56rem; line-height: 1; color: var(--text-muted); align-self: center;
}
/* [⚙ 视图设置] 浮层：只剩「显示角色投影」一颗开关 */
.tl-settings-pop {
  display: flex; flex-direction: column; gap: 2px; min-width: 180px;
  padding: 6px; background: var(--panel-bg); border: 1px solid var(--border-color);
  border-radius: 8px; box-shadow: var(--shadow-md); z-index: var(--z-popover);
}
/* 复选框与文字对齐：flex + align-items:center，input 固定尺寸 + margin:0 + line-height:1，杜绝基线错位 */
.tl-settings-check { display: flex; align-items: center; gap: 8px; padding: 6px 8px; font-size: 0.8rem; line-height: 1; color: var(--text-color); cursor: pointer; border-radius: 6px; user-select: none; }
.tl-settings-check:hover { background: var(--hover-bg); }
.tl-settings-check input { flex: 0 0 auto; width: 14px; height: 14px; accent-color: var(--primary-color); cursor: pointer; margin: 0; }
/* 节奏视角浮层：挂在「故事节奏 ▾」上，列表自身限高 240px 滚动，滚动条极细 */
.tl-pace-view-pop {
  display: flex; flex-direction: column; min-width: 200px; max-width: 280px;
  padding: 6px; background: var(--panel-bg); border: 1px solid var(--border-color);
  border-radius: 8px; box-shadow: var(--shadow-md); z-index: var(--z-popover);
}
.tl-pace-view-head { padding: 4px 8px 6px; font-size: 0.68rem; color: var(--text-muted); }
.tl-pace-view-list {
  display: flex; flex-direction: column; gap: 1px; max-height: 240px; overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--text-color) 18%, transparent) transparent;
}
.tl-pace-view-list::-webkit-scrollbar { width: 8px; }
.tl-pace-view-list::-webkit-scrollbar-track { background: transparent; }
.tl-pace-view-list::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--text-color) 18%, transparent); border-radius: 8px; border: 2px solid transparent; background-clip: padding-box; }
.tl-pace-view-list::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--text-color) 30%, transparent); background-clip: padding-box; }
.tl-pace-view-opt { display: flex; align-items: center; gap: 6px; width: 100%; padding: 6px 8px; border: none; background: transparent; color: var(--text-color); font-family: inherit; font-size: 0.78rem; text-align: left; border-radius: 6px; cursor: pointer; }
.tl-pace-view-opt:hover { background: var(--bg-color); }
.tl-pace-view-opt.on { color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 8%, transparent); }
.tl-pace-view-check { flex: 0 0 auto; width: 13px; display: inline-flex; }
.tl-pace-view-opt-name { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 固定 Y 轴：住在左侧冻结格右端 30px，横向滚动带不走它；数字与轨内虚线用同一套 tlPaceY 坐标 */
.tl-pace-axis { flex: 0 0 auto; position: relative; width: 30px; align-self: stretch; margin-left: auto; }
.tl-pace-axis i {
  position: absolute; right: 5px; transform: translateY(-50%); font-style: normal;
  font-size: 0.58rem; line-height: 1; opacity: .4; font-variant-numeric: tabular-nums;
}
.tl-pace-svg { display: block; pointer-events: none; }
.tl-pace-gridlines { display: none; }
.tl-pace-band-edge { stroke: var(--border-color); stroke-width: 1; opacity: .25; }
.tl-pace-line { stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; opacity: .9; }
.tl-pace-hoverline { stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; opacity: .5; }
/* 全局视角折线：默认淡显（.25 / 1.5px），悬停由 JS 提亮到 1 / 3px。
   SVG 整体 pointer-events:none，命中路径单独开 stroke 收鼠标；可见细线不抢事件。 */
.tl-pace-trackhit { stroke: transparent; stroke-width: 10; fill: none; pointer-events: stroke; cursor: pointer; }
.tl-pace-trackline { stroke-width: 1.5; opacity: .25; fill: none; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; transition: opacity .15s ease, stroke-width .15s ease; }
/* 轨道名气泡：跟游标走，压在曲线上方；pointer-events:none 绝不打断悬停 */
.tl-pace-line-tip {
  position: absolute; top: 0; transform: translateX(-50%); padding: 2px 8px; border-radius: 6px;
  background: var(--panel-bg); color: var(--text-color); border: 1px solid var(--border-color); font-size: 0.66rem; line-height: 1.5; white-space: nowrap;
  display: none; pointer-events: none; box-shadow: var(--shadow-popover); z-index: 6;
}
/* 去 AI 化：移除渐变底色（ambient / 高潮区 / hoverband）与三幕虚线骨架，只留 1.5px 实线折线 + 实心点，背景透明 */
.tl-pace-ambient, .tl-pace-skeleton, .tl-pace-hoverband, .tl-pace-hotruns { display: none; }
.tl-pace-dot { opacity: 1; }
.tl-pace-sel-ring { fill: none; stroke: var(--primary-color); stroke-width: 1.2; opacity: .9; }
/* 章节名：色带顶端一枚极淡的小按钮，点它跳大纲；色带其余地方仍是空白，留给框选 */
.tl-pace-chapter {
  position: absolute; top: 1px; padding: 0 4px; max-width: 200px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-size: 0.6rem; line-height: 13px; color: var(--text-muted); opacity: .55;
  background: none; border: none; border-radius: 3px; cursor: pointer; text-align: left;
}
.tl-pace-chapter:hover { opacity: 1; background: var(--bg-color); color: var(--text-color); }
.tl-pace-empty {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 0.7rem; color: var(--text-muted); opacity: .75; pointer-events: none;
}
/* 游标与三种气泡：全部 pointer-events:none，绝不抢节奏轨自己的鼠标事件 */
.tl-pace-cursor { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--text-muted); opacity: .35; display: none; pointer-events: none; }
.tl-pace-cursor-tip, .tl-pace-tip, .tl-pace-dragtip {
  position: absolute; top: 1px; transform: translateX(-50%); padding: 1px 6px; border-radius: 4px;
  background: var(--panel-bg); color: var(--text-color); border: 1px solid var(--border-color); font-size: 0.64rem; line-height: 1.5; white-space: nowrap;
  display: none; pointer-events: none; box-shadow: var(--shadow-popover); font-variant-numeric: tabular-nums;
}
.tl-pace-marquee {
  position: absolute; display: none; pointer-events: none;
  border: 1px solid var(--primary-color); background: rgba(59,130,246,.10); border-radius: 2px;
}
.tl-tick { position: absolute; top: 10px; transform: translateX(-50%); font-size: 0.7rem; color: var(--text-muted); white-space: nowrap; }
/* 时间流向指示线：贯穿表头全宽，压在刻度文字下方；右端三角箭头指向右方（时间前进）。主题色半透明。 */
.tl-axis-flow {
  position: absolute; left: 0; right: 0; bottom: 6px; height: 0; pointer-events: none;
  border-top: 1px solid color-mix(in srgb, var(--primary-color) 30%, transparent);
}
.tl-axis-flow::after {
  content: ''; position: absolute; right: 0; top: -4px;
  border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  border-left: 7px solid color-mix(in srgb, var(--primary-color) 55%, transparent);
}
.tl-top-hint { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: var(--text-muted); font-size: 0.8rem; }
/* 分类筛选筛出空结果：泳道一片不画，只在画布正中说一句人话 */
.tl-filter-empty { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); text-align: center; color: var(--text-muted); font-size: 0.8rem; line-height: 1.9; white-space: nowrap; }
.tl-lane { position: absolute; left: 0; border-bottom: 1px solid rgba(0,0,0,0.035) !important; }
.tl-lane-head {
  position: absolute; left: 0; width: 100%; display: flex; align-items: center; gap: 6px; padding: 0 10px;
}
.tl-lane-head:hover { background: var(--hover-bg); }
.tl-lane-swatch { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; box-shadow: none; }
.tl-lane-name { flex: 1; min-width: 0; background: transparent; border: 1px solid transparent; color: var(--text-color); font-size: 0.8rem; padding: 2px 4px; border-radius: 4px; }
.tl-lane-name:hover, .tl-lane-name:focus { border-color: var(--border-color); background: var(--bg-color); }
/* 轨道名常态是纯文本：与编辑态 input 同一套 flex/padding/边框占位，双击换框时布局不跳；长名省略号，hover 浮出浅底提示可编辑 */
.tl-lane-name-text { flex: 1; min-width: 0; background: transparent; border: 1px solid transparent; color: var(--text-color); font-size: 0.8rem; padding: 2px 4px; border-radius: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: text; }
.tl-lane-name-text:hover { border-color: var(--border-color); background: var(--bg-color); }
.tl-lane-grip {
  flex: 0 0 auto; width: 14px; text-align: center; color: var(--text-muted);
  cursor: grab; opacity: .4; font-size: .72rem; user-select: none;
}
.tl-lane-grip:hover { opacity: 1; color: var(--primary-color); }
body.tl-ordering, body.tl-ordering * { cursor: grabbing !important; user-select: none; }
.tl-lane-actions { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.tl-lane-eye, .tl-lane-lock, .tl-lane-more { background: none; border: none; color: var(--text-muted); cursor: pointer; padding: 2px 6px; font-size: 14px; opacity: 0; transition: opacity .15s ease; }
.tl-lane-head:hover .tl-lane-eye, .tl-lane-head:hover .tl-lane-lock, .tl-lane-head:hover .tl-lane-more,
.tl-lane-eye:focus-visible, .tl-lane-lock:focus-visible, .tl-lane-more:focus-visible { opacity: 1; }
.tl-lane-eye:hover { color: var(--primary-color); }
.tl-lane-lock:hover { color: var(--warning-color); }
.tl-lane-more:hover { color: var(--text-color); }
.tl-lane-head-hidden { opacity: 0.6; }
/* 轨道头「…」菜单：分类 / 改色 / 删除。挂在 body 上，用 fixed 贴着按钮，不受冻结列 overflow 裁剪 */
.tl-lane-menu {
  position: fixed; z-index: var(--z-popover); width: 250px; padding: 8px;
  background: var(--panel-bg); border: 1px solid var(--border-color);
  border-radius: 4px; box-shadow: var(--shadow-popover);
  display: flex; flex-direction: column; gap: 14px;
}
.tl-lane-menu-sec { display: flex; flex-direction: column; gap: 3px; }
.tl-lane-menu-label { font-size: .68rem; color: var(--text-muted); padding-left: 2px; }
.tl-lane-menu-row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
/* 轨道分类三个按钮强制单行、1:1:1 等宽、不换行、居中 */
.tl-lane-menu-types { flex-wrap: nowrap; justify-content: space-between; gap: 8px; }
.tl-lane-menu-types .tl-lane-menu-item { flex: 1 1 0; justify-content: center; text-align: center; padding: 4px 2px; min-width: 0; white-space: nowrap; }
.tl-lane-menu-types .tl-lane-menu-item span { white-space: nowrap; }
/* 「默认跟随角色色」：整行第一个特殊色块（虚线主色框 + 角色色圆环），不占满整行 */
.tl-lane-menu-char-color { border: 1px dashed var(--primary-color); background: transparent; }
.tl-lane-menu-char-color:hover { outline: 2px solid var(--primary-color); outline-offset: 1px; }
.tl-lane-menu-char-ring { display: block; width: 11px; height: 11px; margin: 0 auto; border-radius: 50%; background: var(--c); box-shadow: inset 0 0 0 2px rgba(255,255,255,.75); }
.tl-lane-menu-item {
  flex: 1 1 auto; display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid transparent; background: transparent; color: var(--text-color);
  font-family: inherit; font-size: .76rem; padding: 4px 6px; border-radius: 3px; cursor: pointer;
}
.tl-lane-menu-item:hover { background: var(--hover-bg); }
.tl-lane-menu-item.on { border-color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
.tl-lane-menu-danger { color: var(--danger-color); justify-content: center; flex: 0 0 100%; }
.tl-lane-menu-danger:hover { background: color-mix(in srgb, var(--danger-color) 10%, transparent); }
.tl-menu-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.tl-menu-dot-none { background: transparent; border: 1px solid var(--border-color); }
/* 轨道分类图标：统一纯黑——主线实心圆、支线空心圆环、角色线人像；约14px并垂直居中 */
.tl-type-ic {
  display: inline-block; width: 14px; min-width: 14px; height: 14px; min-height: 14px;
  border-radius: 50%; vertical-align: middle; margin-right: 8px; box-sizing: border-box;
}
.tl-type-ic--solid { background-color: var(--text-color); }
.tl-type-ic--hollow { border: 1.5px solid var(--text-color); background-color: transparent; }
.tl-type-ic--person { color: var(--text-color); }
.tl-type-ic--person svg { width: 13px; height: 13px; display: block; margin: 0 auto; }
.tl-lane-menu-swatch { box-sizing: border-box; width: 18px; height: 18px; border: 1px solid var(--border-color); border-radius: 3px; padding: 0; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.tl-lane-menu-swatch:hover { outline: 2px solid var(--primary-color); outline-offset: 1px; }
/* 颜色整行：单行 nowrap 紧凑收纳（1 个角色色块 + 8 预设 + 末尾「+」） */
.tl-lane-menu-colors { flex-wrap: nowrap; gap: 6px; }
.tl-lane-menu-picker-btn { box-sizing: border-box; width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; border: 1px dashed var(--border-color); border-radius: 3px; background: var(--bg-color); color: var(--text-muted); font-size: .7rem; cursor: pointer; }
.tl-lane-menu-picker-btn:hover { color: var(--primary-color); border-color: var(--primary-color); }
.tl-lane-menu-picker-hidden { position: absolute; width: 0; height: 0; opacity: 0; padding: 0; border: 0; pointer-events: none; }
/* 区间条（下层） */
.tl-bar { position: absolute; background: color-mix(in srgb, var(--track-color, var(--text-muted)) 6%, var(--panel-bg)); border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); padding: 6px 8px; overflow: hidden; cursor: grab; }
.tl-point-card:not(.tl-mirror)::before, .tl-bar:not(.tl-mirror)::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--track-color, var(--primary-color)); z-index: 1;
  /* 纯装饰色条：别让 3px 的伪元素抢走卡片左缘的点击 */
  pointer-events: none;
}
.tl-point-card.tl-derived::before { width: 4px; }
.tl-bar-title { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; }
.tl-bar-date { font-size: 11px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; }
/* 点事件卡片（置顶浮层）：扁平化——极浅底（bg-color 加深 5%）、2px 圆角、左侧 3px 轨道色条代替框线，无白底无重阴影 */
.tl-point-card { position: absolute; background: color-mix(in srgb, var(--track-color, var(--text-muted)) 6%, var(--panel-bg)); border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); overflow: hidden; padding: 8px 10px; cursor: grab; }
.tl-point-card:hover { box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 2px 6px rgba(0,0,0,0.08); }
.tl-ev-title { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; }
.tl-ev-date { font-size: 11px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; }
/* 角色线投影（影子卡）：主轨道事件在角色线上的只读镜像。
   半透明 + 虚线边框 = "这不是这条线原生的事"，左上角一枚相扣圆环说明来处；
   不接 mousedown，所以按住拖它一动不动，点它直接呼出原事件的编辑面板。 */
.tl-mirror { background: var(--panel-bg); border: none !important; border-left: 3px dashed var(--text-muted) !important; border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); cursor: pointer; opacity: .7; }
.tl-mirror .tl-ev-title, .tl-mirror .tl-bar-title { opacity: 1; }
.tl-mirror .tl-ev-date, .tl-mirror .tl-bar-date { opacity: 1; }
.tl-mirror:hover { opacity: .85; }
/* 影子卡的投影标记：不再绝对定位占角，而是缩成标题行里的一个小前缀，和标题同基线 */
.tl-mirror-mark { display: inline-flex; align-items: center; margin-right: 4px; color: var(--text-muted); opacity: .6; pointer-events: none; vertical-align: middle; }
/* 张力 ≥8 的高潮点：红色细环（去重阴影）。边框色由渲染层就地写成 #ef4444（内联 border-color 会盖过类选择器），
   这里只补一道细环 box-shadow，并接管 hover。 */
.tl-point-card.tl-high-tension, .tl-bar.tl-high-tension { box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 0 0 1px color-mix(in srgb, var(--danger-color) 45%, transparent); }
.tl-point-card.tl-high-tension:hover, .tl-bar.tl-high-tension:hover { box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 2px 6px rgba(0,0,0,0.08), 0 0 0 1px color-mix(in srgb, var(--danger-color) 65%, transparent); }
/* 张力滑块：轨道（带下方刻度）+ 右侧数字输入框，一行摆平 */
.tl-tension-control { display: flex; align-items: flex-start; gap: 10px; }
.tl-tension-slider { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
/* 滑块放进 .tl-tension-slider 这列后，flex:1 会沿纵向拉伸高度，故改为定高撑满宽度 */
.tl-tension-control input[type="range"] { flex: 0 0 auto; width: 100%; min-width: 0; accent-color: var(--primary-color); cursor: pointer; }
/* 0/5/8/10 按 0-10 上的真实百分比定位；左右各让出 7px（滑块头半宽），刻度才对得上滑块中心 */
.tl-tension-ticks { position: relative; height: 14px; margin: 1px 7px 0; font-size: 0.62rem; line-height: 14px; color: var(--text-muted); font-variant-numeric: tabular-nums; user-select: none; }
.tl-tension-ticks > span { position: absolute; top: 0; transform: translateX(-50%); }
.tl-tension-ticks > span:first-child { transform: translateX(0); }
.tl-tension-ticks > span:last-child { transform: translateX(-100%); }
/* 数字框：固定窄宽压过全局 input{width:100%}；隐藏原生 spinner，免得两位数字被挤掉 */
.tl-tension-num { flex: 0 0 auto; align-self: flex-start; width: 52px; min-width: 52px; box-sizing: border-box; text-align: center; font-variant-numeric: tabular-nums; -moz-appearance: textfield; appearance: textfield; }
.tl-tension-num::-webkit-inner-spin-button, .tl-tension-num::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.tl-tension-num.hot { color: var(--danger-color); font-weight: 700; border-color: var(--danger-color); }
/* 历史规则保留：wireTensionField 仍会更新这个 output 与 .hot 态，数字已改由上面的输入框承担，故只隐藏显示 */
.tl-tension-out { flex: 0 0 auto; min-width: 2.2em; text-align: center; font-size: 0.78rem; color: var(--text-muted); font-variant-numeric: tabular-nums; display: none; }
.tl-tension-out.hot { color: var(--danger-color); font-weight: 700; }
/* 滑杆的用途说明跟在 label 后面（同一行，不额外占表单高度）；到了 8 才浮出"会亮红"这句即时反馈 */
.tl-tension-hint { margin-left: 8px; font-weight: 400; font-size: 0.68rem; color: var(--text-muted); }
.tl-tension-note { display: none; margin-top: 4px; font-size: 0.68rem; color: var(--danger-color); }
.tl-tension-note.show { display: block; }
/* 高潮角标：右下角红色实心药丸（⚡+分数）。右上角已经是 📖 / 冲突 / 悬停按钮的地盘，不再往上叠。
   悬停时它不退场（不在 .tl-point-card:hover > 淡出名单里）——高潮这件事不该只在静止时看得见。 */
.tl-hot-mark {
  position: absolute; right: 4px; bottom: 4px; z-index: 3; pointer-events: none;
  display: inline-flex; align-items: center; gap: 1px; padding: 0 4px; border-radius: 3px;
  background: var(--danger-color); color: var(--on-accent); font-size: 0.6rem; line-height: 1.5; font-variant-numeric: tabular-nums;
}
.tl-point-card:has(.tl-hot-mark) .tl-ev-date, .tl-bar:has(.tl-hot-mark) .tl-bar-date { padding-right: 30px; }
/* 参与角色堆叠：绝对浮在卡片右下角，不参与卡片内部的行高计算 —— 点模式的行距和泳道高度是按行数算好的，
   多占一行会把整条泳道撑歪。高潮药丸守住角落，堆叠就向左让开；
   最后一行文字让开多宽由 JS 按实际颗数算好（--tl-cast-w），这里不再写死"两颗"的宽度。 */
.tl-ev-cast { position: absolute; right: 4px; bottom: 4px; z-index: 2; display: inline-flex; align-items: center; }
.tl-point-card:has(.tl-hot-mark) .tl-ev-cast { right: 32px; }
.tl-ev-cast-btn { padding: 0; border: none; background: none; line-height: 0; cursor: pointer; }
.tl-ev-cast-btn .ol-cast-ava { width: 14px; height: 14px; font-size: 9px; margin-left: 0; }
.tl-ev-cast-btn + .tl-ev-cast-btn, .tl-ev-cast-more { margin-left: -4px; }
.tl-ev-cast-more {
  flex: 0 0 auto; width: 14px; height: 14px; box-sizing: border-box; padding: 0 1px; display: inline-flex;
  align-items: center; justify-content: center; border: 1.5px solid var(--panel-bg); border-radius: 50%;
  background: color-mix(in srgb, var(--text-muted) 14%, var(--panel-bg)); color: var(--text-color);
  font-size: 8.5px; line-height: 1; letter-spacing: -.02em; opacity: .8;
}
.tl-point-card:has(.tl-ev-cast) .tl-ev-snapshot,
.tl-point-card:has(.tl-ev-cast):not(:has(.tl-ev-snapshot)) .tl-ev-date { padding-right: var(--tl-cast-w, 46px); }
.tl-point-card:has(.tl-ev-cast):has(.tl-hot-mark) .tl-ev-snapshot,
.tl-point-card:has(.tl-ev-cast):has(.tl-hot-mark):not(:has(.tl-ev-snapshot)) .tl-ev-date { padding-right: calc(var(--tl-cast-w, 46px) + 30px); }
/* POV 搜索选择：字段本身是"头像 + 名字 + 搜索/更换"三件套，值住在两个隐藏域里（event-pov / event-pov-id） */
.tl-pov-field { display: flex; align-items: center; gap: 6px; min-height: 28px; }
.tl-pov-field .tl-pov-ava { flex: 0 0 auto; }
.tl-pov-ava-legacy { background: var(--text-muted) !important; }
.tl-pov-name { flex: 1 1 auto; min-width: 0; font-size: 0.8rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-pov-pick, .tl-pov-clear {
  flex: 0 0 auto; border: none; background: transparent; color: var(--text-muted);
  border-radius: 2px; font-size: 0.7rem; padding: 3px 4px; cursor: pointer; transition: color .15s, background .15s;
}
.tl-pov-pick:hover:not(:disabled) { color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 7%, transparent); }
.tl-pov-pick:disabled { opacity: .5; cursor: not-allowed; }
.tl-pov-clear { padding: 2px 5px; line-height: 1; }
.tl-pov-clear:hover { color: var(--danger-color); background: color-mix(in srgb, var(--danger-color) 8%, transparent); }
.tl-pov-hint { font-size: 0.7rem; color: var(--text-muted); }
/* 出场角色：胶囊一排 + 一颗入口按钮（"搜索角色"/"编辑"是同一颗，只是字跟着状态变）。
   胶囊横向排队、放不下才换行；长名字在胶囊内部截断，撑不出横向滚动条。 */
.tl-cast-field { display: flex; align-items: center; flex-wrap: wrap; gap: 5px; min-height: 28px; }
.tl-cast-chips { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 4px; min-width: 0; }
.tl-cast-chip {
  display: inline-flex; align-items: center; gap: 4px; max-width: 140px; padding: 1px 3px 1px 2px;
  border: 1px solid color-mix(in srgb, var(--border-color) 75%, transparent); border-radius: 4px;
  background: color-mix(in srgb, var(--text-muted) 8%, transparent); font-size: 11px; line-height: 16px;
}
.tl-cast-chip .ol-lp-ava { width: 14px; height: 14px; font-size: 9px; }
.tl-cast-chip-n { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-cast-chip .tl-pov-clear { padding: 0 3px; border: none; border-radius: 4px; font-size: 11px; }
.tl-cast-hint { margin-left: 8px; font-weight: 400; font-size: 0.68rem; color: var(--text-muted); }
/* 角色选择浮层要压在事件弹窗（.modal-overlay 是 1000）之上：两个类一起选，特异性自然盖过 .ol-links-pop */
.ol-links-pop.tl-char-pop { z-index: var(--z-popover); }
/* 卡片上的 POV 头像角标：唯一一张能点的角标，所以 pointer-events 必须是开的（老的缩写角标是不可点的装饰） */
.tl-pov-chip {
  position: absolute; top: 3px; left: 4px; z-index: 3; padding: 0; border: none; background: none;
  line-height: 0; cursor: pointer; border-radius: 50%;
}
.tl-pov-chip:hover { box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary-color) 45%, transparent); }
.tl-point-card:has(.tl-pov-chip) .tl-ev-title { padding-left: 22px; }
/* 轨道头绑定了角色：那颗纯色圆点换成角色头像，尺寸一致，220px 里不会挤坏名字输入框 */
.tl-lane-char { flex: 0 0 auto; width: 16px; height: 16px; padding: 0; border: none; background: none; line-height: 0; cursor: pointer; border-radius: 50%; }
.tl-lane-char:hover { box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary-color) 45%, transparent); }
/* 卡片第三行：这个角色此刻是什么样（👤 林晚（24岁 · 青年期））；没绑定就整行不输出 */
.tl-ev-snapshot { font-size: 0.62rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; margin-top: 1px; }
.tl-point-card:has(.tl-ev-snapshot) .tl-ev-title { padding-right: 16px; }
/* 卡片内部参与角色头像：统一为灰阶，让轨道色条成为唯一彩色来源 */
.tl-point-card .ol-cast-ava, .tl-bar .ol-cast-ava, .tl-mirror .ol-cast-ava,
.tl-point-card .tl-pov-chip .ol-lp-ava { background: var(--text-muted) !important; color: var(--on-accent) !important; border-color: transparent !important; }
/* 卡片右上角的【关联场景】书签角标：点了跳左侧大纲，不参与拖拽、不打开 Inspector */
.tl-scene-link { position: absolute; top: 2px; right: 3px; width: 18px; height: 18px; padding: 0; border: none; border-radius: 4px; background: none; font-size: 12px; line-height: 1; cursor: pointer; opacity: .72; z-index: 2; transition: opacity .15s ease, background .15s ease; }
.tl-scene-link:hover { opacity: 1; background: color-mix(in srgb, var(--primary-color) 16%, transparent); }
/* 角标占住的那一角不让标题文字顶上去：标题该省略还是省略，只是少 20px */
.tl-bar:has(.tl-scene-link) .tl-bar-title, .tl-point-card:has(.tl-scene-link) .tl-ev-title { padding-right: 20px; }
/* 卡片悬停操作：右上角只留一颗 ⋯（悬停卡片才出现），真正的操作交给 PopoverManager 的纵向菜单。
   ⋯ 只有 18px、3px 圆角、无阴影，不压标题也不做胶囊。 */
.tl-quick { position: absolute; top: 2px; right: 2px; z-index: 3; display: none; }
.tl-point-card:hover > .tl-quick, .tl-bar:hover > .tl-quick { display: inline-flex; }
.tl-quick-trigger {
  width: 18px; height: 18px; padding: 0; border: none; border-radius: 3px;
  background: var(--panel-bg); color: var(--text-muted); opacity: .72;
  font-family: inherit; font-size: 11px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.tl-quick-trigger:hover { opacity: 1; color: var(--primary-color); background: var(--bg-color); }
/* ⋯ 与静态场景书签/冲突角标同住右上角：悬停时淡掉那两枚，只留 ⋯ 这一层，避免图标压图标 */
.tl-point-card:hover > .tl-scene-link, .tl-bar:hover > .tl-scene-link,
.tl-point-card:hover > .tl-conflict-mark { opacity: 0; pointer-events: none; }
.tl-drag-ghost .tl-quick { display: none !important; }
/* ⋯ 会占住标题右端 20px：标题该省略还是省略，只是提前收口，不会被按钮压住 */
.tl-bar:has(.tl-quick) .tl-bar-title, .tl-point-card:has(.tl-quick) .tl-ev-title { padding-right: 20px; }

/* ⋯ 弹出的纵向操作菜单：图标 + 文字横项，位置由 PopoverManager 统一算（fixed） */
.tl-quick-menu {
  position: fixed; z-index: var(--z-popover); min-width: 136px; padding: 4px;
  background: var(--panel-bg); border: 1px solid var(--border-color);
  border-radius: 4px; box-shadow: var(--shadow-popover);
  display: flex; flex-direction: column; gap: 1px;
}
.tl-quick-menu-item {
  display: flex; align-items: center; gap: 7px; width: 100%; padding: 5px 8px;
  border: none; border-radius: 3px; background: none; color: var(--text-color);
  font-family: inherit; font-size: .76rem; text-align: left; white-space: nowrap; cursor: pointer;
}
.tl-quick-menu-item:hover { background: var(--hover-bg); }
.tl-quick-menu-item i, .tl-quick-menu-item svg { width: 12px; height: 12px; flex: 0 0 auto; font-size: 11px; color: var(--text-muted); }
.tl-quick-menu-item:hover i, .tl-quick-menu-item:hover svg { color: var(--primary-color); }
/* armConfirm 把菜单项换成「确认删除？」时沿用这一档，菜单保持打开等第二下 */
.tl-quick-menu-item.is-confirm { background: color-mix(in srgb, var(--danger-color) 14%, var(--panel-bg)); color: var(--danger-color); font-weight: 600; }
.tl-qm-del, .tl-qm-del i { color: var(--danger-color); }
.tl-qm-del:hover { background: color-mix(in srgb, var(--danger-color) 10%, transparent); }
.tl-qm-del:hover i { color: var(--danger-color); }
/* 从大纲的线框时钟图标跳过来时闪 2 秒（timeline.js 挂 .tl-link-flash，到点自己摘） */
.tl-point-card.tl-link-flash, .tl-bar.tl-link-flash { animation: tl-link-flash 0.5s ease-out 4; }
@keyframes tl-link-flash {
  0%, 100% { background: color-mix(in srgb, var(--bg-color), #000 5%); box-shadow: none; }
  50% { background: color-mix(in srgb, var(--accent-color) 22%, var(--bg-color)); box-shadow: 0 0 0 2px var(--accent-color); }
}
/* ===== 列表视图：与画布共用同一套 events/lines，只换一种读法 ===== */
/* 画布/列表切换：与「现实/虚构」共用 .tl-mode-toggle 分段控制器样式，这里只补一点左间距 */
.tl-view-toggle { margin-left: 2px; }
/* Notion / 飞书 那种「读表」感：细线 + 浅斑马 + 压到 40px 的行高。
   截断住在 .tl-cell 这一层，td 只当格子，所以表格宽度永远由 colgroup 说了算，不会被长文撑爆。 */
.tl-list {
  width: 100%; min-width: 940px; table-layout: fixed; border-collapse: collapse;
  background: var(--panel-bg); font-size: 0.8rem;
}
.tl-list th {
  position: sticky; top: 0; z-index: 10; text-align: left; white-space: nowrap;
  background: var(--bg-color); border-bottom: 1px solid var(--border-color);
  padding: 12px 16px; vertical-align: middle; font-size: 0.72rem; font-weight: 600; color: var(--text-muted);
}
/* 列分隔线只画在表头：数据区留满屏细横线，竖线一画满就变成绩单 */
.tl-list th:not(:last-child) { border-right: 1px solid var(--border-color); }
.tl-list td {
  border-bottom: 1px solid var(--border-color); padding: 12px 16px;
  vertical-align: middle; overflow: hidden; white-space: nowrap;
}
.tl-list tbody tr:nth-child(even) { background: color-mix(in srgb, var(--text-color) 2%, transparent); }
.tl-list tbody tr { cursor: pointer; }
.tl-list tbody tr:hover { background: color-mix(in srgb, var(--primary-color) 5%, transparent); }
.tl-list tbody tr.tl-list-row.active { background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
.tl-cell { display: block; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-list-date .tl-cell { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.tl-list-line { display: flex; align-items: center; gap: 8px; }
.tl-list-core { display: flex; align-items: center; gap: 4px; }
.tl-list-core .tl-cell { color: var(--text-muted); font-size: 0.72rem; }
/* 行内编辑：默认一根边框都没有，看上去就是表里的文字；只有真在敲（聚焦）才描出边 */
.tl-list-edit {
  width: 100%; box-sizing: border-box; border: 1px solid transparent; background: transparent;
  color: var(--text-color); font: inherit; padding: 3px 5px; border-radius: 4px; cursor: text;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tl-list-edit:focus { border-color: var(--primary-color); background: var(--bg-color); outline: none; }
/* 列表里的书签角标不再绝对定位：它跟在「三要素速览」文字后面 inline 排 */
.tl-list .tl-scene-link { position: static; width: auto; height: auto; flex: 0 0 auto; opacity: .5; }
/* POV 头像在表格里同理：跟着文字 inline 排，圆角鼠标态保留 */
.tl-list .tl-pov-chip { position: static; flex: 0 0 auto; }
.tl-list .tl-pov-chip .ol-lp-ava { width: 16px; height: 16px; }
/* 高潮行：最左格画一道 3px 红脊。用背景渐变而不是 box-shadow——表格是 border-collapse，阴影会被相邻格压住 */
.tl-list-row.tl-hot > td:first-child { background: linear-gradient(to right, var(--danger-color) 3px, transparent 3px); }
.tl-list-row.tl-hot .tl-list-title-cell .tl-list-edit { color: var(--danger-color); }
.tl-list-empty td { text-align: center; color: var(--text-muted); height: 56px; cursor: default; }

/* ===== 体验升级：轨道分类 + 场景卡核心三要素 =====
   全部绝对定位 / Flex 附加：不动冻结网格尺寸、不改卡片宽高、不参与拖拽缩放计算 */
/* 轨道头类型标识：统一 16px 占位，颜色以 line.color 为准（主线实心圆 / 支线空心环 / 角色线人形或头像） */
.tl-lane-head { padding-left: 10px; }
/* 轨道头图标一律由 tlLaneSwatchHtml 的内联 style 画：这里只管不换行与垂直对齐，
   绝不再设 display/width/height，避免空标签被外部 class 撑塌或覆盖内联尺寸 */
.tl-lane-marker { flex: 0 0 auto; vertical-align: middle; }
/* 分类圆点 / 人像已改由 tlLaneSwatchHtml 的内联样式直接绘制：
   原先靠 --main/--sub/--person 的 ::before 伪元素，在 inline-flex 容器里常不渲染（支线空心圆空白的根因） */
.tl-lane-name--main { font-weight: 600; }
/* 卡片角标：pointer-events:none，绝不抢走卡片的拖拽与点击 */
.tl-pov-badge {
  position: absolute; top: 3px; left: 5px; display: inline-flex; align-items: center; gap: 3px;
  font-size: 0.62rem; line-height: 1; color: var(--text-muted); pointer-events: none; z-index: 2;
}
.tl-pov-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-color); flex: 0 0 auto; }
.tl-conflict-mark {
  position: absolute; top: 2px; right: 3px; font-size: 11px; line-height: 1;
  color: var(--warning-color); pointer-events: none; z-index: 2;
}
/* 角标里的线框图标（OL_IC 那套 SVG）自带 width/height 属性，不压一下会把按 font-size 排好的格子顶出去：
   统一收成 1em、随字色（currentColor），和当初那颗 emoji 占的位置对齐。 */
.tl-scene-link svg, .tl-conflict-mark svg, .tl-hot-mark svg, .tl-ev-snapshot svg,
.tl-tension-note svg { width: 1em; height: 1em; vertical-align: -0.12em; flex: 0 0 auto; }
/* 标题只加内边距避让角标，省略号该省还是省，绝不遮挡 */
.tl-point-card:has(.tl-pov-badge) .tl-ev-title { padding-left: 34px; }
.tl-point-card:has(.tl-conflict-mark) .tl-ev-title { padding-right: 20px; }
/* 同一角挤了书签和闪电：闪电让到书签左边，标题内边距一起加宽 */
.tl-point-card:has(.tl-scene-link) .tl-conflict-mark { right: 21px; }
.tl-point-card:has(.tl-scene-link):has(.tl-conflict-mark) .tl-ev-title { padding-right: 40px; }

/* 右侧 Inspector */
.timeline-inspector { position: absolute; top: 68px; right: 12px; bottom: 12px; width: 500px; background: var(--panel-bg); border: none; border-radius: 12px; box-shadow: -8px 0 24px rgba(0,0,0,0.06); z-index: var(--z-drawer); display: flex; flex-direction: column; overflow: hidden; }
[data-theme="dark"] .timeline-inspector:not(.tl-ins-float) { box-shadow: -8px 0 28px rgba(0,0,0,0.5); }
.timeline-inspector.hidden { display: none; }
/* 末日模拟（≤540px）：写死 500px 宽在手机端会溢出左缘约 137px，改为满宽抽屉 */
@media (max-width: 540px) {
  .timeline-inspector { left: 12px; right: 12px; width: auto; top: 60px; bottom: 8px; }
}
.tl-ins-head { display: flex; justify-content: space-between; align-items: center; padding: 14px 16px; border-bottom: 1px solid var(--border-color); font-weight: 600; flex-shrink: 0; }
.tl-ins-head-title { display: inline-flex; align-items: center; gap: 6px; color: var(--text-color); font-weight: 700; font-size: 1rem; }
.tl-ins-head-title svg { width: 14px; height: 14px; color: var(--text-color); flex: 0 0 auto; }
.tl-ins-body { flex: 1; overflow-y: auto; padding: 16px 18px; scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--text-muted) 50%, transparent) transparent; }
.tl-ins-body::-webkit-scrollbar { width: 6px; }
.tl-ins-body::-webkit-scrollbar-track { background: transparent; }
.tl-ins-body::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--text-muted) 50%, transparent); border-radius: 3px; }
.tl-ins-body::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--text-muted) 70%, transparent); }
/* 事件表单去边框：下划线式（底部 1px 浅灰线，focus 主题色底线）；标签更小更灰；项间距 20px */
.tl-ins-body .form-group { margin-bottom: 20px; }
.tl-ins-body .form-group > label { font-size: 0.7rem; font-weight: 400; color: var(--text-muted); margin-bottom: 5px; }
.tl-ins-body input[type="text"], .tl-ins-body textarea, .tl-ins-body .cselect[data-cselect], .tl-ins-body .tl-date-input {
  border: 1px solid var(--border-color); border-radius: var(--input-radius); background: var(--input-sunken); padding: var(--input-pad); box-shadow: none; width: 100%; color: var(--text-color); font-family: inherit; font-size: 0.85rem;
}
/* 轨道下拉是 div，用更高特异性与同行输入框对齐高度，避免比输入框矮一截 */
.tl-ins-body .cselect[data-cselect] { height: 32px; }
.tl-ins-body textarea { resize: vertical; }
.tl-ins-body input[type="text"]:focus-visible, .tl-ins-body textarea:focus-visible, .tl-ins-body .cselect[data-cselect]:focus-visible, .tl-ins-body .tl-date-input:focus-visible {
  background: var(--panel-bg); outline: none; box-shadow: 0 0 0 1px var(--primary-color), var(--input-focus-ring);
}
.tl-ins-footer { display: flex; justify-content: flex-end; gap: 10px; padding: 12px 14px; border-top: 1px solid var(--border-color); flex-shrink: 0; }
.tl-ins-title { font-size: 1.05rem; font-weight: 700; color: var(--text-color); margin-bottom: 12px; word-break: break-word; }
.tl-ins-field { margin-bottom: 12px; font-size: 0.85rem; }
.tl-ins-field label { display: block; color: var(--text-muted); font-size: 0.72rem; margin-bottom: 4px; }
.tl-ins-input { width: 100%; padding: var(--input-pad); border: 1px solid var(--border-color); border-radius: var(--input-radius); background: var(--input-sunken); color: var(--text-color); font-size: 0.85rem; box-sizing: border-box; }
.tl-ins-input:focus-visible { background: var(--panel-bg); outline: none; box-shadow: 0 0 0 1px var(--primary-color), var(--input-focus-ring); }
.tl-ins-textarea { resize: vertical; min-height: 60px; }
.tl-ins-body input[type="range"] { accent-color: var(--primary-color); width: 100%; }
.tl-ins-head-actions { display: flex; align-items: center; gap: 6px; }
.tl-ins-list { display: flex; flex-direction: column; gap: 6px; }
.tl-ins-list-item { display: flex; align-items: center; gap: 8px; padding: 8px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); background: transparent; width: 100%; cursor: pointer; color: var(--text-color); text-align: left; font: inherit; }
.tl-ins-list-item:hover { background: var(--bg-color); }
.tl-ins-list-main { flex: 1; min-width: 0; }
.tl-ins-list-main .t { font-size: 0.82rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-ins-list-main .d { font-size: 0.72rem; color: var(--text-muted); }

/* ===== Inspector 事件表单（角色档案视图已废除：头像统一走角色中枢弹窗） ===== */
.timeline-inspector { border-left: none; }
/* 事件表单里可跳转的头像 / 名字 / 参与角色胶囊主体（× 仍只负责移除） */
.tl-pov-field .tl-pov-ava[data-char], .tl-pov-field .tl-pov-name[data-char], .tl-cast-chip[data-char] { cursor: pointer; }
.tl-pov-field .tl-pov-name[data-char]:hover { color: var(--primary-color); }
.tl-cast-chip[data-char]:hover { background: color-mix(in srgb, var(--primary-color) 8%, transparent); }
/* 日期组件 */
.era-row { display: flex; gap: 16px; margin-bottom: 12px; font-size: 0.9rem; }
.era-row label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; white-space: nowrap; }
.form-hint { font-size: 0.78rem; color: var(--text-muted); margin-top: 6px; }
/* 单输入框日期控件（现实模式）：文本框负责打字，日历图标负责弹面板 */
.tl-date-control { display: flex; align-items: center; gap: 6px; position: relative; }
.tl-date-input { flex: 1; min-width: 0; padding: 6px 8px; border: 1px solid var(--border-color); border-radius: var(--input-radius); background: var(--input-sunken); color: var(--text-color); font-size: 0.85rem; box-sizing: border-box; cursor: text; }
.tl-date-input:focus-visible { border-color: var(--primary-color); outline: none; box-shadow: var(--input-focus-ring); }
.tl-date-hint { margin-top: 4px; font-size: 0.72rem; color: var(--text-muted); line-height: 1.4; }
.tl-date-clear { flex-shrink: 0; }
.tl-date-cal-btn { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 34px; padding: 0; border: 1px solid var(--border-color); border-radius: var(--radius-btn); background: var(--panel-bg); color: var(--text-muted); cursor: pointer; transition: color .15s, background .15s, border-color .15s; }
.tl-date-cal-btn:hover { color: var(--primary-color); background: var(--hover-bg); border-color: var(--primary-color); }
.tl-date-cal-btn[aria-expanded="true"] { color: var(--primary-color); background: var(--hover-bg); border-color: var(--primary-color); }
.tl-date-cal-btn:focus-visible { outline: none; box-shadow: var(--input-focus-ring); }
/* 移动端系统原生选择器宿主：有布局盒、不可见、不吃点击，由 showPicker() 在用户手势里唤起 */
.tl-date-native { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; border: 0; padding: 0; pointer-events: none; }
/* 双日历浮层：圆角与输入框同源，阴影走浮层令牌（暗色模式有对应的低透明度覆盖） */
.tl-cal { position: fixed; z-index: var(--z-overlay-tool); background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: var(--radius-lg); box-shadow: var(--shadow-popover); padding: 14px; user-select: none; }
.tl-cal-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.tl-cal-navs { display: flex; align-items: center; gap: 4px; }
.tl-cal-nav { display: inline-flex; align-items: center; justify-content: center; background: none; border: 1px solid var(--border-color); border-radius: var(--radius-btn); width: 32px; height: 32px; cursor: pointer; color: var(--text-color); }
.tl-cal-nav:hover { background: var(--hover-bg); }
/* 纪元分段控件：两颗并排共用外框，选中侧是实心底而不是只有文字变色 */
.tl-cal-era { display: inline-flex; padding: 2px; gap: 2px; border: 1px solid var(--border-color); border-radius: var(--radius-btn); background: var(--bg-color); }
.tl-cal-era-btn { background: none; border: 0; border-radius: 4px; padding: 5px 10px; cursor: pointer; color: var(--text-muted); font-size: 0.78rem; white-space: nowrap; }
.tl-cal-era-btn:hover { color: var(--text-color); }
.tl-cal-era-btn.active { background: var(--primary-color); color: var(--panel-bg); font-weight: 600; box-shadow: var(--shadow-sm); }
.tl-cal-grid { display: flex; gap: 16px; }
.tl-cal-month { min-width: 280px; }
.tl-cal-month-title { text-align: center; font-size: 0.82rem; font-weight: 600; margin-bottom: 6px; white-space: nowrap; }
/* 年月快速选择器：标题按钮 + 二级面板（.tl-cal 内部普通子节点，不新建浮层） */
.tl-cal-month-title.is-trigger {
  display: block; width: 100%; box-sizing: border-box; padding: 2px 4px;
  border: 1px solid transparent; border-radius: var(--radius-btn);
  background: transparent; color: var(--text-color);
  font: inherit; font-size: 0.82rem; font-weight: 600;
  text-align: center; white-space: nowrap; cursor: pointer;
}
.tl-cal-month-title.is-trigger:hover { background: var(--hover-bg); border-color: var(--border-color); }
.tl-cal-month-title.is-trigger[aria-expanded="true"] { border-color: var(--primary-color); color: var(--primary-color); }
.tl-cal-month-title.is-trigger:focus-visible { outline: none; box-shadow: var(--input-focus-ring); }
.tl-cal-ym {
  box-sizing: border-box; padding: 10px; margin-bottom: 10px;
  border: 1px solid var(--border-color); border-radius: var(--radius-md);
  background: var(--bg-color); box-shadow: var(--shadow-sm);
  max-height: min(360px, calc(100dvh - 64px)); overflow-y: auto;
}
.tl-cal-ym-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-bottom: 8px; }
.tl-cal-ym-nav {
  flex: 0 0 28px; width: 28px; height: 28px; line-height: 1;
  border: 1px solid var(--border-color); border-radius: var(--radius-btn);
  background: transparent; color: var(--text-color); font-size: 1rem; cursor: pointer;
}
.tl-cal-ym-nav:hover { background: var(--hover-bg); }
.tl-cal-ym-range {
  flex: 1 1 auto; min-width: 0; text-align: center; font-size: 0.76rem;
  color: var(--text-color); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tl-cal-ym-years { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; margin-bottom: 10px; }
.tl-cal-ym-months { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.tl-cal-ym-year, .tl-cal-ym-month {
  min-width: 0; padding: 5px 2px; box-sizing: border-box;
  border: 1px solid var(--border-color); border-radius: var(--radius-btn);
  background: var(--input-sunken); color: var(--text-color);
  font-size: 0.72rem; font-family: inherit; line-height: 1.3;
  white-space: nowrap; text-align: center; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis;
}
.tl-cal-ym-year:hover, .tl-cal-ym-month:hover { border-color: var(--primary-color); color: var(--primary-color); }
.tl-cal-ym-year.current, .tl-cal-ym-month.current {
  background: var(--primary-color); color: var(--panel-bg);
  border-color: var(--primary-color); font-weight: 600;
}
.tl-cal-ym-year:focus-visible, .tl-cal-ym-month:focus-visible, .tl-cal-ym-nav:focus-visible {
  outline: none; box-shadow: var(--input-focus-ring);
}
.tl-cal-week, .tl-cal-week-row { display: grid; grid-template-columns: repeat(7, 40px); }
.tl-cal-days { display: flex; flex-direction: column; }
.tl-cal-week span { text-align: center; font-size: 0.7rem; color: var(--text-muted); line-height: 26px; }
/* 触控目标 40×40，字号保持 0.78rem 不缩 */
.tl-cal-day, .tl-cal-empty { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; font-size: 0.78rem; border-radius: 2px; box-sizing: border-box; }
.tl-cal-day { cursor: pointer; position: relative; }
.tl-cal-day:hover { background: var(--hover-bg); }
/* 非当前月：仍可点（跨月选区间需要），只降一档灰度；color-mix 在亮/暗两套主题下都给中间灰 */
.tl-cal-day.tl-cal-outside { color: color-mix(in srgb, var(--text-color) 55%, var(--panel-bg)); }
/* 四层状态之一 · 今天：细圆环（伪元素，不会被焦点的 box-shadow 吃掉） */
.tl-cal-day.tl-cal-today::after { content: ''; position: absolute; inset: 4px; border: 1.5px solid var(--primary-color); border-radius: 50%; pointer-events: none; }
/* 四层状态之二 · 已落定区间：浅主题底 */
.tl-cal-day.in-range { background: color-mix(in srgb, var(--primary-color) 16%, transparent); border-radius: 0; }
/* 四层状态之三 · 悬停 / 键盘焦点预览：比区间再浅一档，纯视觉不写数据 */
.tl-cal-day.tl-cal-preview { background: color-mix(in srgb, var(--primary-color) 8%, transparent); border-radius: 0; }
/* 四层状态之四 · 起点 / 终点 / 单点：实心主题色圆 */
.tl-cal-day.selected, .tl-cal-day.first-pick { background: var(--primary-color); color: var(--panel-bg); border-radius: 50%; font-weight: 600; box-shadow: var(--shadow-sm); }
.tl-cal-day.selected::after { border-color: color-mix(in srgb, var(--panel-bg) 55%, transparent); }
/* 焦点：外圈光晕，与表单其它输入框同一套语言，不用浏览器默认虚线框 */
.tl-cal-day.tl-cal-focus { outline: none; box-shadow: 0 0 0 2px var(--panel-bg), 0 0 0 4px color-mix(in srgb, var(--primary-color) 55%, transparent); z-index: 1; }
/* 读屏播报：视觉隐藏但保留可访问性（不能 display:none，否则读屏不会播报） */
.tl-cal-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
/* 窄屏：双月改为纵向堆叠 + 面板自身滚动，375px 不挤碎 */
@media (max-width: 767px) {
  .tl-cal { max-width: calc(100vw - 16px); max-height: calc(100dvh - 24px); overflow-y: auto; padding: 10px; box-sizing: border-box; }
  .tl-cal-head { flex-wrap: wrap; justify-content: center; gap: 6px; }
  .tl-cal-grid { flex-direction: column; gap: 12px; }
  .tl-cal-month { min-width: 0; width: 100%; }
  .tl-cal-week, .tl-cal-week-row { grid-template-columns: repeat(7, minmax(40px, 1fr)); }
  .tl-cal-day, .tl-cal-empty { width: 100%; min-width: 0; }
  .tl-date-cal-btn { width: 40px; height: 40px; }
  .tl-date-clear { min-width: 40px; }
  /* 320px：年份 2 列（BC 的「公元前44年」放得下完整文案）、月份 4 列，全部 minmax(0,1fr) */
  .tl-cal-ym { padding: 8px; margin-bottom: 8px; }
  .tl-cal-ym-years { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tl-cal-ym-months { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .tl-cal-ym-year, .tl-cal-ym-month { font-size: 0.7rem; }
}
/* 角色卡片：索引速览（身份 / 关系 / 底部操作），长文设定一律交给人物档案 */
/* 模块一：真正的瀑布流（Masonry）——JS 按“最矮列”分配卡片，首行从左到右铺满，
   之后每张卡片立刻填补短卡片下方的空白，绝不保留等高留白。视觉顺序左→右、首行优先。 */
/* 卡片瀑布流容器：纯 CSS Flex 弹性均分宽度，拖拽侧栏时卡片实时跟手缩放（橡皮筋效果）；
   仅在列数跨越临界点时由 JS 重建，其余时间零 DOM 操作、零卡顿 */
#character-list-grid { display: flex; justify-content: center; align-items: flex-start; gap: 20px; margin-top: 20px; }
.cc-masonry-col { flex: 1; min-width: 280px; max-width: 380px; display: flex; flex-direction: column; gap: 20px; }
.character-card {
  height: auto;
  width: 100%;
  display: flex; flex-direction: column; gap: 12px;
  background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: 8px; padding: 14px;
  box-shadow: none;
  transition: box-shadow 0.18s ease, border-color 0.18s ease;
}
/* 模块五：卡片空白处不再作为跳转入口，唯一的跳转是右下角铅笔；故指针用默认态 */
.character-card { cursor: default; }
.character-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.04); }
.character-card.selected { border-color: var(--text-muted); }
.cc-zone--identity { display: block; }
.cc-zone--dynamic { display: flex; flex-direction: column; gap: 6px; min-height: 0; }
.cc-identity-main { flex: 1; min-width: 0; }

/* ===== 属性面板风格：卡片竖排"左侧浅灰标签 + 右侧深色数值" ===== */
.cc-zone--identity .info-row,
.cc-zone--dynamic .info-row { margin-bottom: 6px; }
.cc-zone--dynamic .info-row:last-child { margin-bottom: 0; }
/* 单行属性：标签固定宽度并右对齐，所有冒号在垂直方向绝对对齐 */
.info-row { display: flex; align-items: baseline; }
.info-row .label {
  flex: 0 0 48px; width: 48px; margin-right: 8px; text-align: right;
  font-size: 14px; line-height: 1.5; color: var(--text-muted); flex-shrink: 0;
}
.info-row .cc-value { font-size: 13px; line-height: 1.5; color: var(--text-color); word-break: break-word; min-width: 0; }
/* 姓名：18px 纯黑加粗，作为整张卡片的视觉锚点 */
.cc-value.value-name { font-size: 18px; font-weight: 700; color: var(--text-color); }
/* 定位 / 阵营：胶囊标签（丢弃逗号，阵营用浅灰底小胶囊） */
.tag-role {
  display: inline-flex; align-items: center; padding: 1px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 500; line-height: 18px; color: var(--on-accent); white-space: nowrap; vertical-align: middle;
}
.tag-role--protagonist { background: var(--role-protagonist); }
.tag-role--antagonist { background: var(--role-antagonist); }
.tag-role--mentor { background: var(--role-mentor); }
.tag-role--supporting { background: var(--role-supporting); }
.tag-role--extra { background: var(--role-extra-bg); color: var(--role-extra-fg); }
.tags-wrap { display: inline-flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.tag-pill {
  display: inline-flex; align-items: center; padding: 1px 8px; border-radius: var(--radius-pill);
  font-size: 11px; line-height: 16px; color: var(--text-muted); background: var(--hover-bg); border: 1px solid var(--border-strong); white-space: nowrap;
}
.cc-value .tags-wrap { margin: -1px 0; }
/* 关系区：标签独立成行，下方内容整体缩进，与数值左边缘对齐 */
.cc-zone--dynamic .relation-head { margin-bottom: 2px; }
.relation-body { padding-left: 56px; }
.relation-body .char-rel-mini { margin: 2px 0 6px; }
.relation-body .char-rel-mini svg { display: block; max-width: 100%; height: 100%; }
.character-card .char-appearance { font-size: 12px; line-height: 1.5; color: var(--text-muted); white-space: normal; overflow: visible; text-overflow: clip; }
.cc-faction-row { display: flex; flex-wrap: wrap; align-items: center; margin-top: 4px; }
/* 基础徽标：12px 浅灰，只做一行速览 */
.cc-basic-badge { font-size: 12px; line-height: 1.4; color: var(--text-muted); margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 退场角色：极小灰点 + 头像轻降透明度，标识色依然保留 */
.cc-status-dot { width: 6px; height: 6px; margin-left: 6px; border-radius: 50%; background: var(--text-muted); flex: 0 0 auto; }
.character-avatar.is-dim { opacity: 0.65; }
/* 头像：无图时由内联 style 铺角色专属标识色，文字统一白色；1px 白边保留 */
.character-avatar { width: 42px; height: 42px; border-radius: 50%; background: var(--text-muted); color: var(--on-accent); display: flex; align-items: center; justify-content: center; font-size: 15px; overflow: hidden; border: 1px solid var(--border-strong); flex-shrink: 0; }
.character-avatar img { width: 100%; height: 100%; object-fit: cover; }
/* 姓名是整张卡片的视觉锚点：14px 加粗纯黑，带「姓名：」字段名前缀 */
.character-name { font-size: 14px; font-weight: 700; color: var(--text-color); line-height: 1.35; word-break: break-word; display: flex; flex-wrap: wrap; align-items: center; }
/* 关系区字段名：与基础徽标同档浅灰小字 */
.cc-section-label { font-size: 11px; line-height: 1.4; color: var(--text-muted); }
/* 底部操作区：细分割线 + 左关联跳转 / 右铅笔编辑 */
.cc-footer { display: flex; justify-content: space-between; align-items: center; gap: 8px; border-top: 1px solid var(--border-color); padding-top: 8px; margin-top: auto; }
.cc-jump-links, .cdp-jump-links { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.cc-jump-btn { display: inline-flex; align-items: center; gap: 3px; padding: 2px 3px; border: none; border-radius: var(--radius-btn); background: transparent; color: var(--text-muted); font: inherit; font-size: 11px; line-height: 1; cursor: pointer; transition: color .15s, background .15s; }
.cc-jump-btn svg { width: 13px; height: 13px; flex: 0 0 auto; }
.cc-jump-btn:hover { color: var(--text-color); background: var(--hover-bg); }
.cc-jump-num { font-size: 11px; font-variant-numeric: tabular-nums; }
.cc-more-btn { width: 24px; height: 24px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; padding: 0; border: none; border-radius: 6px; background: transparent; color: var(--text-muted); cursor: pointer; transition: color .15s, background .15s; }
.cc-more-btn:hover { color: var(--text-color); background: var(--hover-bg); }
.cc-more-btn svg { width: 13px; height: 13px; }

/* ===== 模块二：身份头部区 ===== */
.cc-header { display: flex; gap: 16px; align-items: flex-start; position: relative; }
.cc-avatar {
  width: 80px; height: 80px; border-radius: 50%; overflow: hidden; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center; color: var(--on-accent); font-size: 32px; line-height: 1;
}
.cc-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cc-avatar span { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
.cc-header-main { flex: 1; min-width: 0; }
.cc-header-top { position: relative; display: flex; align-items: center; gap: 8px; padding-right: 28px; }
.cc-name { font-size: 20px; font-weight: 600; color: var(--text-color); line-height: 1.3; word-break: break-word; }
.cc-link {
  position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--text-muted); cursor: pointer; border-radius: 6px;
}
.cc-link:hover { color: var(--text-color); background: var(--hover-bg); }
.cc-link svg { width: 16px; height: 16px; }
.cc-header-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; margin-top: 10px; }
.cc-grid-cell { display: flex; min-width: 0; font-size: 12px; line-height: 1.5; }
.cc-grid-label { color: var(--text-muted); flex: 0 0 auto; }
.cc-grid-value { color: var(--text-color); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 别名 / 阵营：单独整行，字号更小、颜色更浅（弱化处理），与下方属性双排拉开层次 */
.cc-meta-row { display: flex; margin-top: 6px; font-size: 11px; line-height: 1.5; }
.cc-meta-label { color: var(--text-muted); flex: 0 0 auto; }
.cc-meta-value { color: var(--text-muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 关系网络区：左标题 + 右侧「上图下标签」——缩略图占满整行居中，胶囊列表在下方自然换行 */
.cc-rel-zone { display: flex; align-items: flex-start; gap: 8px; }
.cc-rel-title { flex: 0 0 40px; width: 40px; color: var(--text-muted); font-size: 13px; line-height: 1.6; }
.cc-rel-cols { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.cc-rel-mini-col { flex: 0 0 auto; width: 100%; min-width: 0; }
.cc-rel-list { flex: 1 1 auto; min-width: 0; }

/* ===== 模块三：核心文本区（性格 / 背景 / 目标） ===== */
.cc-core { display: flex; flex-direction: column; gap: 6px; }
.cc-core-line { display: flex; align-items: flex-start; gap: 4px; }
.cc-core-label { width: 40px; flex: 0 0 auto; color: var(--text-muted); font-size: 13px; line-height: 1.6; }
.cc-core-text {
  flex: 1; min-width: 0; color: var(--text-color); line-height: 1.6; word-break: break-word;
  display: block; overflow: visible;
}
.cc-core-empty { color: var(--text-muted); }
/* 展开/收起：纯 JS 按字数截断后拼接的真实按钮，不再依赖 CSS 自带省略号 */
.expand-btn {
  display: inline-block; vertical-align: baseline; border: none; background: none; color: var(--text-muted);
  cursor: pointer; font: inherit; font-size: 13px; line-height: 1.6; padding: 0 2px;
}
.expand-btn:hover { color: var(--text-color); text-decoration: underline; }

/* 深色主题：新部件沿用黑白灰，仅把灰阶反过来 */
[data-theme="dark"] .cc-name { color: var(--text-color); }
[data-theme="dark"] .cc-grid-label,
[data-theme="dark"] .cc-core-label,
[data-theme="dark"] .cc-core-empty { color: var(--text-muted); }
[data-theme="dark"] .cc-grid-value,
[data-theme="dark"] .cc-core-text { color: var(--text-color); }
[data-theme="dark"] .cc-avatar { border-color: var(--border-strong); }
[data-theme="dark"] .cc-link { color: var(--text-muted); }
[data-theme="dark"] .cc-link:hover { color: var(--text-color); background: var(--hover-bg); }
[data-theme="dark"] .expand-btn { color: var(--text-muted); }
[data-theme="dark"] .expand-btn:hover { color: var(--text-color); }
[data-theme="dark"] .cc-meta-label,
[data-theme="dark"] .cc-rel-title { color: var(--text-muted); }
[data-theme="dark"] .cc-meta-value { color: var(--text-muted); }

/* 档案页 / 弹窗头部：与卡片同一套徽标与跳转图标 */
.char-head-badge { font-size: 12px; color: var(--text-muted); margin-left: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 16em; }
/* 卡片上的文字块一律无底色、无阴影，只有纯净文本；可编辑性用 text 光标 + 悬停虚线下划线提示 */
.char-editable { cursor: text; }
.char-editable:hover .char-text { text-decoration: underline dotted; text-underline-offset: 3px; }
.char-editable.is-editing:hover .char-text { text-decoration: none; }
.cc-name { font-size: 14px; font-weight: 700; }
.cc-name, .cc-factions { margin-right: 2px; }
.cc-line { font-size: 13px; line-height: 1.65; color: var(--text-color); word-break: break-word; background: none; box-shadow: none; }
.cc-zone--core .cc-line { font-weight: 500; }
/* 详细设定区（背景）：字号与颜色都比核心简介低一档，视觉层级回到"身份 → 主张 → 细节" */
.cc-line--background { font-size: 12px; line-height: 1.7; color: var(--text-muted); }
/* 原地编辑：无边框、无背景，只留一条主题色底部虚线 */
.char-inline {
  display: block; width: 100%; margin: 0; padding: 1px 2px;
  border: none; border-bottom: 1px dashed var(--primary-color); border-radius: 0; outline: none; background: transparent;
  font: inherit; color: var(--text-color); caret-color: var(--primary-color);
}
.char-inline--single { display: inline-block; width: auto; min-width: 7em; max-width: 100%; vertical-align: middle; }
/* 只有姓名那一格编辑时才用姓名字号，别名/阵营编辑保持各自的小字 */
.cc-name .char-inline--single { font-size: 14px; font-weight: 700; }
.cc-factions .char-inline--single { font-size: 11px; font-weight: 400; min-width: 9em; }
.char-inline--multi { font-size: 13px; line-height: 1.6; resize: vertical; overflow: auto; }
.cc-line .char-inline--multi { font-weight: 500; }
.cc-line--background .char-inline--multi { font-size: 12px; font-weight: 400; }
.char-role { display: inline-flex; align-items: center; margin-left: 6px; padding: 1px 8px; border-radius: 999px; font-size: 11px; line-height: 16px; font-weight: 500; color: var(--on-accent); white-space: nowrap; flex-shrink: 0; vertical-align: middle; }
.char-role--protagonist { background: var(--role-protagonist); }
.char-role--antagonist { background: var(--role-antagonist); }
.char-role--mentor { background: var(--role-mentor); }
.char-role--supporting { background: var(--role-supporting); }
.char-role--extra { background: var(--role-extra-bg); color: var(--role-extra-fg); }
/* 最新动向：纪事最后一笔提上来的一枚小标签，和定位标签并排站。悬停看完整时间轴（title） */
.char-move { display: inline-flex; align-items: center; margin-left: 6px; padding: 1px 8px; border-radius: 999px; font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; vertical-align: middle; flex-shrink: 0; max-width: 12em; overflow: hidden; text-overflow: ellipsis; background: color-mix(in srgb, var(--primary-color) 12%, transparent); color: var(--primary-color); border: 1px solid color-mix(in srgb, var(--primary-color) 28%, transparent); }
/* 已经退场的角色压成灰，不再跟活人抢颜色 */
.char-move--dead { background: var(--move-dead-bg); border-color: var(--move-dead-bd); color: var(--move-dead-fg); }
.char-move--dark { background: var(--move-dark-bg); border-color: var(--move-dark-bg); color: var(--move-dark-fg); }
.char-move--awake { background: var(--move-awake-bg); border-color: var(--move-awake-bg); color: var(--move-awake-fg); }
[data-theme="dark"] .character-card .char-move { background: var(--role-extra-bg); border-color: var(--border-strong); color: var(--role-extra-fg); }
[data-theme="dark"] .character-card .char-move--dead { background: var(--move-dead-bg); border-color: var(--move-dead-bd); color: var(--move-dead-fg); }
[data-theme="dark"] .character-card .char-move--dark { background: var(--move-dark-bg); color: var(--move-dark-fg); }
[data-theme="dark"] .character-card .char-move--awake { background: var(--move-awake-bg); color: var(--move-awake-fg); }
.cc-factions { display: inline-flex; flex-wrap: wrap; align-items: center; min-width: 0; }
.char-faction { display: inline-flex; align-items: center; margin-left: 6px; padding: 1px 8px; border-radius: var(--radius-pill); font-size: 11px; line-height: 16px; font-weight: 500; color: var(--text-muted); white-space: nowrap; background: var(--panel-bg); border: 1px solid var(--border-strong); vertical-align: middle; flex-shrink: 0; }
.char-faction-more { margin-left: 4px; font-size: 11px; color: var(--text-muted); line-height: 16px; }
.char-alias { font-size: 11px; font-weight: 400; color: var(--text-muted); margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.char-appearance { font-size: 11px; font-weight: 400; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.char-appearance-panel { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 4px; }
.char-appearance-link { background: none; border: none; padding: 2px 4px; color: var(--primary-color); cursor: pointer; font-size: 0.85rem; }
.char-appearance-link:hover { text-decoration: underline; }
.char-appearance-empty { color: var(--text-muted); font-size: 0.85rem; }
.char-text { white-space: pre-wrap; word-break: break-word; }
/* 卡片字段强制前缀：浅灰加粗，和内容拉开一层但不抢戏 */
.char-label { color: var(--text-muted); font-weight: 700; }
.char-empty { color: var(--text-muted); }
.char-toggle, .char-expand-btn { background: none; border: none; padding: 0 2px; margin: 0; color: var(--primary-color); cursor: pointer; font-size: 0.8rem; line-height: 1; }
.char-toggle:hover, .char-expand-btn:hover { text-decoration: underline; }
/* 关系胶囊：12px，与基础徽标同一档字号 */
.char-relations { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 0.8rem; line-height: 1.6; }
.char-relation-capsule { display: inline-flex; align-items: center; gap: 5px; padding: 1px 8px; border-radius: 999px; border: 1px solid var(--border-color); background: var(--bg-color); cursor: pointer; font-size: 12px; color: var(--text-color); line-height: 1.5; white-space: nowrap; max-width: 100%; overflow: hidden; }
.char-relation-capsule:hover { border-color: var(--primary-color); }
.rel-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
.rel-name { font-weight: 500; flex-shrink: 0; max-width: 7em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rel-dir { color: var(--text-muted); flex-shrink: 0; }
.rel-type { color: var(--text-muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.char-add-rel { background: none; border: none; padding: 0 2px; color: var(--primary-color); cursor: pointer; font-size: 0.8rem; }
.char-add-rel:hover { text-decoration: underline; }
/* 虚线“+ 添加关系”胶囊 */
.char-add-relation { display: inline-flex; align-items: center; padding: 1px 10px; border-radius: 999px; border: 1px dashed var(--primary-color); background: transparent; color: var(--primary-color); cursor: pointer; font-size: 0.75rem; line-height: 1.5; white-space: nowrap; }
.char-add-relation:hover { background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
/* 角色详情页关系面板里的添加按钮：独占一行、居中 */
.char-add-relation-panel { width: 100%; justify-content: center; margin-top: 4px; border-radius: 0; border: 1px dashed var(--vd-line-strong); background: transparent; color: var(--vd-ink-soft); font-family: var(--vd-mono); font-size: 11px; letter-spacing: 1px; }
.vd-dossier .char-add-relation-panel:hover { border-color: var(--vd-ink); color: var(--vd-ink); }
/* 关系明细浮层 */
.relation-detail-popup { position: fixed; z-index: var(--z-popover); min-width: 220px; max-width: 320px; background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); padding: 10px 12px; }
.relation-detail-title { font-weight: 600; font-size: 0.85rem; color: var(--text-color); padding-bottom: 6px; margin-bottom: 8px; border-bottom: 1px solid var(--border-color); }
.relation-detail-list { display: flex; flex-direction: column; gap: 2px; }
.relation-detail-item { display: block; width: 100%; text-align: left; background: none; border: none; padding: 5px 8px; border-radius: var(--radius-sm); cursor: pointer; font-size: 0.8rem; color: var(--text-color); }
.relation-detail-item:hover { background: var(--bg-color); color: var(--primary-color); }
/* 关系弹窗只读上下文 */
.relation-context { font-size: 0.9rem; color: var(--text-muted); margin: 0 0 16px; padding: 8px 12px; background: var(--bg-color); border-radius: var(--radius-sm); border-left: 3px solid var(--primary-color); }
.relation-context b { color: var(--text-color); font-weight: 700; }
/* 关系弹窗叠在角色详情页上层 */
#modal-relation { z-index: var(--z-modal); }
/* ========== 角色顶部标签栏（Page Tabs）+ 极简复古档案页（Vintage Dossier） ========== */
/* 标签栏高度只在这里定义一次：档案页拿它算自己的可视高度 */
:root { --pt-h: 42px; }
/* 现代档案馆色板：一张浅灰纸（#F7F7F8），没有白卡片也没有阴影。
   分隔只允许两种东西：1px 细线（--vd-line）或纯留白。标识色只出现在竖条、方块、进度条这三处点缀。
   .vd-pop 也单独声明一遍：齿轮面板被 PopoverManager 挂到 body 上，脱离了 .vd-dossier 的继承链，
   不在自己身上拿一份色板，弹出来就是一张没有底色的透明面板。 */
.vd-dossier, .vd-pop {
  /* 治本重构（UI Step 1）：档案馆色板改为引用全局基础令牌，暗色自动跟随，
     不再依赖下方已删除的 [data-theme="dark"] .vd-* 补丁 */
  --vd-paper: var(--paper-bg);
  --vd-paper-2: var(--paper-bg-2);
  --vd-ink: var(--paper-ink);
  --vd-ink-soft: var(--paper-ink-soft);
  --vd-ink-mute: var(--paper-ink-mute);
  --vd-line: var(--paper-line);
  --vd-line-soft: var(--paper-line-soft);
  --vd-line-strong: var(--paper-line-strong);
  --vd-seal: var(--paper-seal);
  --vd-gold: var(--cp-accent, var(--paper-gold));
  --vd-type: var(--font-sans);
  --vd-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
  --vd-shadow: none;
  --vd-shadow-pop: var(--shadow-pop);
  --vd-hover: var(--paper-hover);
  --vd-radius: var(--radius-sm);
  /* 吸顶目录压在右栏顶上，模块标题滚进来时要给这段吸顶层留出自身高度 */
  --vd-toc-h: 56px;
}
/* 提权：盖过全局 .modal-content 的底色（同特异性、更靠后会被抢），确保纸张底色稳赢 */
.modal-content.vd-dossier { background: var(--paper-bg); color: var(--paper-ink); }
/* 默认不出现：它不属于任何 .view-container，直接住在 #canvas-area 里，
   靠 JS 同步的 char-tabs-on 状态类上台（口径见 characters.js 的 syncCharacterPageUI） */
.page-tabs-bar {
  display: none; align-items: flex-end; gap: 6px; height: var(--pt-h); flex-shrink: 0;
  padding: 4px 14px 0; background: var(--panel-bg); border-bottom: 1px solid var(--border-color);
  overflow-x: auto; overflow-y: hidden;
}
.page-tabs-bar.char-tabs-on { display: flex; }
/* 再兜一道，和 .mm-dock 那套同一个路子：只允许"角色页正开着"时占位。
   JS 万一漏了一次同步，这条栏也不可能在思维导图/时间线/关系网上面露出来。
   三个 ID + 三个类，特异性压得住 .char-tabs-on，所以这条永远说了算 */
#canvas-area:not(:has(> #character-list-container.active)):not(:has(> #character-detail-container.active)) > .page-tabs-bar { display: none; }
.page-tab {
  display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto;
  max-width: 210px; height: 34px; padding: 0 6px 0 10px; cursor: pointer;
  border: 1px solid var(--border-color); border-bottom: none; border-radius: 2px 2px 0 0;
  background: color-mix(in srgb, var(--bg-color) 55%, var(--panel-bg)); color: var(--text-muted);
  transition: background 0.15s, color 0.15s;
}
.page-tab:hover { background: var(--bg-color); color: var(--text-color); }
.page-tab.active {
  background: var(--bg-color); color: var(--text-color); font-weight: 600;
  border-color: color-mix(in srgb, var(--primary-color) 42%, var(--border-color));
  box-shadow: inset 0 2px 0 var(--primary-color);
}
/* "角色列表"主页标签：常驻、无 ×，靠图标 + 加粗字体表明它是这一视图的家 */
.page-tab--home { font-weight: 700; color: var(--text-color); padding-right: 12px; margin-right: 2px; }
.pt-icon { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 16px; font-size: 11px; color: var(--primary-color); }
.pt-avatar {
  width: 22px; height: 22px; flex-shrink: 0; border-radius: 50%; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--primary-color) 18%, var(--panel-bg)); color: var(--text-color); font-size: 12px; line-height: 1;
  border: 1px solid var(--border-strong);
}
.pt-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pt-emoji { display: block; }
.pt-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.82rem; }
.pt-close {
  width: 18px; height: 18px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
  padding: 0; margin-right: 2px; border: none; border-radius: 50%; background: transparent;
  color: inherit; font-size: 15px; line-height: 1; cursor: pointer;
}
.pt-close:hover { background: color-mix(in srgb, var(--danger-color) 20%, transparent); color: var(--danger-color); }
/* 标签栏常驻期间，卡片列表也要让出顶部那 42px，否则外层会多一条滚动条；
   导出截图把标签栏收起时（has-char-tabs 被摘掉）它重新占满整屏。 */
#canvas-area.has-char-tabs > .view-container#character-list-container { height: calc(100% - var(--pt-h)); }
/* 详情页：整页高度锁死在"标签栏以下的画布"，页面本身绝不滚（画布层再兜一道 overflow:hidden），
   滚动完全交给左右两栏各自处理，互不拉扯。 */
.view-container#character-detail-container { display: none; flex-direction: column; height: calc(100% - var(--pt-h)); overflow: hidden; }
.canvas-area:has(> #character-detail-container.active) { overflow: hidden; }
.view-container#character-detail-container.active { display: flex; }
.character-detail-page { transition: opacity 0.25s ease, transform 0.25s ease; }
/* 点出场章节跳大纲时整页变淡让位，闪烁结束自动恢复（数据与输入一概不动） */
.character-detail-page.cdp-dim { opacity: 0.22; transform: scale(0.985); transform-origin: top left; pointer-events: none; }
/* 档案页外壳：极浅纸灰底，无衬线，正文 14px / 1.8，全靠极细线与留白分层 */
.vd-dossier { background: var(--vd-paper); color: var(--vd-ink); font-family: var(--vd-type); font-size: 14px; line-height: 1.8; }
.cdp-head {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-shrink: 0;
  padding: 12px 32px; background: var(--vd-paper); border-bottom: 1px solid var(--vd-line);
}
.cdp-crumb { display: inline-flex; align-items: center; gap: 8px; font-size: 0.85rem; color: var(--vd-ink-soft); min-width: 0; font-family: var(--vd-type); }
.cdp-crumb i { color: var(--primary-color); }
.cdp-crumb-sep { opacity: 0.4; }
.cdp-crumb b { color: var(--vd-ink); font-size: 0.9rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* FILE NO.：档案编号取自角色 id，纯展示，不进 App.data。等宽字体 + 极大字距 = 打字机索引号 */
.vd-file-no { display: inline-flex; gap: 8px; font-family: var(--vd-mono); font-size: 10px; letter-spacing: 3px; color: var(--vd-ink-soft); text-transform: uppercase; padding: 2px 0; }
.vd-file-no b { color: var(--vd-ink); font-weight: 500; letter-spacing: 2px; }
.cdp-head-actions { display: inline-flex; align-items: center; gap: 10px; flex-shrink: 0; }
.cdp-gear-wrap { position: relative; z-index: 30; }
/* 档案页的按钮一律收进极简口径：无边框、浅底、药丸圆角，和卡片一样只靠阴影分层 */
.vd-dossier .btn { border: 1px solid var(--vd-line-strong); border-radius: var(--radius-btn); background: transparent; color: var(--vd-ink-soft); padding: 4px 12px; font-family: var(--vd-mono); font-size: 11px; font-weight: 500; letter-spacing: 1px; transition: border-color 0.16s ease, color 0.16s ease; }
.vd-dossier .btn:hover { background: transparent; color: var(--vd-ink); border-color: var(--vd-ink); }
.vd-dossier .btn-danger { border-color: color-mix(in srgb, var(--danger-color) 45%, transparent); color: var(--danger-color); }
.vd-dossier .btn-danger:hover { border-color: var(--danger-color); color: var(--danger-color); background: transparent; filter: none; }
.cdp-saved-hint { font-size: 0.75rem; color: var(--vd-ink-soft); opacity: 0.7; transition: color 0.2s, opacity 0.2s; font-family: var(--vd-type); letter-spacing: 1px; }
.cdp-saved-hint.is-saved { color: var(--vd-seal); opacity: 1; }
/* 这一层只是"两栏的格子"，自己绝不滚：overflow 一旦给它，鼠标落在栏间隙就会带动整页 */
.cdp-wrap { flex: 1; min-height: 0; overflow: hidden; padding: 20px 32px 20px; }
.cp-layout {
  display: grid; grid-template-columns: 120px 380px minmax(0, 1fr);
  /* 行高必须钉成 minmax(0,1fr)：写成 auto 的话行会被最长的那一栏撑到内容高度，
     栏内 overflow: auto 就永远不触发，超出部分直接被 .cdp-wrap 裁掉 */
  grid-template-rows: minmax(0, 1fr);
  gap: 24px; align-items: stretch; height: 100%; min-height: 0; max-width: 1440px; margin: 0 auto;
}
/* 三栏各自一个滚动上下文：overscroll-behavior: contain 是这节的关键——
   某一栏滚到头时不再把惯性传给外层，任何一栏滚动绝不会拉扯其它栏，反之同理 */
.cp-nav, .cp-mid, .cp-main {
  height: 100%; min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  scrollbar-gutter: stable;
}
/* 最左导航栏：固定宽度、浅灰底，与右侧白底区分 */
.cp-nav {
  display: flex; flex-direction: column; gap: 14px;
  padding: 16px 14px; background: var(--vd-paper); border: 1px solid var(--vd-line); border-radius: var(--vd-radius);
  box-shadow: none;
}
.cp-nav-top { display: flex; flex-direction: column; gap: 10px; }
.cp-nav-top .cdp-gear-wrap { position: relative; z-index: 30; }
/* 左栏顶部：图标工具栏（折叠/展开/模块），悬浮提示，缩减纵向空间 */
.cp-nav-toolbar { display: flex; gap: 6px; }
.cp-nav-icon {
  flex: 1; display: flex; align-items: center; justify-content: center;
  height: 30px; border: 1px solid var(--vd-line); background: var(--vd-paper-2, var(--panel-bg));
  border-radius: 6px; color: var(--vd-ink); cursor: pointer; font-size: 13px;
}
.cp-nav-icon:hover { border-color: var(--vd-ink-soft); background: var(--vd-hover); }
/* 模块显隐开关面板（图标切换折叠，勾选框常驻左栏内） */
.cp-nav-modules { display: flex; flex-direction: column; gap: 6px; }
.cp-nav-modules.is-collapsed .cp-nav-modules-body { display: none; }
.cp-nav-modules-body { display: flex; flex-direction: column; gap: 2px; padding: 4px 2px 2px; }
/* 目录改为垂直列表 */
.cp-nav .vd-toc { display: flex; flex-direction: column; gap: 2px; flex-wrap: nowrap; min-width: 0; }
.cp-nav .vd-toc-item {
  padding: 6px 8px; border-radius: 4px; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cp-nav .vd-toc-item + .vd-toc-item { margin-left: 0; }
.cp-nav .vd-toc-item + .vd-toc-item::before { content: none; }
.cp-nav .vd-toc-item:hover { background: var(--vd-hover); }
.cp-nav .vd-toc-item.is-active { background: var(--vd-hover); color: var(--vd-ink); font-weight: 600; border-bottom: none; }

/* 中间概览栏：固定 ~320px，白底卡片感，独立滚动 */
.cp-mid {
  display: flex; flex-direction: column; gap: 16px;
  padding: 4px 2px 32px;
}
.cp-main {
  display: flex; flex-direction: column; gap: 26px; min-width: 0;
  padding: 0 10px 40px 0;
}
.cp-nav::-webkit-scrollbar, .cp-mid::-webkit-scrollbar, .cp-main::-webkit-scrollbar,
.cp-nav::-webkit-scrollbar-track, .cp-mid::-webkit-scrollbar-track, .cp-main::-webkit-scrollbar-track { background: transparent; }
.cp-nav::-webkit-scrollbar-thumb, .cp-mid::-webkit-scrollbar-thumb, .cp-main::-webkit-scrollbar-thumb,
.cp-nav::-webkit-scrollbar-thumb:hover, .cp-mid::-webkit-scrollbar-thumb:hover, .cp-main::-webkit-scrollbar-thumb:hover {
  background: rgba(17, 24, 39, 0.12); border-radius: 999px; border: 2px solid transparent; background-clip: content-box;
}
/* 头部属性面板：头像放大居中置顶，下方所有字段垂直堆叠，每行"左浅灰标签 + 右深色内容" */
.cp-card {
  display: flex; flex-direction: column; gap: 16px;
  align-items: stretch; padding-bottom: 16px; border-bottom: 1px solid var(--vd-line);
}
.cp-card-avatar { width: 100%; display: flex; flex-direction: column; gap: 8px; align-items: center; }
.cp-card .cp-avatar { width: 85%; max-width: 340px; height: auto; aspect-ratio: 1 / 1; border-radius: 14px; }
.cp-card-avatar .cp-avatar-tools { width: 85%; max-width: 340px; justify-content: center; }
/* 字段行：左标签固定宽度右对齐，右控件占满；统一成"标签：内容"纵向列表，行距拉开呼吸感 */
.cp-prop { display: grid; grid-template-columns: 64px 1fr; align-items: start; column-gap: 10px; min-width: 0; }
.cp-prop > label { font-size: 13px; line-height: 1.6; padding-top: 3px; color: var(--text-muted); text-align: right; white-space: nowrap; }
.cp-prop--stack { align-items: start; }
.cp-prop--stack > label { padding-top: 6px; }
/* 中栏头部重构：姓名居中放大；属性双列网格；别名/标识色各占单行 */
.cp-name-row { display: flex; justify-content: center; margin: 4px 0 2px; }
.cp-name-row .cp-name-input {
  width: 100%; text-align: center; border: none; outline: none; box-shadow: none;
  background: transparent; font-size: 22px; font-weight: 700; color: var(--text-color); padding: 2px 0;
}
.cp-name-row .cp-name-input::placeholder { color: var(--text-placeholder); }
.cp-attr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 18px; margin-top: 4px; }
.cp-attr-grid .cp-prop { grid-template-columns: 48px 1fr; column-gap: 8px; }
.cp-prop-val { min-width: 0; display: flex; align-items: center; }
/* 极简输入框：去掉原生边框与描边，浅灰底 + 圆角，深灰文字，高度约 28px */
.cp-prop .cp-input {
  border: 1px solid var(--border-color) !important; outline: none !important;
  background: var(--input-sunken); border-radius: var(--input-radius); height: 28px; width: 100%;
  padding: 0 10px; color: var(--text-color); font-size: 13px; box-sizing: border-box;
}
.cp-prop .cp-input::placeholder { color: var(--text-placeholder); }
.cp-prop .cp-input:focus-visible { background: color-mix(in srgb, var(--input-sunken) 82%, var(--text-color) 18%); box-shadow: var(--input-focus-ring); }
.cp-prop .cselect.cp-input { cursor: pointer; }
/* 姓名：字号放大加粗，作为属性面板的视觉锚点（透明底，像标题） */
.cp-prop--name .cp-input { height: auto; font-size: 20px; font-weight: 700; background: transparent; color: var(--text-color); padding: 2px 0; border: none; }
.cp-prop--name .cp-input:focus-visible { box-shadow: inset 0 -1px 0 var(--primary-color); }
.cp-prop--name > label { line-height: 28px; }
/* 标识色：色板 + 取色器并排（覆盖全局 .cp-color-row 的 grid 布局） */
.cp-prop .cp-color-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cp-prop .cp-color-presets { justify-content: flex-start; }
.cp-prop .cp-color { width: 30px; height: 28px; border: none; background: none; padding: 0; cursor: pointer; flex-shrink: 0; }
/* 阵营：多行控件（标签盒 + 预设 + 提示），标签与首行基线对齐 */
.cp-prop--stack { align-items: start; }
.cp-prop--stack > label { margin-top: 4px; }
.cp-prop--stack .cp-prop-val { flex-direction: column; align-items: stretch; gap: 6px; }
/* 头像：100px 方形、4px 极小圆角、1px 细边框，不加任何大面积底色 */
.vd-frame { border: none; background: transparent; padding: 0; box-shadow: none; align-self: flex-start; }
.vd-frame-cap { display: none; }
.cp-avatar {
  width: 100px; height: 100px; border-radius: 4px; overflow: hidden; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid var(--border-strong);
  box-shadow: 0 1px 3px rgba(0,0,0,0.03);
  cursor: pointer; transition: border-color 0.15s, box-shadow 0.15s;
}
.cp-avatar:hover { border-color: var(--vd-ink-soft); box-shadow: none; }
.cp-avatar:focus-visible { outline: 1px solid var(--vd-ink); outline-offset: 3px; }
.cp-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cp-avatar.has-image { border-color: var(--vd-line); }
/* 档案页 / 弹窗大头像：无图时同样铺角色专属标识色，姓名兜底与缺省人形一律白字 */
.cp-avatar:not(.has-image) { background: var(--cp-accent, var(--text-muted)); }
.cp-avatar:not(.has-image) .cp-avatar-name,
.cp-avatar:not(.has-image) .cp-avatar-fallback { color: var(--on-accent); }
/* 拖进来时只换一条实线边框提示"松手就上传"，不铺彩色底 */
.cp-avatar.is-dragover { border-color: var(--vd-ink); border-style: dashed; box-shadow: none; }
.cp-avatar-fallback { font-size: 2rem; line-height: 1.2; color: var(--vd-ink-soft); text-align: center; }
/* 头像缺省的人形线稿：三处格子（卡片 16px / 浮层 16px / 档案页大框）各按自己的字号缩放，颜色跟着格子走 */
.cp-avatar-fallback svg { width: 1.1em; height: 1.1em; vertical-align: -0.15em; }
/* 大图缺省（弹窗/档案页）：完整姓名居中写进框里；一行放不下就省略号，绝不溢出框外 */
.cp-avatar-name {
  display: block; max-width: 100%; padding: 0 8px;
  font-family: var(--vd-type); font-size: 15px; font-weight: 600; line-height: 1.3; letter-spacing: 1px;
  color: var(--vd-ink-soft); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ol-cast-ava svg, .ol-lp-ava svg { width: 11px; height: 11px; }
.cp-avatar.has-image .cp-avatar-fallback { display: none; }
.cp-avatar-tools { display: flex; align-items: center; gap: 8px; width: 100%; }
.cp-avatar-clear { display: none; font-size: 0.75rem; padding: 5px 10px; white-space: nowrap; }
.cp-hidden-file { display: none; }
/* 档案馆口径的填写区：平时就是普通文字，只留一条 1px 点线说"这里可以填"。
   悬停给一层 rgba(0,0,0,.02)（等于没有），聚焦换成 1px 深灰实线——不给蓝底、光晕和白卡片。
   背景一律 transparent，浏览器默认的那圈灰边框在这里不存在。 */
.cp-field { display: grid; grid-template-columns: 76px minmax(0, 1fr); align-items: baseline; gap: 4px 12px; width: 100%; }
.cp-field > label { font-family: var(--vd-type); font-size: 11px; font-weight: 500; letter-spacing: 1px; color: var(--vd-ink-soft); }
/* 英文键只是编号用的锚点：等宽、更小、更浅，靠 "::" 之外的部分一律让中文读 */
.cp-key { font-family: var(--vd-mono); font-size: 10px; font-weight: 500; letter-spacing: 1.5px; color: var(--vd-ink-soft); opacity: 0.7; }
.cp-key::after { content: " / "; letter-spacing: 0; opacity: 0.6; }
.vd-dossier .cp-field input, .vd-dossier .cp-field textarea, .vd-dossier .cp-field .cselect,
.vd-dossier .cf-box input {
  border: none; border-bottom: 1px dotted var(--vd-line); border-radius: 0;
  background: transparent; color: var(--vd-ink); padding: 3px 2px; font-size: 14px; line-height: 1.8;
  transition: background 0.16s ease, border-color 0.16s ease;
}
/* 正文阅读态：连虚线都不留，只靠留白分行；占位用透明底线，聚焦时才不会顶下去一格 */
.vd-dossier .vd-mod-body textarea, .vd-dossier .vd-mod-body input {
  border: none; border-bottom: 1px solid transparent; border-radius: 0;
  background: transparent; color: var(--vd-ink); padding: 3px 2px; font-size: 14px; line-height: 1.8;
  transition: background 0.16s ease, border-color 0.16s ease;
}
.vd-dossier .cp-field input:hover, .vd-dossier .cp-field .cselect:hover,
.vd-dossier .vd-mod-body textarea:hover, .vd-dossier .vd-mod-body input:hover { background: var(--vd-hover); }
.vd-dossier .cp-field input:focus-visible, .vd-dossier .cp-field .cselect:focus-visible,
.vd-dossier .vd-mod-body textarea:focus-visible, .vd-dossier .vd-mod-body input:focus-visible {
  outline: none; border-bottom: 1px solid var(--vd-ink-mute); background: transparent;
}
.vd-dossier ::placeholder { color: var(--vd-ink-soft); opacity: 0.45; }
/* 左侧字段一行一条：11px 短标签在左，填的值顶到右边，读起来就是档案卡的键值对 */
.cp-side .cp-field { grid-template-columns: 108px minmax(0, 1fr); }
.cp-side .cp-field > label { align-self: auto; text-align: left; }
.cp-side .cp-field input, .cp-side .cp-field .cselect-text { text-align: right; }
/* 姓名、阵营这种"占一整块"的字段不参与键值对排版：标题在上，内容自己铺开 */
.cp-field--name, .cp-field--stack { display: block; }
.cp-field--stack > label { display: block; margin-bottom: 6px; }
.cp-field--name { margin-top: 6px; }
.vd-name-line { display: flex; align-items: baseline; gap: 12px; width: 100%; }
.cp-field input.cp-name-input { font-size: 26px; font-weight: 700; text-align: left; line-height: 1.4; padding: 2px 0; font-family: var(--vd-type); letter-spacing: 2px; }
/* 状态印章：档案馆只认细线框，绿底胶囊这种"贴上去的贴纸"改成 1px 细框 + 小字 */
.vd-stamp {
  display: inline-flex; align-items: center; font-family: var(--vd-mono); font-size: 10px; font-weight: 500;
  letter-spacing: 2px; color: var(--vd-seal); background: transparent;
  border: 1px solid color-mix(in srgb, var(--vd-seal) 45%, transparent); border-radius: 0; padding: 2px 7px;
  white-space: nowrap; flex-shrink: 0;
}
.vd-stamp:empty { display: none; }
/* 基础信息四格：CSS Grid 一行两条键值对，标签列固定在左，值列顶到右 */
.cp-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.cp-grid--infobox { grid-template-columns: minmax(0, 1fr); gap: 10px 26px; width: 100%; }
.cp-grid--infobox .cp-field { grid-template-columns: 108px minmax(0, 1fr); }
.cp-grid--infobox .cp-field input, .cp-grid--infobox .cp-field .cselect { font-size: 14px; font-weight: 500; }
/* 标识色：一个自选色块 + 一排 14px 小圆点，圆点下面用等宽小字写 COLOR PALETTE */
.cp-color-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px; }
.cp-field input.cp-color { width: 28px; height: 14px; padding: 0; border: 1px solid var(--vd-line); border-bottom: 1px solid var(--vd-line); border-radius: 2px; background: transparent; cursor: pointer; justify-self: start; }
.cp-color-presets { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; align-items: center; }
/* JS 生成的那 6 个色点是内联 30px 方块，这里统一收成 14px 圆点（内联样式只能靠 !important 覆盖） */
.cp-color-presets .color-preset { width: 14px !important; height: 14px !important; border-radius: 50% !important; border: 1px solid color-mix(in srgb, var(--text-color) 14%, transparent); padding: 0; cursor: pointer; }
.cp-color-presets .color-preset:hover { box-shadow: 0 0 0 2px color-mix(in srgb, var(--text-color) 10%, transparent); }
.cp-color-presets:not(:empty)::after {
  content: "COLOR PALETTE"; width: 100%; text-align: right;
  font-family: var(--vd-mono); font-size: 10px; letter-spacing: 2px;
  color: var(--vd-ink-soft);
}
.cp-side .form-hint { text-align: left; color: var(--vd-ink-soft); font-size: 11px; letter-spacing: 0.5px; }
/* ========== 目录（TOC）：一行纯文本，用中点分隔；吸顶，点击 = 直接把对应模块滚到顶 ========== */
.vd-toc-bar {
  position: sticky; top: 0; z-index: 10; flex-shrink: 0;
  display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  /* 左右各外扩一条，把滚动条槽那 10px 也盖住，正文不会从吸顶栏侧边漏出去 */
  margin: 0 -10px 0 0; padding: 12px 10px;
  background: var(--vd-paper); border-bottom: 1px solid var(--vd-line);
  box-shadow: none;
}
/* 目录就是一句话：11px、字距拉开、点与点之间一条细线，选中项只加一根下划线，不给底色 */
.vd-toc { display: flex; flex-wrap: wrap; gap: 0; font-family: var(--vd-type); font-size: 11px; letter-spacing: 1px; min-width: 0; }
.vd-toc-item { border: none; background: transparent; padding: 4px 0; cursor: pointer; font: inherit; color: var(--vd-ink-soft); border-radius: 0; transition: color 0.16s ease; }
.vd-toc-item + .vd-toc-item { margin-left: 9px; }
.vd-toc-item + .vd-toc-item::before { content: "·"; margin-right: 9px; color: var(--vd-line); pointer-events: none; }
.vd-toc-item:hover { color: var(--vd-ink); background: transparent; }
.vd-toc-item.is-active { color: var(--vd-ink); background: transparent; font-weight: 600; border-bottom: 1px solid var(--vd-ink-mute); }
.vd-toc-actions { display: inline-flex; gap: 8px; flex-shrink: 0; }
/* ========== 模块：没有卡片、没有阴影、没有底色——只靠一条 1px 细线和留白分块 ========== */
.vd-mod { border: none; border-top: 1px solid var(--vd-line); border-radius: 0; background: transparent; box-shadow: none; scroll-margin-top: var(--vd-toc-h); }
.vd-mod.is-off { display: none; }
.vd-mod-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 14px 0 8px; cursor: pointer; border-radius: 0; background: transparent; transition: background 0.16s ease; }
.vd-mod-head:hover { background: var(--vd-hover); }
.vd-mod.is-folded > .vd-mod-head { border-radius: 0; }
/* 标题 = 左侧 4×16 标识色方块 + 18px 加粗中文 + 右侧 11px 英文副标，=== 那种符号装饰一概不进这里 */
.vd-mod-title { display: inline-flex; align-items: center; gap: 10px; font-family: var(--vd-type); font-size: 18px; font-weight: 700; line-height: 1.4; color: var(--vd-ink); letter-spacing: 0.2px; }
.vd-mod-title::before { content: ""; width: 2px; height: 14px; border-radius: 0; background: var(--cp-accent, var(--vd-ink)); flex-shrink: 0; }
/* 英文副标只是给眼睛一个归类提示：等宽小字、浅灰、拉开字距，绝不跟中文标题抢重量 */
.vd-mod-en { font-family: var(--vd-mono); font-size: 11px; font-weight: 400; letter-spacing: 2px; color: var(--vd-ink-soft); text-transform: uppercase; white-space: nowrap; }
/* 悬停正文（或标题）时在标题右侧浮出一枚"EDIT"角标：
   角标画在标题行而不是正文上，所以永远不会盖住用户正在读的那一行字 */
.vd-mod-title::after {
  content: "EDIT"; font-family: var(--vd-mono); font-size: 10px; font-weight: 500; letter-spacing: 2px;
  color: var(--vd-ink-soft); background: transparent;
  border: 1px solid var(--vd-line); border-radius: 0; padding: 1px 7px; margin-left: 4px; white-space: nowrap;
  opacity: 0; transform: translateY(-1px); transition: opacity 0.16s ease, transform 0.16s ease;
}
/* 只有长文本模块才有这枚角标（其余模块的正文不是纯打字，标"编辑"反而误导） */
.vd-mod:not(:has(.vd-mod-body > textarea)) > .vd-mod-head .vd-mod-title::after { content: none; }
/* 折着 = 不想看它，正在打字 = 已经在编辑：这两种情况下角标都不出来 */
.vd-mod:where(:not(.is-folded):not(:has(.vd-mod-body:focus-within))) > .vd-mod-head:hover .vd-mod-title::after,
.vd-mod:where(:not(.is-folded):not(:has(.vd-mod-body:focus-within))):has(.vd-mod-body:hover) > .vd-mod-head .vd-mod-title::after { opacity: 1; transform: none; }
/* 折叠符：档案馆只用最细的 − / +，浅灰、等宽，不做旋转动画 */
.vd-fold-mark { font-family: var(--vd-mono); font-size: 14px; line-height: 1; color: var(--vd-ink-soft); flex-shrink: 0; opacity: 0.5; transition: opacity 0.16s ease; }
.vd-fold-mark::before { content: "−"; }
.vd-mod-head:hover .vd-fold-mark { opacity: 1; }
.vd-mod.is-folded .vd-fold-mark::before { content: "+"; }
/* 折叠动画：max-height 收放 + 结束时才掐掉可见性，展开瞬间就能看见 */
.vd-mod-body { position: relative; padding: 2px 0 18px; max-height: 1800px; overflow: hidden; transition: max-height 0.26s ease, opacity 0.2s ease, visibility 0s; }
.vd-mod.is-folded > .vd-mod-body { max-height: 0; opacity: 0; visibility: hidden; padding-top: 0; padding-bottom: 0; transition: max-height 0.24s ease, opacity 0.16s ease, visibility 0s 0.24s; }
.vd-mod-body textarea { display: block; width: 100%; resize: none; min-height: 76px; font-size: 14px; line-height: 1.8; color: var(--vd-ink); field-sizing: auto; max-height: 40vh; overflow-y: auto; scrollbar-width: none; -ms-overflow-style: none; }
.vd-mod-body textarea::-webkit-scrollbar { display: none; }
.vd-hint { margin: 8px 0 0; font-size: 0.8rem; color: var(--vd-ink-soft); }
/* 机密打码：编辑着是正常文字，一旦勾上"设为机密"，阅读态整块涂黑，鼠标悬停才显影 */
.vd-secret { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; font-family: var(--vd-type); font-size: 0.72rem; letter-spacing: 1px; color: var(--vd-ink-soft); cursor: pointer; }
/* 勾选框不是"印在纸上的字"，把上面那条通用底线规则收回 */
.vd-dossier .vd-secret input, .vd-dossier .vd-module-opt input { border: none; padding: 0; }
.vd-secret input { width: 14px; height: 14px; margin: 0; accent-color: var(--vd-seal); }
.vd-mod-body textarea.is-redacted { background: var(--paper-censor); color: transparent; border-bottom-color: var(--paper-censor); caret-color: var(--vd-gold); }
.vd-mod-body textarea.is-redacted:hover, .vd-mod-body textarea.is-redacted:focus { color: var(--paper-censor-fg); }
.vd-mod.is-redacted > .vd-mod-body::after {
  content: "REDACTED"; position: absolute; top: 6px; left: 2px; pointer-events: none; z-index: 1;
  font-family: var(--vd-mono); font-size: 10px; letter-spacing: 3px; color: var(--paper-censor-fg);
  background: var(--paper-censor); border-radius: 0; padding: 2px 8px; transform: rotate(-2deg); opacity: 0.9;
}
/* ========== 战力维度：3–8 边纯 SVG 雷达，顶点可拖 + 双击维度名改名 ========== */
.vd-mod--side { width: 100%; border-top: none; }
.vd-mod--side .vd-mod-title { font-size: 13px; }
.vd-mod--side .vd-mod-head { padding: 10px 0 6px; }
.vd-mod--side > .vd-mod-body { padding: 2px 0 10px; }
.vd-radar-wrap { position: relative; width: 100%; }
.vd-radar { cursor: default; }
.vd-radar:focus-visible { outline: 1px solid var(--vd-ink-mute); outline-offset: 2px; border-radius: 0; }
/* 固定 340px 高的方形画布 + 与 viewBox 同一个比例：1 用户单位 ≈ 1 屏幕像素，
   字号按 px 写就是真正看到的大小，侧栏被压窄时整体等比缩小、绝不变形 */
.vd-radar svg { display: block; width: 100%; max-width: 420px; height: auto; aspect-ratio: 420 / 340; margin: 0 auto; overflow: visible; user-select: none; -webkit-user-select: none; }
/* 档案馆的雷达只有细线：1px 网格、10% 填充、空心顶点，没有任何涂死的块面 */
.vd-radar .vd-radar-grid { fill: none; stroke: var(--vd-line-strong); stroke-width: 1; }
.vd-radar .vd-radar-shape { fill: var(--cp-accent, var(--vd-ink)); fill-opacity: 0.1; stroke: var(--cp-accent, var(--vd-ink)); stroke-width: 1.5; stroke-linejoin: round; pointer-events: none; }
/* 维度名可以双击改名，所以这一层要把指针接住（数值只是读数，继续穿透） */
.vd-radar .vd-radar-label { font-family: var(--vd-type); font-size: 13px; font-weight: 500; letter-spacing: 0.5px; fill: var(--vd-ink-soft); pointer-events: all; cursor: text; transition: fill 0.16s ease; }
.vd-radar .vd-radar-label:hover, .vd-radar .vd-radar-label:focus { fill: var(--vd-ink); outline: none; }
.vd-radar .vd-radar-val { font-family: var(--vd-mono); font-size: 14px; font-weight: 500; letter-spacing: 0; fill: var(--cp-accent, var(--vd-ink)); pointer-events: none; }
.vd-radar:hover .vd-radar-shape { opacity: 0.9; }
/* 顶点可以直接拖着改：外面那只透明大圆负责接手指（所以 pointer-events 全给它），里面那颗才是看得见的顶点 */
.vd-radar-hit { fill: transparent; stroke: none; pointer-events: all; cursor: grab; touch-action: none; }
.vd-radar-hit:hover { fill: color-mix(in srgb, var(--cp-accent, var(--vd-ink)) 10%, transparent); }
/* 空心顶点：只有一圈 1.5px 细线，里面是纸色，绝不填成一颗实心豆子 */
.vd-radar-handle { fill: none; stroke: var(--cp-accent, var(--vd-ink)); stroke-width: 1; pointer-events: none; transform-box: fill-box; transform-origin: center; transition: transform 0.12s; }
.vd-radar-hit:hover + .vd-radar-handle { transform: scale(1.4); stroke-width: 1.5; }
.vd-radar.is-dragging { cursor: grabbing; }
.vd-radar.is-dragging .vd-radar-hit, .vd-radar.is-dragging .vd-radar-hit:hover { cursor: grabbing; fill: color-mix(in srgb, var(--cp-accent, var(--vd-ink)) 14%, transparent); }
.vd-radar.is-dragging .vd-radar-handle { transform: scale(1.5); stroke-width: 1.5; }
/* 拖拽中的数值气泡：墨底纸字的小胶囊，画在顶点内侧，绝不出 SVG 边界 */
.vd-radar-tip { pointer-events: none; }
.vd-radar-tip-bg { fill: var(--vd-ink); stroke: none; rx: 2; }
.vd-radar-tip-num { fill: var(--vd-paper-2); font-family: var(--vd-mono); font-size: 12.5px; font-weight: 500; letter-spacing: 0.5px; }
/* 双击维度名时悬浮在文字上的改名框：position: fixed，坐标按文字的 getBoundingClientRect 现算 */
.vd-radar-rename {
  position: fixed; z-index: var(--z-popover); height: 30px; padding: 0 10px;
  border: none; border-bottom: 1px solid var(--vd-ink-mute); border-radius: 0;
  background: var(--vd-paper-2); color: var(--vd-ink);
  font-family: var(--vd-type); font-size: 14px; font-weight: 600; text-align: center;
  box-shadow: var(--vd-shadow-pop); outline: none;
}
/* 齿轮浮层本体：挂载/摘除、定位、点外与 Esc 关闭全部归 PopoverManager——
   在 DOM 里就是开着，所以不再用 display:none + .is-open 自己开关。 */
.vd-pop { position: fixed; z-index: var(--z-popover); min-width: 210px; padding: 14px 16px; background: var(--vd-paper-2); border: 1px solid var(--vd-line); border-radius: 4px; box-shadow: var(--vd-shadow-pop); }
/* 战力进度条：常驻雷达下方，脱离浮层。属性名在左、2px 细轨道在中、数值在右 */
.vd-stats-bars { display: flex; flex-direction: column; gap: 16px; margin-top: 20px; width: 100%; }
.vd-bar-row { display: grid; grid-template-columns: 62px minmax(0, 1fr) 36px; align-items: center; gap: 12px; }
.vd-bar-name { font-size: 11px; letter-spacing: 1px; color: var(--vd-ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vd-bar-track { position: relative; height: 2px; border-radius: 0; background: var(--vd-line-strong); overflow: hidden; }
.vd-bar-fill { position: absolute; left: 0; top: 0; height: 100%; border-radius: 0; background: var(--cp-accent, var(--vd-gold)); transition: width 0.08s linear; }
.vd-bar-range { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; border: none; background: transparent; opacity: 0; cursor: pointer; -webkit-appearance: none; appearance: none; }
.vd-stats-num { text-align: right; font-family: var(--vd-mono); font-size: 12px; font-variant-numeric: tabular-nums; color: var(--vd-ink); font-weight: 500; letter-spacing: 0.5px; }
.vd-bar-track:hover .vd-bar-fill { filter: none; }
/* 维度设置面板：屏幕居中模态（居中由 PopoverManager 的 place:'center' 算），内部可滚动，绝不飞出视口 */
.vd-pop--dim {
  max-height: 80vh;
  overflow-y: auto;
  width: min(340px, 90vw);
  background: var(--vd-paper-2);
}
.vd-pop-title { font-family: var(--vd-mono); font-size: 10px; font-weight: 500; letter-spacing: 2px; color: var(--vd-ink); margin: 0 0 10px; padding-bottom: 8px; border-bottom: 1px solid var(--vd-line); }
/* 齿轮只有一粒米大，贴在雷达右上角，不占雷达的绘图区（图标是 currentColor 线稿，随字色变色） */
.vd-radar-gear {
  position: absolute; top: 2px; right: 2px; z-index: 2; width: 24px; height: 24px; padding: 0;
  border: none; border-radius: 2px; background: transparent;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--vd-ink-soft); font-family: var(--vd-type); font-size: 13px; line-height: 1; cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
}
.vd-radar-gear:hover, .vd-radar-gear:focus-visible { color: var(--vd-gold); background: var(--vd-hover); outline: none; }
/* 维度设置面板：一行一维，名称改完即落库，数字只是给个眼下是多少的参照 */
.vd-dim-row { display: grid; grid-template-columns: minmax(0, 1fr) 30px 20px; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px solid var(--vd-line-soft); }
.vd-dim-name { width: 100%; border: none; border-bottom: 1px solid transparent; border-radius: 0; background: transparent; padding: 2px 0; font: inherit; font-size: 0.85rem; color: var(--vd-ink); transition: border-color 0.16s ease; }
.vd-dim-name:hover, .vd-dim-name:focus { background: transparent; border-bottom-color: var(--vd-gold); outline: none; }
.vd-dim-val { font-family: var(--vd-type); font-size: 0.8rem; text-align: right; color: var(--cp-accent, var(--vd-ink)); font-variant-numeric: tabular-nums; }
.vd-pop--dim .vd-row-add { margin-top: 10px; width: 100%; justify-content: center; }
.vd-pop--dim .vd-row-add[disabled] { opacity: 0.45; cursor: not-allowed; }
/* 形状快捷切换：一排浅底胶囊，当前边数用标识色实底 */
.vd-shape-switch { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--vd-line-soft); }
.vd-shape-btn { padding: 4px 12px; border: 1px solid var(--vd-line); border-radius: 0; background: transparent; color: var(--vd-ink-soft); font-family: var(--vd-type); font-size: 0.78rem; letter-spacing: 0.3px; cursor: pointer; transition: background 0.16s ease, color 0.16s ease; }
.vd-shape-btn:hover { background: var(--vd-line-soft); color: var(--vd-ink); }
.vd-shape-btn.is-active { background: color-mix(in srgb, var(--vd-gold) 14%, transparent); color: var(--vd-gold); font-weight: 600; }
.vd-module-opt { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 0.85rem; cursor: pointer; }
.vd-module-opt input { width: 14px; height: 14px; margin: 0; accent-color: var(--vd-gold); }
/* ========== 情绪板 / 技能物品 / 角色纪事 ========== */
.vd-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 12px; }
.vd-gallery.is-empty { display: none; }
/* 主档案页情绪板（立绘与参考）：空状态时给出引导文案，不再整块消失 */
#char-gallery.is-empty { display: grid; place-items: center; min-height: 130px; border: 1px dashed var(--vd-line); border-radius: 4px; }
#char-gallery.is-empty::before { content: "点击或拖入图片，作为角色的参考图/立绘集"; color: var(--vd-ink-soft); font-size: 13px; text-align: center; padding: 20px; line-height: 1.6; }
.vd-gallery.is-dragover, #char-mod-gallery > .vd-mod-body.is-dragover { outline: 2px dashed var(--cp-accent, var(--vd-gold)); outline-offset: 4px; border-radius: 2px; }
/* 空档案不摆假相框：整块模块体就是投图区，光标给个"放这里"的暗示 */
#char-mod-gallery > .vd-mod-body { cursor: copy; }
.vd-gal-item { position: relative; margin: 0; padding: 0; background: transparent; border: 1px solid var(--vd-line); border-radius: 2px; overflow: hidden; box-shadow: none; transition: border-color 0.16s ease; }
.vd-gal-item:hover { transform: none; box-shadow: none; border-color: var(--vd-ink-mute); }
.vd-gal-item img { display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover; cursor: zoom-in; }
.vd-gal-cap { margin: 0; padding: 5px 9px; font-family: var(--vd-type); font-size: 0.68rem; letter-spacing: 0.2px; color: var(--vd-ink-soft); background: transparent; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vd-gal-del { position: absolute; top: 6px; right: 6px; width: 20px; height: 20px; padding: 0; border: 1px solid var(--vd-line); border-radius: 2px; background: rgba(255, 255, 255, 0.9); color: var(--vd-ink-soft); font-size: 13px; line-height: 1; cursor: pointer; opacity: 0; transition: opacity 0.16s ease, background 0.16s ease, color 0.16s ease; }
.vd-gal-item:hover .vd-gal-del { opacity: 1; }
.vd-gal-del:hover { background: var(--vd-seal); color: var(--on-accent); }
.vd-inventory { display: flex; flex-direction: column; gap: 6px; }
.vd-inv-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto; align-items: center; gap: 8px; border-bottom: 1px solid var(--vd-line-soft); padding: 4px 6px 4px 0; border-radius: 0; transition: background 0.16s ease; }
.vd-inv-name, .vd-inv-note { width: 100%; min-width: 0; box-sizing: border-box; }
.vd-inv-row:hover { background: var(--vd-hover); }
.vd-inv-name { font-weight: 600; color: var(--vd-ink); }
.vd-row-del { width: 20px; height: 20px; padding: 0; border: none; border-radius: 2px; background: transparent; color: var(--vd-ink-soft); font-size: 13px; line-height: 1; cursor: pointer; transition: background 0.16s ease, color 0.16s ease; }
.vd-row-del:hover { background: color-mix(in srgb, var(--vd-seal) 16%, transparent); color: var(--vd-seal); }
.vd-row-add { margin-top: 10px; font-family: var(--vd-mono); font-size: 11px; letter-spacing: 1px; padding: 5px 14px; border-radius: 0; }
.vd-radar-grid-group { pointer-events: none; }
/* 情绪板大图预览 */
.vd-lightbox { display: none; position: fixed; inset: 0; z-index: 1300; align-items: center; justify-content: center; background: rgba(17, 24, 39, 0.72); cursor: zoom-out; backdrop-filter: blur(2px); }
.vd-lightbox.is-open { display: flex; }
.vd-lightbox img { max-width: 86vw; max-height: 84vh; padding: 0; background: var(--vd-paper-2); border: 1px solid var(--vd-line); border-radius: 2px; box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35); }
.vd-lightbox-close { position: absolute; top: 18px; right: 24px; width: 34px; height: 34px; border: 1px solid rgba(255, 255, 255, 0.45); border-radius: 2px; background: rgba(255, 255, 255, 0.14); color: var(--on-accent); font-size: 18px; line-height: 1; cursor: pointer; }
.vd-lightbox-close:hover { background: var(--vd-seal); }
/* 档案页里的关系胶囊与出场链接：同样只用 1px 细线框 + 小字，不铺色块 */
.vd-mod-body .char-relations-panel { gap: 8px; line-height: 1.9; }
.vd-mod-body .char-relation-capsule { font-size: 12px; letter-spacing: 1px; padding: 2px 8px; border: 1px solid var(--vd-line-strong); border-radius: 0; background: transparent; color: var(--vd-ink); transition: border-color 0.16s ease, color 0.16s ease; }
.vd-mod-body .char-relation-capsule:hover { border-color: var(--vd-ink); color: var(--vd-ink); background: transparent; }
.vd-mod-body .char-add-relation { font-size: 12px; letter-spacing: 1px; padding: 2px 8px; border: 1px dashed var(--vd-line-strong); border-radius: 0; background: transparent; color: var(--vd-ink-soft); }
.vd-mod-body .char-add-relation:hover { border-style: solid; border-color: var(--vd-ink); color: var(--vd-ink); background: transparent; }
.vd-mod-body .char-appearance-link { font-size: 0.9rem; padding: 3px 6px; }
/* 阵营标签：档案馆用硬朗线框 + # 前缀，1px 直角小框，不铺任何底色 */
.cf-box { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-height: 30px; padding: 4px 0; border: none; border-bottom: 1px dotted var(--vd-line); border-radius: 0; background: transparent; cursor: text; transition: border-color 0.16s ease; }
.cf-box:focus-within { border-bottom: 1px solid var(--vd-ink-mute); background: transparent; box-shadow: none; }
.cf-pills { display: contents; }
.cf-box input { flex: 1; min-width: 130px; width: auto; border: none; background: transparent; padding: 2px 0; font-size: 14px; }
.cf-box input:focus-visible { outline: none; }
.cf-pill { display: inline-flex; align-items: center; gap: 5px; padding: 1px 4px 1px 7px; border-radius: var(--radius-btn); font-size: 12px; letter-spacing: 1px; line-height: 1.7; font-weight: 500; white-space: nowrap; font-family: var(--vd-type); color: var(--vd-ink); background: transparent; border: 1px solid var(--vd-ink); }
.cf-pill::before { content: "#"; font-family: var(--vd-mono); color: var(--vd-ink-soft); }
.cf-pill-x { width: 16px; height: 16px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; padding: 0; border: none; border-radius: 0; background: transparent; color: var(--vd-ink-soft); font-size: 13px; line-height: 1; cursor: pointer; }
.cf-pill-x:hover { background: transparent; color: var(--vd-seal); }
/* 预设阵营：点一下即变标签；只有别的角色用过的阵营才列出来，没有就整行不输出 */
.cf-presets { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.cf-presets:empty { display: none; }
.cf-preset { display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; border-radius: 0; font-family: var(--vd-type); font-size: 12px; letter-spacing: 1px; line-height: 1.7; cursor: pointer; color: var(--vd-ink-soft); background: transparent; border: 1px dashed var(--vd-line-strong); transition: color .16s ease, border-color .16s ease; }
.cf-preset::before { content: "+"; font-family: var(--vd-mono); }
.cf-preset:hover { color: var(--vd-ink); border-style: solid; border-color: var(--vd-ink); background: transparent; }
/* ============================================================
   响应式：角色档案全页（Vintage Dossier）
   断点策略：
   · >1200px  三栏（左目录 120 / 中基础 380 / 右详细 1fr）—— 桌面原版
   · 769–1200 平板 / 窄屏电脑：保留两栏。左栏目录固定 ~100px 仍独立滚动做锚点，
             中栏与右栏合并进右侧同一列纵向排列，视线不再走 Z 字。
   · ≤768px   手机 / 小屏平板：目录索引改造为顶部"横向可滑动 Tabs 栏"（吸顶），
             中右栏全部纵向堆叠，模块之间加明显分割线。
   ============================================================ */
/* —— 平板 / 窄屏电脑：两栏（左目录 + 右合并内容） —— */
@media (max-width: 1200px) {
  .cdp-wrap { overflow-y: auto; padding: 16px 20px 36px; }
  .cp-layout {
    grid-template-columns: 100px minmax(0, 1fr);
    grid-template-rows: auto auto;
    gap: 0 18px; height: auto; max-width: none;
  }
  /* 左栏：钉在右列顶部、自身内容过长时内部滚动，不随右列一起滚走 */
  .cp-nav {
    grid-column: 1; grid-row: 1 / span 2;
    position: sticky; top: 0; align-self: start;
    height: auto; max-height: calc(100vh - 32px); overflow-y: auto;
    padding: 14px 10px;
  }
  /* 中栏 + 右栏：合并进同一列（各自不再独立滚动，交给 .cdp-wrap 整页滚动） */
  .cp-mid { grid-column: 2; grid-row: 1; height: auto; overflow: visible; padding: 0 2px 20px; }
  .cp-main { grid-column: 2; grid-row: 2; height: auto; overflow: visible; padding: 0 2px 24px; gap: 22px; }
  .vd-gallery { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
}
/* —— 手机 / 小屏平板：单栏 + 顶部横向 Tabs 目录 —— */
@media (max-width: 768px) {
  .cdp-wrap { overflow-y: auto; padding: 8px 12px 32px; }
  .cp-layout {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;
    gap: 0; height: auto; max-width: none;
  }
  /* 目录索引 → 顶部横向可滑动 Tabs 栏（吸顶），编辑用的工具栏 / 模块开关在手机上收起 */
  .cp-nav-top, .cp-nav-modules { display: none; }
  .cp-nav {
    grid-column: 1; grid-row: 1;
    position: sticky; top: 0; z-index: 25;
    flex-direction: row; align-items: center; gap: 8px;
    margin-bottom: 10px; padding: 8px 10px;
    overflow-x: auto; overflow-y: hidden;
    box-shadow: 0 2px 6px rgba(0,0,0,0.06);
  }
  .cp-nav .vd-toc { flex-direction: row; flex-wrap: nowrap; gap: 6px; padding: 2px; overflow-x: auto; }
  .cp-nav .vd-toc-item {
    white-space: nowrap; text-align: center; border: 1px solid var(--vd-line);
    border-radius: 999px; padding: 6px 14px;
  }
  .cp-nav .vd-toc-item.is-active { background: var(--vd-ink); color: var(--vd-paper); border-color: var(--vd-ink); }
  .cp-mid { grid-column: 1; grid-row: 2; height: auto; overflow: visible; padding: 0 2px 16px; }
  .cp-main { grid-column: 1; grid-row: 3; height: auto; overflow: visible; padding: 0 2px 24px; gap: 0; }
  /* 模块之间明显分割：上边框 + 留白（吸顶 Tabs 的滚动锚点偏移） */
  .cp-main .vd-mod { padding: 18px 0; border-top: 1px solid var(--vd-line); scroll-margin-top: 60px; }
  .cp-main .vd-mod:first-child { border-top: none; padding-top: 4px; }
  .vd-gallery { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
  .vd-mod-body textarea { min-height: 120px; }
}
@media (max-width: 620px) {
  .vd-inv-row { grid-template-columns: minmax(0, 1fr) auto; }
  .vd-inv-note { grid-column: 1 / -1; }
}
/* ===== 档案页深色模式：同一张纸换成暗黑牛皮纸（暖调，绝不纯黑纯白） =====
   --vd-* 现引用独立 --paper-* 令牌（见 :root / [data-theme=dark]），
   浅色=暖米羊皮纸，暗色=暗黑牛皮纸，完全脱离全局令牌与 theme.js 自定义主题注入。 */
[data-theme="dark"] .vd-dossier, [data-theme="dark"] .character-detail-page { background: var(--paper-bg) !important; color: var(--paper-ink) !important; }
[data-theme="dark"] .vd-dossier .cp-field input, [data-theme="dark"] .vd-dossier .cp-field textarea,
[data-theme="dark"] .vd-dossier .vd-mod-body textarea,
[data-theme="dark"] .vd-dossier .cf-box input, [data-theme="dark"] .vd-dossier .vd-inv-row input,
[data-theme="dark"] .vd-dossier input, [data-theme="dark"] .vd-pop input {
  background-color: transparent !important; color: var(--paper-ink) !important;
}
[data-theme="dark"] .vd-dossier .cp-field input:focus-visible, [data-theme="dark"] .vd-dossier .cp-field textarea:focus-visible,
[data-theme="dark"] .vd-dossier .vd-mod-body textarea:focus-visible { border-bottom-color: var(--vd-ink-mute) !important; background: transparent !important; }
[data-theme="dark"] .vd-dossier .cp-field input:hover, [data-theme="dark"] .vd-dossier .cp-field textarea:hover,
[data-theme="dark"] .vd-dossier .vd-mod-body textarea:hover { background: var(--vd-hover) !important; }
[data-theme="dark"] .vd-dossier .cf-box { background-color: transparent !important; }
[data-theme="dark"] .vd-dossier .cf-pill { border-color: var(--vd-line) !important; color: var(--vd-ink) !important; background: transparent !important; }
[data-theme="dark"] .vd-dossier .cf-pill::before { color: var(--vd-ink-mute); }
[data-theme="dark"] .vd-dossier .vd-mod-body .char-relation-capsule { color: var(--paper-ink) !important; }
[data-theme="dark"] .vd-dossier .vd-gal-del { background: color-mix(in srgb, var(--paper-bg) 85%, transparent); color: var(--paper-ink-soft); }
[data-theme="dark"] .vd-dossier .vd-radar-rename { color: var(--paper-ink); }
[data-theme="dark"] .vd-dossier .cdp-head, [data-theme="dark"] .character-detail-page .cdp-head { background: var(--paper-bg-2); border-bottom-color: var(--vd-line); }
[data-theme="dark"] .vd-dossier .cdp-crumb b, [data-theme="dark"] .character-detail-page .cdp-crumb b { color: var(--paper-ink); }
[data-theme="dark"] .vd-dossier .vd-file-no, [data-theme="dark"] .character-detail-page .vd-file-no { color: var(--vd-ink-soft); border-color: var(--vd-line-soft); }
[data-theme="dark"] .vd-dossier .cp-field > label, [data-theme="dark"] .vd-dossier .vd-mod-title { color: var(--paper-ink); }
/* 英文键在深色里照样只做"编号"，不许跟中文标签比亮度 */
[data-theme="dark"] .vd-dossier .cp-key { color: var(--vd-ink-soft); }
[data-theme="dark"] .vd-dossier .vd-mod { background: transparent; border-top-color: var(--vd-line); }
[data-theme="dark"] .vd-dossier .vd-mod-head { background: transparent; border-bottom-color: var(--vd-line); }
[data-theme="dark"] .vd-dossier .vd-frame { background: transparent; border-color: transparent; box-shadow: none; }
[data-theme="dark"] .vd-dossier .vd-toc-bar { border-bottom-color: var(--vd-line); }
[data-theme="dark"] .vd-dossier .vd-toc-item { color: var(--vd-ink-soft); }
/* 三栏新增部件的深色适配：导航栏改暗色纸面，名片文字/分隔提亮 */
[data-theme="dark"] .vd-dossier .cp-nav, [data-theme="dark"] .character-detail-page .cp-nav { background: var(--paper-bg-2); border-color: var(--paper-line-strong); }
[data-theme="dark"] .vd-dossier .cp-nav .vd-toc-item, [data-theme="dark"] .character-detail-page .cp-nav .vd-toc-item { color: var(--paper-ink-soft); }
[data-theme="dark"] .vd-dossier .cp-nav .vd-toc-item.is-active, [data-theme="dark"] .character-detail-page .cp-nav .vd-toc-item.is-active { color: var(--paper-ink); }
[data-theme="dark"] .vd-dossier .cp-card, [data-theme="dark"] .character-detail-page .cp-card { border-bottom-color: var(--paper-line-strong); }
/* 属性面板新增部件的深色适配：纸面标签 / 纸面数值，输入框改用暗色纸面 */
[data-theme="dark"] .vd-dossier .cp-prop > label, [data-theme="dark"] .character-detail-page .cp-prop > label { color: var(--paper-ink-soft); }
[data-theme="dark"] .vd-dossier .cp-prop .cp-input, [data-theme="dark"] .character-detail-page .cp-prop .cp-input { background: var(--paper-bg-2); color: var(--paper-ink); }
[data-theme="dark"] .vd-dossier .cp-prop .cp-input::placeholder, [data-theme="dark"] .character-detail-page .cp-prop .cp-input::placeholder { color: var(--paper-ink-mute); }
[data-theme="dark"] .vd-dossier .cp-prop .cp-input:focus-visible, [data-theme="dark"] .character-detail-page .cp-prop .cp-input:focus-visible { background: color-mix(in srgb, var(--paper-bg-2) 72%, var(--paper-ink) 28%); box-shadow: 0 0 0 3px color-mix(in srgb, var(--paper-gold) 22%, transparent); }
[data-theme="dark"] .vd-dossier .cp-prop--name .cp-input, [data-theme="dark"] .character-detail-page .cp-prop--name .cp-input { background: transparent; color: var(--paper-ink); }
[data-theme="dark"] .vd-dossier .cp-name-row .cp-name-input, [data-theme="dark"] .character-detail-page .cp-name-row .cp-name-input { color: var(--paper-ink); }
[data-theme="dark"] .vd-dossier .cp-name-row .cp-name-input::placeholder, [data-theme="dark"] .character-detail-page .cp-name-row .cp-name-input::placeholder { color: var(--paper-ink-mute); }
[data-theme="dark"] .vd-dossier .cp-nav-icon, [data-theme="dark"] .character-detail-page .cp-nav-icon { background: var(--paper-bg-2); border-color: var(--paper-line-strong); color: var(--paper-ink-soft); }
[data-theme="dark"] .vd-dossier .cp-nav-icon:hover, [data-theme="dark"] .character-detail-page .cp-nav-icon:hover { background: var(--paper-surface-hover); border-color: var(--paper-line-strong); }
[data-theme="dark"] .vd-dossier #char-gallery.is-empty::before, [data-theme="dark"] .character-detail-page #char-gallery.is-empty::before { color: var(--paper-ink-mute); }
[data-theme="dark"] .vd-dossier .vd-pop { background: var(--paper-bg-2); border-color: var(--vd-line); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); }
[data-theme="dark"] .vd-dossier .vd-pop--stats { background: color-mix(in srgb, var(--paper-bg-2) 95%, transparent); }
[data-theme="dark"] .vd-dossier .vd-pop--dim { background: var(--paper-bg-2); }
[data-theme="dark"] .vd-dossier .vd-shape-btn { background: transparent; border-color: var(--vd-line); color: var(--vd-ink-soft); }
[data-theme="dark"] .vd-dossier .vd-shape-btn:hover { background: var(--vd-line-soft); color: var(--vd-ink); }
[data-theme="dark"] .vd-dossier .vd-shape-btn.is-active { background: color-mix(in srgb, var(--vd-gold) 22%, transparent); color: var(--vd-gold); }
[data-theme="dark"] .vd-dossier .vd-stats-val, [data-theme="dark"] .vd-dossier .vd-hint { color: var(--vd-ink-soft); }
[data-theme="dark"] .vd-dossier .vd-dossier input[type="range"] { accent-color: var(--paper-gold); }
[data-theme="dark"] .vd-dossier .vd-row-del { color: var(--vd-ink-soft); border-color: var(--vd-line-soft); }
[data-theme="dark"] .vd-dossier .vd-lightbox img { background: var(--paper-bg-2); border-color: var(--vd-line); }
[data-theme="dark"] .vd-dossier .page-tabs-bar, [data-theme="dark"] .character-detail-page .page-tabs-bar { background: var(--paper-bg-2); border-bottom-color: var(--paper-line-strong); }
[data-theme="dark"] .vd-dossier .page-tab, [data-theme="dark"] .character-detail-page .page-tab { background: color-mix(in srgb, var(--paper-bg) 70%, transparent); border-color: var(--paper-line-strong); color: var(--paper-ink-soft); }
[data-theme="dark"] .vd-dossier .page-tab.active, [data-theme="dark"] .character-detail-page .page-tab.active { background: var(--paper-bg-2); color: var(--paper-ink); border-color: var(--vd-gold); }
/* theme.js 注入的深色补丁把裸 input 的 border-color 涂成实色，档案页这些"平时只是文字"的框会被划出一道灰线。
   这里按更高的特异性收回来：只有聚焦时才露出标识色底线。 */
[data-theme="dark"] .vd-dossier .cp-field input, [data-theme="dark"] .vd-dossier .cp-field .cselect,
[data-theme="dark"] .vd-dossier .vd-mod-body input, [data-theme="dark"] .vd-dossier .vd-mod-body textarea,
[data-theme="dark"] .vd-dossier .cf-box input, [data-theme="dark"] .vd-dossier .vd-dim-name { border-color: transparent !important; }
[data-theme="dark"] .vd-dossier .vd-dim-name { border-bottom-color: transparent !important; }
[data-theme="dark"] .vd-dossier .vd-dim-name:focus { border-bottom-color: var(--vd-gold) !important; }
[data-theme="dark"] .vd-dossier .vd-radar-rename { border-color: transparent !important; border-bottom-color: var(--vd-gold) !important; background: var(--paper-bg-2) !important; }
[data-theme="dark"] .vd-dossier .btn:not(.color-preset-btn) { border: 1px solid var(--vd-line) !important; background: transparent !important; color: var(--vd-ink-soft) !important; text-shadow: none !important; }
[data-theme="dark"] .vd-dossier .btn:not(.color-preset-btn):hover { border-color: var(--paper-ink) !important; background: transparent !important; color: var(--paper-ink) !important; }
/* 吸顶目录压在正文上时要有实色底，半透明会让滚过去的字透出残影 */
[data-theme="dark"] .vd-dossier .vd-toc-bar { background: var(--paper-bg); box-shadow: 0 10px 12px -12px rgba(0, 0, 0, 0.6); }
[data-theme="dark"] .vd-dossier .vd-mod-title::after { background: color-mix(in srgb, var(--vd-gold) 18%, transparent); }
[data-theme="dark"] .cp-nav::-webkit-scrollbar-thumb, [data-theme="dark"] .cp-mid::-webkit-scrollbar-thumb, [data-theme="dark"] .cp-main::-webkit-scrollbar-thumb { background: var(--paper-line-strong); background-clip: content-box; }
/* 档案弹层 / 导航栏滚动条：暗色纸张下用暖调半透明线，绝不用冷灰 */
[data-theme="dark"] .cdm-col::-webkit-scrollbar-thumb, [data-theme="dark"] .cdm-nav-col::-webkit-scrollbar-thumb { background: var(--paper-line-strong); }
/* 浮层里那几道 4px 细滚动条：深色底下黑色半透明等于看不见，换一枚浅灰的。 */
[data-theme="dark"] .ol-links-pop::-webkit-scrollbar-thumb, [data-theme="dark"] .ol-lp-body::-webkit-scrollbar-thumb, [data-theme="dark"] .ol-lp-list::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--text-muted) 30%, transparent); }
[data-theme="dark"] .ol-links-pop, [data-theme="dark"] .ol-lp-body, [data-theme="dark"] .ol-lp-list { scrollbar-color: color-mix(in srgb, var(--text-muted) 30%, transparent) transparent; }
/* 节点：外层布局盒 + 内层装饰盒
   .mm-node 只管位置和尺寸，永远是矩形，不画背景也不裁剪自己；
   形状/底色/阴影画在内层 .mm-node-skin 上，边框由它内部的 SVG 层描边（不参与尺寸与命中）。
   为什么这样分：菱形、六边形这类形状要靠 clip-path 裁切，而数字徽标和右下角悬浮球以前是卡片的孩子，
   一裁就被切掉半个、还点不中。现在徽标和球挂在外层矩形盒上，装饰盒怎么裁都碰不到它们。
   层级约定见文件末尾「层叠契约」：连线层 z-index 0 永远在节点层 z-index 1 之下，
   装饰盒底色必须是实色（--mm-node-bg），半透明会让粗线从卡片里透出来像是盖住了节点。
   点击范围＝外层矩形（异形也一样）：装饰盒与光环都 pointer-events:none，
   clip-path 裁掉的斜角处点击不会漏到背后的画布或连线上，而是由外层盒接住。 */
.mm-node { position: absolute; min-width: var(--mm-node-min-w); max-width: var(--mm-node-max-w); min-height: var(--mm-node-min-h);
  padding: var(--mm-node-pad); cursor: move; z-index: 1; user-select: none;
  /* 图片安全区：异形（圆/菱形/六边形）能容纳的内接矩形比外接盒小，图片按这个比例再收一道，
     否则图占满 100% 时四个角必然压到斜边/圆弧外面，被 clip-path 切成缺角的图。
     0.7 的来源：内接正方形理论上限 1/√2≈0.707，取整留一点边框余量。
     --mm-img-cap-w/h 是"这一形状下图片的宽高上限"，由比例现算，JS 只引用不重算；
     比例值与 js/data-model.js 的 MM_IMAGE_SAFE 严格同值（沙箱有反向校验）。 */
  --mm-img-safe: 1;
  --mm-img-cap-w: calc(100% * var(--mm-img-safe));
  --mm-img-cap-h: calc(var(--mm-img-max-h) * var(--mm-img-safe));
  /* 只给观感属性加过渡；left/top 绝不在这里动，否则拖拽会被缓动拖后腿 */
  transition: opacity 0.25s ease-out, transform 0.2s ease-out; }
/* 选中 / 落点提示的光环：与装饰盒同一个裁剪，向外扩 4px，形状多轮廓就粗出一圈等宽的边。
   clip-path 会把 box-shadow 一起裁掉，所以异形节点的"亮边"只能靠这层垫在后面。 */
.mm-node-halo { position: absolute; inset: -4px; z-index: -2; pointer-events: none;
  border-radius: var(--mm-shape-radius, var(--mm-skin-radius, var(--mm-node-radius)));
  clip-path: var(--mm-shape-clip, none); background: transparent; transition: background 0.18s ease-out; }
/* 装饰盒：inset:0 与外层布局盒严丝合缝，所以连线锚点先按外层矩形取点、再投影回形状轮廓就能贴边
   （见 mindmap.js 的 mmShapeAnchor）。三层兜底：用户自定 --mm-skin-* > 层级默认 --mm-role-* > 全局令牌。 */
/* 装饰盒只管底色/形状/阴影；边框改由内部的 .mm-node-border SVG 层画，CSS border 一律不再用。
   overflow:hidden + border-radius 负责裁掉 SVG 描边露在轮廓外的那一半（异形再由 clip-path 裁）。 */
.mm-node-skin { position: absolute; inset: 0; z-index: -1; pointer-events: none; overflow: hidden;
  border-radius: var(--mm-shape-radius, var(--mm-skin-radius, var(--mm-node-radius)));
  background: var(--mm-skin-bg, var(--mm-role-bg, var(--mm-node-bg)));
  box-shadow: var(--mm-skin-shadow, var(--mm-role-shadow, var(--mm-node-shadow)));
  clip-path: var(--mm-shape-clip, none);
  transition: background 0.2s ease-out, border-color 0.2s ease-out, box-shadow 0.2s ease-out; }
/* 正文区：内容自己负责裁剪，卡片外壳不再 overflow:hidden，免得把徽标、悬浮球和图片控制点一起裁掉。
   裁剪范围必须跟形状一致：菱形/六边形只裁装饰盒的话，内容（尤其图片）会从斜边外面探出来，
   看着就是"图比框大"。这里再套一层同一个 clip-path，内容永远落在可见轮廓内；
   圆角同理，用同一串半径变量，内容盒比外盒缩进一个 padding，内切圆天然在外圆之内。
   图片控制点不住这一层：它是 .mm-node 的直接孩子（见下面「图片等比缩放」），
   否则菱形/六边形里探出轮廓的那颗点会被这一刀的 clip-path 直接切没。 */
.mm-node-content { position: relative; overflow: hidden;
  border-radius: var(--mm-shape-radius, var(--mm-skin-radius, var(--mm-node-radius)));
  clip-path: var(--mm-shape-clip, none); }
/* 形状表：新增一种形状 = data-model.js 的 MM_SHAPES 登记 + 这里补一条 + 面板图标网格补一格
   （菱形/六边形还要在 mindmap.js 的 MM_SHAPE_POLYS 里同步同一组比例，连线才贴得住）。
   --mm-shape-radius / --mm-shape-clip 由形状自己决定，优先级压过用户自定的 --mm-skin-radius：
   胶囊和正圆的圆角是形状本身，不该被"圆角"数值改成方角。
   文字安全区加在 .mm-node-content 上（不是外层 padding）：异形可用的内部空间比外接矩形小，
   把文字推进形状里才不会溢出去；放在内容层是为了让"叶子节点紧凑 padding"这类规则不会把它顶掉。 */
/* 圆 / 菱形 / 六边形额外声明 --mm-img-safe：形状内接区只有外接盒的七成，
   图片（含用户手动缩放出来的宽度）就按这个比例收，顶不到斜边和圆弧。
   数值必须与 js/data-model.js 的 MM_IMAGE_SAFE 同值。 */
.mm-node[data-shape="rect"] { --mm-shape-radius: 0; }
.mm-node[data-shape="capsule"] { --mm-shape-radius: 999px; }
.mm-node[data-shape="circle"] { --mm-shape-radius: 50%; --mm-img-safe: 0.7; min-width: calc(var(--mm-node-min-h) + 8px); }
.mm-node[data-shape="circle"] .mm-node-content { padding: 12px 14px; }
.mm-node[data-shape="sticky-note"] { --mm-shape-radius: 0; }
.mm-node[data-shape="quote"] { --mm-shape-radius: 4px; }
/* 菱形：四个角全是斜边，内容必须住进"内接矩形"才不会被削角。
   横向留 28%（文字只占中间 44% 宽），纵向留 25%——padding 百分比一律按宽度解析，
   所以纵向留白随卡片变宽等比变高，内容块始终钉在菱形中部，长文本也不会顶到上下顶点。
   安全区随卡片撑高自动变高：内容多一行，卡片与菱形一起长大，锚点仍贴外形边缘。
   长文本照常换行；正文超过 3 行走 .mm-node-body--clamp 折叠，不会被斜边切断。 */
.mm-node[data-shape="diamond"] { --mm-shape-clip: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); --mm-img-safe: 0.7;
  --mm-safe-pad-y: 25%;
  /* 图片高度再收一道：菱形里宽度已被内容区压到一半，竖图若还能长到 210px，
     首尾两端会探出斜边。0.34 × 300 ≈ 100px，正好落在安全区内。 */
  --mm-img-cap-h: calc(var(--mm-img-max-h) * 0.34); }
/* 不再给菱形单独压小 min-width：内容区只剩中间一半宽，卡片太窄时标题会被挤成每行两三个字，
   这里回落到全局 170px 下限，文字才排得开。 */
.mm-node[data-shape="diamond"] .mm-node-content { padding: var(--mm-safe-pad-y) 28%; }
/* 六边形：上下各有一段 12% 宽的内切平边，左右才是尖角。
   横向留 15% 让文字落在平边宽度内，纵向只需 10px——危险区在左右尖角，不在上下。 */
.mm-node[data-shape="hexagon"] { --mm-shape-clip: polygon(12% 0, 88% 0, 100% 50%, 88% 100%, 12% 100%, 0 50%); --mm-img-safe: 0.7; }
.mm-node[data-shape="hexagon"] .mm-node-content { padding: 10px 15%; }
/* ===== 装饰盒内的 SVG 边框层（唯一边框来源）=====
   纯视觉：铺满装饰盒、宽高 100%、pointer-events:none；
   外层 .mm-node 仍是唯一布局/命中盒，尺寸测量与连线锚点完全不读这层。 */
.mm-node-border { position: absolute; inset: 0; display: block; width: 100%; height: 100%;
  overflow: hidden; pointer-events: none; }
.mm-node-border-line, .mm-node-border-accent {
  fill: none; stroke: var(--mm-skin-bc, var(--mm-role-bc, var(--mm-node-border-color)));
  stroke-dasharray: var(--mm-skin-bdash, none); stroke-linecap: var(--mm-skin-bcap, butt);
  stroke-linejoin: round; }
/* 2×线宽：外侧一半被装饰盒裁掉，内侧留下的正好一圈 bw；实/虚/点线靠 dasharray 直接画 */
.mm-node-border-line { stroke-width: calc(var(--mm-skin-bw, var(--mm-role-bw, var(--mm-node-border-width))) * 2); }
/* 便签 / 引言的加粗边（原 3× border-*-width）改用 SVG 短线段 */
.mm-node-border-accent { stroke-width: calc(var(--mm-skin-bw, var(--mm-role-bw, var(--mm-node-border-width))) * 6); stroke-dasharray: none; }
/* 双线：第二层等比缩一圈 + 单倍描边，显隐由 --mm-skin-bdouble 控制 */
.mm-node-border-double { opacity: var(--mm-skin-bdouble, 0);
  stroke-width: var(--mm-skin-bw, var(--mm-role-bw, var(--mm-node-border-width)));
  transform-box: fill-box; transform-origin: center; transform: scale(0.9); }
/* 异形：clip-path 会吃掉 box-shadow，沿用 drop-shadow；圆角归零避免 overflow 把尖角削圆 */
.mm-node[data-shape="diamond"] .mm-node-skin,
.mm-node[data-shape="hexagon"] .mm-node-skin {
  border-radius: 0; box-shadow: none; filter: var(--mm-skin-shadow, var(--mm-role-shadow, var(--mm-node-drop))); }
.collapse-btn { box-shadow: 0 1px 3px rgba(0,0,0,0.2); font-weight: bold; }
[data-theme="dark"] .collapse-btn { background: var(--panel-bg) !important; border-color: var(--primary-color) !important; color: var(--text-color) !important; text-shadow: 0 1px 2px rgba(0,0,0,0.5); }
.mm-node:hover .mm-node-skin { box-shadow: var(--mm-skin-shadow, var(--mm-node-shadow-hover)); }
.mm-node[data-shape="diamond"]:hover .mm-node-skin,
.mm-node[data-shape="hexagon"]:hover .mm-node-skin { filter: var(--mm-skin-shadow, var(--mm-node-drop-hover)); }
/* 选中：边框色属于用户自定（内联 --mm-skin-bc），类规则压不动内联变量，
   所以高亮一律走"光环 + 环影"两条不碰颜色变量的路，矩形和异形节点表现一致。 */
.mm-node.selected .mm-node-skin { box-shadow: var(--mm-skin-shadow, var(--mm-node-ring)); }
.mm-node.selected .mm-node-halo { background: color-mix(in srgb, var(--accent-color) 55%, transparent); }
/* 标题/正文排版走"令牌 + 层级默认"双层：属性面板把用户明确设过的项写成
   --mm-tfs / --mm-cfs 这类自定义属性（挂在 .mm-node 内联 style 上），
   这里只写 var(令牌, 默认值)。内联自定义属性权重天然高于任何类规则，
   所以不需要靠书写顺序抢覆盖，也不会出现"面板没动过就把根节点大标题压回普通字号"。 */
.mm-node-header { font-weight: var(--mm-tfw, 600); margin-bottom: 6px; font-size: var(--mm-tfs, 0.95rem); text-align: var(--mm-talign, left); color: var(--mm-tcolor, var(--mm-node-title-color)); white-space: pre-wrap; word-break: break-word; overflow-wrap: break-word; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
/* 图片一律"框住再等比塞进去"：max-width:100% 是相对图包层说的——安全区百分比只算一次（算在 wrap 上），
   这里再乘一遍就变成 49%，图会被无谓地缩成一圈；而它必须是 100%，行内写死的宽度才会被容器反顶回来
   （等比缩放到 100% 而不是被 overflow 切断）。max-height 保证不撑高卡片，
   object-fit:contain 保证触顶/触边时只留白、不拉伸、不被切断。未缩放的图按 100px 高的缩略图显示，
   用户拖过控制点后，行内把上限抬到 --mm-img-cap-h（= 高度令牌 × 形状安全区，任何情况下都不许写成 none）。 */
.mm-node-image { display: block; width: 100%; max-width: 100%; max-height: 100px; object-fit: contain; border-radius: 4px; margin-bottom: 6px; cursor: zoom-in; -webkit-user-drag: none; user-select: none; }
/* ===== 图文布局与画布内图片等比缩放（对标 Xmind）=====
   图片包在 .mm-node-image-wrap 里；控制点不在这一层（这层跟着形状被裁），挂在 .mm-node 上。
   图在上/下 → 纵向 flex，图在左/右 → 横向 flex。左右布局时图片最多占一半，
   文字区（.mm-node-text）用 min-width:0 允许收缩，保证正文永远不会被图挤没。
   安全区百分比只算一层，算在 wrap 上（--mm-img-cap-w）：矩形/胶囊是 100%，
   圆/菱形/六边形是 70%（形状内接区），所以图占满时也顶不到斜边和圆弧。
   media 与 img 都只写 100%：img 再乘一次就成了 49%，图会白白缩成一圈；
   而 img 保持 100%，行内写死的宽度才会被容器等比顶回来，而不是被 overflow 切断。 */
.mm-node-media { display: flex; gap: 6px; align-items: flex-start; max-width: 100%; max-height: 100%; overflow: hidden; }
.mm-media-top, .mm-media-bottom { flex-direction: column; }
.mm-media-left, .mm-media-right { flex-direction: row; }
.mm-node-text { flex: 1 1 auto; min-width: 0; }
.mm-node-image-wrap { position: relative; display: block; max-width: var(--mm-img-cap-w, 100%); max-height: var(--mm-img-cap-h); overflow: hidden; line-height: 0; }
.mm-media-left .mm-node-image-wrap, .mm-media-right .mm-node-image-wrap { max-width: min(50%, var(--mm-img-cap-w, 100%)); }
.mm-node-media .mm-node-image { margin-bottom: 0; }
/* 卡片默认 300px 上限是给纯文字节点定的；带图的卡片放宽，缩放才能看出效果。
   :has 判"里面有图包盒"，纯文字节点不受影响。宽度加在 .mm-node 上——
   300px 上限本来就挂在它身上，只改内层会被外层继续夹住。 */
.mm-node:has(.mm-node-image-wrap) { max-width: 560px; }
/* 图片缩放控制点：平时隐形，只在 hover / 选中时浮现（识别优于回忆，不遮挡阅读）。
   touch-action:none 是移动端能拖的前提；光标斜箭头 = 一眼看懂"往这拖"。
   图层解耦：它是 .mm-node 的直接孩子，不是 .mm-node-image-wrap 的孩子——
   内容层跟着形状走 clip-path，点在图片角上往往探出菱形/六边形的轮廓，挂在里面会被整颗裁掉。
   位置不写死：left/top 由 mindmap.js 的 mmPositionImageHandle() 按图片真实左下角算（逻辑像素），
   所以"图在左/图在右"时点会跟着走到图片那一侧，而不是死守卡片角落。
   translate(-50%,-50%) 把点自身中心对齐到那个角，点以后改多大都不会偏；
   z-index 夹在内容层（auto）与悬浮球/徽标（10）之间：压住背景和正文，又不盖住菜单球。
   右下角那颗 ⋯ 悬浮球 z=10 钉死在卡片角上，控制点锚在图片左下角，两者物理错开不会误触。 */
.mm-node-resize-handle { position: absolute; left: 0; top: 0; width: 11px; height: 11px; border-radius: 50%;
  background: var(--primary-color); border: 2px solid var(--mm-node-bg); box-shadow: 0 1px 3px rgba(0,0,0,.35);
  cursor: nwse-resize; touch-action: none; opacity: 0; transition: opacity .15s; z-index: 8;
  transform: translate(-50%, -50%); }
.mm-node:hover .mm-node-resize-handle, .mm-node.selected .mm-node-resize-handle { opacity: 1; }
.mm-node-body { font-size: var(--mm-cfs, 0.8rem); font-weight: var(--mm-cbw, 400); text-align: var(--mm-calign, left); color: var(--mm-ccolor, var(--mm-node-body-color)); line-height: 1.5; margin-top: 6px; padding-right: 28px; white-space: pre-wrap; word-break: break-word; overflow-wrap: break-word; }
.mm-node-body--clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }
.mm-node-toggle { background: none; border: none; padding: 0 2px; margin: 4px 0 0; color: var(--primary-color); cursor: pointer; font-size: 0.8rem; line-height: 1; }
.mm-node-toggle:hover { text-decoration: underline; }
/* 渐进式操作栏：平时那颗球完全不显示（连半透明影子都不留），只在 hover/选中/菜单打开时出现。
   出现带 0.15s 延迟——鼠标快速扫过画布不会满屏亮球；消失不带延迟，立即收起。
   球锚定在"外层布局盒"的右下角（不是装饰盒）：形状再怎么变，球的位置都固定、也永远点得中；
   正文右侧留了 28px 不排文字，正好是球的专属通道。 */
.mm-node-ball { position: absolute; right: 5px; bottom: 5px; width: var(--mm-ball-size); height: var(--mm-ball-size); padding: 0; border: 1px solid var(--mm-panel-border); border-radius: 50%; background: var(--mm-ball-bg); color: var(--mm-ball-color); font-size: 13px; line-height: 1; cursor: pointer; opacity: 0; transition: opacity 0.15s, background 0.15s, color 0.15s; }
.mm-node:hover .mm-node-ball, .mm-node.selected .mm-node-ball, .mm-node-ball.open { opacity: 1; transition-delay: 0.15s; }
.mm-node-ball:hover, .mm-node-ball.open { background: var(--mm-ball-bg-hover); color: var(--on-accent); border-color: var(--mm-ball-bg-hover); }
/* 节点操作浮动面板：fixed 定位挂在 body 上，不进入画布布局，因此不会挤压画布空间。
   用 opacity/visibility 代替 display 硬切，展开收起都是淡入+微位移，不再闪现 */
.mm-node-menu { position: fixed; z-index: 3000; display: flex; flex-direction: column; gap: 2px; min-width: 148px; padding: 6px; background: var(--mm-panel-bg); border: 1px solid var(--mm-panel-border); border-radius: var(--mm-panel-radius); box-shadow: var(--mm-panel-shadow); opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(4px) scale(0.98); transform-origin: right bottom; transition: opacity 0.25s ease-out, transform 0.25s ease-out, visibility 0s linear 0.25s; }
.mm-node-menu.active { opacity: 1; visibility: visible; pointer-events: auto; transform: none; transition: opacity 0.25s ease-out, transform 0.25s ease-out, visibility 0s; }
.mm-node-menu-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border: none; border-radius: 6px; background: transparent; color: var(--text-color); font-family: inherit; font-size: 0.8rem; text-align: left; cursor: pointer; white-space: nowrap; }
.mm-node-menu-item:hover { background: var(--bg-color); color: var(--primary-color); }
.mm-node-menu-item.danger:hover { background: color-mix(in srgb, var(--danger-color) 12%, transparent); color: var(--danger-color); }
.mm-node-menu-sep { height: 1px; margin: 3px 4px; background: var(--mm-panel-border); }
/* 关联图标右键菜单：沿用 PopoverManager 的浮层外壳，只收窄成两行菜单 */
.mm-pop.mm-olink-menu { display: flex; flex-direction: column; gap: 2px; min-width: 170px; padding: 6px; }
/* 视口裁剪占位：节点移出可视区时只保留外壳，正文与图片不建 DOM */
.mm-node-ph { color: var(--mm-ph-color); font-size: 0.8rem; font-style: italic; }
.collapse-btn { cursor: pointer !important; z-index: 10; pointer-events: auto !important; }
.mm-node-ball { pointer-events: auto !important; z-index: 10; }
.mm-node.dragging { opacity: 0.8; transition: none; }
.mm-node.dragging .mm-node-skin { box-shadow: 0 8px 25px rgba(0,0,0,0.3); }
/* 拖拽落点高亮一律走光环：outline/背景都画在装饰盒上会被 clip-path 裁掉，光环是往外扩的，天然不会 */
.mm-node.drag-over .mm-node-halo { background: color-mix(in srgb, var(--accent-color) 45%, transparent); }
.mm-node.drag-over .mm-node-skin { box-shadow: 0 8px 24px rgba(0,0,0,0.2); }
/* 大纲条目拖到节点上 = 将挂为子节点：用 outline（不撑盒）与更亮的光环把语义标清楚 */
.mm-node.mm-outline-drop-target { outline: 2px dashed var(--primary-color); outline-offset: 3px; }
.mm-node.mm-outline-drop-target .mm-node-halo { background: color-mix(in srgb, var(--primary-color) 62%, transparent); }
/* 拖拽悬停画布空白：整圈浅色虚线 = "此处可松手，凭空生成一个新节点"。
   用 outline 不用 border——border 会撑大滚动容器造成一抖，outline 浮在边缘不改布局 */
#mindmap-container.canvas-drag-over { outline: 2px dashed color-mix(in srgb, var(--primary-color) 55%, transparent); outline-offset: -4px; }
/* ===== 智能对齐辅助线：拖拽中贴在吸附目标中心，pointer-events:none 保证不拦截任何鼠标操作 =====
   只有横竖两条（v 竖线 / h 横线），绝不会有斜线。
   显示由 #mindmap-container.mm-drag-active 这一道总闸控制：只有真正在拖节点的几十毫秒里才可能亮线，
   松手 / 视图切换 / 重绘时把闸门一关，就算 .show 类因任何异常没来得及摘掉，屏幕上也不会有残留线。 */
.mm-guide { position: absolute; z-index: 2; pointer-events: none; display: none; }
#mindmap-container.mm-drag-active .mm-guide.show { display: block; }
.mm-guide-v { top: 0; bottom: 0; width: 0; border-left: 1.5px dashed var(--mm-guide-color); }
.mm-guide-h { left: 0; right: 0; height: 0; border-top: 1.5px dashed var(--mm-guide-color); }
/* ===== 拖拽变层级落点提示：蓝框=松手后 A 将成为 B 的子节点，红框=会成环、将被拒绝 ===== */
.mm-node.mm-drop-ok .mm-node-halo { background: color-mix(in srgb, var(--primary-color) 55%, transparent); }
.mm-node.mm-drop-ok .mm-node-skin { box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 35%, transparent), var(--shadow-md); }
.mm-node.mm-drop-ok { transform: scale(1.03); }
.mm-node.mm-drop-bad .mm-node-halo { background: color-mix(in srgb, var(--danger-color) 55%, transparent); }
.mm-node.mm-drop-bad .mm-node-skin { box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger-color) 35%, transparent); }
/* ===== 分支聚焦：非聚焦子树的节点/连线压暗；编辑框所在节点由 JS 保证不会被加暗 ===== */
.mm-node.mm-focus-dim { opacity: 0.15; }
.mm-node.mm-focus-dim:hover { opacity: 0.4; }
.mm-conn { transition: opacity 0.25s ease-out; }
.mm-conn.mm-conn-dim { opacity: 0.15; }
/* 退出聚焦按钮：默认藏在工具栏最左，聚焦时淡入滑下 */
.mm-focus-exit { display: none; border-color: var(--primary-color); color: var(--primary-color); }
.mm-focus-exit.show { display: inline-flex; animation: focus-exit-in 0.25s ease-out; }
@keyframes focus-exit-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
/* 折叠展开：刚显示出来的子孙节点弹入一次（一次性动画，不干扰拖拽定位） */
.mm-node-pop { animation: layer-pop-in var(--dur-fast) var(--ease-standard); }

/* 外观面板 / 备忘录内容展开：从 display 硬切改为显示时淡入下滑 */
#customization-panel { animation: drop-fade-in 0.25s ease-out; }
.memo-content.expanded { animation: drop-fade-in 0.25s ease-out; }
@keyframes drop-fade-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* ================= 认知体验打磨：分组 / 层级 / 徽标 / 联动 ================= */
/* 格式塔·共同区域：选中父节点时把"它 + 直接子节点"整块圈一层极淡虚线框。
   pointer-events:none 保证不吃点击；夹在连线层(z-index 0)与节点层(1)之间，
   既盖住连线底色，又绝不遮挡节点文字与数字徽标。 */
.mm-group-zone { position: absolute; z-index: 0; pointer-events: none; opacity: 0;
  border: 1.5px dashed color-mix(in srgb, var(--primary-color) 32%, transparent);
  background: color-mix(in srgb, var(--primary-color) 4%, transparent);
  border-radius: 18px; transition: opacity 0.25s ease-out; }
.mm-group-zone.show { opacity: 1; }

/* 视觉层级：根最重（满墨+粗边+大字号）、分支次之、叶子最轻。
   层级默认一律写成 --mm-role-* 变量，由装饰盒按"用户自定 > 层级 > 全局令牌"的顺序取用，
   所以面板里选过的颜色/粗细永远压过层级默认，没选过的才跟着根/分支/叶子变。 */
.mm-node[data-role="root"] { --mm-role-bw: calc(var(--mm-node-border-width) + 1px); --mm-role-shadow: var(--shadow-md); }
.mm-node[data-role="root"] .mm-node-header { font-size: var(--mm-tfs, 1.12rem); font-weight: var(--mm-tfw, 700); }
.mm-node[data-role="branch"] { --mm-role-bg: color-mix(in srgb, var(--primary-color) 6%, var(--mm-node-bg)); }
.mm-node[data-role="branch"] .mm-node-header { font-size: var(--mm-tfs, 0.98rem); font-weight: var(--mm-tfw, 600); }
/* 叶子最轻，但底色必须是实色：往画布色方向压一点、只靠边框和阴影减淡来做"轻"，
   一旦写成 transparent，父子那条粗线就会从卡片里透出来，看起来像线盖住了节点。 */
.mm-node[data-role="leaf"] { --mm-role-bg: color-mix(in srgb, var(--mm-node-bg) 82%, var(--bg-color));
  --mm-role-bc: color-mix(in srgb, var(--mm-node-border-color) 42%, transparent); --mm-role-shadow: none;
  padding-left: 8px; padding-right: 8px; min-height: 0; }
.mm-node[data-role="leaf"] .mm-node-header { font-weight: var(--mm-tfw, 500); font-size: var(--mm-tfs, 0.88rem); color: var(--mm-tcolor, var(--text-muted)); }
/* 折叠后的父节点是这块分支的视觉锚点：徽标给数字，阴影给"下面还压着东西"。
   用 data-collapsed 而不是临时 class——任何一次重绘（自动排版、撤销）后锚点都还在。 */
.mm-node[data-collapsed="1"] .mm-node-skin { box-shadow: var(--mm-skin-shadow, var(--shadow-md)); }

/* 数字徽标：折叠态显示"藏了几个子节点"，展开态只留一颗极小的半透明点。
   定位挂在"外层布局盒"右上角：形状怎么变（菱形/圆形/六边形）徽标都在同一个位置，
   既不会被 clip-path 裁掉一半，也永远点得中——这正是它不做成装饰盒子元素的原因。 */
.mm-collapse-badge { position: absolute; top: 8px; right: 8px; min-width: 18px; height: 18px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
  border: 1px solid color-mix(in srgb, var(--primary-color) 45%, transparent); border-radius: 9px;
  background: color-mix(in srgb, var(--primary-color) 10%, transparent); color: var(--primary-color);
  font-size: 0.68rem; font-weight: 700; line-height: 1; }
.mm-collapse-badge.collapsed { background: color-mix(in srgb, var(--primary-color) 20%, transparent); }
.mm-collapse-badge:hover { background: var(--primary-color); color: var(--panel-bg); border-color: var(--primary-color); }
.mm-badge-dot { width: 5px; height: 5px; border-radius: 50%; background: color-mix(in srgb, var(--text-muted) 55%, transparent); display: block; }
.mm-node[data-role="root"] .mm-collapse-badge { min-width: 22px; height: 22px; font-size: 0.76rem; border-radius: 11px; }
/* 标题给徽标让出右端 24px：徽标与文字不会叠在一起，也就不会出现"看不见的高亮块" */
.mm-node-header { padding-right: 24px; }

/* 撤销/重做的位移：节点从"上一笔的位置"滑回来，不再原地闪现。
   一次性 animation（0.2s，落在 0.15–0.25s 区间）+ 变量偏移，不碰 left/top、更不进拖拽帧。 */
@keyframes mm-node-move { from { transform: translate(var(--mm-anim-x, 0), var(--mm-anim-y, 0)); } to { transform: none; } }
.mm-node-move-anim { animation: mm-node-move 0.2s ease-out; }

/* ===== 顶部岛台（对标 Xmind）=====
   居中一颗药丸：毛玻璃 + 药丸圆角 + 极弱阴影，不画硬边框（分隔靠留白和一条 1px 淡线）。
   它是 #canvas-area 的绝对定位子元素（和右侧属性面板同一个不滚动的坐标系），
   靠 top/left/transform 钉死在可视区顶部正中 —— 画布怎么拖、缩放到多少，它都不动。
   之前住在滚动容器里用 sticky，只能钉住上下，横向平移时整颗岛台会跟着画布跑。
   默认 display:none，只在思维导图视图激活时现身；隐藏时 offsetHeight 为 0，
   视口补偿（mmDockBand）自动归零，不需要 JS 再同步一次。 */
.mm-dock { position: absolute; top: 16px; left: 50%; transform: translateX(-50%);
  z-index: 30; /* .canvas-overlay(20)=右侧面板层，画布整体(#mindmap-container)=0 层 */
  display: none; align-items: stretch; gap: 2px; pointer-events: none;
  width: max-content; padding: 5px 8px;
  background: color-mix(in srgb, var(--panel-bg) 74%, transparent);
  backdrop-filter: blur(14px) saturate(1.35); -webkit-backdrop-filter: blur(14px) saturate(1.35);
  border: none; border-radius: 999px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06); }
/* 岛台本体（透明边缘和两颗按钮之间的缝隙）不吃鼠标：点它等于点画布，照常框选；
   只有里面的按钮、分隔线自己把鼠标收回来 */
.mm-dock > * { pointer-events: auto; }
/* 只有思维导图页开着时才显示：靠 .view-container.active 这个既有状态类判定，
   切视图 / 回首页都不用额外写 JS */
.canvas-area:has(> #mindmap-container.active) > .mm-dock { display: flex; }
/* 上图标下文字（11px）：图标负责识别，文字负责确认，比纯图标少猜、比纯文字省地方 */
.mm-dock-btn { display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-width: 50px; padding: 5px 9px; border: none; border-radius: 13px; background: transparent;
  color: var(--text-color); font-family: inherit; font-size: 11px; line-height: 1.1; cursor: pointer;
  transition: background 0.15s, color 0.15s; }
.mm-dock-btn i { font-size: 14px; }
.mm-dock-btn:hover { background: color-mix(in srgb, var(--primary-color) 12%, transparent); color: var(--primary-color); }
.mm-dock-btn.active { background: color-mix(in srgb, var(--primary-color) 18%, transparent); color: var(--primary-color); }
/* 子节点/同级：没有"恰好选中一个节点"时置灰，按了也没目标，别让用户白点一次 */
/* 不再用原生 disabled（disabled 元素连 click 都不派发，用户点了毫无反馈）；
   改成 aria-disabled 的可点按钮，由 JS 给出「请先选中一个节点」的 Toast。 */
.mm-dock-btn[aria-disabled="true"] { opacity: 0.36; cursor: not-allowed; }
.mm-dock-btn[aria-disabled="true"]:hover { background: transparent; color: var(--text-color); }
.mm-dock-sep { flex: 0 0 auto; width: 1px; margin: 7px 4px; background: color-mix(in srgb, var(--border-color) 75%, transparent); }
/* 聚焦模式才出现的"退出聚焦"：上面 .mm-focus-exit 已定义 display:none / .show:inline-flex，
   这里再按岛台按钮的写法收一次口，避免两条规则互相顶掉 */
.mm-dock-btn.mm-focus-exit { display: none; }
.mm-dock-btn.mm-focus-exit.show { display: inline-flex; animation: focus-exit-in 0.25s ease-out; }

/* 大纲 ↔ 画布联动：两侧都只加/减 class，绝不重绘（大纲里有正在敲字的输入框） */
.chapter-item.mm-link-active, .scene-item.mm-link-active { background: color-mix(in srgb, var(--accent-color) 22%, var(--bg-color)); box-shadow: inset 3px 0 0 var(--accent-color); }
.mm-node-locate .mm-node-halo { animation: mm-locate-flash 0.5s ease-out 4; }
/* 定位闪烁改成动画光环的背景：box-shadow 会被异形裁剪吃掉，光环是向外扩的一圈，任何形状都亮得起来 */
@keyframes mm-locate-flash { 0%, 100% { background: transparent; } 50% { background: color-mix(in srgb, var(--accent-color) 60%, transparent); } }
.chapter-item.mm-link-flash, .scene-item.mm-link-flash { animation: mm-link-flash 0.5s ease-out 4; }
@keyframes mm-link-flash { 0%, 100% { background: var(--bg-color); } 50% { background: color-mix(in srgb, var(--accent-color) 35%, var(--bg-color)); } }

/* ===== 全站两步点击关联（LinkBus）=====
   模式类 is-linking 挂 body，可落点元素在进模式时被标 .lb-target-valid（出模式一次摘干净）。
   目标整体轻微压暗，悬停恢复并描一圈主色；来源元素用主色描边告诉作者"正在为谁挑"。 */
body.is-linking { cursor: crosshair; }
body.is-linking .lb-target-valid { opacity: .55; cursor: crosshair;
  transition: opacity .15s ease, box-shadow .15s ease, border-color .15s ease; }
body.is-linking .lb-target-valid:hover { opacity: 1;
  box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--accent-color) 70%, transparent); }
body.is-linking .lb-target-valid * { cursor: crosshair; }
body.is-linking .lb-source { outline: 1.5px solid var(--accent-color); outline-offset: 1px; }
/* 画布节点被压得更暗一些，悬停恢复亮度并给 skin/halo 上主色（异形卡片靠这套才不被裁掉） */
body.is-linking .mm-node.lb-target-valid { opacity: .4; transition: opacity .15s ease; }
body.is-linking .mm-node.lb-target-valid:hover { opacity: 1; }
body.is-linking .mm-node.lb-target-valid:hover .mm-node-border-line,
body.is-linking .mm-node.lb-source .mm-node-border-line,
body.is-linking .mm-node.lb-target-valid:hover .mm-node-border-accent,
body.is-linking .mm-node.lb-source .mm-node-border-accent { stroke: var(--accent-color); }
body.is-linking .mm-node.lb-target-valid:hover .mm-node-halo { background: color-mix(in srgb, var(--accent-color) 18%, transparent); }
/* 旧的反向模式已并入 LinkBus 的 body.is-linking 规则 */
/* ===== 岛台「连线」按钮的两步点击模式（mm-conn-linking，mindmap.js）=====
   与 LinkBus 共用引导条但不共用模式类：它只在画布内生效，十字光标只给画布与节点；
   起点节点亮绿色光环（halo 是 pointer-events:none 的装饰层，异形节点也裁不丢）。 */
body.mm-conn-linking #canvas-area, body.mm-conn-linking #canvas-area * { cursor: crosshair; }
.mm-node.link-start .mm-node-halo { background: color-mix(in srgb, var(--success-color) 60%, transparent); }
/* 双向永久标记。节点这颗挂在外层布局盒左上角：右上是折叠徽标、右下是悬浮球、
   图片左下是缩放点，四个角各干各的，谁也不盖谁；它现在是通用关联入口（LinkBus），
   按住它不会拖动节点（main.js 的 mousedown 会跳过带 data-action / data-link-start 的目标） */
.mm-node-olink { position: absolute; top: 7px; left: 7px; z-index: 8;
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; padding: 0; border: none; background: transparent;
  color: var(--text-muted); border-radius: 4px; opacity: .8; cursor: pointer;
  transition: opacity .15s, color .15s, background .15s; }
.mm-node-olink:hover { opacity: 1; color: var(--accent-color); background: color-mix(in srgb, var(--text-muted) 12%, transparent); }
/* 标题给左上角这颗让出 20px（右端那 24px 是给折叠徽标的，同一套做法） */
.mm-node[data-oid] .mm-node-header, .mm-node[data-oid] .mm-node-title-input { padding-left: 20px; }
/* 大纲行这颗是普通 flex 子项，不绝对定位：行盒不许 position:relative（见上面红线）。
   它现在同时是入口：点一下切到画布并定位那颗节点，所以按无边框按钮的口径重设一遍。
   常驻显示（不属于悬停才现身的那三格），因为"这一场已经进了画布"是要扫一眼就能看见的。 */
.ol-link-badge { flex: 0 0 auto; display: inline-flex; align-items: center; padding: 2px; border: none; background: none;
  color: var(--text-muted); opacity: .7; border-radius: 4px; cursor: pointer; transition: opacity .15s, color .15s, background .15s; }
.ol-link-badge:hover { opacity: 1; color: var(--primary-color); background: color-mix(in srgb, var(--text-muted) 10%, transparent); }
/* 岛台下方的浮动引导条：和岛台同一个不滚动坐标系（住在 #canvas-area 里），
   默认完全不参与布局，只有 .show 期间现身；z-index 比岛台高一层，不能被药丸压住 */
.mm-link-bar { position: absolute; top: 66px; left: 50%; transform: translate(-50%, -6px); z-index: 31;
  display: none; align-items: center; gap: 8px; padding: 7px 10px 7px 14px; max-width: calc(100% - 32px);
  background: color-mix(in srgb, var(--panel-bg) 88%, transparent);
  backdrop-filter: blur(12px) saturate(1.3); -webkit-backdrop-filter: blur(12px) saturate(1.3);
  border: 1px solid color-mix(in srgb, var(--accent-color) 45%, var(--border-color));
  border-radius: 999px; box-shadow: var(--shadow-md);
  color: var(--text-color); font-size: .82rem; line-height: 1.4; white-space: nowrap; pointer-events: auto; }
.mm-link-bar.show { display: flex; transform: translate(-50%, 0); animation: mm-link-bar-in .22s ease-out; }
@keyframes mm-link-bar-in { from { opacity: 0; transform: translate(-50%, -8px); } to { opacity: 1; transform: translate(-50%, 0); } }
/* 成功态：绿边、收起"取消"按钮（此刻已经没什么可取消的了），1.6 秒后由 JS 摘掉 .show */
.mm-link-bar[data-kind="ok"] { border-color: color-mix(in srgb, var(--success-color) 55%, var(--border-color)); }
.mm-link-bar[data-kind="ok"] .mm-link-bar-btn { display: none; }
.mm-link-bar-msg { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.mm-link-bar-btn { flex: 0 0 auto; border: none; background: transparent; color: var(--text-muted);
  font-family: inherit; font-size: .76rem; padding: 3px 9px; border-radius: 999px; cursor: pointer;
  transition: background .15s, color .15s; }
.mm-link-bar-btn:hover { background: color-mix(in srgb, var(--text-muted) 14%, transparent); color: var(--text-color); }
/* 连线选中 / 删除 */
#nodes-container { pointer-events: none; }
.mm-node { pointer-events: auto; }
/* 连线默认样式全部走 Design Tokens：JS 只在数据自带颜色/线宽时写内联样式，
   内联样式优先级高于这里的类样式，因此用户手动设置的红色线条不会被主题覆盖。 */
.mm-conn-line { stroke: var(--mm-line-color); stroke-width: var(--mm-line-width); transition: stroke 0.15s, stroke-width 0.15s, filter 0.15s; }
.mm-conn-hit { cursor: pointer; pointer-events: stroke; }
.mm-conn:hover .mm-conn-line { opacity: 0.8; }
.mm-conn.selected .mm-conn-line { stroke-linecap: round; stroke-linejoin: round; stroke-width: calc(var(--mm-line-width) + 1.5px); filter: drop-shadow(0 0 4px var(--mm-line-color)); }
.mm-conn-delete { cursor: pointer; pointer-events: all; }
.mm-conn-delete circle { fill: var(--danger-color); stroke: var(--mm-panel-bg); stroke-width: 1.5px; transition: r 0.15s; }
.mm-conn-delete:hover circle { r: 12; }
.mm-conn-delete path { stroke: var(--mm-panel-bg); stroke-width: 2; stroke-linecap: round; }
.mm-conn-label { pointer-events: none; }
.mm-conn-label-bg { fill: var(--mm-conn-label-bg); stroke: var(--mm-conn-label-border); stroke-width: 1; }
.mm-conn-label-text { fill: var(--mm-conn-label-text); font-size: 12px; font-weight: 600; }
.mm-conn-btn { cursor: pointer; pointer-events: all; }
.mm-conn-btn-bg { fill: var(--mm-conn-btn-bg); stroke: var(--mm-conn-btn-border); stroke-width: 1.2; }
.mm-conn-btn-text { fill: var(--mm-conn-btn-text); font-size: 11px; }
.mm-conn-btn:hover .mm-conn-btn-bg { fill: var(--mm-conn-btn-border); }
.mm-conn-btn:hover .mm-conn-btn-text { fill: var(--on-accent); }
.mm-node-title-input { width: 100%; font-weight: var(--mm-tfw, 600); font-size: var(--mm-tfs, 0.95rem); text-align: var(--mm-talign, left); padding: 2px 4px; margin-bottom: 6px; border: 1px solid var(--accent-color); border-radius: 4px; background: var(--panel-bg); color: var(--text-color); cursor: text; user-select: text; white-space: pre-wrap; word-break: break-word; overflow-wrap: break-word; resize: none; }
#selection-box { position: absolute; display: none; border: 1.5px dashed var(--accent-color); background: rgba(139, 92, 246, 0.12); pointer-events: none; z-index: 2; }
/* 思维导图视图容器：独立层叠上下文。画布内部（连线 0 / 节点 1 / 拖拽中的节点）的层级
   比较只在容器内进行，节点 z-index 再高也被容器封顶（z-index:0）—— 外层的右侧面板(20)
   与顶部岛台(30)永远盖得住它，且不受页面其他元素干扰 */
#mindmap-container { position: relative; z-index: 0; isolation: isolate; }
/* 连线迷你悬浮面板已废弃：连线编辑统一收进右侧「节点属性」面板的「连线设置」一节，
   画布上不再出现第二块编辑面板。 */
/* 按钮 */
/* ========== 按钮系统：主 / 次 / 幽灵 / 危险 四层级 ========== */
.btn {
  padding: 8px 16px;
  border-radius: var(--radius-btn);
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text-color);
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 500;
  line-height: 1.2;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease, transform 0.08s ease;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  white-space: nowrap;
}
/* Secondary（次操作）：透明底 + 极淡边框，hover 浮现极淡底色；按下微缩 */
.btn:hover { background: var(--hover-bg); }
.btn:active { transform: scale(0.98); }
/* Primary（主操作）：实心主色 + 极微弱弥散阴影；hover 提亮、active 微缩 */
.btn-primary { background: var(--primary-color); color: var(--panel-bg); border-color: transparent; box-shadow: var(--shadow-sm); }
.btn-primary:hover { background: color-mix(in srgb, var(--primary-color), var(--panel-bg) 12%); }
.btn-primary:active { transform: scale(0.98); box-shadow: none; }
/* Ghost（幽灵按钮）：无边框无底，hover 浮现极淡底色 */
.btn-ghost { background: transparent; border-color: transparent; }
.btn-ghost:hover { background: var(--hover-bg); }
/* Danger（危险）：浅红底 + 深红字（普通删除）；最高危彻底删除加 .solid 实心红 */
.btn-danger { background: color-mix(in srgb, var(--danger-color) 10%, var(--panel-bg)); color: var(--danger-color); border-color: color-mix(in srgb, var(--danger-color) 30%, transparent); }
.btn-danger:hover { background: color-mix(in srgb, var(--danger-color) 16%, var(--panel-bg)); }
.btn-danger.solid { background: var(--danger-color); color: var(--on-accent); border-color: transparent; }
.btn-danger.solid:hover { background: color-mix(in srgb, var(--danger-color), #000 12%); }
/* ========== 行内二次确认（utils.js armConfirm）的按钮皮肤 ==========
   第一次点击只把按钮文字换成"确认…"并起 3 秒计时：底色淡成 danger 的浅 tint、
   描边和文字改用 danger 色；原来实心 danger 的按钮反向成描边款，一眼能看出"这颗在等我确认第二下"。
   超时 / 点到别处由 JS 撤回调回原样，这里只负责武装期间的样子。
   选择器写成 .btn.is-confirm 两级是为了压过 .btn-danger:hover 这类同级规则，不用 !important。 */
.btn.is-confirm, .btn.is-confirm:hover, .btn.btn-danger.is-confirm:hover, .btn.btn-primary.is-confirm:hover {
  background: color-mix(in srgb, var(--danger-color) 12%, var(--panel-bg));
  border: 1px solid var(--danger-color);
  color: var(--danger-color);
  box-shadow: none; transform: none; filter: none;
}
.btn.is-confirm { font-weight: 600; }
/* 只装图标的窄按钮（面板标题栏那颗垃圾桶）被武装时：收一点内边距，让"确认？"两个字放得下 */
.btn-icon.is-confirm, .btn.btn-icon.is-confirm { padding: 6px 8px; font-size: 0.72rem; gap: 0; }
body.dragging-sidebar, body.dragging-sidebar * { cursor: col-resize !important; }
body.dragging-sidebar-dark, body.dragging-sidebar-dark * { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><path d='M5 4 L14 12 L5 20 L5 16 L10 12 L5 8 Z' fill='white' stroke='%23111' stroke-width='1.5' stroke-linejoin='round'/><path d='M19 4 L10 12 L19 20 L19 16 L14 12 L19 8 Z' fill='white' stroke='%23111' stroke-width='1.5' stroke-linejoin='round'/></svg>") 12 12, col-resize !important; }
.btn-icon { padding: 6px; border-radius: var(--radius-btn); background: transparent; border-color: transparent; }
.btn:disabled, .btn[disabled], .btn[aria-disabled="true"] { opacity: 0.4; cursor: not-allowed; pointer-events: none; box-shadow: none; transform: none; }
/* 全局禁用态：淡化 + 禁用光标（覆盖非 .btn 的表单控件与自定义 [aria-disabled="true"] 元素） */
:disabled, [aria-disabled="true"] { opacity: 0.4; cursor: not-allowed; }

/* —— 原生下拉控件已从源码彻底移除，不再保留任何原生外观覆写 ——
   全站下拉皮肤由 CSelect（div 触发器 + ul/li 面板，见文件末尾）统一承担。 */
.cselect:not(.cp-input):hover { border-color: var(--border-strong); }
.cselect:not(.cp-input):focus-visible {
  border-color: var(--primary-color);
  background-color: color-mix(in srgb, var(--input-sunken) 85%, var(--text-color) 15%);
}
/* ========== 大纲：极简排版（无边框 · 悬停浮现操作 · 用留白分层） ==========
   对标 Notion / Linear 的清爽感：默认状态下容器、章节行、场景行、输入框都没有边框和背景，
   只有文字。层级靠缩进和行间距表达，不画分割线；操作按钮默认 opacity:0 且不吃点击，
   悬停时才浮现（不吃点击是为了防止"看不见的删除按钮"被误触）。 */
#outline-container {
  --ol-gutter: 20px;      /* 场景块整体缩进量 */
  --ol-badge-w: 46px;     /* 状态标签固定等宽：文字长短不一也不会把标题挤得每行错位 */
  --ol-handle-w: 12px;
  --ol-gap: 6px;
  --ol-anchor-gap: 8px;   /* "摘要/大纲"锚点到输入框的固定距离 */
  --ol-text-pad: calc(var(--ol-handle-w) + var(--ol-gap) * 2 + var(--ol-badge-w)); /* 摘要/正文左边缘 = 场景标题左边缘 */
  position: relative;
  font-size: 0.85rem;
}
/* ---- 大纲文字各档灰度 + 辅助文字：浅色主题按设计稿给到的取值 ----
   写成变量而不是直接塞进每条规则：theme.js 的深色补丁会给所有 input/textarea 铺 #f8fafc !important，
   压住它只能靠带 id 的 !important，那些规则要复用同一批取值，深色/浅色各配一套才不用把数值抄两遍。 */
:root {
  --ol-ink-chapter: #1A1A1A; /* 章节标题：整套里最深的一档，加粗 */
  --ol-ink-scene: #333;      /* 场景标题：中深 */
  --ol-ink-summary: #666;    /* 摘要/正文：中灰（两者同号同灰） */
  --ol-ink-detail: #888;     /* 次级浅灰：顶部模板按钮默认文字 */
  --ol-ink-anchor: #B0B0B0;  /* "摘要/大纲"两个常驻锚点 */
  --ol-ink-tag: #A0A0A0;     /* 标题行内的标签文字 */
}
/* 深色底下同样的六个值会糊成一片黑：按 --text-color/--text-muted 的明度重配一套，层级顺序保持不变 */
[data-theme="dark"] {
  /* 六档灰度锚定全局文字令牌（color-mix 保层级顺序），彻底脱离 slate，深浅双模自适应 */
  --ol-ink-chapter: var(--text-color);
  --ol-ink-scene: color-mix(in srgb, var(--text-color) 82%, var(--text-muted));
  --ol-ink-summary: var(--text-muted);
  --ol-ink-detail: color-mix(in srgb, var(--text-muted) 72%, var(--bg-color));
  --ol-ink-anchor: color-mix(in srgb, var(--text-muted) 62%, var(--bg-color));
  --ol-ink-tag: color-mix(in srgb, var(--text-muted) 70%, var(--bg-color));
}
/* 章节之间宽裕（24px），场景之间紧凑（8px） */
.chapter-item { background: none; border: none; border-radius: 6px; padding: 0; margin: 0 0 24px; }
.chapter-item:last-child { margin-bottom: 8px; }
/* 角色卡定位章节时闪烁（characters.js 会加 .chapter-flash，1.5s 后自己摘掉） */
.chapter-item.chapter-flash { animation: chapter-flash 1.5s ease; border-radius: 6px; }
@keyframes chapter-flash {
  0% { background-color: color-mix(in srgb, var(--warning-color) 28%, transparent); }
  100% { background-color: transparent; }
}
.chapter-body { padding-left: var(--ol-gutter); }
.scene-item { background: none; border: none; padding: 0; margin: 0 0 8px; }
.scene-item:last-child { margin-bottom: 0; }
.chapter-header, .scene-header { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ol-gap); row-gap: 2px; min-height: 26px; }
.scene-header { min-height: 24px; }
/* 右侧统计与操作永不参与挤压标题：宽度不够时整行换到下一行，标题始终占满第一行 */
.outline-scene-count { flex: 0 0 auto; font-size: 0.68rem; font-weight: 400; color: var(--text-muted); opacity: .8; white-space: nowrap; }
.outline-row-actions { display: flex; gap: 2px; flex: 0 0 auto; margin-left: auto; }
/* 行内图标按钮：无框无底，只有线框图标本身 */
.ol-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0; border: none; background: none; color: var(--text-muted); border-radius: 5px; cursor: pointer; opacity: 0; pointer-events: none; transition: opacity .18s ease, color .18s ease, background .18s ease; }
.ol-icon-btn:hover { color: var(--text-color); background: color-mix(in srgb, var(--text-muted) 12%, transparent); }
.ol-icon-btn:focus-visible { opacity: 1; pointer-events: auto; }
.ol-icon-danger:hover { color: var(--danger-color); background: color-mix(in srgb, var(--danger-color) 12%, transparent); }
/* 悬停所在行 → 这一行的操作浮现（章节头/场景头各自独立，不连带点亮整章） */
.chapter-header:hover .ol-icon-btn, .scene-header:hover .ol-icon-btn { opacity: 1; pointer-events: auto; }
/* 折叠箭头：常驻但极淡，只有它能折叠 */
.ol-fold { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; padding: 0; border: none; background: none; color: var(--text-muted); border-radius: 4px; cursor: pointer; opacity: .55; transition: opacity .15s, background .15s; }
.ol-fold:hover { opacity: 1; background: color-mix(in srgb, var(--text-muted) 12%, transparent); }
.ol-fold svg { transition: transform .18s ease; }
.chapter-item:not(.collapsed) .ol-fold svg { transform: rotate(90deg); }
.chapter-item.collapsed { margin-bottom: 20px; }
.chapter-item.collapsed .chapter-body { display: none; }
/* 拖拽手柄：仅悬停出现，位置固定（占位常驻，出现时不会把文字推来推去） */
.outline-drag-handle { flex: 0 0 auto; width: var(--ol-handle-w); display: inline-flex; align-items: center; justify-content: center; color: var(--text-muted); cursor: grab; user-select: none; -webkit-user-select: none; touch-action: none; opacity: 0; transition: opacity .18s ease; }
.ol-row:hover .outline-drag-handle { opacity: .5; }
.outline-drag-handle:hover { opacity: 1 !important; }
.outline-drag-handle:active { cursor: grabbing; }
/* 触屏没有 hover：手柄默认 opacity:0 在手机上会"看不见/摸不着"，这里强制可见并放大点击区 */
@media (hover: none) {
  .outline-drag-handle { opacity: .6 !important; width: 34px; }
  .ol-row:active .outline-drag-handle { opacity: 1 !important; }
}
/* 标题：无边框无底，悬停/聚焦只有一条浅下划线（box-shadow 画线，不占高度、不抖动）
   min-width 80px 是底线：标题一个字都没打时，这一行也点得到、敲得进。 */
.ol-title { flex: 1 1 0; min-width: 80px; width: auto; border: none; outline: none; background: none; border-radius: 3px; padding: 2px 3px; color: var(--ol-ink-scene); font-family: inherit; transition: box-shadow .15s ease; }
/* "标签和标题分了家"的根因就是上一条的 flex: 1 1 0——输入框被拉伸吃掉整行剩余宽度，
   排它后面的标签组只能贴到最右边，看上去和正文是两截。
   field-sizing: content 让输入框只占文字的实际宽度，标签组自然紧跟回来，中间只剩那 8px。
   用 @supports 一次判定：不支持的浏览器原地保留旧布局，纯 CSS，没有任何 JS 参与测量。 */
@supports (field-sizing: content) {
  .ol-title { flex: 0 1 auto; max-width: 100%; field-sizing: content; }
}
/* 字号阶梯（px 写死，不用 rem 折算）：章 16/600 → 场 14/500 → 摘要·正文 13/400。
   摘要与正文同号同灰（#666），正文只是可折叠的那一档；层级靠字号、字重与留白建立，不靠色块 */
.ol-chapter-title { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; color: var(--ol-ink-chapter); }
.ol-scene-title { font-size: 14px; font-weight: 500; }
.ol-title::placeholder { color: var(--text-muted); opacity: .35; }
.ol-title:hover { background: var(--hover-bg); border-radius: 3px; }
.ol-title:focus-visible { box-shadow: inset 0 -1px 0 var(--primary-color); background: var(--hover-bg); }
/* 摘要行 / 细节行：左侧"摘要/大纲"两个字是常驻锚点——打了字以后 placeholder 就没了，
   靠它告诉作者这一行是什么。10px、极淡、不加粗，像柱子一样钉在左侧，绝不和标题抢层级 */
.scene-line { display: flex; align-items: flex-start; gap: var(--ol-anchor-gap); }
/* 锚点盒子宽度 = 场景标题左边缘(70px) 减掉那 8px 间距：文字钉在左边不动，输入框左边缘永远和标题对齐
   颜色一律用 var(--ol-ink-anchor)（浅色主题解析为 #B0B0B0，深色主题自动换一档亮度），
   并把 opacity: 1 与 -webkit-text-fill-color 一起写死：任何全局继承来的加粗、透明度、填充色都盖不动它。 */
.ol-field-label { flex: 0 0 auto; width: calc(var(--ol-text-pad) - var(--ol-anchor-gap)); padding-left: var(--ol-handle-w); color: var(--ol-ink-anchor); -webkit-text-fill-color: var(--ol-ink-anchor); opacity: 1; font-weight: 400; font-size: 10px; line-height: 20px; letter-spacing: .06em; white-space: nowrap; user-select: none; -webkit-user-select: none; }
.scene-summary, .scene-content { display: block; box-sizing: border-box; flex: 1 1 auto; min-width: 0; width: auto; margin: 0; border: none; outline: none; background: none; resize: none; overflow-y: auto; max-height: 40vh; font-family: inherit; padding: 1px 3px; transition: box-shadow .15s ease; }
/* 摘要：比标题矮一档的那行字，常驻显示，随内容自然撑高；超过 40vh 阈值后内部无痕滚动 */
.scene-summary { min-height: 20px; color: var(--ol-ink-summary); font-size: 13px; line-height: 1.6; scrollbar-width: none; -ms-overflow-style: none; }
.scene-summary::-webkit-scrollbar { display: none; }
.scene-summary::placeholder { color: var(--text-muted); font-size: 11px; opacity: .38; }
.scene-summary:hover, .scene-summary:focus-visible { box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--text-muted) 22%, transparent); }
/* 大纲正文：默认整条不出现；展开后与摘要同号同灰（13px/#666），不留任何底色/描边/竖线，层级只靠留白 */
.ol-detail-line { display: none; margin-top: 4px; padding: 0; background: none; box-shadow: none; }
.scene-item.ol-content-open .ol-detail-line { display: flex; }
.scene-content { height: auto; min-height: 20px; color: var(--ol-ink-summary); font-size: 13px; line-height: 1.6; background: transparent !important; border: none !important; scrollbar-width: none; -ms-overflow-style: none; }
/* 正文随内容自然撑高、无高度上限；overflow 已 hidden，这里再藏一遍滚动条兜底 */
.scene-content::-webkit-scrollbar { display: none; }
.scene-content:focus-visible { background: transparent !important; }
.scene-content::placeholder { color: var(--text-muted); font-size: 11px; opacity: .3; }
/* 展开/收起大纲细节的双箭头：悬停浮现；已展开时保持半可见，方便随手收起 */
.ol-expand-btn svg { transition: transform .18s ease; }
.scene-item.ol-content-open .ol-expand-btn { opacity: .5; pointer-events: auto; }
.scene-item.ol-content-open .ol-expand-btn svg { transform: rotate(180deg); }
.scene-header:hover .ol-expand-btn { opacity: 1; pointer-events: auto; }
/* 字数统计：全大纲对比度最低的一处，11px 浅灰，点它才能改目标字数；不收缩，只让标题吸收剩余宽度 */
.ol-words { flex: 0 0 auto; padding: 1px 4px; border: none; background: none; border-radius: 4px; color: var(--text-muted); opacity: .55; font-family: inherit; font-size: 0.68rem; white-space: nowrap; cursor: pointer; transition: opacity .15s, background .15s; }
.ol-words:hover { opacity: 1; background: color-mix(in srgb, var(--text-muted) 10%, transparent); }
/* 一个字没写、也没设目标 → 再淡一档：留在原位可以点，但视觉上几乎不存在 */
.ol-words-quiet { opacity: .3; }
/* 时间线微标签：去彩色，透明底 + 极细浅灰边框 + 浅灰文字，安静跟在标题旁；闪电只在张力 ≥8 时出现 */
.ol-tl-chip {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 3px; max-width: 132px; padding: 1px 6px;
  border: 1px solid color-mix(in srgb, var(--text-muted) 25%, transparent); border-radius: 999px;
  background: transparent;
  color: var(--text-muted); font-family: inherit; font-size: 9px; line-height: 14px; white-space: nowrap;
  cursor: pointer; transition: background .15s ease, border-color .15s ease;
}
.ol-tl-chip:hover { background: color-mix(in srgb, var(--text-muted) 8%, transparent); border-color: color-mix(in srgb, var(--text-muted) 40%, transparent); }
.ol-tl-chip-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* 胶囊里的图标缩到 10px（9px 字号旁边摆 13px 会压过文字），线宽按同比例补回 1.7，
   最终落地的描边粗细和面板里其他 13px/1.3 图标一致 —— SVG 缩放会连 stroke-width 一起缩。 */
.ol-tl-chip-ic { flex: 0 0 auto; display: inline-flex; align-items: center; }
.ol-tl-chip-ic svg, .ol-tl-chip-bolt svg { width: 10px; height: 10px; stroke-width: 1.7; }
.ol-tl-chip.is-high { font-weight: 700; }
.ol-tl-chip-bolt { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--danger-color); }
/* 行内目标字数输入框：只在编辑那一颗字的位置出现，同样没有边框 */
.ol-target-input { flex: 0 0 auto; width: 62px; padding: 1px 4px; border: none; outline: none; background: none; border-radius: 3px; color: var(--text-color); font-family: inherit; font-size: 0.68rem; box-shadow: inset 0 -1px 0 var(--primary-color); }
.ol-target-input::-webkit-outer-spin-button, .ol-target-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ol-target-input { -moz-appearance: textfield; appearance: textfield; }
/* ---- 标题行内的场景标签：9px 极小浅灰胶囊，跟在标题之后，最多两颗、多余折成 "+N" ---- */
/* 没有标签的场景连这一组都不建出来（空字段不输出 DOM），所以它一出现就是实打实的内容。
   flex-shrink 给 0：侧栏被拖窄、标题被拉长，压力一律先落到标题那条 min-width:80px 的底线，
   标签组一律不参与压缩——上一版它收缩系数 100，被压成 3 个字母加省略号就是这个锅。
   max-width: 100% 是它唯一的封顶：一行确实放不下，就整组换到第二行（组内 flex-wrap: wrap），
   仍然不 overflow:hidden，绝不拦腰裁字。 */
.ol-tags { flex: 0 0 auto; max-width: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; row-gap: 2px; margin-left: 2px; }
/* 行内 gap 6px + 这 2px = 标题文字与第一颗标签之间固定 8px 的呼吸距离 */
/* 行内标签：透明底 + 极细浅灰边框，9px、#A0A0A0、圆角 3px，安静跟在标题旁边。
   flex-shrink 必须是 0：胶囊不参与压缩，标题再长也轮不到它被挤成 "#英雄…"。
   宽度上限放宽到 120px——只有真的长过 120px 的标签才截断，正常长度一律完整显示。 */
.ol-tag { flex: 0 0 auto; display: inline-flex; align-items: center; padding: 1px 4px; border: 1px solid color-mix(in srgb, var(--text-muted) 22%, transparent); border-radius: 3px; background: transparent; color: var(--ol-ink-tag); font-size: 9px; line-height: 14px; max-width: 120px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: default; transition: background .15s ease, border-color .15s ease, color .15s ease; }
.ol-tag:hover { background: color-mix(in srgb, var(--text-muted) 8%, transparent); border-color: color-mix(in srgb, var(--text-muted) 35%, transparent); color: var(--text-color); }
/* 省略号真正落在名字上：胶囊本体是 inline-flex，只有这颗 span 会被压窄 */
.ol-tag-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ol-tag-x { flex: 0 0 auto; width: 11px; height: 11px; padding: 0; border: none; background: none; color: var(--text-muted); font-family: inherit; font-size: 9px; line-height: 11px; text-align: center; cursor: pointer; border-radius: 50%; opacity: 0; pointer-events: none; transition: opacity .15s ease, color .15s ease; }
.ol-tag:hover .ol-tag-x { opacity: .6; pointer-events: auto; } /* × 的位置常驻占位，出现时不会把后面的标签推来推去 */
.ol-tag-x:hover { opacity: 1 !important; color: var(--danger-color); }
/* 搜索框里的已选胶囊：它是"当前生效的条件"，比行内标签深一档，仍然只用灰度不用彩色。
   宽度上限给到 140px：条件本身要看得清，别一进来就先被截成"…"。
   line-height 收到 12px（连同 1px 上下内边距 = 14px 高）：比输入框那行 16px 还矮一点，
   胶囊进来以后不会把搜索框顶高，垂直居中由 align-self 兜住。 */
.ol-tag-filter { max-width: 140px; background: color-mix(in srgb, var(--text-muted) 22%, transparent); color: var(--text-color); font-size: 10px; line-height: 12px; }
.ol-tag-filter:hover { background: color-mix(in srgb, var(--text-muted) 28%, transparent); }
.ol-tag-filter .ol-tag-x { opacity: .5; pointer-events: auto; } /* 框里的 × 常驻可点：一眼看得出能删，不用先悬停才知道 */
.ol-tag-filter .ol-tag-x:hover { opacity: 1; }
/* ---- 顶部搜索过滤框：模板按钮下面一只带放大镜的浅色容器 ----
   给整只框铺一层与主题匹配的极浅灰底（往面板色里掺一点文字色），8px 圆角 + 内 padding，
   一眼看得出"这里能敲字"；内部 input 保持透明，底色只由容器提供。聚焦时容器外圈浮一层极淡主题色光圈。 */
#ol-search { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 14px; padding: 6px 12px; border: none; border-radius: 8px; background: color-mix(in srgb, var(--text-color) 7%, var(--panel-bg)); box-shadow: none; transition: box-shadow .2s ease, background .2s ease; }
/* 聚焦态：容器外圈一圈极淡主题色外发光（不再画下划线） */
#ol-search:focus-within { box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary-color) 12%, transparent); }
.ol-search-icon { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--ol-ink-summary); opacity: 1; }
.ol-search-icon svg { width: 14px; height: 14px; }
/* 没有已选胶囊时把这枚空 span 收起来：否则它凭空多吃一道 8px gap，放大镜到输入框就成了 16px */
.ol-search-chips:empty { display: none; }
/* 胶囊组：align-self:center 保证它在框里垂直居中；组的高度由下面的 line-height 收紧，绝不撑高搜索框 */
.ol-search-chips { display: inline-flex; flex-wrap: wrap; align-items: center; align-self: center; gap: 4px; row-gap: 2px; min-width: 0; }
.ol-search-input { flex: 1 1 90px; min-width: 60px; padding: 1px 2px; border: none; outline: none; background: none; color: var(--text-color); font-family: inherit; font-size: 13px; line-height: 18px; }
.ol-search-input::placeholder { color: var(--text-placeholder); opacity: 1; font-size: 13px; }
/* "清除全部"只在框里真有标签时出现（没有条件可清时不给一颗点了没反应的按钮） */
/* position/z-index 是热区保险：框里同排还有放大镜、胶囊、输入框，任何一层都不许压住它 */
.ol-search-clear { position: relative; z-index: 2; flex: 0 0 auto; padding: 1px 5px; border: none; background: none; color: var(--text-muted); font-family: inherit; font-size: 10px; line-height: 15px; cursor: pointer; border-radius: 4px; opacity: .7; transition: opacity .15s, color .15s, background .15s; }
.ol-search-clear:hover { opacity: 1; color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 8%, transparent); }
/* 下拉列表：挂在 document.body 上、position:fixed，坐标由 JS 用 getBoundingClientRect() 现算。
   为什么不留在搜索框里：侧栏 .panel-content 是 overflow-y:auto + overflow-x:hidden，
   而一个方向不是 visible 时，另一个方向的 visible 也会被浏览器一起算成 auto —— 它两个方向都裁。
   框内的绝对定位子元素被封死在这层里：标签一多，下拉下半截就被面板底边切掉，滚也滚不到，
   看上去正是"被大纲文字盖住"。挂到 body 之后，侧栏怎么裁都裁不到它。
   实底（--panel-bg 不带透明度）+ 描边 + 阴影三样一起给：它压在正文之上，必须一眼看得出是一整层浮起来的东西。 */
.ol-search-drop { display: none; position: fixed; left: 0; top: 0; z-index: 900; max-height: 224px; overflow-y: auto; scrollbar-width: none; -ms-overflow-style: none; padding: 3px; border: 1px solid color-mix(in srgb, var(--border-color) 75%, transparent); border-radius: 8px; background: var(--panel-bg); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); }
/* 深色底下 rgba(0,0,0,.1) 的阴影等于没有：加深一档，靠阴影而不是靠描边把层次拉开 */
[data-theme="dark"] .ol-search-drop { box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45); }
.ol-search-drop::-webkit-scrollbar { display: none; }
.ol-search-drop.ol-drop-open { display: block; }
.ol-drop-item { display: flex; align-items: center; gap: 6px; width: 100%; padding: 3px 6px; border: none; background: none; color: var(--text-muted); font-family: inherit; font-size: 11px; line-height: 16px; text-align: left; border-radius: 5px; cursor: pointer; transition: background .12s ease, color .12s ease; }
.ol-drop-item:hover { background: color-mix(in srgb, var(--text-muted) 10%, transparent); color: var(--text-color); }
/* ↑↓ 的键盘高亮和鼠标悬停是同一档极浅底：两条路径指向同一个东西，就该长得一样（不用彩色描边诱导） */
.ol-drop-item.active { background: color-mix(in srgb, var(--text-muted) 14%, transparent); color: var(--text-color); }
/* 已经选中的那一项只把名字加深：它在列表里和在高亮里都只是灰度差一档 */
.ol-drop-item.picked .ol-drop-name { color: var(--text-color); font-weight: 600; }
.ol-drop-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ol-drop-cnt { flex: 0 0 auto; font-size: 9px; opacity: .5; }
.ol-drop-empty { padding: 6px; border: none; background: none; color: var(--text-muted); font-size: 10px; opacity: .55; text-align: center; }
/* 全局搜索的三组结果：组头只是分栏记号，不参与键盘选中（见 olSyncSearchBox） */
.ol-drop-group { padding: 5px 6px 2px; font-size: 9px; letter-spacing: .04em; color: var(--text-muted); opacity: .6; }
/* "还有 N 条"：说清楚被上限收掉的那些去哪了——不写这行，人以为搜到的就是全部 */
.ol-drop-more { padding: 3px 6px 5px; font-size: 9.5px; color: var(--text-muted); opacity: .55; }
/* 场景那行两小条：第一行「章 > 场」当路牌，第二行才是命中片段，所以整颗改成竖排 */
.ol-drop-scene { flex-direction: column; align-items: flex-start; gap: 1px; padding: 4px 6px; }
.ol-drop-path { flex: 0 1 auto; min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-color); }
/* 命中片段允许折行：项目里的硬规矩是绝不拦腰裁字，宁可让这一格高一点 */
.ol-drop-snip { width: 100%; font-size: 10px; line-height: 14px; color: var(--text-muted); }
/* <mark> 的浏览器默认是一坨荧光黄，这里改成跟主题走的一层薄色 */
.ol-drop-hl { background: color-mix(in srgb, var(--primary-color) 18%, transparent); color: inherit; border-radius: 2px; padding: 0 1px; }
.ol-drop-char .ol-lp-ava { width: 14px; height: 14px; font-size: 9px; }
/* 过滤后一条都不剩：大纲里留一行极淡说明，让列表看起来是"筛空了"而不是"坏了" */
.ol-filter-empty { padding: 2px 0 10px; color: var(--text-muted); font-size: 0.75rem; opacity: .6; }
/* 模板骨架行内选择条：浮层开在模板按钮组正下方（挂载/定位/关闭由 PopoverManager 接管），
   和过滤条同一档字号，一行摆完三个小按钮，不给弹窗 */
.ol-tpl-mode { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; padding: 5px 8px; border-radius: 8px; background: var(--panel-bg); border: 1px solid color-mix(in srgb, var(--border-color) 60%, transparent); box-shadow: var(--shadow-md); z-index: 900; font-size: 0.72rem; color: var(--text-muted); }
.ol-tpl-mode-tip { margin-right: 2px; }
.ol-tpl-mode-btn { padding: 2px 9px; font-size: 0.7rem; border-radius: 5px; }
/* 默认档（追加）只把文字加深，不用主色铺底：安全选项不该看起来像"高亮诱惑" */
.ol-tpl-mode-yes { color: var(--text-color); }
/* 过滤模式：手柄原地"断电"而不是抽走——抽走会让整列文字左移十几像素，切一次过滤就跳一次版。
   pointer-events:none 让它根本抓不起来（dragstart 里还有一句 JS 兜底），淡到几乎看不见就是"此刻不能用"。 */
#outline-container.ol-filtered .outline-drag-handle { pointer-events: none; opacity: .16 !important; }
/* 章内新增：一条浅灰文字，悬停加深 */
.ol-add-scene { display: inline-flex; align-items: center; gap: 4px; margin-top: 4px; padding: 2px 6px 2px 0; width: auto; border: none; background: none; color: var(--text-muted); opacity: .5; font-size: 0.75rem; font-family: inherit; cursor: pointer; border-radius: 4px; transition: opacity .15s, color .15s; }
.ol-add-scene:hover { opacity: 1; color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 8%, transparent); }
/* 状态标签：文字胶囊，绝不用纯色圆点；配色只动文字色 + 极浅底色 */
.status-badge { flex: 0 0 auto; min-width: var(--ol-badge-w); padding: 1px 8px; border: none; border-radius: 999px; background: var(--sb-bg); color: var(--sb-fg); font-family: inherit; font-size: 0.68rem; line-height: 16px; text-align: center; white-space: nowrap; cursor: pointer; transition: background .15s, color .15s; }
.status-badge:hover { filter: brightness(0.95); }
.status-badge:focus-visible { outline: 1px solid var(--primary-color); outline-offset: 1px; }
.status-badge.todo, .ol-status-dot.todo { --sb-fg: var(--text-muted); --sb-bg: color-mix(in srgb, var(--text-muted) 12%, transparent); }
.status-badge.draft, .ol-status-dot.draft { --sb-fg: var(--status-draft); --sb-bg: color-mix(in srgb, var(--status-draft) 14%, transparent); }
.status-badge.revise, .ol-status-dot.revise { --sb-fg: var(--status-revise); --sb-bg: color-mix(in srgb, var(--status-revise) 16%, transparent); }
.status-badge.done, .ol-status-dot.done { --sb-fg: var(--success-color); --sb-bg: color-mix(in srgb, var(--success-color) 16%, transparent); }
[data-theme="dark"] .status-badge.draft, [data-theme="dark"] .ol-status-dot.draft { --sb-fg: var(--status-draft); }
[data-theme="dark"] .status-badge.revise, [data-theme="dark"] .ol-status-dot.revise { --sb-fg: var(--status-revise); }
[data-theme="dark"] .status-badge.done, [data-theme="dark"] .ol-status-dot.done { --sb-fg: #8FC49B; }
/* 状态菜单：皮肤完全复用关联浮层，宽度由 JS 一处给；色点把徽标那套配色原样搬到菜单里当图例 */
.ol-status-dot { width: 10px; height: 10px; flex: 0 0 auto; border-radius: 50%; background: var(--sb-bg); box-shadow: inset 0 0 0 1.5px var(--sb-fg); }
.ol-status-pop .ol-lp-row.on { background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
/* 深色兜底：theme.js 注入的暗色补丁会给 input/textarea 铺底色描边、给行加卡片阴影，
   这里按无边框设计逐层覆盖掉（选择器带 id，特异性压过它；高亮态除外，见下） */
#outline-container .ol-title, #outline-container .scene-summary,
#outline-container .scene-content, #outline-container .ol-target-input { background-color: transparent !important; border-color: transparent !important; }
/* 四级字色同样要压过 theme.js 的 !important，否则深色模式下标题/摘要/细节会糊成同一个白；
   走 --ol-ink-* 变量：深浅两套值只在上面定义一次，这里不必再抄一遍具体色值 */
#outline-container .ol-title { color: var(--ol-ink-scene) !important; }
#outline-container .ol-chapter-title { color: var(--ol-ink-chapter) !important; }
#outline-container .ol-target-input,
#ol-search .ol-search-input { color: var(--text-color) !important; }
#outline-container .scene-summary { color: var(--ol-ink-summary) !important; }
/* 大纲正文与摘要同号同灰，深色下同样压过 theme.js 的 !important */
#outline-container .scene-content { color: var(--ol-ink-summary) !important; }
#outline-container .scene-summary::placeholder { color: var(--text-muted) !important; opacity: .38 !important; }
#outline-container .scene-content::placeholder { color: var(--text-muted) !important; opacity: .3 !important; }
/* 搜索框里那颗输入框要"住进"浅色容器里：暗色补丁给它铺的 #0f172a 会在框中间戳出一块深色矩形 */
#ol-search .ol-search-input { background-color: transparent !important; border-color: transparent !important; }
/* 占位符浅色主题走基础规则的 var(--text-placeholder)；只有深色才需要这条 !important 压住 theme.js 的暗色补丁 */
[data-theme="dark"] #ol-search .ol-search-input::placeholder { color: var(--text-placeholder) !important; opacity: .7 !important; }
/* 深色下细节框同样完全不透明底（上面刚去掉便签底色，这里不能再给它补回来） */
[data-theme="dark"] .ol-detail-line { background: none; box-shadow: none; }
/* 链接模式（点选关联大纲）期间这条一律让路：那是要用户"去点某一行"的时刻，
   每一行都得亮着，用 :not 豁免而不是再补一条 !important——叠 !important 只会让后面更难赢 */
[data-theme="dark"]:not(.mm-link-mode) #outline-container .chapter-item:not(.mm-link-active):not(.mm-link-flash):not(.chapter-flash),
[data-theme="dark"]:not(.mm-link-mode) #outline-container .scene-item:not(.mm-link-active):not(.mm-link-flash) { background: transparent !important; box-shadow: none !important; text-shadow: none !important; }
/* 完成度小计里的 M/N 数字：与 done 同色系做轻微强调，不加粗不加底 */
.ol-cnt-num { color: var(--success-color); opacity: .95; }
/* 章节分类圆点：全篇只有这一颗点，8px 实心；与标题间距 10px（行内 gap 6px + 这 4px margin） */
.ol-color-dot { flex: 0 0 auto; width: 8px; height: 8px; margin-right: 4px; border-radius: 50%; border: none; padding: 0; cursor: pointer; opacity: .9; transition: transform .12s ease; }
.ol-color-dot:hover { transform: scale(1.35); }
/* 预设色板：挂载/定位/关闭由 PopoverManager 统一接管（body 上 fixed，贴底上翻），同一时间只开一个 */
.ol-palette { display: flex; gap: 8px; padding: 8px 10px; background: var(--panel-bg); border: 1px solid color-mix(in srgb, var(--border-color) 60%, transparent); border-radius: 999px; box-shadow: var(--shadow-md); z-index: 900; }
.ol-swatch { width: 14px; height: 14px; border-radius: 50%; border: none; padding: 0; cursor: pointer; transition: transform .12s; }
.ol-swatch:hover { transform: scale(1.25); }
/* 空章节放置区：无边框设计里唯一的虚线，只用于给拖拽一个可见的接收位 */
.ol-empty-drop { margin-top: 6px; padding: 8px 10px; border: 1px dashed color-mix(in srgb, var(--text-muted) 35%, transparent); border-radius: 6px; color: var(--text-muted); font-size: 0.72rem; opacity: .6; transition: .15s; }
#outline-container.ol-dragging .ol-empty-drop { border-color: var(--primary-color); color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 7%, transparent); opacity: 1; }
/* 拖拽现场：被拖行淡出、插入线、非法落点红虚线（松手/Esc 由 clearDragState 统一清） */
.chapter-item.ol-drag-source, .scene-item.ol-drag-source { opacity: .35; }
.outline-drop-line { position: absolute; height: 2px; background: var(--primary-color); border-radius: 1px; pointer-events: none; z-index: 6; box-shadow: 0 0 4px color-mix(in srgb, var(--primary-color) 60%, transparent); }
.chapter-item.ol-drop-invalid { outline: 1.5px dashed var(--danger-color); outline-offset: -1px; border-radius: 6px; }
/* 拖到折叠章：章头描一圈浅色虚线 + 底下浮一行占位提示，章体照常缩着。
   提示走绝对定位（.chapter-header 本来就是 position:relative），所以悬停来回切换时
   这一格的高度一个像素都不变——悬停就撑开几百像素，插入线和鼠标立刻错位，根本放不下。
   真正的展开留到 drop（见 applySceneDrop）。 */
.chapter-item.ol-drop-head .chapter-header {
  background: color-mix(in srgb, var(--primary-color) 9%, transparent);
  outline: 1px dashed color-mix(in srgb, var(--primary-color) 55%, transparent);
  outline-offset: -1px;
  border-radius: 6px;
}
.chapter-item.ol-drop-head .chapter-header::after {
  content: '松开鼠标，放入此章';
  position: absolute;
  left: 22px;
  top: calc(100% + 4px); /* 让开压在章底下那条插入线（2px / z-index:6） */
  z-index: 7;
  padding: 1px 6px;
  border: 1px dashed color-mix(in srgb, var(--primary-color) 45%, transparent);
  border-radius: 5px;
  background: var(--panel-bg);
  color: var(--primary-color);
  font-size: 10px;
  line-height: 15px;
  white-space: nowrap;
  pointer-events: none; /* 提示不是按钮：碰它不该吃掉松手那一下 */
}
/* 拖拽期间行内小控件一律不吃点击，避免悬停浮现的按钮在半透明行上被误触 */
#outline-container.ol-dragging .ol-actions, #outline-container.ol-dragging .ol-fold,
#outline-container.ol-dragging .ol-color-dot, #outline-container.ol-dragging .status-badge,
#outline-container.ol-dragging .ol-words, #outline-container.ol-dragging .ol-add-scene { pointer-events: none; }
/* 章头总字数：与小计同色的极弱灰，数字本身不做任何强调 */
.ol-ch-words { opacity: .85; }
/* 删除章节/场景行内确认：浮层原地盖住标题行，行高不变，列表不跳 */
.chapter-header, .scene-header { position: relative; }
.ol-del-confirm { position: absolute; inset: -4px -6px; display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding: 0 8px; background: var(--panel-bg); border-radius: 8px; box-shadow: var(--shadow-md); font-size: .75rem; color: var(--text-muted); }
.ol-del-yes, .ol-del-no { padding: 2px 10px; font-size: .74rem; }
.ol-del-yes { background: var(--danger-color); color: var(--on-accent); border: none; border-radius: 4px; cursor: pointer; }
.ol-del-no { background: transparent; border: 1px solid var(--border-color); border-radius: 4px; color: var(--text-muted); cursor: pointer; }
/* 备忘录 */
.memo-item { background: var(--memo-bg); border-radius: var(--radius-sm); padding: 12px; margin-bottom: 12px; border-left: 4px solid var(--warning-color); transition: 0.2s; }
.memo-item.done { opacity: 0.6; border-left-color: var(--success-color); background: var(--memo-done-bg); }
.memo-header { display: flex; justify-content: space-between; align-items: center; cursor: pointer; font-weight: 600; }
/* 标题前的状态记号（对勾 / 沙漏线稿）：图标只是记号，语义由整行的 .done 皮肤 + 那颗「完成/撤销」按钮承担 */
.memo-state { display: inline-flex; align-items: center; vertical-align: -0.18em; color: var(--text-muted); }
.memo-state.done { color: var(--success-color); }
.memo-content { margin-top: 10px; display: none; }
.memo-content.expanded { display: block; }
.memo-textarea { width: 100%; border: 1px solid var(--border-color); border-radius: var(--radius-sm); padding: 8px; font-family: inherit; font-size: 0.8rem; resize: vertical; min-height: 60px; }
.memo-title-input { width: 100%; border: 1px solid var(--border-color); border-radius: var(--radius-sm); padding: 6px 8px; font-family: inherit; font-size: 0.8rem; }
/* 模态框 */
.modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: var(--scrim-color); z-index: var(--z-modal); display: none; justify-content: center; align-items: center; backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.modal-overlay.active { display: flex; animation: layer-fade var(--dur-base) var(--ease-standard); }
.modal-content { background: var(--panel-bg); padding: 24px; border: none; border-radius: var(--radius-lg); width: 500px; max-width: 90%; max-height: 90vh; overflow-y: auto; box-shadow: var(--shadow-pop); animation: layer-pop-in var(--dur-base) var(--ease-standard); }
.export-tip { margin: 2px 0 14px; color: var(--text-muted); font-size: 0.85rem; }
.export-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.export-item { cursor: pointer; }
.export-item input { display: none; }
.export-box { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 14px 8px 12px; border: 1px solid transparent; border-radius: 10px; background: var(--hover-bg); transition: all 0.15s; text-align: center; position: relative; }
.export-box i { font-size: 1.15rem; color: var(--text-muted); transition: color 0.15s; }
.export-name { font-size: 0.85rem; font-weight: 600; color: var(--text-color); }
.export-desc { font-size: 0.72rem; color: var(--text-muted); }
.export-item input:checked + .export-box { border-color: var(--primary-color); box-shadow: inset 0 0 0 1px var(--primary-color); background: color-mix(in srgb, var(--primary-color) 7%, var(--bg-color)); }
.export-item input:checked + .export-box::after { content: '✓'; position: absolute; top: 6px; right: 7px; width: 16px; height: 16px; border-radius: 50%; background: var(--primary-color); color: var(--panel-bg); font-size: 10px; font-weight: 700; line-height: 16px; text-align: center; }
.export-item input:checked + .export-box i, .export-item input:checked + .export-box .export-name { color: var(--primary-color); }
/* ===== 导出系统扩展：以下全部为新增规则，上方既有 .export-* 美术规则一条未改 ===== */
.export-header-actions { display: inline-flex; align-items: center; gap: 8px; }
.export-import-btn { width: auto; padding: 0 10px; gap: 6px; font-size: 0.78rem; font-weight: 600; color: var(--text-muted); }
.export-import-btn:hover { color: var(--primary-color); }
.export-cell { min-width: 0; }
.export-svg-icon { width: 1.15rem; height: 1.15rem; color: var(--text-muted); transition: color 0.15s; }
.export-item input:checked + .export-box .export-svg-icon { color: var(--primary-color); }
.export-card-format { display: block; width: 100%; margin-top: 6px; padding: 5px 6px; font-size: 0.72rem; color: var(--text-color); background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: var(--radius-md); outline: none; cursor: pointer; }
.export-card-format:focus { border-color: var(--primary-color); }
.export-card-format[hidden] { display: none; }
.export-per-card-tip { margin: -4px 0 8px; font-size: 0.74rem; color: var(--text-muted); }
.export-drop-hint { display: none; margin: 4px 0 12px; padding: 10px; text-align: center; font-size: 0.82rem; font-weight: 600; color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 7%, var(--bg-color)); border: 1px dashed var(--primary-color); border-radius: var(--radius-md); }
#modal-export.export-dragging .export-drop-hint { display: block; }
.export-loading { position: fixed; inset: 0; z-index: 3000; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.42); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); }
.export-loading[hidden] { display: none; }
.export-loading-card { min-width: 260px; padding: 28px 34px; text-align: center; background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.16); color: var(--text-color); }
.export-loading-card p { margin: 14px 0 4px; font-size: 0.95rem; font-weight: 600; }
.export-loading-card small { color: var(--text-muted); font-size: 0.76rem; }
.export-spinner { width: 34px; height: 34px; margin: 0 auto; border: 3px solid var(--border-color); border-top-color: var(--primary-color); border-radius: 50%; animation: exportSpin 0.8s linear infinite; }
@keyframes exportSpin { to { transform: rotate(360deg); } }
.modal-footer { display: flex; justify-content: flex-end; gap: 12px; margin-top: 18px; }
.modal-header { font-size: 1.15rem; font-weight: 700; margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center; color: var(--text-color); }
.form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 6px; font-weight: 500; font-size: 0.9rem; }
/* 表单标签里的「点选关联」入口：跟标签文字同一基线，不撑高一行，也不抢标签本身的份量 */
.form-link-btn { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; padding: 0;
  margin-left: 4px; vertical-align: middle; border: none; background: transparent; color: var(--text-muted);
  border-radius: 4px; opacity: .6; cursor: pointer; transition: opacity .15s, color .15s, background .15s; }
.form-link-btn:hover { opacity: 1; color: var(--accent-color); background: color-mix(in srgb, var(--text-muted) 12%, transparent); }
.form-hint { font-size: 0.7rem; color: var(--text-muted); opacity: 0.7; margin-top: 4px; line-height: 1.4; }
.form-row { display: flex; gap: 10px; }
.form-row .form-group { flex: 1; }
input, textarea { width: 100%; padding: var(--input-pad); border-radius: var(--input-radius); border: 1px solid var(--border-color); background: var(--input-sunken); color: var(--text-color); font-family: inherit; line-height: var(--input-line-height); transition: border-color 0.2s, box-shadow 0.2s; }
input:focus-visible, textarea:focus-visible { border-color: var(--primary-color); outline: none; box-shadow: var(--input-focus-ring); }
input::placeholder, textarea::placeholder { color: var(--text-placeholder); }
input[type="file"] { padding: 6px; }
input[type="color"] { height: 45px; padding: 2px; cursor: pointer; }
/* 全站滑块大一统：极细灰轨 + 主色填充 + 纯白圆形滑块头（带微阴影）。
   --range-fill 由 js/ui/range.js 按 min/max/value 实时写入；Firefox 走 ::-moz-range-progress。
   .vd-bar-range 是 opacity:0 的绝对定位覆盖层，排除掉，绝不碰它的 inset/height:100%。 */
input[type="range"]:not(.vd-bar-range) {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 16px; box-sizing: border-box;
  margin: 0; padding: 0; border: 0; border-radius: 0;
  background: transparent; cursor: pointer;
  --range-fill: 0%;
}
input[type="range"]:not(.vd-bar-range)::-webkit-slider-runnable-track {
  height: 3px; border-radius: 999px;
  background: linear-gradient(to right,
    var(--primary-color) var(--range-fill), var(--tooltip-track) var(--range-fill));
}
input[type="range"]:not(.vd-bar-range)::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 14px; height: 14px; margin-top: -5.5px;
  border-radius: 50%; background: #FFFFFF;
  border: 1px solid var(--border-strong);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
}
input[type="range"]:not(.vd-bar-range)::-moz-range-track {
  height: 3px; border-radius: 999px; background: var(--tooltip-track);
}
input[type="range"]:not(.vd-bar-range)::-moz-range-progress {
  height: 3px; border-radius: 999px; background: var(--primary-color);
}
input[type="range"]:not(.vd-bar-range)::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%; background: #FFFFFF;
  border: 1px solid var(--border-strong);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
}
input[type="range"]:not(.vd-bar-range):focus-visible::-webkit-slider-thumb,
input[type="range"]:not(.vd-bar-range):focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 26%, transparent);
}
/* 编辑型档案（vd-dossier）刻意无边框、聚焦也无光晕，仅保留其编辑式底边强调 */
.vd-dossier input, .vd-dossier textarea { border: none; border-radius: 0; }
.vd-dossier input:focus-visible, .vd-dossier textarea:focus-visible { box-shadow: none; }
/* 隐藏态只是 opacity:0：不加 pointer-events:none 的话，右下角会留一块
   z-index:2000 的透明矩形，把下面的时间线雷达 / 按钮 / 卡片点击全抢走。 */
.toast { position: fixed; bottom: 30px; right: 30px; background: var(--text-color); color: var(--bg-color); padding: 12px 24px; border-radius: var(--radius-sm); opacity: 0; transition: 0.3s; z-index: var(--z-toast); box-shadow: var(--shadow-md); pointer-events: none; }
.toast.show { opacity: 1; }
.toast.error { background: var(--danger-color); }
.template-btns { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
/* 极简幽灵按钮：撤掉 .btn 那套白底 + 描边 + 24px 大胶囊（压在大纲顶部显得生硬廉价）。
   默认无框无底、只留次级灰文字；悬停浮出一层极浅灰底，按下把文字加深到最深一档。 */
.template-btn { flex: 1; min-width: 0; justify-content: center; padding: 5px 8px; border: none; border-radius: var(--radius-btn); background: transparent; backdrop-filter: none; box-shadow: none; color: var(--ol-ink-detail); font-size: 13px; font-weight: 400; transition: background 0.18s ease, color 0.18s ease, transform 0.08s ease; }
.template-btn:hover { background: var(--hover-bg); color: var(--ol-ink-scene); }
.template-btn:active { background: var(--hover-bg); color: var(--ol-ink-chapter); transform: scale(0.98); }
/* 深色下 [data-theme="dark"] .btn 会给所有按钮铺实底：用同特异性靠后覆盖，把模板按钮拉回无框无底；
   悬停/按下改用提亮的底色 token 取代硬编码白 */
[data-theme="dark"] .template-btn { background: transparent; color: var(--ol-ink-detail); }
[data-theme="dark"] .template-btn:hover { background: var(--hover-bg); color: var(--ol-ink-scene); }
[data-theme="dark"] .template-btn:active { background: var(--hover-bg); color: var(--ol-ink-chapter); transform: scale(0.98); }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg-color); }
::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 5px; }
::-webkit-scrollbar-thumb:hover { background: var(--primary-color); }
/* ========== 关系网画布：纯平底 + 极淡点阵 ========== */
/* 只剩一层 22px 点阵，颜色压到 rgba(0,0,0,0.04)：远处的 110px 网格线容易和浅色连线打架，索性撤掉 */
#network-svg {
  background-color: var(--bg-color);
  background-image: radial-gradient(circle at center, rgba(0, 0, 0, 0.04) 1px, transparent 1.5px);
  background-size: 22px 22px;
}
[data-theme="dark"] #network-svg {
  background-image: radial-gradient(circle at center, rgba(255, 255, 255, 0.05) 1px, transparent 1.5px);
}
/* 节点：纯色实心圆 + 2px 纯白描边。fill/stroke/stroke-width 由 relations.js 写成属性（导出截图走
   SVG 序列化，外链样式表进不去，所以关键视觉必须是属性），这里只留光标、字体与过渡。
   字体不再写死 font-size：头像字号按半径算、标签字号按 labelInside 切换，都靠属性给，
   这条规则一旦带 size 会把它们统一压成 10px（头像小成一粒米就是这么来的） */
.node { cursor: move; transition: opacity 0.25s ease; }
.node text { font-family: 'Inter', sans-serif; }
/* 连线：绝对平行直线、1.5px、颜色按关系类型（盟友蓝/敌对红/爱情粉）。
   静止态 stroke-opacity 0.4 写在属性上（导出要看得见），悬停/图例强调时由下面这些类提到 1 并加粗 */
.link { transition: stroke-opacity 0.18s ease, stroke-width 0.18s ease, opacity 0.25s ease; }
.link.is-active { stroke-opacity: 1; stroke-width: 3px; }
.link.is-bundled.is-active { stroke-width: 5px; }
/* 加宽命中线：1.5px 的线本身点不中，事件全交给这条透明线 */
/* 命中线 8px：CSS 规则优先级高于 JS 的 presentation attribute，两边必须同步，
   否则 14px 的透明区域会盖过 .attr('stroke-width', 8)，鼠标离很远就提前高亮 */
.link-hit { fill: none; stroke: transparent; stroke-width: 8px; stroke-linecap: round; cursor: pointer; }
.node.dim, .link.dim { opacity: 0.15; }
/* 关系图 D3 提示：pageX/pageY 定位需 absolute；显隐由 rel-graph.js 的 opacity 控制，视觉继承 .app-tooltip */
.app-tooltip--d3 { position: absolute; visibility: visible; }
.type-edit-row { display: flex; gap: 8px; align-items: center; }
.type-edit-row input[type="text"] { flex: 1; }
.type-edit-row input[type="color"] { width: 48px; height: 38px; padding: 2px; flex: 0 0 auto; }
.manage-type-list { display: flex; flex-direction: column; gap: 6px; max-height: 260px; overflow: auto; }
.type-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border-color); border-radius: var(--radius-sm); }
.type-row .type-dot { width: 14px; height: 14px; border-radius: 50%; flex: 0 0 auto; }
.type-row .type-name { flex: 1; font-weight: 500; }
.type-row .type-count { font-size: 0.75rem; opacity: 0.7; }
.type-row .btn { padding: 2px 10px; font-size: 0.75rem; }
/* 岛台右侧图标组：与左侧图例色块垂直居中，两枚纯图标间距 8px */
.legend-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.legend-icon-btn {
  width: 28px; height: 28px; padding: 0; box-sizing: border-box; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; border-radius: 6px; background: transparent;
  color: var(--text-muted); font-family: inherit; font-size: 13px; line-height: 1;
  cursor: pointer; transition: background .15s, color .15s;
}
.legend-icon-btn > i { width: 14px; height: 14px; display: inline-flex; align-items: center; justify-content: center; font-size: 13px; }
.legend-icon-btn:hover, .legend-icon-btn.is-open { background: var(--hover-bg); color: var(--text-color); }
[data-theme="dark"] .legend-icon-btn:hover,
[data-theme="dark"] .legend-icon-btn.is-open { background: var(--hover-bg); color: var(--text-color); }
/* 两枚纯图标之间一条极细浅灰竖线，只在「更多」存在时出现 */
.legend-actions [data-action="legend-more"] { position: relative; }
.legend-actions [data-action="legend-more"]::after {
  content: ''; position: absolute; right: -5px; top: 50%; transform: translateY(-50%);
  width: 1px; height: 14px; background: var(--border-strong); pointer-events: none;
}
[data-theme="dark"] .legend-actions [data-action="legend-more"]::after { background: color-mix(in srgb, var(--text-muted) 50%, transparent); }
.color-preset-btn { border: 2px solid transparent !important; transition: transform 0.2s, border-color 0.2s; cursor: pointer; }
.color-preset-btn:hover { transform: scale(1.15); border-color: var(--primary-color) !important; }
.theme-preset.active, .color-preset-btn.active { outline: 3px solid var(--accent-color); outline-offset: 2px; transform: scale(1.05); box-shadow: var(--shadow-md); }
/* 原 <768px 抽屉规则已迁移到文件末尾「阶段 A · 响应式主框架」统一实现：
   旧规则被 HTML 内联 position:relative 与 .collapsed{width:0!important} 击穿，
   且 .view-tabs{display:none} 会抹掉唯一的视图切换入口。
   现改为 <1200px 双栏覆盖式 Drawer + 顶栏「更多」菜单承载视图切换。 */
/* ========== 角色卡片页：复合检索输入组 + 轻量筛选浮层 ========== */
.char-page-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 6px 20px 12px; flex-wrap: wrap; }
.char-toolbar-left { display: flex; align-items: center; gap: 12px; flex: 1 1 auto; min-width: 0; }
.char-toolbar-right { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
/* 复合控件：放大镜 + 输入框 + 分隔线 + 内嵌漏斗（带红色数字角标），一整块就是一个 Input Group */
.char-search-group { position: relative; display: flex; align-items: center; flex: 1 1 360px; max-width: 460px; height: 38px; box-sizing: border-box; border: 1px solid var(--border-color); border-radius: var(--radius-lg); background: var(--bg-color); transition: border-color 0.16s ease, box-shadow 0.16s ease; }
.char-search-group:focus-within { border-color: var(--primary-color); box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary-color) 18%, transparent); }
.char-search-ic { flex: 0 0 auto; margin-left: 11px; color: var(--text-muted); font-size: 0.82rem; pointer-events: none; }
.char-search { flex: 1 1 auto; min-width: 0; height: 100%; padding: 0 8px 0 9px; border: none; outline: none; background: transparent; color: var(--text-color); font-size: 0.85rem; box-sizing: border-box; }
.char-search::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.char-search-clear { flex: 0 0 auto; border: none; background: transparent; color: var(--text-muted); cursor: pointer; font-size: 0.78rem; padding: 4px 6px; line-height: 1; }
.char-search-clear:hover { color: var(--text-color); }
.char-search-sep { flex: 0 0 auto; width: 1px; height: 20px; background: var(--border-color); }
.char-filter-trigger { position: relative; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; gap: 4px; height: 100%; width: 44px; border: none; background: transparent; color: var(--text-muted); font-size: 0.9rem; cursor: pointer; border-radius: 0 var(--radius-lg) var(--radius-lg) 0; }
.char-filter-trigger:hover { color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 8%, transparent); }
.char-filter-trigger.is-active { color: var(--primary-color); }
.char-filter-badge { position: absolute; top: 1px; right: 1px; min-width: 16px; height: 16px; padding: 0 4px; display: inline-flex; align-items: center; justify-content: center; border-radius: 999px; background: var(--danger-color); color: var(--on-accent); font-size: 0.66rem; font-weight: 600; line-height: 1; }
.char-filter-badge[hidden] { display: none; }
.char-result-count { font-size: 0.78rem; color: var(--text-muted); white-space: nowrap; margin-right: 4px; }
.char-add-icon { background: var(--border-strong); border: none; color: var(--text-muted); border-radius: 50%; width: 34px; height: 34px; }
.char-add-icon:hover { background: color-mix(in srgb, var(--border-strong), var(--text-color) 10%); color: var(--text-color); }
[data-theme="dark"] .char-add-icon { background: var(--border-strong); color: var(--text-muted); }
[data-theme="dark"] .char-add-icon:hover { background: color-mix(in srgb, var(--border-strong), var(--text-color) 10%); color: var(--text-color); }
/* 筛选浮层（Popover）：悬浮在漏斗下方，不遮卡片、无黑遮罩、可一边点选一边看结果 */
.char-filter-pop { position: fixed; z-index: 3000; width: 380px; max-width: calc(100vw - 24px); max-height: min(76vh, 620px); display: flex; flex-direction: column; background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); overflow: hidden; }
.char-filter-pop[hidden] { display: none !important; pointer-events: none !important; }
.char-filter-pop-head { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--border-color); font-size: 0.88rem; font-weight: 600; }
.char-filter-pop-close { border: none; background: transparent; color: var(--text-muted); font-size: 1.15rem; line-height: 1; cursor: pointer; padding: 2px 6px; border-radius: 6px; }
.char-filter-pop-close:hover { background: var(--bg-color); color: var(--text-color); }
/* 透明捕获层永远不吃点击（关闭靠 document 判断外部点击）：卡片按钮随时可点，真正无缝预览 */
.char-filter-catch { position: fixed; inset: 0; z-index: 2999; background: transparent; pointer-events: none; }
.char-filter-catch[hidden] { display: none !important; pointer-events: none !important; }
.char-filter-pop-body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 12px 14px; display: flex; flex-direction: column; gap: 12px; }
.char-pop-section-label { font-size: 0.72rem; font-weight: 600; color: var(--text-muted); letter-spacing: 0.04em; }
.char-filter-pop-foot { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 14px; border-top: 1px solid var(--border-color); background: var(--panel-bg); }
.char-filter-count { font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; }
.char-filter-foot-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.char-filter-reset { font-size: 0.76rem; padding: 5px 10px; }
.char-filter-done { flex: 0 0 auto; font-size: 0.78rem; padding: 5px 14px; }
/* 已选条件标签：复合搜索框正下方、卡片列表正上方，灰色小标签逐个可删 */
.char-active-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 0 20px 10px; }
.char-active-tags[hidden] { display: none !important; pointer-events: none !important; }
.char-active-tags-label { font-size: 0.75rem; color: var(--text-muted); margin-right: 2px; }
.char-active-tag { display: inline-flex; align-items: center; gap: 6px; padding: 3px 7px 3px 9px; border: 1px solid var(--border-color); background: var(--bg-color); color: var(--text-muted); border-radius: 6px; font-size: 0.74rem; cursor: pointer; }
.char-active-tag i { font-size: 0.64rem; }
.char-active-tag:hover { border-color: var(--danger-color); color: var(--text-color); }
.char-active-tag:hover i { color: var(--danger-color); }
/* ========== 控件规范：胶囊代替原生 Checkbox，下拉框统一美化 ========== */
.char-filter-chip { position: relative; display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border: 1px solid var(--border-color); border-radius: 999px; background: var(--bg-color); color: var(--text-color); font-size: 0.75rem; cursor: pointer; user-select: none; white-space: nowrap; transition: background 0.14s ease, border-color 0.14s ease, color 0.14s ease; }
.char-filter-chip:hover { border-color: var(--primary-color); }
/* 原生 checkbox 完全隐藏，只保留它承载的语义与 change 事件 */
.char-filter-chip input[type="checkbox"] { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; padding: 0; pointer-events: none; }
.char-filter-chip.checked { border-color: var(--primary-color); background: var(--primary-color); color: var(--panel-bg); font-weight: 600; }
.char-filter-chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* 高级筛选行：自定义下拉与同行 .char-filter-text 等高、同圆角（皮肤统一由 .cselect 承担） */
.char-filter-pop .cselect:not(.cp-input) { height: 32px; padding: 0 8px; border-radius: 6px; font-size: 0.78rem; }
/* .char-filter-controls 是横向滚动容器，外扩 box-shadow 焦点环会被裁掉，改用内描边 */
.char-filter-pop .cselect:focus-visible { box-shadow: none; outline: 2px solid var(--primary-color) !important; outline-offset: -3px; border-color: var(--primary-color); }
/* 高级条件「严格单行」：所有控件 nowrap，胶囊区改为横向滚动，绝不上下散架 */
.char-filter-rows { display: flex; flex-direction: column; gap: 8px; }
.char-filter-row { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; min-width: 0; }
.char-filter-field { flex: 0 0 104px; width: 104px; }
.char-filter-controls { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 5px; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin; }
.char-filter-controls::-webkit-scrollbar { height: 4px; }
.char-filter-controls::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 2px; }
.char-filter-scope { flex: 0 0 76px; width: 76px; }
.char-filter-text { flex: 1 1 auto; min-width: 60px; height: 32px; box-sizing: border-box; padding: 0 8px; border: 1px solid var(--border-color); border-radius: 6px; background: var(--bg-color); color: var(--text-color); font-family: inherit; font-size: 0.78rem; }
.char-filter-text:focus { outline: none; border-color: var(--primary-color); }
.char-filter-num { flex: 0 0 62px; width: 62px; }
.char-filter-num-sep { flex: 0 0 auto; color: var(--text-muted); font-size: 0.75rem; }
.char-filter-appearance { flex: 0 0 100px; width: 100px; }
.char-filter-row .char-filter-chips { flex-wrap: nowrap; }
.char-filter-row .char-filter-chip { flex: 0 0 auto; padding: 3px 9px; }
.char-filter-del { flex: 0 0 auto; width: 28px; height: 28px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--border-color); border-radius: 6px; background: var(--bg-color); color: var(--text-muted); font-size: 0.8rem; line-height: 1; cursor: pointer; }
.char-filter-del:hover { border-color: var(--danger-color); color: var(--danger-color); }
/* 快捷筛选区：五组分面（阵营 / 叙事定位 / 状态 / 性别 / 种族）+ 成长标签 */
.char-quick { display: flex; flex-direction: column; gap: 14px; }
.char-quick-group { display: flex; flex-direction: column; gap: 6px; }
.char-quick-label { font-size: 0.72rem; color: var(--text-muted); font-weight: 600; }
.char-quick-search { width: 100%; height: 30px; box-sizing: border-box; padding: 0 8px; border: 1px solid var(--border-color); border-radius: 6px; background: var(--bg-color); color: var(--text-color); font-family: inherit; font-size: 0.78rem; }
.char-quick-search:focus { outline: none; border-color: var(--primary-color); }
.char-quick-more { align-self: flex-start; margin-top: 2px; padding: 2px 0; font-size: 0.72rem; color: var(--primary-color); background: transparent; border: none; cursor: pointer; }
.char-quick-more:hover { text-decoration: underline; }
.char-quick-empty { font-size: 0.72rem; color: var(--text-muted); }
/* 高级组合条件折叠区 */
.char-adv-details { border-top: 1px solid var(--border-color); padding-top: 10px; }
.char-adv-details > summary { cursor: pointer; list-style: none; font-size: 0.8rem; color: var(--text-color); user-select: none; padding: 4px 0; }
.char-adv-details > summary::-webkit-details-marker { display: none; }
.char-adv-details > summary::before { content: '▸ '; color: var(--text-muted); }
.char-adv-details[open] > summary { margin-bottom: 10px; }
.char-adv-details[open] > summary::before { content: '▾ '; }
.char-filter-add { margin-top: 10px; align-self: flex-start; font-size: 0.76rem; padding: 5px 12px; }
.character-list-empty { margin: 20px; padding: 40px 20px; text-align: center; background: var(--panel-bg); border: 1px dashed var(--border-color); border-radius: var(--radius-lg); }
.char-filter-empty-title { font-size: 1rem; font-weight: 600; color: var(--text-color); }
.char-filter-empty-hint { margin-top: 8px; font-size: 0.85rem; color: var(--text-muted); }
/* ========== 关系网搜索（光环高亮） ========== */
/* 高亮一律用 SVG <circle> 描边，禁止 filter: drop-shadow——移动端每次重绘都重算滤镜，卡到掉帧 */
.rel-search { position: relative; display: inline-flex; align-items: center; }
.rel-search > i { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); font-size: 0.8rem; color: var(--text-muted); pointer-events: none; }
/* 搜索框 34px 高、8px 圆角 */
.rel-search input { width: 200px; height: 34px; box-sizing: border-box; padding: 0 30px 0 32px; border-radius: 8px; border: 1px solid color-mix(in srgb, var(--border-color) 75%, transparent); background: color-mix(in srgb, var(--panel-bg) 55%, transparent); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); color: var(--text-color); font-size: 0.85rem; touch-action: manipulation; transition: background 0.18s, border-color 0.18s, box-shadow 0.18s; }
.rel-search:hover > i { color: var(--text-color); }
.rel-search input:hover { background: color-mix(in srgb, var(--panel-bg) 72%, transparent); }
.rel-search input:focus-visible { border-color: var(--primary-color); outline: none; background: var(--panel-bg); box-shadow: var(--input-focus-ring); }
/* ========== 关系网搜索高亮 ========== */
/* 高亮一律用 SVG <circle> 描边 + 一层 6px 轻 drop-shadow，禁止高斯模糊滤镜：
   移动端每次重绘都重算滤镜，卡到掉帧 */
.search-halo { pointer-events: none; transition: opacity 0.25s ease; }
@media (hover: hover) and (pointer: fine) {
  .search-halo.halo-glow { filter: drop-shadow(0 0 6px currentColor); }
}
/* 触屏拖节点时页面不跟着滚：d3.zoom 本身会置 touch-action:none，这里显式写死，不依赖库版本 */
#network-svg, #network-svg .node { touch-action: none; }
/* ========== 交互式图例 ========== */
.legend-wrap { position: relative; }
.legend-items { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: center; }
.legend-item { display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px; min-height: 34px; border: 1px solid transparent; border-radius: 999px; background: transparent; color: var(--text-color); font-family: inherit; font-size: 0.75rem; line-height: 1; cursor: pointer; user-select: none; -webkit-user-select: none; touch-action: manipulation; transition: background 0.15s, opacity 0.15s; }
.legend-item i { width: 12px; height: 12px; border-radius: 2px; flex-shrink: 0; }
.legend-item.muted { text-decoration: line-through; opacity: 0.4; }
.legend-item:active { background: color-mix(in srgb, var(--primary-color) 14%, transparent); }
/* 「更多」已迁入 .legend-actions 走 .legend-icon-btn 纯图标，不再用带边框的文字胶囊 */
/* 「更多」面板交给 PopoverManager：挂 body、position:fixed、上下防遮挡翻转，
   显示口径归开合本身（PM 只负责摘挂），所以这里不能有 display:none 门禁。
   唯一例外是壳里那份还没被挂载过的原件，它留在卡片里，得藏起来。 */
.legend-more-menu { position: fixed; display: flex; flex-direction: column; gap: 2px; min-width: 150px; max-height: 260px; overflow-y: auto; padding: 8px; background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); z-index: var(--z-popover); }
#relation-legend-card .legend-more-menu { display: none; }
.legend-more-menu .legend-item { width: 100%; justify-content: flex-start; }
/* 点按图例后淡出的关系线：只改 opacity，不动 stroke、不加滤镜 */
.link.rel-muted { opacity: 0.1; }
.badge.rel-muted { opacity: 0.12; }
/* 学术图谱模式的线上文字：药丸底色由 relations.js 写成 fill 属性（跟画布同色），这里只描个极淡的边 */
.link-label { transition: opacity 0.25s ease; }
.link-label-pill { stroke: color-mix(in srgb, var(--border-color) 45%, transparent); stroke-width: 1px; }
.link-label-text { font-family: 'Inter', var(--font-sans), sans-serif; }
/* 悬停加粗只给真鼠标设备；平板走点按，不触发 hover 态 */
@media (hover: hover) {
  .legend-item:hover { background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
  .link.rel-emph { stroke-opacity: 1; stroke-width: 3px; }
  .link.is-bundled.rel-emph { stroke-width: 6px; }
}
/* ========== 关系网：现代悬浮式布局 ========== */
/* 复合选择器（ID+class）压过 .view-container.active 的 display:block，
   未激活时仍走 display:none，否则关系网会在别的视图里常驻显示 */
.view-container#relation-container { display: none; flex-direction: column; height: 100%; overflow: hidden; }
.view-container#relation-container.active { display: flex; }
/* 只给关系视图关掉外层滚动条：思维导图/时间线仍靠 .canvas-area 滚动定位 */
.canvas-area.relation-mode { overflow: hidden; }
.rel-toolbar { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 20px; background: color-mix(in srgb, var(--panel-bg) 58%, transparent); backdrop-filter: blur(10px) saturate(1.15); -webkit-backdrop-filter: blur(10px) saturate(1.15); border-bottom: 1px solid color-mix(in srgb, var(--border-color) 65%, transparent); box-shadow: 0 6px 18px color-mix(in srgb, var(--text-color) 5%, transparent); position: relative; z-index: 6; }
/* 工具栏按钮：白/浅灰胶囊 + 毛玻璃。纯黑底只留给最外侧主按钮（+ 关系）。
   :not(.btn-primary) 必须保留，否则主按钮的纯黑实心会被半透明薄底盖掉。 */
.rel-toolbar .btn:not(.btn-primary) { background: var(--panel-bg); border-color: var(--border-color); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.rel-toolbar .btn:not(.btn-primary):hover { background: var(--hover-bg); color: var(--text-color); border-color: transparent; }
/* 图标统一 16px 且自身垂直居中：文字与图标混排时不会一行里高低不齐 */
.rel-toolbar .btn > i {
  flex: 0 0 auto; width: 16px; height: 16px; font-size: 16px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-muted); transition: color 0.16s;
}
.rel-toolbar .btn:not(.btn-primary):hover > i { color: var(--text-color); }
.rel-toolbar .btn-primary > i { color: var(--panel-bg); }
/* 开关类按钮的激活态：极淡底 + 主色文字，取代原先突兀的黑底 btn-primary */
.rel-toolbar .btn:not(.btn-primary).is-on { background: var(--hover-bg); color: var(--text-color); border-color: transparent; }
.rel-toolbar .btn:not(.btn-primary).is-on > i { color: var(--text-color); }
/* 纯图标按钮：常态透明无胶囊，只有悬浮 / 激活才浮出极淡底；Tooltip 走原生 title */
.rel-toolbar .btn.rel-icon-btn { width: 34px; height: 34px; padding: 0; box-sizing: border-box; justify-content: center; border-radius: var(--radius-btn); background: transparent; border-color: transparent; }
.rel-toolbar .btn.rel-icon-btn:hover { background: var(--hover-bg); color: var(--text-color); border-color: transparent; }
.rel-toolbar .btn.rel-icon-btn:hover > i { color: var(--text-color); }
[data-theme="dark"] .rel-toolbar .btn.rel-icon-btn:hover { background: var(--hover-bg); }
[data-theme="dark"] .rel-toolbar .btn:not(.btn-primary):hover,
[data-theme="dark"] .rel-toolbar .btn:not(.btn-primary).is-on { background: var(--hover-bg); color: var(--text-color); }
[data-theme="dark"] .rel-toolbar .btn:not(.btn-primary):hover > i,
[data-theme="dark"] .rel-toolbar .btn:not(.btn-primary).is-on > i { color: var(--text-color); }
.rel-toolbar-left { display: flex; align-items: center; gap: 14px; flex: 1 1 auto; min-width: 0; }
.rel-toolbar-left h3 { display: none; margin: 0; }
.rel-toolbar-center { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.rel-toolbar-right { display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex: 0 0 auto; }
.network-stage { position: relative; flex: 1 1 auto; min-height: 0; margin: 0 20px 20px; border-radius: 16px; overflow: hidden; background: var(--bg-color); border: 1px solid color-mix(in srgb, var(--border-color) 70%, transparent); box-shadow: var(--shadow-md); }
#d3-network-container { overflow: hidden; }
/* ===== 左侧滑出侧边栏 ===== */
.rel-drawer {
  position: absolute; top: 0; left: 0; z-index: 20;
  width: 320px; max-width: 88%;
  height: auto; max-height: 80vh; overflow-y: auto;
  display: flex; flex-direction: column;
  background: var(--panel-bg); border-radius: 0 14px 14px 0;
  box-shadow: 4px 0 32px rgba(0,0,0,0.10);
  transform: translateX(-100%); transition: transform .3s ease;
  pointer-events: none;
  scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--text-muted) 50%, transparent) transparent;
}
.rel-drawer::-webkit-scrollbar { width: 6px; }
.rel-drawer::-webkit-scrollbar-track { background: transparent; }
.rel-drawer::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--text-muted) 50%, transparent); border-radius: 3px; }
.rel-drawer.open { transform: translateX(0); pointer-events: auto; }
[data-theme="dark"] .rel-drawer { background: var(--panel-bg); box-shadow: 4px 0 28px rgba(0,0,0,.5); }
.rel-drawer-head {
  display: flex; align-items: center; justify-content: space-between;
  flex: 0 0 auto; padding: 16px 18px 12px 16px; border-bottom: 1px solid var(--border-color);
}
.rel-drawer-title { font-size: 1rem; font-weight: 700; color: var(--text-color); }
[data-theme="dark"] .rel-drawer-title { color: var(--text-color); }
.rel-drawer-close {
  width: 26px; height: 26px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  border: none; border-radius: 8px; background: transparent; color: var(--text-muted);
  font-size: 1.1rem; line-height: 1; cursor: pointer;
}
.rel-drawer-close:hover { background: var(--hover-bg); color: var(--text-color); }
.rel-drawer-body { flex: 0 1 auto; min-height: 0; padding: 0; }
/* 只有整栏一层滚动：网格结果不再叠第二层滚动条 */
.rel-drawer[data-mode="search"] .rel-drawer-body { display: flex; flex-direction: column; overflow: visible; padding: 0; }
/* 多选胶囊标签栏：单行铺满、不换行、不横向滚动——六个胶囊全部完整可见 */
.rel-cat-tabs {
  display: flex; flex-wrap: nowrap; gap: 4px; flex: 0 0 auto;
  padding: 12px 12px 6px; overflow: visible;
}
.rel-cat-pill {
  flex: 0 0 auto; border: none; border-radius: 14px; padding: 4px 7px;
  background: var(--hover-bg); color: var(--text-muted); font: inherit; font-size: 10px;
  line-height: 1.2; white-space: nowrap; cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.rel-cat-pill:hover { background: var(--border-strong); color: var(--text-color); }
.rel-cat-pill.is-active { background: var(--text-color); color: var(--panel-bg); }
[data-theme="dark"] .rel-cat-pill { background: var(--hover-bg); color: var(--text-color); }
[data-theme="dark"] .rel-cat-pill.is-active { background: var(--text-color); color: var(--panel-bg); }
/* 搜索结果：固定 3 列网格，滚动交给整栏 */
.rel-drawer-results {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 12px; padding: 12px;
  overflow: visible; flex: 1 1 auto; min-height: 0; align-content: start;
}
.rel-drawer-results::-webkit-scrollbar { width: 6px; }
.rel-drawer-results::-webkit-scrollbar-track { background: transparent; }
.rel-drawer-results::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--text-muted) 50%, transparent); border-radius: 3px; }
[data-theme="dark"] .rel-drawer-results { scrollbar-color: color-mix(in srgb, var(--text-muted) 50%, transparent) transparent; }
.rel-drawer-empty { grid-column: 1 / -1; padding: 28px 8px; text-align: center; font-size: 0.8rem; color: var(--text-placeholder); }
.rel-drawer-title .rel-drawer-count { font-size: 0.72rem; font-weight: 500; color: var(--text-placeholder); margin-left: 2px; }
.rel-card {
  display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0;
  padding: 8px 4px; border: none; border-radius: 8px; background: transparent;
  color: var(--text-color); font: inherit; text-align: center; cursor: pointer;
}
.rel-card:hover, .rel-card.is-active { background: var(--hover-bg); }
[data-theme="dark"] .rel-card:hover, [data-theme="dark"] .rel-card.is-active { background: var(--hover-bg); }
.rel-card-ava {
  width: 48px; height: 48px; flex: 0 0 auto; box-sizing: border-box;
  border-radius: 50%; border: 1px solid var(--border-strong); overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ol-ava-c, var(--text-muted)); color: var(--on-accent); font-size: 16px; line-height: 1;
  cursor: pointer; transition: transform .2s ease;
}
.rel-card-ava:hover { transform: scale(1.05); }
.rel-card-ava img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }
.rel-card-name { display: block; width: 100%; font-size: 12px; line-height: 1.3; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rel-card-meta { display: block; width: 100%; font-size: 10px; line-height: 1.3; color: var(--text-placeholder); text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Enter / 结果卡片命中的节点：搜索命中的光环同步加强 */
.node.search-focus .search-halo { stroke-width: 7; stroke-opacity: .6; }

/* 左下角毛玻璃悬浮图例卡：父级 .network-stage 已是 relative，不需要再挤占画布高度 */
.relation-legend { position: absolute; left: 20px; bottom: 20px; z-index: 10; display: flex; align-items: center; gap: 15px; max-width: calc(100% - 40px); padding: 10px 15px; border-radius: 12px; background: color-mix(in srgb, var(--panel-bg) 62%, transparent); backdrop-filter: blur(10px) saturate(1.15); -webkit-backdrop-filter: blur(10px) saturate(1.15); border: 1px solid color-mix(in srgb, var(--border-color) 55%, transparent); box-shadow: 0 4px 12px rgba(0,0,0,0.1); transition: opacity 0.2s; }
[data-theme="dark"] .relation-legend { box-shadow: 0 4px 12px rgba(0,0,0,0.45); }
.legend-main { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; min-width: 0; }
.relation-legend.collapsed .legend-main { display: none; }
.legend-collapse-btn { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; min-height: 34px; padding: 6px 12px; border: 1px solid var(--border-color); border-radius: 999px; background: transparent; color: var(--text-muted); font-family: inherit; font-size: 0.75rem; line-height: 1; cursor: pointer; user-select: none; -webkit-user-select: none; touch-action: manipulation; }
.legend-collapse-btn i { transition: transform 0.2s; }
.relation-legend.collapsed .legend-collapse-btn i { transform: rotate(180deg); }
/* 节点拖到卡片上方时卡片自动半透明，不挡操作：由 bindDragFade 加类，纯旁路不改 D3 拖拽 */
.relation-legend.legend-behind { opacity: 0.35; }
/* ========== 角色卡片：迷你关系缩略图（画在上，关系胶囊列表在下，鼠标联动） ========== */
/* 整块是字符串 SVG，不进 D3 也不进物理模拟；没有关系的角色不会输出这一块 */
/* 缩略图高度强制锁在 120~140px（绝不随宽度膨胀）；SVG 完全透明，融入卡片、无浅色背景块 */
.char-rel-mini { position: relative; width: 100%; height: 130px; background: transparent; padding: 2px 0 0; }
/* 卡片里「上图下标签」：缩略图独占整行，高度取规范上限 140px（≤150px） */
.cc-rel-mini-col .char-rel-mini { height: 140px; }
.char-rel-mini svg { display: block; width: 100%; height: 100%; overflow: visible; }
.char-rel-mini .rm-arm { transition: opacity 0.16s ease; }
.char-rel-mini .rm-arm line { stroke-width: 1.4px; stroke-linecap: round; opacity: 0.8; transition: stroke-width 0.16s ease, opacity 0.16s ease; }
.char-rel-mini .rm-dot { stroke: var(--panel-bg); stroke-width: 1px; transition: opacity 0.16s ease; }
.char-rel-mini .rm-self circle { stroke: var(--panel-bg); stroke-width: 1.5px; }
/* 名字用极小字号：不放大、不加粗，只负责"这一坨是谁" */
.char-rel-mini .rm-label { fill: var(--text-muted); font-family: var(--font-sans); pointer-events: none; transition: fill 0.16s ease; }
/* 圆心里的 Emoji/首字：不抢鼠标（hover 联动认的是外层 g.rm-arm），也不让拖选选中 */
.char-rel-mini .rm-node-text { font-family: var(--font-sans); pointer-events: none; user-select: none; }
/* 联动高亮：hover 下方某条关系胶囊时，上方对应的那条线加粗变亮，其余压到近透明 */
.char-rel-mini.is-focused .rm-arm:not(.is-hi) { opacity: 0.22; }
.char-rel-mini.is-focused .rm-self { opacity: 0.5; }
.rm-arm.is-hi line { stroke-width: 2.6px; opacity: 1; }
.rm-arm.is-hi .rm-dot { stroke-width: 1.6px; }
.rm-arm.is-hi .rm-label { fill: var(--text-color); font-weight: 600; }
[data-theme="dark"] .char-rel-mini .rm-dot,
[data-theme="dark"] .char-rel-mini .rm-self circle { stroke: var(--text-muted); }
/* 详情页的关系缩略图：摆在胶囊列表上方、居中，透明底（去掉那块难看的浅色方块）。
   严格限宽限高（max-width 320 / max-height 180），绝不让关系图占满整屏。 */
.char-rel-mini-wrap { flex: 0 0 100%; display: flex; justify-content: center; align-items: center; max-width: 320px; max-height: 150px; margin: 0 auto 8px; padding: 2px 0; overflow: hidden; background: transparent; }
.char-rel-mini-wrap .char-rel-mini { width: 100%; max-width: 320px; height: 140px; }
.vd-mod-body .char-rel-mini-wrap { background: transparent; }
/* 联动反过来那半边：鼠标停在缩略图的某个点上，下方属于这个人的胶囊跟着亮。 */
.char-relation-capsule.is-hi { border-color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 12%, var(--bg-color)); }
.vd-mod-body .char-relation-capsule.is-hi { background: color-mix(in srgb, var(--vd-gold) 24%, transparent); }
[data-theme="dark"] .rel-toolbar { box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35); }
[data-theme="dark"] .network-stage { border-color: var(--border-strong); }
/* 轻量弹窗添加关系 */
.modal-quick-relation { width: 460px; }
.quick-rel-row { display: flex; align-items: flex-end; gap: 10px; }
.quick-rel-row .form-group { flex: 1 1 0; min-width: 0; margin-bottom: 0; }
.quick-rel-arrow { flex: 0 0 auto; padding-bottom: 11px; color: var(--text-muted); font-size: 0.85rem; }
@media (max-width: 520px) {
  .quick-rel-row { flex-direction: column; align-items: stretch; gap: 12px; }
  .quick-rel-row .form-group { margin-bottom: 0; }
  .quick-rel-arrow { display: none; }
}
/* 触屏热区放大：手指比鼠标粗，34px 的按钮在平板上难点准 */
@media (pointer: coarse) {
  .legend-item { min-height: 40px; padding: 10px 14px; }
  .legend-collapse-btn { min-height: 40px; }
  .legend-actions .legend-icon-btn { width: 34px; height: 34px; }
  .rel-toolbar-center .btn, .rel-toolbar-right .btn { min-height: 40px; }
}
/* ============================================================
   节点属性侧栏（双击节点滑出，取代原来的居中弹窗）
   层叠与布局契约：
   · .canvas-overlay 是 #canvas-area 的绝对定位子元素，inset:0 只覆盖画布本身，
     左右两栏是它的兄弟节点，不参与这条排版链 —— 面板再宽也不会挤压大纲/备忘录。
   · overlay 自身 pointer-events:none，只有 .mm-inspector.open 才吃事件，
     收起状态下画布空白处的框选、滚轮缩放完全不受影响。
   · overflow:hidden + 面板 translateX 出界（而不是 display:none）：
     收起时不产生横向滚动条，展开/收起还能走 transform 过渡。
   ============================================================ */
.canvas-overlay { position: absolute; inset: 0; z-index: 20; overflow: hidden; pointer-events: none; }
.mm-inspector { position: absolute; top: 14px; right: 14px; bottom: 14px; width: 320px; max-width: calc(100% - 28px); max-height: calc(100% - 28px);
  display: flex; flex-direction: column; box-sizing: border-box; background: var(--panel-bg);
  border: 1px solid var(--border-color); border-radius: var(--radius-lg); box-shadow: var(--shadow-md);
  color: var(--text-color); font-size: 0.85rem; overscroll-behavior: contain;
  transform: translateX(calc(100% + 24px)); opacity: 0; visibility: hidden; pointer-events: none;
  transition: transform 0.22s ease-out, opacity 0.18s ease-out, visibility 0s linear 0.22s; }
.mm-inspector.open { transform: none; opacity: 1; visibility: visible; pointer-events: auto;
  transition: transform 0.22s ease-out, opacity 0.18s ease-out, visibility 0s; }
.mm-inspector-header { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 12px 14px; border-bottom: 1px solid var(--border-color); font-weight: 600; font-size: 0.9rem; }
.mm-inspector-header i { margin-right: 6px; color: var(--primary-color); }
.mm-inspector-body { flex: 1 1 auto; min-height: 0; max-height: 100%; overflow-y: auto; overscroll-behavior: contain; padding: 14px; }
/* 节点 / 连线 / 批量从头到尾共用同一块 body：内部两节按模式切换，
   未激活那节用 hidden 收掉，不重建 DOM、不影响外壳位置与尺寸 */
#inspector-node-fields[hidden],
#inspector-connection-section[hidden] { display: none; }
/* 连线设置是本节第一行，小节顶部那条虚线就不再画 */
#inspector-connection-section > .mm-inspector-conn-sect { margin-top: 0; padding-top: 0; border-top: 0; }
/* header 上的模式徽标：切到连线时也一眼看得出仍是同一个「节点属性」面板 */
.mm-inspector-mode-chip { display: inline-block; margin-left: 8px; padding: 2px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--primary-color) 14%, transparent); color: var(--primary-color);
  font-size: 0.68rem; font-weight: 600; vertical-align: middle; }
/* 连线模式只有 6 项设置：高度随内容收起，不再独占画布右侧一整列 */
#mm-node-inspector[data-mode="edge"] { bottom: auto; }
#mm-node-inspector[data-mode="edge"] .mm-inspector-body { flex: 0 1 auto; }
/* 批量编辑：只藏单节点专属的文本输入 / 图片 / 删除，样式项（字号/对齐/形状/颜色/边框/分支）照常开放 */
#mm-node-inspector[data-mode="batch"] [data-batch-hide] { display: none !important; }
.mm-inspector-batch-hint { margin: 0 0 12px; padding: 8px 10px; border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--primary-color) 12%, transparent); color: var(--primary-color);
  font-size: 0.72rem; line-height: 1.5; }
.mm-inspector-batch-hint[hidden] { display: none; }
.mm-inspector-body .form-group { margin-bottom: 12px; }
.mm-inspector-body label { display: block; margin-bottom: 5px; font-size: 0.75rem; color: var(--text-muted); }
.mm-inspector-body textarea { width: 100%; box-sizing: border-box; font-family: inherit; font-size: 0.85rem; line-height: 1.5;
  padding: 8px 10px; border: 1px solid var(--border-color); border-radius: var(--radius-md);
  background: var(--bg-color); color: var(--text-color); resize: vertical; overscroll-behavior: contain; }
.mm-inspector-body textarea:focus-visible { outline: none; border-color: var(--primary-color); }
.mm-inspector-sect { display: flex; align-items: center; gap: 6px; margin: 16px 0 8px; padding-top: 10px;
  border-top: 1px dashed var(--border-color); font-size: 0.72rem; letter-spacing: 0.04em; color: var(--text-muted); }
.mm-inspector-row { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.mm-inspector-row > span, .mm-inspector-row > label { flex: 0 0 44px; font-size: 0.75rem; color: var(--text-muted); }
.mm-inspector-row input[type="range"] { flex: 1 1 auto; min-width: 0; accent-color: var(--primary-color); }
.mm-inspector-row b { flex: 0 0 auto; min-width: 22px; text-align: right; font-size: 0.75rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* 分段控件：对齐/加粗这类"就三四个选项"的开关，比下拉少一次点击 */
.mm-seg { display: inline-flex; gap: 4px; }
.mm-seg-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 30px;
  border: 1px solid var(--border-color); border-radius: var(--radius-md); background: transparent;
  color: var(--text-muted); font-family: inherit; cursor: pointer; transition: background 0.15s, color 0.15s, border-color 0.15s; }
.mm-seg-btn:hover { border-color: var(--primary-color); color: var(--primary-color); }
.mm-seg-btn.active { background: var(--primary-color); border-color: var(--primary-color); color: var(--panel-bg); }
/* 分段按钮里的内联 SVG（线型/虚实预览）：描边跟随 currentColor，图标本身不吃点击 */
.mm-seg-btn svg { display: block; pointer-events: none; }
#inspector-image-preview { margin-bottom: 10px; padding: 6px; border: 1px solid var(--border-color); border-radius: var(--radius-md); background: var(--bg-color); text-align: center; }
#inspector-image-preview img { max-width: 100%; max-height: 140px; border-radius: var(--radius-sm); cursor: zoom-in; }
.mm-inspector-btns { display: flex; gap: 8px; }
.mm-inspector-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border-color); }
.mm-inspector-hint { font-size: 0.7rem; color: var(--text-muted); text-align: right; }
/* 窄屏（画布本身不到 360px）时面板贴满右侧，不留一条缝 */
@media (max-width: 768px) { .mm-inspector { top: 8px; right: 8px; bottom: 8px; width: calc(100% - 16px); } }

/* ============================================================
   图形化控件（取代原生 select / color input）
   三件套：.mm-pop-trigger 与 .mm-swatch-btn 是面板上的"入口按钮"，
   .mm-pop 是挂在 body 上的弹出层（形状网格 / 色块矩阵 / 线型预览共用一套皮）。
   弹出层必须脱开 .canvas-overlay 的 overflow:hidden，所以只住 body、用 fixed 定位。
   ============================================================ */
/* 面板入口：形状 / 边框 / 分支线型 */
.mm-pop-trigger { flex: 1 1 auto; min-width: 0; display: inline-flex; align-items: center; gap: 8px; height: 32px;
  padding: 0 10px; border: 1px solid var(--border-color); border-radius: var(--radius-md);
  background: var(--bg-color); color: var(--text-color); font-family: inherit; font-size: 0.8rem; cursor: pointer;
  transition: border-color 0.15s, background 0.15s; }
.mm-pop-trigger:hover { border-color: var(--primary-color); }
.mm-pop-trigger svg { display: block; flex: 0 0 auto; }
.mm-pop-trigger span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 色块入口：--mm-btn-fill 由 JS 写当前生效色；没自定过（is-default）用虚线框表示"跟随" */
.mm-swatch-btn { flex: 0 0 auto; width: 48px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border-color); border-radius: var(--radius-md); cursor: pointer;
  background: var(--mm-btn-fill, var(--bg-color)); color: var(--text-muted); font-family: inherit; font-size: 0.72rem;
  transition: transform 0.12s, box-shadow 0.12s, border-color 0.15s; }
.mm-swatch-btn i { display: none; }
.mm-swatch-btn:hover { transform: scale(1.05); box-shadow: var(--mm-node-ring); }
.mm-swatch-btn.is-default { opacity: 0.6; border-style: dashed; }
/* 无填充：棋盘格底纹，一眼看出"这里没有色" */
.mm-swatch-btn[data-value="transparent"] {
  background: repeating-conic-gradient(var(--border-color) 0% 25%, var(--panel-bg) 0% 50%) 0 / 10px 10px; }
/* 文字作用域提示 + 分区副标题 */
.mm-scope-chip { margin-left: auto; padding: 2px 8px; border-radius: 999px; font-size: 0.68rem; letter-spacing: 0;
  background: color-mix(in srgb, var(--primary-color) 12%, transparent); color: var(--primary-color); }
.mm-sect-hint { margin-left: auto; font-size: 0.66rem; font-weight: 400; letter-spacing: 0; color: var(--text-muted); }
/* 一行里挤多个入口时（边框：样式/颜色/粗细）不再平分宽度，按内容收一收 */
.mm-pop-trigger.compact { flex: 0 0 auto; gap: 5px; padding: 0 8px; }
.mm-swatch-btn.small { width: 30px; }

/* 外观与形状：手风琴分组（形状网格 + 填充 + 边框三件套收在一起，平时可折叠腾出空间） */
.mm-acc { margin: 16px 0 8px; padding-top: 10px; border-top: 1px dashed var(--border-color); }
.mm-acc-head { display: flex; align-items: center; gap: 6px; width: 100%; padding: 0 0 8px; border: none;
  background: none; color: var(--text-muted); font-family: inherit; font-size: 0.72rem; letter-spacing: 0.04em; cursor: pointer; }
.mm-acc-head i.fa-shapes { color: var(--primary-color); }
.mm-acc-caret { transition: transform 0.18s ease-out; font-size: 0.62rem; }
.mm-acc.open .mm-acc-caret { transform: rotate(90deg); }
.mm-acc-body { display: none; }
.mm-acc.open .mm-acc-body { display: block; animation: drop-fade-in 0.2s ease-out; }
/* 形状网格：一格一个 SVG 线稿，选中格高亮；新增形状不用改 HTML（JS 按 MM_SHAPES 生成） */
.mm-shape-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; margin-bottom: 10px; }
.mm-shape-cell { display: flex; align-items: center; justify-content: center; height: 34px; padding: 0;
  border: 1px solid var(--border-color); border-radius: var(--radius-md); background: transparent;
  color: var(--text-muted); cursor: pointer; transition: border-color 0.15s, color 0.15s, background 0.15s; }
.mm-shape-cell svg { display: block; pointer-events: none; }
.mm-shape-cell:hover { border-color: var(--primary-color); color: var(--primary-color); }
.mm-shape-cell.current { border-color: var(--primary-color); color: var(--primary-color);
  background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
/* 排版 / 模板的结构缩略图网格：线条画的小结构图比"树形图"三个字更直观 */
.mm-thumb-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.mm-thumb-grid.wide { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.mm-thumb { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px 6px;
  border: 1px solid var(--border-color); border-radius: var(--radius-md); background: transparent;
  color: var(--text-muted); font-family: inherit; font-size: 0.7rem; cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s; }
.mm-thumb:hover { border-color: var(--primary-color); color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 6%, transparent); }
.mm-thumb.current { border-color: var(--primary-color); color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
.mm-thumb svg { display: block; pointer-events: none; color: currentColor; }
.mm-thumb-name { pointer-events: none; white-space: nowrap; }

/* 弹出层本体：挂载/摘除由 PopoverManager 负责——在 DOM 里就是开着，所以这里不再用 .open 开关可见性，
   只画皮肤和入场动画。z-index 4000 高于节点操作菜单(3000)与迷你面板(3000)，低于模态框遮罩之外的原生取色面板 */
.mm-pop { position: fixed; z-index: var(--z-popover); min-width: 236px; max-width: calc(100vw - 16px);
  max-height: calc(100vh - 16px); overflow-y: auto;
  padding: 10px; background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: 6px;
  box-shadow: var(--shadow-popover);
  color: var(--text-color); font-size: 0.8rem; transform-origin: right center;
  animation: layer-pop-in var(--dur-fast) var(--ease-standard); }
/* 岛台的弹层朝下开（锚点在顶部正中），出现动画也要跟着换个起点，否则像从右边飘过来 */
.mm-pop.below { transform-origin: top center; }

.mm-pop-caption { margin: -2px 0 8px; font-size: 0.68rem; line-height: 1.4; color: var(--text-muted); }
/* 色块矩阵：一族一行，行首写族名；点选即生效，不需要确认按钮 */
.mm-pop-color-grid { display: flex; flex-direction: column; gap: 7px; }
.mm-pop-chip { width: 100%; padding: 4px 8px; border: 1px dashed var(--border-color); border-radius: var(--radius-md);
  background: transparent; color: var(--text-muted); font-family: inherit; font-size: 0.72rem; cursor: pointer;
  text-align: center; transition: border-color 0.15s, color 0.15s, background 0.15s; }
.mm-pop-chip:hover { border-color: var(--primary-color); color: var(--primary-color); background: color-mix(in srgb, var(--primary-color) 8%, transparent); }
.mm-pop-fam { display: flex; align-items: center; gap: 8px; }
.mm-pop-fam-name { flex: 0 0 66px; font-size: 0.68rem; color: var(--text-muted); }
.mm-pop-fam-row { display: flex; gap: 4px; flex: 1 1 auto; }
.mm-pop-swatch { flex: 0 0 auto; width: 22px; height: 22px; padding: 0; border: 1px solid rgba(0, 0, 0, 0.14);
  border-radius: 4px; cursor: pointer; transition: transform 0.12s, box-shadow 0.12s; }
.mm-pop-swatch:hover { transform: scale(1.18); }
/* 当前值描边：眼睛先看到"已经选了这个"，不用再猜 */
.mm-pop-swatch.current, .mm-pop-opt.current { box-shadow: 0 0 0 2px var(--panel-bg), 0 0 0 4px var(--primary-color); }
/* 自定义取色入口：彩虹圆 + 文字 + 加号。
   文字用 --text-color、底用 --input-sunken、圆点描边用 --text-color + --panel-bg 外环，
   选中外圈用 primary/text-color 的 color-mix 中间值：亮暗主题都不依赖单一主色亮度。 */
.mm-pop-custom {
  display: flex; align-items: center; gap: 8px; width: 100%; box-sizing: border-box;
  padding: 5px 8px; border: 1px dashed var(--border-color); border-radius: var(--radius-md);
  background: var(--input-sunken); color: var(--text-color);
  font-family: inherit; font-size: 0.72rem; cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.mm-pop-custom:hover { border-color: var(--primary-color); color: var(--primary-color); }
.mm-pop-custom:focus-visible { outline: none; box-shadow: var(--input-focus-ring); }
.mm-pop-custom.current {
  border-style: solid; border-color: var(--primary-color);
  box-shadow: 0 0 0 2px var(--panel-bg), 0 0 0 4px color-mix(in srgb, var(--primary-color) 65%, var(--text-color));
}
.mm-pop-custom-swatch {
  flex: 0 0 16px; width: 16px; height: 16px; border-radius: 50%;
  background: conic-gradient(#f87171, #fbbf24, #4ade80, #60a5fa, #c084fc, #f87171);
  border: 1px solid var(--text-color); box-shadow: 0 0 0 1px var(--panel-bg);
}
.mm-pop-custom-text { flex: 1 1 auto; text-align: left; }
.mm-pop-custom-plus { flex: 0 0 auto; font-size: 0.9rem; font-weight: 700; line-height: 1; color: var(--text-color); }
/* 原生取色器载体：必须留在 DOM 里 .click() 才能弹系统面板，但不能占布局也不能被看见。
   用 opacity:0 + 1px + absolute 而非 display:none（部分浏览器对隐藏 input 不弹面板）。 */
.mm-pop-native {
  position: absolute; left: 0; top: 0; width: 1px; height: 1px;
  padding: 0; border: 0; opacity: 0; pointer-events: none;
}
/* 选项网格：形状（方格图标）与线型/虚实（线条预览）共用 */
.mm-pop-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.mm-pop-grid.wide { min-width: 216px; }
.mm-pop-opt { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
  min-height: 52px; padding: 7px 4px; border: 1px solid var(--border-color); border-radius: var(--radius-md);
  background: transparent; color: var(--text-muted); font-family: inherit; font-size: 0.7rem; cursor: pointer;
  transition: border-color 0.15s, background 0.15s, color 0.15s; }
.mm-pop-opt:hover { border-color: var(--primary-color); color: var(--primary-color); }
.mm-pop-opt.current { border-color: var(--primary-color); color: var(--primary-color);
  background: color-mix(in srgb, var(--primary-color) 10%, transparent); }
/* 当前形状用不了的选项（菱形/六边形只有实线）：置灰但仍可点，点下去给一句解释，
   比"消失"或"点了没反应"都好——用户要知道为什么少了一档，而不是以为自己点歪了 */
.mm-pop-opt.is-disabled { opacity: 0.42; }
.mm-pop-opt.is-disabled:hover { border-color: var(--border-color); color: var(--text-muted); }
.mm-thumb.is-disabled { opacity: 0.42; }
/* 极窄屏：弹层不许顶破视口，缩略图从 3 列降到 2 列（宁可长一点也不要横向溢出） */
@media (max-width: 400px) {
  .mm-pop { min-width: 0; }
  .mm-thumb-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.mm-pop-opt-icon, .mm-pop-opt-icon svg { display: block; pointer-events: none; }
.mm-pop-opt-name, .mm-pop-opt-label { pointer-events: none; white-space: nowrap; }

/* ============================================================
   节点排版覆盖：全部由 applyNodeStyle 写进 .mm-node 内联的 --mm-tfs / --mm-cfs
   这类自定义属性驱动（见文件前半部分的 .mm-node-header / .mm-node-body）。
   这里不再有任何 .mm-fs-custom 之类的补丁规则——内联自定义属性天然压过类规则，
   所以标题改字号不会影响正文，正文也不会被标题的字号带着走。
   ============================================================ */

/* ============================================================
   大纲·角色头像堆叠 + 关联浮层（实体引用第一轮：角色 ↔ 场景）
   头像 16px 不撑行高；浮层与色板同定位方式，空段连 DOM 都不输出
   ============================================================ */
.ol-cast { display: inline-flex; align-items: center; flex: 0 0 auto; margin-left: 2px; }
.ol-cast-ava { width: 16px; height: 16px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; overflow: hidden; background: var(--ol-ava-c, var(--text-muted)); border: 1.5px solid var(--panel-bg); margin-left: -4px; cursor: pointer; font-size: 10px; line-height: 1; color: var(--on-accent); flex: 0 0 auto; }
.ol-cast-ava:first-child { margin-left: 0; }
.ol-cast-ava img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ol-cast-ava:hover { position: relative; z-index: 2; transform: translateY(-1px); }
/* 摆不下的角色收成一颗 "+N"：和头像同尺寸、同描边、同叠压，只把底色换成一层极浅的灰。
   它是入口不是第二排控件——点它开的就是右侧那张 … 关联浮层，完整名单和增删都在里面。 */
.ol-cast-more { flex: 0 0 auto; width: 16px; height: 16px; box-sizing: border-box; padding: 0 1px;
  display: inline-flex; align-items: center; justify-content: center; border: 1.5px solid var(--panel-bg); border-radius: 50%;
  margin-left: -4px; background: color-mix(in srgb, var(--text-muted) 14%, var(--panel-bg));
  color: var(--text-color); font-family: inherit; font-size: 9px; line-height: 1; letter-spacing: -.02em;
  cursor: pointer; opacity: .78; transition: opacity .15s ease, transform .15s ease; }
.ol-cast-more:hover { opacity: 1; position: relative; z-index: 2; transform: translateY(-1px); }
.ol-cast-more:focus-visible { opacity: 1; outline: 1px solid var(--primary-color); outline-offset: 1px; }
/* 场景行的三格"信息"（伏笔灯泡 / 标签胶囊 / 时间线微标签）平时收起，鼠标进到这一行才现身：
   非悬停态只剩 字数 / 头像堆叠（含 +N）/ 画布徽标。
   用 opacity 不用 display：格子一直占位，划过时标题和字数不会左右跳。
   键盘 Tab 走到胶囊上时（:focus-within 只认格子里的焦点，标题输入框不算）跟着显形，不留隐形的可聚焦控件。 */
.ol-slot[data-slot="fs"], .ol-slot[data-slot="tl"], .scene-header .ol-tags { opacity: 0; pointer-events: none; transition: opacity .18s ease; }
.scene-header:hover > .ol-slot[data-slot="fs"], .scene-header:hover > .ol-slot[data-slot="tl"], .scene-header:hover > .ol-tags,
.ol-slot[data-slot="fs"]:focus-within, .ol-slot[data-slot="tl"]:focus-within, .scene-header .ol-tags:focus-within { opacity: 1; pointer-events: auto; }
/* 场景行右侧的五个格子（伏笔标记 / 标签 / 时间线微标签 / 画布徽标 / 头像堆叠）：
   格子常驻 DOM 才能力求"关联数据改完只重画这一格"，空着就整颗收掉，不留空坑。 */
.ol-slot { display: inline-flex; align-items: center; }
.ol-slot:empty { display: none; }

/* 关联浮层：挂载/定位/关闭由 PopoverManager 统一接管（body 上 fixed，右缘贴头像堆叠，贴底上翻） */
/* 浮层本体：整张面板最高 360px，多出来的部分自己滚——滚动发生在浮层内部，
   PopoverManager 不会把它当成"用户滚走了"；滚动条收成 4px 的一道灰痕，原生那根粗杠子不该出现在这种小面板上。
   .is-tabbed（大纲 … 面板）：顶部一排 Tab + 一块内容区，Tab 常驻、只有内容区滚动；
   没加这个类的（状态下拉、时间线选角色）沿用整张面板自己滚，皮肤完全一样。 */
.ol-links-pop { z-index: 900; background: var(--panel-bg); border: 1px solid color-mix(in srgb, var(--border-color) 60%, transparent); border-radius: 10px; box-shadow: var(--shadow-md); padding: 6px; display: flex; flex-direction: column; gap: 0; max-height: min(360px, calc(100vh - 28px)); overflow-y: auto; scrollbar-width: thin; scrollbar-color: rgba(0, 0, 0, .12) transparent; }
.ol-links-pop::-webkit-scrollbar { width: 4px; }
.ol-links-pop::-webkit-scrollbar-track { background: transparent; }
.ol-links-pop::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, .12); border-radius: 4px; }
/* ---- Tab 栏：一颗 Tab = 一类，选中只靠"文字加深 + 底部一条 2px 线"，不铺底色 ----
   铺底色是控件的脸，这里要的是目录的脸：五类并排，一眼挑一类，内容区一次只说一件事。 */
.ol-links-pop.is-tabbed { padding: 0; overflow: hidden; }
.ol-lp-tabs { flex: 0 0 auto; display: flex; align-items: stretch; gap: 1px; padding: 0 4px; border-bottom: 1px solid color-mix(in srgb, var(--border-color) 45%, transparent); }
.ol-lp-tab { position: relative; display: inline-flex; align-items: center; gap: 3px; padding: 6px 5px 7px; border: none; background: none; cursor: pointer; font-family: inherit; font-size: 11px; line-height: 14px; white-space: nowrap; color: var(--text-muted); opacity: .72; transition: color .15s ease, opacity .15s ease; }
.ol-lp-tab:hover { color: var(--text-color); opacity: 1; }
.ol-lp-tab.on { color: var(--text-color); opacity: 1; }
/* 指示线正好压在 Tab 栏那条 hairline 上（bottom:-1px）：同一个位置两条线重叠，
   才看不出"页签的线"和"分区的线"各画各的。颜色跟文字一起走，不引入第三种颜色。 */
.ol-lp-tab::after { content: ''; position: absolute; left: 3px; right: 3px; bottom: -1px; height: 2px; border-radius: 2px; background: transparent; transition: background .15s ease; }
.ol-lp-tab.on::after { background: var(--text-color); }
/* 数字是名字后面的一颗裸数字，0 个时整颗不输出：Tab 要像目录，不像徽标墙。 */
.ol-lp-tab-n { flex: 0 0 auto; font-size: 10px; opacity: .65; }
/* ---- 内容区：一次只画当前这一类，滚动只发生在这里 ---- */
.ol-lp-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 6px 6px 4px; display: flex; flex-direction: column; scrollbar-width: thin; scrollbar-color: rgba(0, 0, 0, .12) transparent; }
.ol-lp-body::-webkit-scrollbar { width: 4px; }
.ol-lp-body::-webkit-scrollbar-track { background: transparent; }
.ol-lp-body::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, .12); border-radius: 4px; }
/* 内容区自己就是那道滚，列表不再各带一道：两道滚动条套在一起是最难伺候的手感。 */
.ol-links-pop.is-tabbed .ol-lp-list { max-height: none; }
/* 内容区已经留了 6px 内边距，里面的零件贴着边排就行，不再各自加一层缩进。 */
.ol-links-pop.is-tabbed .ol-lp-searchbox { margin-left: 0; margin-right: 0; }
.ol-links-pop.is-tabbed .ol-lp-hint { padding-left: 0; padding-right: 0; }
/* 空态：这一类一个都没有时给一句极浅的话，说清"现在没有"和"从哪里来"，不占版面也不撑框。 */
.ol-lp-empty { font-size: 11px; color: var(--text-muted); opacity: .62; line-height: 1.6; padding: 2px 2px 6px; }
.ol-lp-empty[hidden] { display: none; }
.ol-lp-sec + .ol-lp-sec { margin-top: 16px; }
/* 段头：极小极浅的一颗标题，右边一道细线延伸到面板右缘——分区靠这条线，不靠框。
   （… 面板改成一排 Tab 以后不再需要段头，线交给 Tab 栏自己画；这里留给状态下拉与时间线选角色。） */
.ol-lp-sec-head { font-size: 10px; letter-spacing: .05em; color: var(--text-muted); padding: 0 0 3px 2px; margin-right: -6px; padding-right: 6px; display: flex; align-items: center; gap: 5px; }
.ol-lp-sec-head::after { content: ''; flex: 1 1 auto; min-width: 10px; border-top: 1px solid color-mix(in srgb, var(--border-color) 45%, transparent); }
/* 计数是标题旁边的一个裸数字，不再套胶囊：胶囊是"控件"的脸，这里要的是排版。 */
.ol-lp-count { font-size: 10px; color: var(--text-muted); opacity: .8; }
/* 搜索框：与大纲顶部那只同一口径（无边框、极浅底色、左侧一枚小放大镜）。 */
.ol-lp-searchbox { display: flex; align-items: center; gap: 5px; margin: 0 6px 4px; padding: 3px 7px; border: none; border-radius: 6px; background: color-mix(in srgb, var(--text-muted) 7%, transparent); transition: background .15s ease; }
.ol-lp-searchbox:focus-within { background: color-mix(in srgb, var(--text-muted) 12%, transparent); }
.ol-lp-search-icon { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--text-muted); opacity: .6; }
.ol-lp-search { flex: 1 1 auto; min-width: 0; padding: 0; border: none; outline: none; background: none; box-shadow: none; color: var(--text-color); font-family: inherit; font-size: 11px; line-height: 16px; }
.ol-lp-search::placeholder { color: var(--text-muted); opacity: .55; }
.ol-lp-list { max-height: 168px; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; scrollbar-width: thin; scrollbar-color: rgba(0, 0, 0, .12) transparent; }
.ol-lp-list::-webkit-scrollbar { width: 4px; }
.ol-lp-list::-webkit-scrollbar-track { background: transparent; }
.ol-lp-list::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, .12); border-radius: 4px; }
.ol-lp-row { display: flex; align-items: center; gap: 8px; width: 100%; padding: 4px 8px; border: none; background: transparent; border-radius: 6px; cursor: pointer; text-align: left; font-size: 12px; color: inherit; min-height: 26px; }
.ol-lp-row:hover { background: color-mix(in srgb, var(--text-muted) 10%, transparent); }
.ol-lp-ava { width: 16px; height: 16px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; overflow: hidden; background: var(--ol-ava-c, var(--text-muted)); color: var(--on-accent); font-size: 10px; flex: 0 0 auto; }
.ol-lp-ava img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 名字吃剩下的宽度并省略号截断；点位/图标/操作一律 flex-shrink:0 → 50 个字的长名字也挤不扁它们。 */
.ol-lp-name { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-muted); transition: color .14s ease; }
.ol-lp-sub { flex: 0 0 auto; font-size: 10px; color: var(--text-muted); max-width: 96px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 唯一的状态指示器：行左端一枚 7px 小圆点。空心=还没挂上，实心绿=已挂/就是当前项，
   文字跟着浅灰↔深色。角色、伏笔、时间线、导图、状态、时间线角色选择全部从这一颗读起，不再各写一套勾叉。
   命中区域仍是 16×24（比点本身大一圈），手指和小屏幕都点得到；<span> 版是给"整行本身就是选项"的列表。 */
.ol-lp-mark { flex: 0 0 auto; width: 16px; height: 24px; display: inline-flex; align-items: center; justify-content: center; padding: 0; border: none; background: none; }
button.ol-lp-mark { cursor: pointer; }
.ol-lp-mark::after { content: ''; width: 7px; height: 7px; box-sizing: border-box; border-radius: 50%; border: 1px solid color-mix(in srgb, var(--text-muted) 60%, transparent); background: transparent; transition: background .14s ease, border-color .14s ease; }
button.ol-lp-mark:hover::after { border-color: var(--success-color); }
.ol-lp-mark.on::after { background: var(--success-color); border-color: var(--success-color); }
.ol-lp-split.on .ol-lp-name, .ol-lp-row.on .ol-lp-name { color: var(--text-color); }
.ol-lp-row:hover .ol-lp-name, .ol-lp-split:hover .ol-lp-name { color: var(--text-color); }
/* 事件/节点的身份色：一条 2px 的细轨，只表"属于哪条线/哪种节点"，不承担选中状态。 */
.ol-lp-rail { flex: 0 0 auto; width: 2px; height: 12px; border-radius: 1px; }
.ol-lp-hint { font-size: 11px; color: var(--text-muted); padding: 4px 8px 6px; }
.ol-lp-arrow { flex: 0 0 auto; color: var(--text-muted); font-size: 10px; }
/* 浮层里的标签段：胶囊复用行内那套灰调，× 常驻可点（浮层里没有"悬停才发现"的余裕） */
.ol-lp-tags { display: flex; flex-wrap: wrap; gap: 4px; row-gap: 2px; padding: 2px 0 6px; }
.ol-lp-tags .ol-tag { font-size: 10px; line-height: 16px; }
.ol-lp-tags .ol-tag-x { opacity: .5; pointer-events: auto; }
/* 添加标签是一颗文字入口，不是一颗胶囊：它只是"点这里往下填"，不该长得像表单控件。 */
.ol-lp-tag-add { flex: 0 0 auto; padding: 1px 5px; border: none; background: none; border-radius: 5px; color: var(--text-muted); font-family: inherit; font-size: 10px; line-height: 16px; cursor: pointer; opacity: .7; transition: color .15s, opacity .15s, background .15s; }
.ol-lp-tag-add:hover { color: var(--text-color); opacity: 1; background: color-mix(in srgb, var(--text-muted) 8%, transparent); }
.ol-lp-tag-input { flex: 0 0 auto; width: 108px; padding: 1px 6px; border: none; border-radius: 5px; background: color-mix(in srgb, var(--text-muted) 10%, transparent); outline: none; color: var(--text-color); font-family: inherit; font-size: 10px; line-height: 16px; }
.ol-lp-tag-input::placeholder { color: var(--text-muted); opacity: .5; }
/* 浮层里"一行两件事"：左端那颗点负责挂/摘，中间（名字）负责去那里看。分成两个热区，
   用户不用猜自己这一下究竟会不会改到数据（旧版整行=取消关联，想查档案只能先勾回来）。
   点位与身份色条一律 flex-shrink:0，长名字只在自己的格子里省略。 */
.ol-lp-split { display: flex; align-items: center; gap: 2px; }
.ol-lp-row-main { flex: 1 1 auto; min-width: 0; }
.ol-lp-split .ol-lp-row-main { padding-left: 2px; }
.ol-lp-split .ol-fs-txt { padding-left: 2px; }
.ol-lp-jump { flex: 0 0 auto; width: 20px; height: 22px; padding: 0; border: none; background: transparent; color: var(--text-muted); font-size: 11px; line-height: 1; border-radius: 6px; cursor: pointer; opacity: .45; transition: opacity .15s, background .15s; }
.ol-lp-jump:hover { opacity: 1; background: color-mix(in srgb, var(--text-muted) 12%, transparent); }

/* ============================================================
   伏笔（js/ui/outline.js 末尾）
   左：场景行标题旁的灯泡/对勾微标记（只读信号，改绑在 … 面板）；
   中：关联浮层里的伏笔段（绑定行 + 埋坑/填坑两个动作）；
   右：全局追踪面板（pending 在上、resolved 置灰沉底）。
   全部走 CSS 变量与 --warning-color / --danger-color，主题不需要为它写补丁。
   ============================================================ */
/* 侧栏头部两颗按钮：加号是主操作，伏笔追踪是次操作，靠在一起才不显得各自为政 */
.panel-header-actions { display: inline-flex; align-items: center; gap: 6px; }
/* 场景行标记：常驻可见（它就是"这一场有坑"的信号），线框图标居中。
   只读，所以不给 pointer、不给 hover 底色——点了没反应的东西不该长得像按钮。 */
.ol-fs-mark { flex: 0 0 auto; width: 18px; height: 18px; border-radius: 4px; display: inline-flex; align-items: center; justify-content: center; color: var(--text-muted); opacity: .8; }
.ol-fs-mark.is-payoff { opacity: .62; }

/* 伏笔段（关联浮层里的一段，皮肤与角色/时间线/导图段完全共用） */
/* 绑定行与角色/时间线行同一套读法：左端点位=摘掉，中间是伏笔本身。 */
.ol-fs-txt { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px; padding: 4px 8px; font-size: 12px; }
.ol-fs-row.is-done .ol-fs-txt { opacity: .6; }
.ol-fs-row.is-done .ol-lp-mark { opacity: .55; }
/* 行首的方向图标：灯泡=在这一场埋过，对勾=在这一场填过，和段头一样灰 */
.ol-fs-kind { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--text-muted); }
.ol-fs-where { flex: 0 1 auto; min-width: 0; font-size: 10px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 两个动作改成两颗极轻的文字按钮：它们是入口，不是控件，虚线大框全部去掉。
   摊开时只有文字自己加深（.is-open），表单紧贴在按钮下面，不需要边框来划地盘。 */
.ol-fs-acts { display: flex; flex-direction: column; gap: 1px; padding: 3px 0 4px; }
.ol-fs-act { display: block; width: fit-content; text-align: left; padding: 2px 5px; border: none; background: none; border-radius: 5px; color: var(--text-muted); font-family: inherit; font-size: 11px; cursor: pointer; opacity: .72; transition: color .15s, opacity .15s, background .15s; }
.ol-fs-act:hover { color: var(--text-color); opacity: 1; background: color-mix(in srgb, var(--text-muted) 8%, transparent); }
.ol-fs-act.is-open { color: var(--primary-color); opacity: 1; background: none; }
.ol-fs-new { display: flex; flex-direction: column; gap: 4px; }
.ol-fs-new[hidden] { display: none; }
/* 摊开的表单字段：与搜索框同一无边框浅底口径，靠底色成块，不靠描边。 */
.ol-fs-input { padding: 4px 8px; font-size: 12px; font-family: inherit; border: none; border-radius: 6px; background: color-mix(in srgb, var(--text-muted) 8%, transparent); color: var(--text-color); outline: none; transition: background .15s ease; }
.ol-fs-input:focus-visible { background: color-mix(in srgb, var(--text-muted) 13%, transparent); }
.ol-fs-input::placeholder { color: var(--text-muted); opacity: .5; }
.ol-fs-form-acts { display: flex; justify-content: flex-end; gap: 6px; }
.ol-fs-form-acts .btn { padding: 3px 12px; font-size: 11px; }

/* ---- 追踪面板 ---- */
.modal-foreshadow { width: 620px; }
.fs-count { font-style: normal; font-family: var(--font-serif); font-size: .8rem; color: var(--text-muted); margin-left: 6px; }
.fs-new-input { width: 100%; box-sizing: border-box; padding: 7px 10px; margin-bottom: 12px; font-family: inherit; font-size: 12px; border: 1px solid color-mix(in srgb, var(--border-color) 80%, transparent); border-radius: 8px; background: transparent; color: var(--text-color); outline: none; }
.fs-new-input:focus-visible { border-color: var(--primary-color); }
.fs-new-input::placeholder { color: var(--text-muted); opacity: .5; }
.fs-body { display: flex; flex-direction: column; gap: 14px; }
.fs-empty { font-size: 12px; color: var(--text-muted); line-height: 1.9; padding: 6px 2px; }
.fs-group { display: flex; flex-direction: column; gap: 6px; }
.fs-group-head { display: flex; align-items: center; gap: 6px; padding: 2px 4px; border: none; background: none; color: var(--text-muted); font-family: inherit; font-size: 11px; cursor: pointer; border-radius: 5px; }
.fs-group-head:hover { color: var(--text-color); background: color-mix(in srgb, var(--text-muted) 8%, transparent); }
.fs-fold-icon { display: inline-flex; transition: transform .16s ease; }
.fs-group:not(.is-folded) .fs-fold-icon { transform: rotate(90deg); }
.fs-rows { display: flex; flex-direction: column; gap: 6px; }
/* 一行一件事：标题 + 状态在最上，埋于/填于各占一行（场景名可点跳），描述单独一段，操作压在右下角 */
.fs-row { border: 1px solid color-mix(in srgb, var(--border-color) 70%, transparent); border-radius: 10px; padding: 8px 10px; background: color-mix(in srgb, var(--bg-color) 45%, transparent); }
.fs-pending .fs-row { border-left: 3px solid var(--warning-color); }
.fs-resolved .fs-row { opacity: .62; border-left: 3px solid color-mix(in srgb, var(--text-muted) 40%, transparent); }
.fs-resolved .fs-row:hover { opacity: 1; }
.fs-row-head { display: flex; align-items: center; gap: 8px; }
.fs-title { flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 600; color: var(--text-color); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fs-badge { flex: 0 0 auto; font-size: 10px; line-height: 16px; padding: 0 7px; border-radius: 999px; color: var(--warning-color); background: color-mix(in srgb, var(--warning-color) 14%, transparent); }
.fs-resolved .fs-badge { color: var(--text-muted); background: color-mix(in srgb, var(--text-muted) 12%, transparent); }
.fs-meta { display: flex; align-items: baseline; gap: 6px; margin-top: 4px; font-size: 11px; color: var(--text-muted); }
.fs-meta-k { flex: 0 0 auto; opacity: .7; }
.fs-none { color: var(--text-muted); opacity: .65; }
.fs-jump { border: none; background: none; padding: 0; font-family: inherit; font-size: 11px; color: var(--text-color); cursor: pointer; text-align: left; border-bottom: 1px solid color-mix(in srgb, var(--text-muted) 30%, transparent); }
.fs-jump:hover { color: var(--primary-color); border-bottom-color: var(--primary-color); }
.fs-desc { display: block; width: 100%; text-align: left; margin-top: 6px; padding: 5px 8px; border: 1px dashed color-mix(in srgb, var(--border-color) 75%, transparent); border-radius: 7px; background: none; color: var(--text-color); font-family: inherit; font-size: 11px; line-height: 1.7; white-space: pre-wrap; word-break: break-word; cursor: text; }
.fs-desc:hover { border-color: var(--primary-color); }
.fs-desc-empty { width: auto; color: var(--text-muted); font-style: normal; }
.fs-desc-input { width: 100%; box-sizing: border-box; margin-top: 6px; padding: 5px 8px; border: 1px solid var(--primary-color); border-radius: 7px; background: transparent; color: var(--text-color); font-family: inherit; font-size: 11px; outline: none; }
.fs-acts { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }
.fs-acts .btn { padding: 3px 12px; font-size: 11px; }

/* ============================================================
   搜索联想选择器（js/ui/search-pick.js）
   三处共用：大纲关联浮层、时间线事件表单的「关联场景」、导图节点的「搜索关联大纲」。
   一律走 CSS 变量，主题不用为它写任何补丁；列表默认在文档流里（浮层内），
   只有表单里那种"输入框下面盖一层"的用法才切成绝对定位。
   ============================================================ */
.sp-pick { position: relative; }
.sp-input { width: 100%; padding: 5px 8px; font-family: inherit; font-size: 12px; border: 1px solid var(--border-color); border-radius: 7px; background: transparent; color: var(--text-color); outline: none; }
.sp-input:focus-visible { border-color: var(--primary-color); }
.sp-input::placeholder { color: var(--text-muted); opacity: .55; }
.sp-list { display: flex; flex-direction: column; gap: 1px; max-height: 190px; overflow-y: auto; }
.sp-opt { display: flex; align-items: center; gap: 6px; min-height: 24px; padding: 4px 6px; border: none; background: transparent; color: var(--text-color); font-family: inherit; font-size: 12px; text-align: left; border-radius: 6px; cursor: pointer; width: 100%; }
.sp-opt:hover { background: color-mix(in srgb, var(--text-muted) 12%, transparent); }
.sp-opt.on { color: var(--primary-color); }
.sp-opt-name { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sp-opt-note { flex: 0 0 auto; font-size: 10px; color: var(--text-muted); }
.sp-opt-check { flex: 0 0 auto; width: 12px; color: var(--primary-color); font-size: 11px; }
.sp-opt-clear .sp-opt-name { color: var(--text-muted); }
.sp-opt-clear:hover .sp-opt-name { color: var(--text-color); }
.sp-empty, .sp-more { padding: 5px 6px; font-size: 11px; color: var(--text-muted); }
/* 表单里的联想下拉：盖在后续字段之上（z-index 只在 .sp-pick 这个定位父级内部比，
   弹窗整体层级由 .modal 管，这里不跟它抢） */
.tl-scene-pick { display: flex; align-items: center; gap: 4px; }
.tl-scene-pick .sp-input { flex: 1 1 auto; padding-right: 26px; }
.tl-scene-pick .sp-clear { flex: 0 0 auto; width: 18px; height: 18px; padding: 0; border: none; background: transparent; color: var(--text-muted); font-size: 12px; line-height: 1; border-radius: 50%; cursor: pointer; }
.tl-scene-pick .sp-clear:hover { background: color-mix(in srgb, var(--text-muted) 14%, transparent); color: var(--text-color); }
.tl-scene-pick .sp-list { position: absolute; left: 0; right: 0; top: calc(100% + 3px); z-index: 40; padding: 4px; background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: 9px; box-shadow: var(--shadow-md); }
/* 导图侧的选择浮层：与关联浮层同一套层级与外观（PopoverManager 统一 z-index 900） */
.mm-ol-pop { z-index: 900; display: flex; flex-direction: column; gap: 6px; padding: 8px; background: var(--panel-bg); border: 1px solid color-mix(in srgb, var(--border-color) 60%, transparent); border-radius: 10px; box-shadow: var(--shadow-md); }
.sp-pick-head { font-size: 11px; color: var(--text-muted); display: flex; align-items: center; gap: 6px; }
.sp-pick-cur { margin-left: auto; max-width: 132px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--primary-color); }

/* ============================================================
   角色详情页·出场追踪两段式（已关联 / 文中提到）
   ============================================================ */
.char-app-section { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
.char-app-section:last-child { margin-bottom: 0; }
.char-app-head { font-size: 11px; color: var(--text-muted); display: flex; align-items: center; gap: 6px; padding: 2px 0; }
.char-app-count { background: color-mix(in srgb, var(--text-muted) 14%, transparent); color: var(--text-muted); border-radius: 8px; padding: 0 6px; font-size: 10px; line-height: 14px; }
/* 出场追踪头部的「点选关联」入口：与计数并列，默认淡、悬停才点亮 */
.char-app-link-start { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; padding: 0;
  border: none; background: transparent; color: var(--text-muted); border-radius: 4px; opacity: .6; cursor: pointer;
  transition: opacity .15s, color .15s, background .15s; }
.char-app-link-start:hover { opacity: 1; color: var(--accent-color); background: color-mix(in srgb, var(--text-muted) 12%, transparent); }
.char-appearance-link { display: flex; align-items: center; justify-content: space-between; gap: 8px; text-align: left; }
.char-app-pin .char-app-pin-mark { flex: 0 0 auto; font-size: 10px; color: var(--text-muted); border: 1px solid color-mix(in srgb, var(--border-color) 70%, transparent); border-radius: 8px; padding: 0 6px; line-height: 15px; opacity: 0; transition: opacity .12s; }
.char-app-pin:hover .char-app-pin-mark { opacity: 1; }
.char-app-pin:hover .char-app-pin-mark { color: var(--primary-color); border-color: var(--primary-color); }

/* ============================================================
   角色档案中枢弹窗（js/ui/characters.js → openCharacterDossierModal）
   时间线里点头像原地浮出：不切视图、不打断时间线状态。
   三栏硬布局：左目录 120px / 中栏基础与战力 300px / 右栏详细设定吃满剩余。
   三栏各自 overflow-y: auto + overscroll-behavior: contain：
   滚到头不把惯性传给外层，所以滚动弹窗绝不会带动底下那张画布。
   ============================================================ */
/* 档案抽屉：从右侧滑入的非阻塞侧栏。遮罩常驻渲染但不拦截点击（pointer-events:none），
   左侧关系网照常拖拽/点选，点空白也不会关抽屉；只有抽屉本体接收交互。
   显隐靠 transform 平移（不是 display 切换），滑入滑出才有动画 */
#modal-char-dossier {
  z-index: var(--z-popover); padding: 0;
  display: flex;
  background: transparent; backdrop-filter: none;
  pointer-events: none;
  animation: none;
  transition: background 0.3s ease;
}
#modal-char-dossier.active { background: rgba(0,0,0,0.1); }
#modal-char-dossier .cdm-modal {
  position: fixed; top: 0; right: 0;
  width: 75vw; max-width: 900px; height: 100vh;
  max-height: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--vd-paper); border-radius: 12px 0 0 12px;
  box-shadow: -4px 0 24px rgba(0,0,0,0.15);
  pointer-events: auto;
  transform: translateX(100%);
  transition: transform 0.3s cubic-bezier(0.4,0,0.2,1);
}
#modal-char-dossier.active .cdm-modal { transform: translateX(0); }
#modal-char-dossier .cdm-head { padding: 12px 24px; flex-shrink: 0; }
#modal-char-dossier .cdm-wrap { flex: 1; min-height: 0; padding: 12px 20px 16px; overflow: hidden; }
#modal-char-dossier .cdm-layout {
  display: grid; grid-template-columns: 120px 300px minmax(0, 1fr);
  gap: 0; height: 100%; min-height: 0;
}
/* 左栏：固定 120px 目录，只做锚点，不参与内容阅读 */
.cdm-nav-col {
  display: flex; flex-direction: column; min-height: 0; overflow-y: auto;
  padding: 4px 12px 12px 0; border-right: 1px solid var(--vd-line);
  overscroll-behavior: contain; scrollbar-gutter: stable;
}
.cdm-nav-title {
  margin: 0 0 6px; font-family: var(--vd-mono); font-size: 9px; letter-spacing: 2px;
  color: var(--vd-ink-soft); opacity: 0.7;
}
/* 目录项：一条竖排索引。选中项只加一根标识色短竖条，不给底色、不做胶囊 */
.cdm-nav { display: flex; flex-direction: column; gap: 1px; margin-top: 2px; padding-top: 8px; border-top: 1px solid var(--vd-line); }
.cdm-nav-item {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 5px 0;
  border: none; background: transparent; color: var(--vd-ink-soft);
  font-family: var(--vd-type); font-size: 12px; letter-spacing: 1px; text-align: left;
  cursor: pointer; transition: color 0.16s ease;
}
.cdm-nav-item::before { content: ""; width: 2px; height: 11px; background: transparent; flex-shrink: 0; }
.cdm-nav-item:hover { color: var(--vd-ink); }
.cdm-nav-item.is-active { color: var(--vd-ink); font-weight: 600; }
.cdm-nav-item.is-active::before { background: var(--cp-accent, var(--vd-ink)); }
/* 中栏 / 右栏：两个独立滚动上下文 */
.cdm-col {
  min-height: 0; overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain; scrollbar-gutter: stable;
}
.cdm-col--mid {
  display: flex; flex-direction: column; padding: 2px 18px 26px;
  border-right: 1px solid var(--vd-line);
}
.cdm-col--main { padding: 2px 4px 26px 18px; }
.cdm-col::-webkit-scrollbar, .cdm-nav-col::-webkit-scrollbar { width: 8px; }
.cdm-col::-webkit-scrollbar-thumb, .cdm-nav-col::-webkit-scrollbar-thumb { background: var(--paper-line-strong); border-radius: 999px; border: 2px solid transparent; background-clip: content-box; }

/* 中栏头部：大头像垂直居中，姓名/别名紧跟着写在下面，整块像档案卡的卡头 */
.cdm-id-head { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 10px 0 16px; }
#modal-char-dossier .cdm-avatar { width: 112px; height: 112px; }
/* 弹窗里的头像只是展示，不接下传/点选；别留着可点击的指针和悬停描边骗人 */
#modal-char-dossier #cdm-avatar, #modal-char-dossier #cdm-avatar:hover { cursor: default; border-color: var(--vd-line-strong); }
.cdm-id-main { width: 100%; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 4px; }
.cdm-name-input {
  width: 100%; border: none; background: transparent; text-align: center;
  color: var(--vd-ink); font-family: var(--vd-type); font-size: 18px; font-weight: 600;
  letter-spacing: 1px; padding: 0; line-height: 1.5;
}
.cdm-sub-input {
  width: 100%; border: none; background: transparent; text-align: center;
  color: var(--vd-ink-soft); font-size: 12px; padding: 0; line-height: 1.6;
}
.cdm-name-input:focus-visible, .cdm-sub-input:focus-visible { outline: none; }
/* 基础信息：纯中文"标签："键值行。标签列固定 76px 不换行、右对齐让冒号竖着对齐；
   值列无边框无底色零内边距，读起来像写在纸上的字，聚焦只露光标 */
.cdm-basic { display: flex; flex-direction: column; gap: 0; padding-bottom: 10px; }
.cdm-col--mid .cdm-basic .cp-field { grid-template-columns: 76px minmax(0, 1fr); gap: 0 10px; align-items: center; }
.cdm-col--mid .cdm-basic .cp-field > label {
  white-space: nowrap; text-align: right; font-size: 12px; letter-spacing: 0;
  line-height: 30px; color: var(--vd-ink-soft);
}
#modal-char-dossier .cdm-basic .cp-field input:not(.cp-color),
#modal-char-dossier .cdm-basic .cp-field .cselect.cp-input {
  width: 100%; height: 30px; margin: 0; padding: 0; border: none; border-radius: 0;
  background: transparent !important; box-shadow: none; outline: none;
  color: var(--vd-ink); font-family: var(--vd-type); font-size: 13px; line-height: 30px;
}
#modal-char-dossier .cdm-basic .cp-field input:not(.cp-color):focus,
#modal-char-dossier .cdm-basic .cp-field .cselect.cp-input:focus-visible { outline: none; border: none; }
/* 标识色：缩成一颗与文字同高的小色块，不再铺满整行 */
#modal-char-dossier .cdm-basic input.cp-color {
  width: 22px; height: 14px; padding: 0; border: none; border-radius: 2px;
  background: transparent; cursor: pointer; justify-self: start;
}
#modal-char-dossier .cdm-basic input.cp-color::-webkit-color-swatch-wrapper { padding: 0; }
#modal-char-dossier .cdm-basic input.cp-color::-webkit-color-swatch { border: none; border-radius: 2px; }

/* 模块通用：标题 + 模块体，模块之间只用一条细线分隔 */
.cdm-sec { min-width: 0; flex: 0 0 auto; }
.cdm-sec + .cdm-sec { border-top: 1px solid var(--vd-line); }
.cdm-sec-head { padding: 14px 0 6px; }
.cdm-sec-head .vd-mod-title::after { content: none; }
.cdm-sec .vd-mod-body { padding-bottom: 14px; }
/* 弹窗里雷达的维度名不做改名，别用 I 型光标骗人 */
.cdm-col--mid .vd-radar-label { cursor: default; }
.cdm-col--mid .vd-radar svg { pointer-events: auto; }

/* 右栏长文本：可改高度，但绝不因为内容长就把整栏顶出窗口 */
.cdm-col--main textarea {
  width: 100%; min-height: 78px; resize: vertical;
  border: 1px solid var(--vd-line); border-radius: 2px;
  background: color-mix(in srgb, var(--vd-ink) 2%, transparent);
  color: var(--vd-ink); padding: 8px 10px;
  font-size: 13px; line-height: 1.75; font-family: var(--vd-type);
}
.cdm-col--main textarea:focus-visible { outline: none; border-color: var(--cp-accent, var(--vd-ink)); }

/* 情绪板：整块模块体都是投放区 */
.cdm-drop {
  border: 1px dashed transparent; border-radius: 3px; cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.cdm-drop:hover { border-color: var(--vd-line-strong); }
.cdm-drop.is-dragover {
  border-color: var(--cp-accent, var(--vd-ink));
  background: color-mix(in srgb, var(--cp-accent, var(--vd-ink)) 7%, transparent);
}
.cdm-gal-hint { padding: 16px 10px; text-align: center; }
.cdm-drop .vd-gallery { cursor: pointer; }

/* 档案概览：中栏窄，单列键值对；长值一律省略号，绝不撑破栏目 */
.cdm-kv { display: grid; grid-template-columns: 1fr; gap: 7px 0; margin: 0; }
.cdm-kv > div { display: grid; grid-template-columns: 76px minmax(0, 1fr); align-items: baseline; gap: 10px; }
.cdm-kv dt { margin: 0; font-size: 11px; letter-spacing: 1px; color: var(--vd-ink-soft); white-space: nowrap; }
.cdm-kv dd { margin: 0; font-size: 13px; color: var(--vd-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 出场追踪：只读速览 */
.cdm-trace-line { margin: 0 0 8px; font-size: 12px; color: var(--vd-ink-soft); }
.cdm-trace-line b { color: var(--vd-ink); font-weight: 600; }
.cdm-trace-list { display: flex; flex-wrap: wrap; gap: 5px 6px; margin-bottom: 8px; }
.cdm-trace-item {
  max-width: 13em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 11px; padding: 1px 7px; border: 1px solid var(--vd-line); color: var(--vd-ink-soft);
}
.cdm-trace-more { font-size: 11px; color: var(--vd-ink-soft); align-self: center; }

/* 人际关系：顶部缩略图 + 底部胶囊。胶囊里对方名字超过 6 字一律省略号 */
.cdm-rel-chips { display: flex; flex-wrap: wrap; gap: 6px 8px; padding-top: 8px; }
.cdm-rel-chip { max-width: 100%; }
.cdm-rel-chip .rel-name { max-width: 6em; }
.cdm-rel-chip .rel-type { max-width: 7em; }
.cdm-sec .char-rel-mini-wrap { max-width: 320px; }

.cdm-empty { margin: 0; padding: 4px 0; color: var(--vd-ink-soft); font-size: 12px; }
@media (max-width: 1080px) {
  #modal-char-dossier .cdm-layout { grid-template-columns: 104px 268px minmax(0, 1fr); }
  .cdm-col--mid { padding-right: 14px; }
}
@media (max-width: 860px) {
  #modal-char-dossier { padding: 0; }
  #modal-char-dossier .cdm-modal { width: 100vw; height: 100vh; border-radius: 0; }
  #modal-char-dossier .cdm-wrap { overflow-y: auto; padding: 10px 14px 18px; }
  #modal-char-dossier .cdm-layout { grid-template-columns: 1fr; height: auto; }
  /* 目录索引 → 顶部横向可滑动 Tabs（不换行，超出滚动）；操作头在滚动区之外，绝不会被挤出视口 */
  .cdm-nav-col { border-right: none; border-bottom: 1px solid var(--vd-line); padding: 2px 0 6px; overflow: visible; }
  .cdm-nav { flex-direction: row; flex-wrap: nowrap; overflow-x: auto; gap: 2px 8px; margin-top: 0; padding-top: 4px; border-top: none; }
  .cdm-nav-item { width: auto; white-space: nowrap; }
  .cdm-col { overflow: visible; height: auto; }
  .cdm-col--mid { border-right: none; border-bottom: 1px solid var(--vd-line); padding: 0 0 10px; }
  .cdm-col--main { padding: 0; }
  /* 单栏滚动时，模块之间给出明确分割线 */
  .cdm-col--main .cdm-sec + .cdm-sec { border-top: 1px solid var(--vd-line); margin-top: 12px; padding-top: 12px; }
  .cdm-kv { grid-template-columns: 1fr; }
}


/* ==========================================================================
   角色线派生节点 & 成长弧线 / 角色纪事两区（本轮新增）
   约定：时间线内圆角 <= 4px，不加胶囊大圆角、不加重阴影
   ========================================================================== */

/* ---------- 影子卡（只读投影）上的关联符号 ---------- */
/* 左上 POV 用标准卡位（top/left 由 .tl-pov-chip 提供）：原事件视角头像 + 本线角标 */
.tl-mirror-pov-btn { padding: 0; border: none; background: none; line-height: 0; cursor: pointer; }
.tl-mirror-pov-tag {
  position: absolute; left: 17px; top: 2px; padding: 0 2px; border-radius: 2px;
  background: color-mix(in srgb, var(--ol-ava-c, var(--text-muted)) 18%, var(--panel-bg));
  color: var(--ol-ava-c, var(--text-muted)); font-size: 8px; line-height: 12px; letter-spacing: .5px; white-space: nowrap;
}
/* 「本线」角标伸到标题区，标题内边距一起让开，避免字叠字 */
.tl-point-card:has(.tl-mirror-pov-tag) .tl-ev-title { padding-left: 40px; }

/* ---------- 派生节点卡片（原生事件：实线边框 + 角色线色粗脊） ---------- */
.tl-point-card.tl-derived {
  border: none; border-radius: 6px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.06); background: color-mix(in srgb, var(--track-color, var(--text-muted)) 6%, var(--panel-bg));
}
.tl-point-card.tl-derived .tl-derived-pov-wrap { position: absolute; top: 3px; left: 4px; z-index: 3; display: inline-flex; }
.tl-point-card.tl-derived .tl-derived-pov-wrap .tl-pov-chip { position: static; }
.tl-point-card.tl-derived .tl-derived-pov-main { border-radius: 50%; box-shadow: 0 0 0 1.5px var(--tl-line-c, var(--text-muted)); }
.tl-point-card.tl-derived .tl-derived-pov-main:hover { box-shadow: 0 0 0 2px var(--tl-line-c, var(--text-muted)); }
.tl-point-card.tl-derived .tl-derived-pov-sub {
  position: absolute; right: -8px; bottom: -7px; z-index: 4; line-height: 0; padding: 0; border: none;
  background: none; opacity: .55; cursor: pointer;
}
.tl-point-card.tl-derived .tl-derived-pov-sub .ol-cast-ava { width: 12px; height: 12px; font-size: 8px; }
.tl-point-card.tl-derived .tl-derived-pov-sub:hover { opacity: 1; }

/* Inspector 里的「角色线版本」折叠区 */
.tl-derived-sec { margin-top: 14px; padding-top: 10px; border-top: 1px dashed var(--border-color); }
.tl-derived-sec-sum { cursor: pointer; font-size: 0.82rem; font-weight: 600; color: var(--text-color); list-style: none; }
.tl-derived-sec-sum::-webkit-details-marker { display: none; }
.tl-derived-sec-sum::before { content: "▸ "; color: var(--text-muted); }
.tl-derived-sec[open] .tl-derived-sec-sum::before { content: "▾ "; }
.tl-derived-sec .form-group { margin-top: 8px; }
.tl-growth-inline { display: flex; gap: 8px; }
.tl-growth-field { display: flex; flex-direction: column; gap: 4px; flex: 1 1 0; min-width: 0; }
.tl-growth-field > label { display: block; margin: 0; font-size: 0.7rem; font-weight: 400; line-height: 1.2; color: var(--text-muted); }
.tl-growth-field > input { width: 100%; box-sizing: border-box; }
/* 复选框：flex 对齐 + 禁止折行 + 加大热区；显式锁死尺寸，
   覆盖全局 `input { width: 100% }`（styles.css 2848）把复选框拉满、文字被挤成 min-content 折行的根因 */
label.tl-derived-node-check { display: flex; align-items: center; gap: 8px; width: fit-content; max-width: 100%; margin-top: 10px; padding: 4px 6px 4px 0; font-size: 0.8rem; color: var(--text-muted); cursor: pointer; white-space: nowrap; user-select: none; }
/* 旧尺寸规则保留给未升级的调用点；带 .tl-switch-input 的走下面拨动开关专属规则，避免 15px 锁死 */
label.tl-derived-node-check > input[type="checkbox"]:not(.tl-switch-input) { width: 15px; height: 15px; margin: 0; flex: 0 0 auto; cursor: pointer; accent-color: var(--primary-color); }
/* 生命阶段：年龄推断灰字；读不出数字时 JS 写空串，:empty 让整行自动收起，不留占位空白 */
.tl-stage-infer { margin-top: 2px; font-size: 0.66rem; line-height: 1.3; color: var(--text-muted); }
.tl-stage-infer:empty { display: none; }
/* 成长标签胶囊：胶囊与输入框同排流式布局，焦点反馈整块给 :focus-within */
.tl-tags-field > label { display: block; }
.tl-tags-control { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; box-sizing: border-box; min-height: 32px; padding: 4px 6px; border: 1px solid var(--border-color); border-radius: var(--input-radius); background: var(--input-sunken); cursor: text; }
.tl-tags-control:focus-within { border-color: var(--primary-color); box-shadow: var(--input-focus-ring); }
/* display:contents 让胶囊直接成为 .tl-tags-control 的 flex 子项，输入框永远排在最后一个胶囊后面；
   这个 div 本身无语义，从 a11y 树里退场不会丢内容 */
.tl-tags-list { display: contents; }
.tl-tag { display: inline-flex; align-items: center; gap: 3px; max-width: 100%; padding: 1px 4px 1px 8px; border-radius: 999px; font-size: 0.72rem; line-height: 1.5; color: var(--text-color); background: color-mix(in srgb, var(--primary-color) 12%, var(--panel-bg)); border: 1px solid color-mix(in srgb, var(--primary-color) 30%, var(--border-color)); }
.tl-tag-text { min-width: 0; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-tag-del { flex: 0 0 auto; border: 0; padding: 0 2px; background: transparent; color: var(--text-muted); font-size: 0.9rem; line-height: 1; cursor: pointer; border-radius: 3px; }
.tl-tag-del:hover { color: var(--danger-color); }
.tl-tag-del:focus-visible { outline: none; color: var(--danger-color); box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger-color) 40%, transparent); }
/* 胶囊内输入框：同时压过全局 input 与 .tl-ins-body input[type=text] 的框线/底色/宽度 */
.tl-tags-control input.tl-tags-input,
.tl-ins-body .tl-tags-control input.tl-tags-input[type="text"] { flex: 1 1 90px; min-width: 70px; width: auto; padding: 2px 3px; border: 0; border-radius: 0; background: transparent; box-shadow: none; font-size: 0.8rem; }
/* 全局 input:focus-visible 的光晕盖到这颗裸输入框上会像"框中框"；反馈统一交给 .tl-tags-control:focus-within */
.tl-tags-control input.tl-tags-input:focus-visible,
.tl-ins-body .tl-tags-control input.tl-tags-input[type="text"]:focus-visible { background: transparent; border-color: transparent; box-shadow: none; outline: none; }
.tl-tags-tip { margin-top: 4px; font-size: 0.66rem; color: var(--text-muted); }
/* 成长节点拨动开关：原生 checkbox 只做视觉隐藏，id / checked / Tab / 空格 / label 点击全部保留 */
label.tl-derived-node-check.tl-switch { position: relative; gap: 9px; }
label.tl-derived-node-check.tl-switch > input[type="checkbox"].tl-switch-input { position: absolute; left: 0; top: 50%; width: 1px; height: 1px; margin: 0; padding: 0; opacity: 0; transform: translateY(-50%); flex: 0 0 auto; cursor: pointer; }
.tl-switch-visual { position: relative; display: inline-block; flex: 0 0 auto; width: 34px; height: 18px; box-sizing: border-box; border-radius: 999px; background: var(--tooltip-track); border: 1px solid var(--border-strong); transition: background .16s ease, border-color .16s ease; }
.tl-switch-visual::after { content: ''; position: absolute; left: 2px; top: 50%; transform: translateY(-50%); width: 12px; height: 12px; border-radius: 50%; background: #FFFFFF; box-shadow: 0 1px 3px rgba(0,0,0,.22); transition: left .16s ease; }
.tl-switch-input:checked + .tl-switch-visual { background: var(--primary-color); border-color: var(--primary-color); }
.tl-switch-input:checked + .tl-switch-visual::after { left: 18px; }
.tl-switch-input:focus-visible + .tl-switch-visual { box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 30%, transparent); }
.tl-switch-text { color: var(--text-color); font-size: 0.8rem; }
/* 375px / 320px 末日模拟：张力一行不挤碎，胶囊输入框在窄屏独占一行 */
@media (max-width: 400px) {
  .tl-tension-control { gap: 6px; }
  .tl-tension-num { width: 46px; min-width: 46px; }
  .tl-tension-ticks { font-size: 0.58rem; }
  .tl-tags-control input.tl-tags-input { flex: 1 1 100%; min-width: 0; }
}
.tl-derived-actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.tl-derived-act { font-size: 0.78rem; padding: 4px 10px; border-radius: var(--radius-btn); }

/* =================================================================
   强制兜底（最高优先级）：消灭黄绿残留 + 全局去线框化
   仅在浅色主题下锁死令牌；深色主题走 [data-theme="dark"] 的令牌。
   边框统一透明，仅三大核心分割线（顶栏底边 / 左栏右边 / 右栏左边）恢复极淡灰。
   ================================================================= */
:root:not([data-theme="dark"]) {
  --bg-color: #F5F5F7 !important;
  --panel-bg: #FFFFFF !important;
  --hover-bg: #F3F4F6 !important;
  --text-color: #111827 !important;
  --text-muted: #6B7280 !important;
  --text-placeholder: #9CA3AF !important;
  --border-color: rgba(0,0,0,0.06) !important;
  --primary-color: #111827 !important;
  --primary-hover: #374151 !important;
  --accent-color: #6B7280 !important;
  --success-color: #10B981 !important;
  --warning-color: #F59E0B !important;
  --danger-color: #EF4444 !important;
  --overlay-color: rgba(17,24,39,0.04) !important;
  --memo-bg: #F3F4F6 !important;
  --memo-done-bg: #E5E7EB !important;
  --mm-node-ring: 0 0 0 3px rgba(17,24,39,0.18) !important;
}
input:focus-visible, textarea:focus-visible, .cselect:focus-visible { border-color: var(--primary-color) !important; }
#selection-box { border-color: var(--accent-color) !important; background: rgba(17,24,39,0.06) !important; }

/* ---------- 成长弧线（档案页 / 弹窗） ---------- */
.vd-arc-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 10px; }
.vd-arc-tags:empty { display: none; }
.vd-arc-tag {
  padding: 1px 6px; border: 1px solid var(--vd-line); border-radius: 3px; background: transparent;
  color: var(--vd-ink-soft); font-family: var(--vd-mono); font-size: 10px; letter-spacing: .5px; cursor: pointer;
}
.vd-arc-tag:hover { border-color: var(--vd-gold); color: var(--vd-ink); }
.vd-arc-tag.is-on { border-color: var(--vd-gold); background: color-mix(in srgb, var(--vd-gold) 16%, transparent); color: var(--vd-ink); }
.vd-arc { position: relative; display: flex; flex-direction: column; gap: 0; }
.vd-arc-group { position: relative; padding-left: 18px; }
.vd-arc-group::before { content: ""; position: absolute; left: 5px; top: 4px; bottom: 4px; width: 1px; background: var(--vd-line-soft); }
.vd-arc-node { position: relative; padding: 6px 8px 6px 0; border-radius: 2px; cursor: pointer; }
.vd-arc-node:hover { background: var(--vd-hover); }
.vd-arc-dot {
  position: absolute; left: -16px; top: 12px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--arc-color, var(--text-muted)); border: 1px solid color-mix(in srgb, var(--arc-color, var(--text-muted)) 70%, #000);
}
.vd-arc-node.is-compact .vd-arc-dot { top: 11px; }
.vd-arc-main { min-width: 0; }
.vd-arc-line1 { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.vd-arc-date { font-family: var(--vd-type); font-size: 0.8rem; color: var(--vd-ink-soft); font-variant-numeric: tabular-nums; }
.vd-arc-snap { font-family: var(--vd-mono); font-size: 10px; letter-spacing: .4px; color: var(--vd-gold); }

/* ===================================================================
   全站统一自定义下拉（CSelect）—— 彻底替代浏览器原生下拉控件与原生 datalist
   触发器、箭头、面板、选中高亮全部走全局令牌，任何主题下都不会冒出系统蓝底。
   =================================================================== */
.cselect {
  position: relative; display: flex; align-items: center; gap: 8px;
  cursor: pointer; user-select: none; -webkit-user-select: none;
}
.cselect[hidden] { display: none !important; }
.cselect:not(.cp-input) {
  height: 28px; padding: 0 10px; box-sizing: border-box; width: 100%;
  background: var(--input-sunken); border: 1px solid var(--border-color);
  border-radius: var(--input-radius); color: var(--text-color); font-size: 13px;
}
.cselect-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cselect-arrow {
  flex: 0 0 auto; width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid var(--text-muted); transition: transform .15s ease;
}
.cselect.is-open .cselect-arrow { transform: rotate(180deg); }
.cselect-input {
  flex: 1 1 auto; min-width: 0; border: 0; outline: 0; padding: 0; margin: 0;
  background: transparent; color: inherit; font: inherit;
}
.cselect:focus-visible { outline: none !important; box-shadow: var(--input-focus-ring); }
.cselect.is-disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
/* 下拉面板：复用全站 .pop-layer 基类，圆角/底色/阴影与其它浮层完全一致 */
.cselect-pop { padding: 4px; max-height: 260px; overflow-y: auto; }
.cselect-list { list-style: none; margin: 0; padding: 0; }
.cselect-item {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px;
  border-radius: var(--radius-sm); color: var(--text-color); font-size: 13px;
  white-space: nowrap; cursor: pointer; transition: background .15s ease, color .15s ease;
}
.cselect-item:hover, .cselect-item.is-cursor { background: var(--hover-bg); }
.cselect-item.is-active {
  color: var(--primary-color); font-weight: 600;
  background: color-mix(in srgb, var(--primary-color) 10%, transparent);
}
.cselect-item.is-active::after { content: '✓'; margin-left: auto; font-size: 11px; }
.cselect-empty { padding: 10px; color: var(--text-muted); font-size: 12px; text-align: center; }
.vd-arc-title { font-size: 0.92rem; color: var(--vd-ink); margin-top: 1px; }
.vd-arc-view { margin: 3px 0 0; font-size: 0.82rem; color: var(--vd-ink-soft); line-height: 1.55; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

.vd-arc-mode-split { display: flex; align-items: center; gap: 8px; margin: 10px 0; color: var(--vd-ink-soft); font-family: var(--vd-mono); font-size: 10px; letter-spacing: 1px; }
.vd-arc-mode-split::before, .vd-arc-mode-split::after { content: ""; flex: 1; height: 1px; background: var(--vd-line-soft); }
.vd-arc-empty { margin: 4px 0; font-size: 0.82rem; color: var(--vd-ink-soft); opacity: .8; font-style: italic; }

/* ---------- 成长轨迹：长列表滚动区 + 展开/收起 ---------- */
.vd-arc-scroll { max-height: 320px; overflow-y: auto; overscroll-behavior: contain; }
.cdm-arc-scroll { max-height: 320px; }
.vd-grow-actions { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.vd-grow-btn { padding: 2px 8px; font-size: 0.7rem; border-radius: 3px; line-height: 1.5; }
.vd-grow-add { border-color: var(--vd-gold); color: var(--vd-ink); }
.vd-arc-more {
  display: block; width: 100%; margin-top: 8px; padding: 4px 0;
  border: 1px dashed var(--vd-line-soft); border-radius: 3px; background: transparent;
  color: var(--vd-ink-soft); font-family: var(--vd-mono); font-size: 10px; letter-spacing: .5px; cursor: pointer;
}
.vd-arc-more:hover { border-color: var(--vd-gold); color: var(--vd-ink); }
.vd-arc-more.hidden { display: none; }
/* 从角色档案里直接呼出的 Inspector：跳出时间线容器，浮在当前视图上（不切视图也能就地编辑） */
.timeline-inspector.tl-ins-float {
  position: fixed;
  top: var(--header-height);
  right: 0;
  bottom: 0;
  z-index: 950;
  box-shadow: var(--shadow-lg);
}




/* ---------- age / status 旁的「时间线最后一条快照」提示行 ---------- */
.vd-snap-hint { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 8px 0 0; }
.vd-snap-hint.hidden { display: none; }
.vd-snap-hint-text { font-family: var(--vd-mono); font-size: 10px; letter-spacing: .4px; color: var(--vd-ink-soft); opacity: .7; }
.vd-snap-apply {
  padding: 0 6px; border: 1px solid var(--vd-line); border-radius: 3px; background: transparent;
  color: var(--vd-ink-soft); font-family: var(--vd-mono); font-size: 10px; letter-spacing: .5px; cursor: pointer;
}
.vd-snap-apply:hover { border-color: var(--vd-gold); color: var(--vd-ink); }

/* 档案页侧栏的概览块：和主栏模块保持同一套留白 */
.vd-mod--side #char-overview { margin: 0; }
.vd-mod--side .cdm-kv { margin: 0; }


/* 影子是只读投影：整卡文字不可选，鼠标只用于"点开原事件" */
.tl-mirror, .tl-mirror * { user-select: none; -webkit-user-select: none; }

/* ==========================================================================
   阶段 A · 响应式主框架（PC 小窗口 / 平板）
   作用域严格限定在 .top-bar / .sidebar / .main-container / .drawer-* / .topbar-*。
   刻意不触碰 .cdp- / .vd- / .cc- / .modal / .tl- 等既有美术模块：
   6 宫格导出弹窗、角色瀑布流、档案页设计全部保持原样。
   ========================================================================== */

/* drag-handle 的定位基准从 HTML 内联样式收归 CSS，窄屏才能改成抽屉 */
.sidebar { position: relative; }

#btn-topbar-more { display: none; flex: 0 0 auto; }
.topbar-item-hidden { display: none !important; }

.topbar-more-menu {
  position: fixed; z-index: 4001; min-width: 190px; max-width: min(280px, 86vw);
  max-height: min(60vh, 460px); overflow-y: auto; padding: 6px; box-sizing: border-box;
  background: var(--panel-bg); border: 1px solid var(--border-color);
  border-radius: var(--radius-md); box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; gap: 2px;
}
.topbar-more-menu[hidden] { display: none !important; }
.topbar-more-head { padding: 6px 10px; color: var(--text-muted); font-size: .72rem; letter-spacing: .08em; }
.topbar-more-sep { height: 1px; margin: 4px 6px; background: var(--border-color); opacity: .7; }
.topbar-more-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 38px; padding: 6px 10px; box-sizing: border-box;
  border: none; border-radius: var(--radius-sm, 6px); background: transparent;
  color: var(--text-color); font-family: inherit; font-size: .84rem;
  text-align: left; cursor: pointer;
}
.topbar-more-item:hover, .topbar-more-item:active { background: var(--hover-bg); }

/* —— Step 2：浮层统一收编（置于各浮层定义之后，确保底色 / 圆角 / 阴影一致；
   vd-pop 为战力档案独立主题皮肤，保留其 vd 令牌，不在此强制覆盖） —— */
.pop-layer,
.tl-filter-pop, .tl-search-drop, .tl-settings-pop, .tl-pace-view-pop,
.tl-lane-menu, .tl-quick-menu, .ol-links-pop, .ol-links-pop.tl-char-pop,
.relation-detail-popup, .topbar-more-menu, .legend-more-menu,
.char-filter-pop, .mm-pop {
  background: var(--panel-bg);
  border: 1px solid var(--border-color);
  border-radius: var(--popover-radius);
  box-shadow: var(--popover-shadow);
}
.topbar-more-ic { flex: 0 0 18px; text-align: center; color: var(--text-muted); }
.topbar-more-tx { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.drawer-scrim {
  position: absolute; inset: 0; z-index: var(--z-drawer);
  background: var(--scrim-color);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  opacity: 0; pointer-events: none; transition: opacity .22s ease;
}
/* 遮罩绝对隔离：弹窗开启时抽屉遮罩退场，全站只允许一层遮罩，避免抽屉边缘被双层叠加显形 */
body.modal-open .drawer-scrim { opacity: 0 !important; pointer-events: none !important; }
body.compact-ui .drawer-scrim.is-open { opacity: 1; pointer-events: auto; }

/* ---- <1200px：PC 小窗口 / 横屏平板 ---- */
@media (max-width: 1199px) {
  .top-bar { padding: 0 12px; gap: 8px; }
  .top-bar-left { flex: 0 1 auto; min-width: 0; gap: 8px; }
  .top-bar-left > span { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .top-bar-center { display: none; }               /* 视图切换固定收进「更多」菜单 */
  .top-bar-right { flex: 0 0 auto; }
  #btn-topbar-more { display: inline-flex; }

  /* 两栏脱离文档流：覆盖式抽屉，不挤压中央画布 */
  .sidebar {
    position: absolute !important; top: 0; bottom: 0;
    width: min(320px, 86vw) !important; max-width: 86vw;
    z-index: var(--z-popover); box-shadow: var(--shadow-lg);
    transform: translateX(-102%); visibility: hidden;
    transition: transform .25s ease-out, visibility .25s;
  }
  /* 小屏抽屉：隐藏滚动条但保留滑动。抽屉外壳 .sidebar 不滚动，真正滚动的是内层
     （如 .outline），故连带 .sidebar * 后代一并隐藏；宽屏大纲栏不在本媒体查询内，保留原生滚动条 */
  .sidebar, .sidebar * {
    scrollbar-width: none !important;
    -ms-overflow-style: none !important;
    scrollbar-gutter: stable;
  }
  .sidebar::-webkit-scrollbar, .sidebar *::-webkit-scrollbar { display: none !important; }
  .sidebar#sidebar-left { left: 0; }
  .sidebar.right#sidebar-right { left: auto; right: 0; transform: translateX(102%); }
  /* 窄屏显隐只认 .drawer-open；collapsed 是宽屏语义，不能让其它模块 remove() 时把抽屉弹开 */
  .sidebar.collapsed { width: min(320px, 86vw) !important; overflow: visible; }
  .sidebar.drawer-open { transform: translateX(0) !important; visibility: visible; }
  /* 抽屉宽度固定，禁用会写内联 width 的拖拽手柄 */
  .sidebar > .drag-handle { display: none; }
}

/* ---- <768px：竖屏平板 / 手机 ---- */
@media (max-width: 767px) {
  .top-bar { padding: 0 8px; }
  .top-bar-left > span { font-size: 1rem; }
  .sidebar { width: min(300px, 88vw) !important; }
  .sidebar.collapsed { width: min(300px, 88vw) !important; }
}

/* =========================================================================
   跨浏览器 / 多内核兼容性专项（灵绪 · 补充任务）
   跟随阶段 B（Pointer Events / Hover 消除）与阶段 C（复杂交互）一并落地，
   不单独成阶段。覆盖：iOS 动态视口高度、输入框聚焦放大、惯性滚动、
   Firefox 滚动条、毛玻璃前缀、touch-action、Hover 消除。
   ========================================================================= */

/* ---- 1. iOS Safari 动态视口高度 ----
   仅覆盖带 dvh 的声明：不支持 dvh 的浏览器忽略它，自动回退到上方原始 100vh；
   -webkit-fill-available 兼容老版本 iOS Safari（<15.4）地址栏/工具栏动态收缩。 */
body { height: 100vh; height: -webkit-fill-available; height: 100dvh; }
.home-page { min-height: 100vh; min-height: -webkit-fill-available; min-height: 100dvh; }
.editor-page { height: 100vh; height: -webkit-fill-available; height: 100dvh; }
#timeline-container .timeline-content { height: calc(100vh - var(--header-height) - 56px); height: calc(100dvh - var(--header-height) - 56px); }
.mm-pop { max-height: calc(100vh - 16px); max-height: calc(100dvh - 16px); }
.ol-links-pop { max-height: min(360px, calc(100vh - 28px)); max-height: min(360px, calc(100dvh - 28px)); }
#modal-char-dossier .cdm-modal { height: 100vh; height: 100dvh; }

/* ---- 2. iOS 输入框聚焦自动放大：移动端(<768px)强制 font-size≥16px ---- */
@media (max-width: 767px) {
  input, textarea,
  .tl-search-input, .tl-ins-input, .tl-date-input,
  .cp-input, .cp-name-input, .cp-name-row .cp-name-input,
  .vd-dossier input, .vd-dossier textarea,
  .cf-box input, .rel-search input, #bg-pattern-select {
    font-size: 16px !important;
    line-height: 1.4 !important;
  }
}
/* 通用兜底：禁止 iOS 在聚焦输入框时强制放大页面（div 触发器不会触发 iOS 自动放大） */
input, textarea { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* ---- 3. 滚动惯性（Safari Momentum）+ Firefox 自定义滚动条 ----
   注意：只加在"局部滚动容器"上，绝不碰 canvas-area / 画布滚动根，
   否则会吞掉导图/时间线靠原生滚动实现的平移手势。 */
.panel-content, .cp-main, .cp-nav, .cp-mid, .modal-content, .cdm-wrap,
.ol-links-pop, .ol-lp-body, .ol-lp-list, .mm-inspector-body, .mm-pop,
.tl-ins-body, .sp-list, .vd-arc-scroll, .legend-more-menu, .char-filter-pop-body,
.project-list, #timeline-container .timeline-content, .tl-cal,
.manage-type-list, .tl-settings-check, .cdm-nav, .topbar-more-menu,
.ol-search-drop, .rel-search-results {
  -webkit-overflow-scrolling: touch;   /* Safari 惯性滚动 */
  scrollbar-width: thin;               /* Firefox 细滚动条 */
  scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
}
[data-theme="dark"] .panel-content,
[data-theme="dark"] .cp-main,
[data-theme="dark"] .cp-nav,
[data-theme="dark"] .cp-mid,
[data-theme="dark"] .modal-content,
[data-theme="dark"] .cdm-wrap,
[data-theme="dark"] .mm-pop,
[data-theme="dark"] .tl-ins-body,
[data-theme="dark"] .sp-list,
[data-theme="dark"] .vd-arc-scroll,
[data-theme="dark"] .legend-more-menu,
[data-theme="dark"] .char-filter-pop-body,
[data-theme="dark"] .project-list,
[data-theme="dark"] .cdm-nav,
[data-theme="dark"] .manage-type-list {
  scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
}

/* ---- 5. Safari 毛玻璃：-webkit-backdrop-filter 漏补（其余已在原处写好） ---- */
.vd-lightbox { -webkit-backdrop-filter: blur(2px); }
/* 旧 .tooltip-d3 的毛玻璃补丁已随该类一起废弃：.app-tooltip 用 95% 实色底，无需模糊层 */

/* ===================================================================
   表单高密度排版：高频外露 / 低频收纳 / 同类合并 / 极简紧凑
   只覆盖尺寸与间距，不改任何 id / class 语义 / 事件选择器。
   =================================================================== */
/* 文本类输入框收敛到 32px 起。显式排除 checkbox/radio/range/color/file/hidden：
   .char-filter-chip 里 width/height:1px 的隐藏 checkbox 一旦吃到 min-height 就会撑破胶囊；
   .vd-bar-range 的 height:100% 覆盖层也不能被碰。范围限定 .form-group，避免顶到
   档案页 .cp-prop .cp-input 的 28px 网格（那一区"精修不改结构"）。 */
.form-group input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]),
.form-group textarea { min-height: 32px; box-sizing: border-box; }
textarea { resize: vertical; }              /* 默认只允许纵向拉伸 */
.form-group { margin-bottom: 12px; }
.tl-ins-body { padding: 12px 14px; }
.tl-ins-body .form-group { margin-bottom: 12px; }
.cp-attr-grid .cp-prop > label { font-size: 0.75rem; color: var(--text-muted); }
/* 快捷筛选胶囊：上下左右间距全部收到 6px 以内 */
.char-filter-chip { gap: 4px; padding: 3px 6px; }
.char-filter-row .char-filter-chip { padding: 3px 6px; }
.char-filter-chips { gap: 6px; }
/* 时间线事件表单：POV+参与角色、轨道(40%)+日期(60%) 同类合并，严格单行 */
.tl-form-row--cast, .tl-form-row--line-date {
  display: flex; align-items: flex-start; gap: 10px; margin-bottom: 12px;
}
.tl-form-row--cast > .form-group { flex: 1 1 50%; min-width: 0; margin-bottom: 0; }
.tl-form-row--line-date > .tl-form-line { flex: 0 0 40%; min-width: 0; margin-bottom: 0; }
.tl-form-row--line-date > .tl-form-date { flex: 0 0 60%; min-width: 0; }
.tl-form-row--line-date > .tl-form-date > .form-group { margin-bottom: 0; }
.tl-ins-body textarea { min-height: 0; }   /* rows="2" 说了算，别被全局 min-height 顶高 */
/* 末日模拟：375px 下两栏必然挤碎，窄屏改回竖向，绝不横向溢出 */
@media (max-width: 520px) {
  .tl-form-row--cast, .tl-form-row--line-date { flex-direction: column; }
  .tl-form-row--cast > .form-group,
  .tl-form-row--line-date > .tl-form-line,
  .tl-form-row--line-date > .tl-form-date { flex: 1 1 auto; width: 100%; }
  .tl-form-row--cast > .form-group { margin-bottom: 12px; }
}

/* ===================================================================
   思维导图节点属性 Inspector：三组高密度重排
   只加布局/皮肤，不改任何 id / data-* / 事件选择器；横向行一律 gap:8px + min-width:0。
   =================================================================== */
.mm-ig { display: block; min-width: 0; }
.mm-ig--appearance, .mm-ig--branch {
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border-color);
}
.mm-ig-head { margin-bottom: 8px; }
/* 组分割线只由 .mm-ig--* 提供：旧 .mm-inspector-sect / .mm-acc 自带虚线与上间距会叠成双线，清掉 */
.mm-ig .mm-inspector-sect.mm-ig-head,
.mm-ig--appearance .mm-acc { margin-top: 0; padding-top: 0; border-top: 0; }
.mm-ig--appearance .mm-acc { margin-bottom: 8px; }
.mm-ig-row { display: flex; align-items: center; gap: 8px; min-width: 0; margin-bottom: 8px; }
.mm-ig-row > * { min-width: 0; }                 /* 长内容绝不撑爆横向行 */
.mm-ig-row > .mm-pop-trigger,
.mm-ig-row > input[type="range"] { flex: 1 1 auto; }
.mm-ig-row > .mm-seg, .mm-ig-row > b { flex: 0 0 auto; }
.mm-ig-row > b { font-size: 0.75rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.mm-ig-row .mm-micro, .mm-ig-full > label, .mm-ig .form-group > label {
  font-size: 0.75rem; color: var(--text-muted); flex: 0 0 auto; white-space: nowrap;
}
.mm-ig-full { margin-bottom: 8px; }
.mm-ig-full > input { min-width: 0; width: 100%; }
/* 面板固定 320px、body padding 14px → 内容仅 292px：
   文字行必须把 6 个控件压到 280px 内，才能真正"一行搞定"而不换行 */
.mm-ig-row--text > input[type="range"] { flex: 1 1 80px; min-width: 60px; }
.mm-ig-row--text .mm-seg-btn { width: 24px; height: 24px; padding: 0; }
/* 旧 .is-default 的半透明虚线框与圆点斜线冲突，状态全部交给 ::after 表达 */
.mm-swatch-btn.mm-color-dot.is-default { opacity: 1; border-style: solid; }

/* 全站颜色入口统一：20×20 圆形色块，无颜色 = 带斜线的透明圆 */
.mm-swatch-btn.mm-color-dot {
  position: relative; display: inline-flex; flex: 0 0 20px;
  width: 20px; height: 20px; min-width: 20px; padding: 0;
  border-radius: 50%; border: 1px solid var(--border-strong);
  background: var(--mm-btn-fill, transparent);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
  cursor: pointer; overflow: hidden; vertical-align: middle;
}
.mm-swatch-btn.mm-color-dot:hover { border-color: var(--primary-color); }
.mm-swatch-btn.mm-color-dot:focus-visible {
  outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 26%, transparent);
}
.mm-swatch-btn.mm-color-dot.is-default::after,
.mm-swatch-btn.mm-color-dot.is-mixed::after,
.mm-swatch-btn.mm-color-dot.is-transparent::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background:
    repeating-linear-gradient(45deg, transparent 0 3px,
      color-mix(in srgb, var(--text-muted) 60%, transparent) 3px 4px),
    color-mix(in srgb, var(--panel-bg) 82%, transparent);
}
.mm-swatch-btn.mm-color-dot.is-mixed::after { transform: rotate(90deg); }
.mm-swatch-btn.mm-color-dot.is-transparent { background: transparent; }

/* 边框行：纯图标，不出现任何文字 */
.mm-pop-trigger.mm-icon-only span { display: none; }
.mm-ig-row--border .mm-pop-trigger.compact { flex: 0 0 auto; min-width: 40px; padding: 0 4px; }
.mm-ig-row--border .mm-pop-trigger svg { display: block; }

/* 形状触发器 + 折叠层：保留原 #inspector-shape-grid 及其点击委托，不碰 mm-pop 值传递 */
.mm-shape-trigger {
  display: inline-flex; align-items: center; justify-content: space-between; gap: 6px;
  flex: 1 1 120px; min-width: 0; height: 28px; padding: 0 8px;
  border: 1px solid var(--border-color); border-radius: var(--radius-sm);
  background: var(--input-sunken); color: var(--text-color); cursor: pointer;
}
.mm-shape-trigger:focus-visible { outline: none; border-color: var(--primary-color); box-shadow: var(--input-focus-ring); }
.mm-shape-trigger[aria-expanded="true"] { border-color: var(--primary-color); }
.mm-shape-trigger-icon { display: inline-flex; min-width: 0; }
.mm-shape-trigger-icon svg { width: 22px; height: 22px; display: block; }
.mm-shape-trigger-caret { font-size: 0.65rem; color: var(--text-muted); flex: 0 0 auto; }
/* 形状真浮层：占位容器不占文档流；grid 打开时整个搬进挂 body 的 .pop-layer */
.mm-shape-wrap { display: none; }
.mm-shape-pop { padding: 8px; min-width: 208px; }
.mm-shape-pop .mm-shape-grid { margin-bottom: 0; grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* 边框 4 个纯图标：等宽均分可用宽度，绝对不出现文字 */
.mm-border-styles { display: flex; align-items: center; gap: 8px; flex: 1 1 0; min-width: 0; }
.mm-border-icon {
  position: relative; flex: 1 1 0; min-width: 0; height: 28px; padding: 0;
  border: 1px solid var(--border-color); border-radius: var(--radius-sm);
  background: var(--input-sunken); color: var(--text-muted); cursor: pointer;
}
.mm-border-icon::before {
  content: ''; position: absolute; left: 6px; right: 6px; top: 50%; transform: translateY(-50%);
  border-top: 2px solid currentColor;
}
.mm-border-icon[data-border-style="none"]::before {
  border-top: 0; height: 12px;
  background: linear-gradient(to top right, transparent 46%, currentColor 47%, currentColor 53%, transparent 54%);
}
.mm-border-icon[data-border-style="dashed"]::before { border-top-style: dashed; }
.mm-border-icon[data-border-style="double"]::before {
  border-top: 0; height: 7px;
  border-top: 2px solid currentColor; border-bottom: 2px solid currentColor;
}
.mm-border-icon:hover { border-color: var(--primary-color); color: var(--primary-color); }
.mm-border-icon.active {
  border-color: var(--primary-color); color: var(--primary-color);
  background: color-mix(in srgb, var(--primary-color) 10%, transparent);
}
.mm-border-icon:focus-visible { outline: none; box-shadow: var(--input-focus-ring); }
/* 兼容锚点：原下拉按钮的 id / 绑定全部保留，但视觉上不再出现 */
.mm-compat-hidden { display: none !important; }
.mm-compat-btn { flex: 0 0 40px; min-width: 40px; height: 28px; padding: 0 4px; }
.mm-compat-btn svg { display: block; }

/* 视觉隐藏、读屏可见：320px 面板内容仅 292px，字号标签不能再吃掉 32px */
.mm-sr-only {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
/* 调色盘与形状浮层统一挂 .pop-layer 皮肤，与全站浮层完全一致 */
.mm-pop.pop-layer, .mm-shape-pop.pop-layer {
  background: var(--surface-raised);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-popover);
}

/* 320px 专项：面板 width:320px / max-width:calc(100% - 28px)，body padding:14px
   → 桌面内容 292px、320px 视口下内容 264px。文字组按 264px 精算强制单行。 */
.mm-ig-row--text { flex-wrap: nowrap; gap: 8px; }
.mm-ig-row--text > * { min-width: 0; }
.mm-ig-row--text > input[type="range"] { flex: 1 1 80px; min-width: 48px; }
.mm-ig-row--text > .mm-seg { flex: 0 0 auto; }
/* 375px / 320px 末日模拟：其它组允许换行降级，文字组例外绝不换行 */
@media (max-width: 520px) {
  .mm-ig-row { flex-wrap: wrap; }
  .mm-ig-row--text { flex-wrap: nowrap; gap: 8px; }
  .mm-ig-row > input[type="range"] { flex: 1 1 100px; }
  .mm-ig-row--text > input[type="range"] { flex: 1 1 60px; min-width: 40px; }
  .mm-ig-row > .mm-pop-trigger { flex: 1 1 90px; }
  .mm-shape-trigger { flex: 1 1 100px; }
  .mm-ig--appearance, .mm-ig--branch { margin-top: 12px; padding-top: 12px; }
}

/* 保留的历史死规则说明：下面两组（.mm-ig-row* / .mm-acc* / .mm-compat-hidden 等）
   是上一代 Inspector 布局的覆盖规则；当前主面板已改用 .mm-sec / .mm-sec-row，
   HTML 中 0 命中。按本轮约定不删：连线面板与其它视图仍复用 .mm-swatch-btn /
   .mm-seg-btn / .mm-pop-trigger / .mm-scope-chip 等基础类，整块清理会牵动全站皮肤；
   保留但不新增引用，后续单独开一轮清除。 */

/* ===================================================================
   终极紧凑：60px 形状触发器 / 全图标化 / 4 图标均分整行
   纯 CSS 覆盖，ni-*.js 的文本写入逻辑一行未改。
   =================================================================== */
/* 形状：固定 60px，只留缩略图；整行靠左自然靠拢，不再留大空白 */
.mm-ig-row--shape { justify-content: flex-start; }
.mm-ig-row--shape .mm-shape-trigger {
  flex: 0 0 60px; width: 60px; min-width: 60px; height: 26px;
  padding: 0; justify-content: center; gap: 0;
}
.mm-ig-row--shape .mm-shape-trigger-icon svg { width: 20px; height: 20px; }
.mm-ig-row--shape > .mm-micro { flex: 0 0 auto; }

/* 边框：4 个纯图标均分整行，零多余横向空隙 */
.mm-ig-row--border .mm-border-styles { flex: 1 1 0; width: 100%; gap: 4px; }
.mm-ig-row--border .mm-border-icon { flex: 1 1 0; height: 26px; }
.mm-ig-row--border .mm-border-icon::before { left: 5px; right: 5px; }

/* 线型 / 终点：触发器只看 SVG 图标，JS 写进来的 <span> 文字一律隐藏（业务零改动）。
   混合态原本只有 <span>混合</span>，隐藏后用 :not(:has(svg)) 补一个极简「—」。 */
.mm-ig-row #inspector-branch-type > span,
.mm-ig-row #inspector-branch-arrow > span { display: none !important; }
.mm-ig-row #inspector-branch-type,
.mm-ig-row #inspector-branch-arrow {
  flex: 0 0 44px; width: 44px; min-width: 0; height: 26px;
  justify-content: center; gap: 0; padding: 0 5px;
}
.mm-ig-row #inspector-branch-type svg,
.mm-ig-row #inspector-branch-arrow svg { width: 34px; height: 14px; }
.mm-ig-row #inspector-branch-type:not(:has(svg))::before,
.mm-ig-row #inspector-branch-arrow:not(:has(svg))::before {
  content: '—'; color: var(--text-muted); font-size: 0.75rem;
}

/* 全部分段按钮统一浅灰，彻底去掉深色实底高亮 */
.mm-ig-row .mm-seg-btn,
.mm-ig-row #inspector-branch-dash .mm-seg-btn {
  width: 30px; height: 26px;
  background: var(--input-sunken); border: 1px solid var(--border-color); color: var(--text-muted);
}
.mm-ig-row .mm-seg-btn.active,
.mm-ig-row #inspector-branch-dash .mm-seg-btn.active {
  background: var(--hover-bg, color-mix(in srgb, var(--text-muted) 12%, transparent));
  border-color: var(--primary-color); color: var(--primary-color);
}
.mm-ig-row .mm-seg-btn:hover,
.mm-ig-row #inspector-branch-dash .mm-seg-btn:hover {
  background: var(--hover-bg, color-mix(in srgb, var(--text-muted) 12%, transparent));
  color: var(--text-color);
}
/* 文字行仍要 24px，否则 320px 一行放不下 */
.mm-ig-row--text .mm-seg-btn { width: 24px; height: 24px; }

/* 「作用于：标题」灰底胶囊 → 紧贴标题的极小纯文本 */
.mm-scope-chip {
  margin-left: 4px; padding: 0; border-radius: 0; background: transparent;
  color: var(--text-muted); font-size: 0.66rem; font-weight: 400;
}

/* 颜色圆点旁的撤销箭头：全站统一撤销，不再挂回退按钮；id / 绑定全部保留 */
.mm-compat-hidden, #inspector-color-reset, #inspector-branch-reset { display: none !important; }

/* 粗细滑块与数值压到极限 */
.mm-ig-row--width { gap: 4px !important; }
.mm-ig-row--width > input[type="range"] { flex: 1 1 auto; min-width: 0; }
.mm-ig-row--width > b { flex: 0 0 auto; }

/* 所有行内控件严格垂直居中：滑块 / 图标 / 圆点同一水平线 */
.mm-ig-row { align-items: center; }
.mm-ig-row input[type="range"] { margin: 0; align-self: center; }
.mm-ig-row > .mm-color-dot, .mm-ig-row > .mm-pop-trigger { align-self: center; }

/* ===================================================================
   Inspector 定稿：四大 <details> 折叠 / 严格双列 / 标准标签 / 纯图标
   折叠是原生 details[open] 状态：不写 App.data、不进撤销栈、不落 localStorage
   =================================================================== */
/* 强分割线挂在 details 自身，折叠时边界依旧在 */
.mm-sec { min-width: 0; margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border-color); }
.mm-sec > summary {
  list-style: none; display: flex; align-items: center; gap: 6px; min-width: 0;
  padding: 0 0 8px; cursor: pointer; font-size: 0.82rem; font-weight: 600; color: var(--text-color);
}
.mm-sec > summary::-webkit-details-marker { display: none; }
.mm-sec-ico { flex: 0 0 auto; font-size: 0.82rem; color: var(--primary-color); }
.mm-sec-chevron { flex: 0 0 auto; width: 12px; height: 12px; margin-left: auto;
  color: var(--text-muted); transition: transform 0.18s ease-out; }
.mm-sec[open] > summary .mm-sec-chevron { transform: rotate(90deg); }
.mm-sec > summary:focus-visible { outline: none; border-radius: var(--radius-sm); box-shadow: var(--input-focus-ring); }
.mm-sec-body { min-width: 0; padding: 0 0 2px; }
/* 折叠后仍保留的兼容锚点：id / data-* / 事件绑定全在，只是不占空间 */
.mm-sec-hidden { display: none !important; }

/* 标准标签：正常字号、常规文字色，覆盖 .mm-inspector-body label 的小字灰 */
.mm-sec .mm-sec-row > .mm-lab {
  display: flex; align-items: center; flex: 0 0 48px; width: 48px; min-width: 0;
  margin: 0; font-size: 0.82rem; font-weight: 400; color: var(--text-color); white-space: nowrap;
}
/* 双列：单组行 = 标签 | 控件；两组行 = 标签 | 控件 | 标签 | 控件，绝不出现第三组 */
.mm-sec .mm-sec-row { display: flex; align-items: center; gap: 8px; min-width: 0; margin-bottom: 8px; }
.mm-sec .mm-sec-row:last-child { margin-bottom: 0; }
.mm-sec .mm-sec-row--pair {
  display: grid; align-items: center; gap: 8px;
  grid-template-columns: 48px minmax(0, 1fr) 48px minmax(0, 1fr);
}
.mm-sec .mm-sec-row--pair > .mm-lab { width: 48px; }
.mm-sec-ctl { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0; }
.mm-sec-ctl > input[type="range"] { flex: 1 1 auto; min-width: 0; }
.mm-sec-ctl > b { flex: 0 0 auto; font-size: 0.78rem; font-weight: 600; color: var(--text-color); font-variant-numeric: tabular-nums; }
.mm-sec .mm-sec-row > input[type="text"] { flex: 1 1 auto; min-width: 0; }
/* 保留的死规则：文字模块拆成两行后 .mm-sec-tools 在当前 DOM 已 0 复用。
   按本轮约定不删死规则（删了也只省一行，却会动到 .mm-sec-ctl 的工具组降级），留待后续统一清理。 */
.mm-sec-tools { flex-wrap: nowrap; }

/* 纯图标触发器：固定宽、SVG 居中，按钮内绝不出现文字 */
.mm-sec .mm-sec-row .mm-pop-trigger,
.mm-sec .mm-sec-row .mm-shape-trigger {
  justify-self: start; display: inline-flex; align-items: center; justify-content: center;
  width: 46px; min-width: 0; height: 26px; padding: 0 4px; gap: 0;
}
.mm-sec .mm-sec-row .mm-shape-trigger { width: 60px; }
.mm-sec .mm-sec-row .mm-pop-trigger > span { display: none; }
/* 形状缩略图的容器本身也是 span，必须单独放行，否则触发器会变成空白按钮 */
.mm-sec .mm-sec-row .mm-shape-trigger-icon { display: inline-flex; }
.mm-sec .mm-sec-row .mm-pop-trigger svg,
.mm-sec .mm-sec-row .mm-shape-trigger svg { display: block; max-width: 100%; }
.mm-sec .mm-sec-row > .mm-color-dot { justify-self: start; }
/* 混合态：可见标记放在触发器「内部右上角」，绝对定位不占文档流、不挤压双列第二组。
   ≠ 角标 + 虚线框提供不依赖悬停的可见性；title / aria-label 只作补充说明。 */
.mm-sec .mm-pop-trigger.is-mixed { position: relative; border-style: dashed; color: var(--text-muted); }
/* 角标完全落在触发器内部（top/right:0）：不依赖给 summary 留白，任何滚动位置都不会被
   .mm-inspector-body 的 overflow-y:auto 裁掉，也不挤双列第二组。
   暗色保底：文字/底色取自同一对 --text-color / --panel-bg 令牌（任意主题下必高对比），
   边框改用 primary 与 text-color 的 color-mix 中间值，不再出现「1px panel-bg 边框在暗色下隐形」。 */
.mm-sec .mm-pop-trigger.is-mixed::after,
.mm-sec .mm-color-dot.is-mixed::before {
  content: '≠'; position: absolute; top: 0; right: 0; z-index: 1;
  min-width: 13px; height: 13px; line-height: 11px; padding: 0 2px; box-sizing: border-box;
  border: 1px solid color-mix(in srgb, var(--primary-color) 65%, var(--text-color));
  border-radius: 999px;
  background: var(--panel-bg); color: var(--text-color);
  font-size: 9px; font-weight: 700; text-align: center; pointer-events: none;
}
/* 颜色圆点的 ::after 已被「混合/跟随默认」斜纹占用，角标只能走 ::before */
.mm-sec .mm-color-dot.is-mixed { position: relative; }

/* 「作用于：标题/正文」：极简纯文本，仍可点击切换作用域 */
.mm-sec .mm-scope-chip {
  margin-left: auto; margin-right: 2px; padding: 0; border: 0; background: transparent;
  color: var(--text-muted); font-family: inherit; font-size: 0.68rem; font-weight: 400;
  letter-spacing: 0; cursor: pointer;
}
.mm-sec .mm-scope-chip:hover { color: var(--primary-color); }
.mm-sec .mm-scope-chip:focus-visible { outline: none; box-shadow: var(--input-focus-ring); }

/* 分段 / 平铺图标按钮：统一浅灰底，只有 SVG */
.mm-sec .mm-seg-btn {
  width: 28px; height: 26px; padding: 0;
  background: var(--input-sunken); border: 1px solid var(--border-color); color: var(--text-color);
}
.mm-sec .mm-seg-btn:hover { border-color: var(--primary-color); color: var(--primary-color); }
.mm-sec .mm-seg-btn.active {
  background: var(--hover-bg, color-mix(in srgb, var(--text-muted) 12%, transparent));
  border-color: var(--primary-color); color: var(--primary-color);
}
.mm-sec .mm-seg-btn svg { display: block; pointer-events: none; }

/* 375 / 320px：面板内容仅 264px，标签收窄但双列不塌、控件不换行 */
@media (max-width: 520px) {
  .mm-sec .mm-sec-row--pair {
    gap: 6px; grid-template-columns: 42px minmax(0, 1fr) 42px minmax(0, 1fr);
  }
  .mm-sec .mm-sec-row > .mm-lab { flex-basis: 42px; width: 42px; font-size: 0.78rem; }
  .mm-sec .mm-sec-row--pair > .mm-lab { width: 42px; }
  .mm-sec .mm-sec-row .mm-pop-trigger { width: 40px; }
  .mm-sec .mm-sec-row .mm-shape-trigger { width: 56px; }
}
/* 边框 4 图标下拉浮层：纯图标、无文字，皮肤沿用 .pop-layer */
.mm-border-pop.pop-layer { padding: 8px; background: var(--surface-raised);
  border-radius: var(--radius-md); box-shadow: var(--shadow-popover); }
.mm-border-pop .mm-border-styles { display: flex; gap: 6px; }
.mm-border-pop .mm-border-icon { flex: 0 0 44px; width: 44px; height: 28px; }

/* ---- 4 / 阶段 B：触屏手势与 touch-action ----
   只作用于"可拖拽子元素"：阻止 iOS 把拖拽手势抢去滚动页面 / 触发边缘返回；
   画布本身的平移走各自逻辑（导图/关系网 d3.zoom、时间线原生滚动），不动。 */
.drag-handle,
.tl-bar, .tl-point-card, .tl-pace, .tl-lane-grip,
#char-radar, #cdm-radar, .vd-radar svg, .vd-radar-host, .cdm-radar-host, .cdm-bar-host,
#network-svg, #network-svg .node,
.mm-node {
  touch-action: none;
}

/* ---- 阶段 B：Hover 消除（触屏不残留悬停态） ----
   仅 hover-capable 设备保留悬停反馈；纯触屏用 :active / :focus-visible 代替。 */
@media (hover: none) {
  .btn:hover, .home-btn:hover, .project-item:hover, .mm-thumb:hover,
  .tl-settings-check:hover, .legend-item:hover, .vd-row-del:hover,
  .vd-row-add:hover, .template-btn:hover, .btn-import:hover,
  .rel-toolbar .btn:hover, .topbar-more-menu .btn:hover,
  .cdm-nav-item:hover, .ol-lp-item:hover, .cp-side .cp-field:hover {
    background: transparent !important;
    color: inherit !important;
    transform: none !important;
    box-shadow: none !important;
    border-color: var(--border-color) !important;
  }
  /* 触屏点击反馈：按下态给一点缩放回弹，替代 hover 的"鼠标悬停才知道能点" */
  .btn:active, .home-btn:active, .mm-dock-btn:active, .legend-item:active,
  .template-btn:active, .btn-import:active, .tl-lane-grip:active {
    transform: scale(0.94);
    transition: transform 0.08s ease;
  }
}
/* 键盘 / 焦点可见性：触屏点按后若元素保留焦点，给出清晰描边 */
.btn:focus-visible, .btn-icon:focus-visible, .home-btn:focus-visible, .project-item:focus-visible,
.mm-dock-btn:focus-visible, .tl-lane-grip:focus-visible, .legend-item:focus-visible,
.template-btn:focus-visible, .btn-import:focus-visible,
.ol-icon-btn:focus-visible, .ol-fold:focus-visible, .ol-expand-btn:focus-visible,
.tl-mode-toggle button:focus-visible, .tl-proj-toggle:focus-visible, .tl-search-toggle:focus-visible,
.tl-cal-nav:focus-visible, .tl-cal-era-btn:focus-visible,
.tl-lane-menu-item:focus-visible, .tl-quick-menu-item:focus-visible,
.cc-jump-btn:focus-visible, .cc-more-btn:focus-visible, .cc-link:focus-visible,
.mm-collapse-badge:focus-visible, .collapse-btn:focus-visible,
.mm-pop-swatch:focus-visible, .mm-pop-opt:focus-visible, .mm-pop-trigger:focus-visible,
.cf-pill-x:focus-visible, .ol-tag-x:focus-visible, .ol-tag:focus-visible, .ol-link-badge:focus-visible,
.legend-collapse-btn:focus-visible, .rel-drawer-close:focus-visible, .char-filter-pop-close:focus-visible, .tl-tool-btn:focus-visible, .legend-icon-btn:focus-visible, .char-add-icon:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 15%, transparent);
}
