/* 小狐狸 see me — Foxy 键盘布局可视化编辑器
 * style.css — 整体界面与键盘预览样式
 */

:root {
  --bg: #121316;
  --bg-soft: #1a1c20;
  --card: #202329;
  --card-inner: #262a31;
  --border: #34383f;
  --text: #e6e8ec;
  --text-dim: #9aa0a8;
  --accent: #4c8dff;
  --accent-soft: #2c3a55;
  --danger: #e2564f;
  --ok: #48c26a;
  --warn: #e2a33f;
  --err: #ee6b64;
  --sel: #ffb020;
  --radius: 10px;
  --mono: ui-monospace, SFMono-Regular, Consolas, "Courier New", monospace;
  /* 中央操作区（内容列）的几何：顶栏与 main 共用，浮动工具也靠它对齐。
   * 改宽度/内边距时请只改这两个变量 —— 浮动按钮的"贴边"完全依赖它们。 */
  --content-max: 1298px;
  --content-pad-x: 20px;
}

* { box-sizing: border-box; }
/* hidden 属性兜底：UA 样式里的 [hidden]{display:none} 会被作者样式覆盖，
 * 例如 .form-row{display:flex} 会让带 hidden 的行照常显示。
 * 缺了这条，#op-folder / #op-folder-hint 的 hidden 就形同虚设。 */
[hidden] { display: none !important; }
html { scrollbar-color: #3a3e45 #16171a; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.6 -apple-system, "Segoe UI", "Microsoft YaHei", "PingFang SC", sans-serif;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code { font-family: var(--mono); background: #2b2f36; padding: 1px 5px; border-radius: 4px; font-size: 0.92em; }

/* ---------------- 网页配色（整站明暗主题） ----------------
 * 机制：`:root` 仍是暗色基准（现状零变化）；浅色整套覆盖写在 `html.site-light` 下。
 * JS 只在 <html> 上切换 site-light / site-dark / site-auto 三类：
 * · site-light —— 强制浅色；
 * · site-dark  —— 强制暗色（= 基准，不写覆盖）；
 * · site-auto  —— 无覆盖，跟随下面这条媒体查询（系统浅色时自动变浅）。
 * 默认 site-auto：用户没点过按钮就跟系统；点过则 localStorage 记住选择。
 * 注意：这是**整站配色**，与键盘预览的 .kb-dark/.kb-light（只管键盘那块）无关。 */
@media (prefers-color-scheme: light) {
  html.site-auto {
    --bg: #f2f3f5;
    --bg-soft: #e8eaed;
    --card: #ffffff;
    --card-inner: #f0f1f4;
    --border: #d4d7dd;
    --text: #1c1e22;
    --text-dim: #5f656e;
    --accent-soft: #d7e6ff;
  }
}
html.site-light {
  --bg: #f2f3f5;
  --bg-soft: #e8eaed;
  --card: #ffffff;
  --card-inner: #f0f1f4;
  --border: #d4d7dd;
  --text: #1c1e22;
  --text-dim: #5f656e;
  --accent-soft: #d7e6ff;
}
/* 浅色下仍用变量、但散落在各处的硬编码深色（ hover / 输入框 / 顶栏渐变等） */
html.site-light body, html.site-auto body { color-scheme: light; }
@media (prefers-color-scheme: light) {
  html.site-auto .topbar { background: linear-gradient(180deg, #ffffff, #eef0f3); }
  html.site-auto button, html.site-auto .mini-button, html.site-auto .repo-link { background: #ffffff; }
  html.site-auto button:hover, html.site-auto .mini-button:hover, html.site-auto .repo-link:hover { background: #eef0f3; border-color: #b9bdc4; }
  /* 主按钮蓝底赢回（同下方的 site-light 病根：白底 tag 规则权重更高） */
  html.site-auto button.primary, html.site-auto .mini-button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
  html.site-auto button.primary:hover, html.site-auto .mini-button.primary:hover { background: #6ba2ff; border-color: #6ba2ff; color: #fff; }
  html.site-auto .mini-input, html.site-auto .mini-select { background: #ffffff; color: #1c1e22; }
  html.site-auto code { background: #e8eaed; }
  html.site-auto .pill { background: #eef0f3; }
  html.site-auto details.card > summary, html.site-auto .panel > summary { background: #eef0f3; }
  html.site-auto .dialog-titlebar { background: #eef0f3; }
  html.site-auto .dialog-toolbar { background: #f0f1f4; }
  html.site-auto .json-editor { background: #ffffff; color: #1c1e22; }
  html.site-auto .json-issues { background: #f0f1f4; }
  html.site-auto .def-summary:hover { background: #eef0f3; }
  html.site-auto .def-item.def-row-click:hover { background: #eef0f3; border-color: #b9bdc4; }
  html.site-auto .picker-item:hover { background: #eef0f3; }
  html.site-auto .picker-item.active { background: #d7e6ff; }
  html.site-auto .picker-group { background: #eef0f3; }
  html.site-auto .search-clear:hover { background: #e2e4e9; color: #1c1e22; }
  html.site-auto .issue-locatable:hover { background: #e8eaed; }
  html.site-auto .usage-item { background: #f0f1f4; }
  html.site-auto .usage-item.usage-jump:hover { background: #e2e9f5; }
  html.site-auto .action-def-editor .action-editor, html.site-auto .macro-step, html.site-auto .action-editor { background: #f0f1f4; }
  html.site-auto .macro-step-index { background: #e2e4e9; }
  html.site-auto .footnote code { background: #e8eaed; }
}
html.site-light .topbar { background: linear-gradient(180deg, #ffffff, #eef0f3); }
html.site-light button, html.site-light .mini-button, html.site-light .repo-link { background: #ffffff; }
html.site-light button:hover, html.site-light .mini-button:hover, html.site-light .repo-link:hover { background: #eef0f3; border-color: #b9bdc4; }
/* 浅色下主按钮蓝底必须赢回上面的白底覆盖：`html.site-light button` 权重 (0,1,2)
 * 高过 `button.primary` 的 (0,1,1)（多一个 html tag），不单独写就是白字浮白底、
 * 对比度 1.0 完全隐形（应用/新建按键定义/新建动作/新建宏全中招，与 pill 同一病根）。 */
html.site-light button.primary, html.site-light .mini-button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
html.site-light button.primary:hover, html.site-light .mini-button.primary:hover { background: #6ba2ff; border-color: #6ba2ff; color: #fff; }
html.site-light .mini-input, html.site-light .mini-select { background: #ffffff; color: #1c1e22; }
html.site-light code { background: #e8eaed; }
html.site-light .pill { background: #eef0f3; }
html.site-light details.card > summary, html.site-light .panel > summary { background: #eef0f3; }
html.site-light .dialog-titlebar { background: #eef0f3; }
html.site-light .dialog-toolbar { background: #f0f1f4; }
html.site-light .json-editor { background: #ffffff; color: #1c1e22; }
html.site-light .json-issues { background: #f0f1f4; }
html.site-light .def-summary:hover { background: #eef0f3; }
html.site-light .def-item.def-row-click:hover { background: #eef0f3; border-color: #b9bdc4; }
html.site-light .picker-item:hover { background: #eef0f3; }
html.site-light .picker-item.active { background: #d7e6ff; }
html.site-light .picker-group { background: #eef0f3; }
html.site-light .search-clear:hover { background: #e2e4e9; color: #1c1e22; }
html.site-light .issue-locatable:hover { background: #e8eaed; }
html.site-light .usage-item { background: #f0f1f4; }
html.site-light .usage-item.usage-jump:hover { background: #e2e9f5; }
html.site-light .action-def-editor .action-editor, html.site-light .macro-step, html.site-light .action-editor { background: #f0f1f4; }
html.site-light .macro-step-index { background: #e2e4e9; }
html.site-light .footnote code { background: #e8eaed; }
/* card-inner 在浅色下已是 #f0f1f4，区段内的 .variant-row / .gesture-row 分隔线同步变浅 */
html.site-light .variant-row, html.site-light .gesture-row { border-bottom-color: #d4d7dd; }
@media (prefers-color-scheme: light) {
  html.site-auto .variant-row, html.site-auto .gesture-row { border-bottom-color: #d4d7dd; }
}

/* ---------------- 顶栏 ---------------- */
.topbar {
  padding: 14px 20px 10px;
  border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg, #1d2026, #16181c);
}
/* 标题在左，撤销/重做固定在右侧；窄屏自动换行（按钮组换到下一行右对齐） */
.topbar-main {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  max-width: var(--content-max);
  margin: 0 auto;
}
.topbar-title { min-width: 0; }
.topbar-actions { display: flex; gap: 8px; align-items: center; flex-shrink: 0; }
.topbar h1 { margin: 0; font-size: 22px; letter-spacing: 0.5px; display: flex; align-items: center; gap: 10px; }
/* 顶栏标题本身是本项目仓库的链接：保持标题观感，不作为普通链接染成蓝色 */
.brand-link { color: inherit; text-decoration: none; display: inline-block; }
.brand-link:hover { text-decoration: none; opacity: 0.85; }
/* 标题前的狐狸图标：与文字基线对齐，纯装饰不参与朗读 */
.topbar .logo {
  font-size: 24px;
  margin-right: 7px;
  vertical-align: -2px;
  line-height: 1;
}
/* 顶栏右侧的仓库链接（带 GitHub 图标）*/
.repo-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #2c3037;
  color: var(--text);
  font-size: 13px;
  line-height: 1.5;
  text-decoration: none;
}
.repo-link:hover { background: #363b44; border-color: #4a4f58; text-decoration: none; }
.repo-link svg { flex-shrink: 0; }
/* 标题后的仓库图标按钮：无文字纯图标，比右侧常驻按钮小一圈 */
.repo-link-title { padding: 4px 7px; border-radius: 6px; line-height: 1; }
/* 网页配色三图标按钮组：无文字，当前项高亮 */
.site-theme-group { display: inline-flex; gap: 4px; align-items: center; }
.icon-only { padding: 5px 8px; line-height: 1; display: inline-flex; align-items: center; }
.icon-only svg { display: block; }
.site-theme-btn.active { border-color: var(--accent); color: var(--accent); }
/* 图标左侧的细分隔线，把仓库链接与撤销/重做按钮区分开 */
.topbar-sep { width: 1px; align-self: stretch; margin: 2px 2px; background: var(--border); }
/* 键盘可达性：焦点可见 */
.brand-link:focus-visible, .repo-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.topbar h2 { margin: 4px 0 0; font-size: 13px; font-weight: normal; color: var(--text-dim); }

main { padding: 14px var(--content-pad-x) 40px; max-width: var(--content-max); margin: 0 auto; }

/* ---------------- 面板与卡片 ---------------- */
.panel, details.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin: 0 0 14px;
  padding: 0;
  overflow: hidden;
}
details.card > summary, .panel > summary {
  cursor: pointer;
  padding: 9px 14px;
  font-weight: 600;
  background: #23262c;
  border-bottom: 1px solid var(--border);
  user-select: none;
  list-style-position: inside;
}
details.card > summary::-webkit-details-marker, .panel > summary::-webkit-details-marker { color: var(--text-dim); }
details.card[open] > summary { border-bottom: 1px solid var(--border); }
details.card > *:not(summary) { margin-left: 14px; margin-right: 14px; }
details.card > *:last-child { margin-bottom: 14px; }
details.card > summary + * { margin-top: 12px; }

.inner-card {
  background: var(--card-inner);
  border: 1px solid var(--border);
  border-radius: 8px;
  margin: 10px 0 !important;
  padding: 0;
}
.inner-card > summary {
  cursor: pointer;
  padding: 7px 12px;
  font-weight: 600;
  font-size: 13px;
  color: #cdd2da;
  user-select: none;
}
/* 浅色下折叠子卡标题（基本信息/手势/状态变体/按键颜色覆盖等）加深，
 * 否则浅灰白字浮浅底、看不清 */
html.site-light .inner-card > summary { color: #1c1e22; }
@media (prefers-color-scheme: light) {
  html.site-auto .inner-card > summary { color: #1c1e22; }
}
.inner-card .inner-card-body { padding: 10px 12px; }

.settings-block { padding: 10px 0; }
.mini-title { font-size: 12px; color: var(--text-dim); margin: 10px 0 6px; font-weight: 600; }

/* ---------------- 标签页 ---------------- */
.tabs { display: flex; gap: 6px; margin: 0 0 14px; flex-wrap: wrap; }
.tab {
  padding: 8px 18px;
  background: var(--bg-soft);
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 8px 8px 0 0;
  border-bottom: none;
  cursor: pointer;
  font-size: 14px;
}
.tab:hover { color: var(--text); }
.tab.active { background: var(--card); color: var(--text); font-weight: 600; border-color: var(--border); }
/* 浅色下未选中 tab 会被 `html.site-light button` 的白底盖成全白（tag 规则权重更高），
 * 与选中的白底只差字色字重，对比太弱；给未选中项浅灰底，选中白底+深字，对比拉开。 */
html.site-light .tab:not(.active) { background: #eef0f3; }
@media (prefers-color-scheme: light) {
  html.site-auto .tab:not(.active) { background: #eef0f3; }
}
/* 弹出菜单是有 popup profile 语义的另一类文档标签，仅用间距分隔，
 * 不改边框样式（避免与无效/残缺状态的虚线框混淆）。 */
.tab.tab-popup { margin-left: 22px; }
.tabpanel { display: none; }
.tabpanel.active { display: block; }

.workbench { display: flex; gap: 14px; align-items: flex-start; }
.col-main { flex: 1 1 auto; min-width: 0; }
.col-side { flex: 0 0 380px; }
/* 竖排时 align-items 的语义会从「行轴的顶对齐」翻转成「列轴的水平对齐」：
 * 沿用基准规则的 flex-start 会让子项按 max-content 收缩，宽网格（如 cc_grid_4
 * 的 48 列，.gedit 固有 1889px）就把 .col-main 撑到 1911px → 文档整体横向溢出
 * → 页面被横向放大、纵向反而滚不动，updateFloatTools() 永远读到 scrollTop=0，
 * 右上撤销/重做与右下回到顶部三个浮动按钮在布局编辑页再也不出现。
 * 竖排下必须 stretch，让 .col-main 与容器同宽，超宽网格只在自己的
 * .gedit-wrap 里横向滚动（这正是 .gedit-wrap{overflow-x:auto} 的用途）。 */
@media (max-width: 980px) {
  .workbench { flex-direction: column; align-items: stretch; }
  .col-main, .col-side { flex: 1 1 auto; width: 100%; max-width: 100%; }
}

/* ---------------- 工具栏与表单 ---------------- */
.toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 8px 0; }
button, .mini-button {
  background: #2c3037;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 5px 12px;
  cursor: pointer;
  font-size: 13px;
  line-height: 1.5;
}
button:hover, .mini-button:hover { background: #363b44; border-color: #4a4f58; }
button:disabled { opacity: 0.45; cursor: not-allowed; }
button.primary, .mini-button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
button.primary:hover { background: #6ba2ff; }
button.danger, .mini-button.danger { background: transparent; border-color: #6b3835; color: var(--danger); }
button.danger:hover, .mini-button.danger:hover { background: #3a2321; }
.icon-button {
  padding: 2px 8px; font-size: 13px; background: transparent; border: 1px solid transparent; color: var(--text-dim);
}
.icon-button:hover { color: var(--text); border-color: var(--border); }
.icon-button.danger:hover { color: var(--danger); }

.mini-input, .mini-select {
  background: #17191d;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 8px;
  font-size: 13px;
  max-width: 100%;
}
.mini-input.wide, .mini-select.wide { width: min(320px, 100%); }
.mini-input:focus, .mini-select:focus, textarea:focus { outline: 1px solid var(--accent); }
/* 高度比滑杆：跟 mini-label 一行，宽 110px 不抢位置；浅色下 track 用边框色 */
.mini-range { width: 110px; accent-color: var(--accent); cursor: pointer; }
/* 紧凑数字输入框（按键外观三项：圆角 / 水平间隙 / 垂直间隙）。
 * 原来这三项用滑杆，占的横向空间与可选范围无关（都固定 110px），
 * 而它们的范围很窄（0–24 / 0–16）—— 改成数字框后一行能省下可观空间，
 * 让六个开关挤进同一行；上下箭头也便于精确微调。 */
.num-input {
  width: 56px; flex: 0 0 auto; font-family: var(--mono);
  padding: 3px 4px 3px 6px; text-align: right;
}
/* 数字框右侧的单位（dp）：与输入框贴紧，不占额外换行点 */
.pt-unit { font-size: 11px; color: var(--text-dim); margin-left: -2px; }
#pt-height-val { font-family: var(--mono); font-size: 12px; min-width: 36px; display: inline-block; }
.mini-label { font-size: 12px; color: var(--text-dim); white-space: nowrap; display: inline-flex; align-items: center; gap: 4px; }
.mini-label.check { cursor: pointer; }
label.check { cursor: pointer; white-space: nowrap; }

.form-row { margin: 6px 0; display: flex; flex-direction: column; gap: 4px; }
.form-row.form-inline { flex-direction: row; align-items: center; gap: 8px; flex-wrap: wrap; }
.form-grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 4px 14px; }
/* 布局高度覆盖（竖屏/横屏两个数字框）：列宽按内容收紧、两格靠拢（左对齐不拉满整行），
 * 输入框给到 130px（原来浏览器默认窄，placeholder「空=跟随全局」都显示不全）。 */
.height-override-grid { grid-template-columns: auto auto; justify-content: start; gap: 4px 12px; }
.height-override-grid .mini-input { width: 130px; flex: 0 0 auto; }

/* jscolor 颜色输入框：色块即输入框背景，f5a-see-me 同款点击弹出 */
.color-input { width: 150px; font-family: var(--mono); cursor: pointer; }
/* 高级颜色子卡：未配置时标题置灰，已配置时圆点点亮 */
.color-adv-card { margin: 6px 0 0 !important; }
.color-adv-card > summary { color: var(--text-dim); font-weight: 500; }
.color-state-card { margin: 6px 0 0 !important; }
.color-state-card > summary {
  display: flex; align-items: center; gap: 6px;
  font-weight: 500; color: var(--text-dim);
}
.color-state-card.has-colors > summary { color: #cdd2da; }
.color-state-card.has-colors .color-state-dot { background: var(--accent); }
.color-state-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: #3a3f47; border: 1px solid var(--border); flex: 0 0 auto;
}
@media (max-width: 640px) {
  .color-input { width: 100%; }
}

/* 需要补选文件夹时的醒目提示行：黄框 + 黄底 + 高亮按钮。
 * 触发场景：选中 definitions.json（它自己不是布局），或布局导入后仍有引用解析不了。 */
.folder-hint {
  background: rgba(226, 163, 63, 0.12);
  border: 1px solid var(--warn);
  border-left-width: 4px;
  border-radius: 6px;
  padding: 8px 10px;
  margin: 8px 0;
  align-items: center;
  gap: 10px;
}
.folder-hint-text {
  flex: 1 1 260px;
  min-width: 0;
  color: var(--warn);
  font-size: 12.5px;
  line-height: 1.55;
  white-space: normal;   /* 覆盖 .mini-label 的 nowrap，长文案要换行 */
  word-break: break-word;
}
/* 需要用户动手的按钮：黄底实心，明显区别于普通按钮 */
.mini-button.attention {
  background: var(--warn);
  border-color: var(--warn);
  color: #1a1c20;
  font-weight: 600;
  flex: 0 0 auto;
}
.mini-button.attention:hover { background: #f0b455; border-color: #f0b455; }
.mini-button.attention:focus-visible { outline: 2px solid var(--warn); outline-offset: 2px; }

.status { font-size: 12.5px; color: var(--text-dim); margin: 6px 0; word-break: break-all; }
.status.ok, .st-ok { color: var(--ok); }
.status.error, .st-error { color: var(--err); }
.status.warn, .st-warn { color: var(--warn); }
.meta-details { margin-top: 4px; }
.meta-details > summary { cursor: pointer; color: var(--text-dim); font-size: 12px; user-select: none; }
.meta-list { margin: 4px 0 0; padding-left: 20px; font-size: 12.5px; }
.meta-list li { margin: 2px 0; }
/* 可定位的校验条目：可点击跳到对应按键 */
.issue-locatable {
  cursor: pointer;
  border-radius: 4px;
  padding: 1px 4px;
  margin-left: -4px;
  transition: background 0.12s;
}
.issue-locatable:hover { background: #2d333d; }
.issue-locatable:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }
.issue-path { color: var(--text-dim); font-size: 11.5px; margin-left: 6px; }
.st-dim { color: var(--text-dim); }
/* 定位/新建后的高亮：**一直亮到用户下一次点击或按键**。
 *
 * 为什么不再用「一次跑完就淡出」的 animation（历史上是 .def-flash / .issue-flash，
 * 1.1–1.2s 淡出）：落点常在长列表里，高亮在用户还没扫到、还没看清跳到哪时就已经
 * 消失了（用户明确要求改成持续）。CSS 动画**表达不了**「持续到用户操作」——
 * 时长写死就无从知道用户何时看到，所以改由 app.js 的 holdFlash 挂一次性监听、
 * 在用户 pointerdown/keydown 时移除本类。
 *
 * 蓝（accent）= 非问题：新建的条目、从「使用数」跳过来的目标；
 * 红（err）  = 有问题：校验详情里点过来的出错位置（列表条目）；
 * 黄（sel）  = 布局里的按键：它本来就被选中、自带黄框，用同色系光晕轻轻强调。
 * 三类都持续，只是颜色语义不同。 */
.flash-hold {
  animation: flash-hold 1.4s ease-in-out infinite;
  border-color: var(--accent);
}
.flash-hold.flash-hold-err { animation-name: flash-hold-err; border-color: var(--err); }
/* 布局里的按键：**红**要"压住"它自带的黄框才看得出是出错色，所以把 outline 的
 * 颜色也改成红（而不是再加一层）—— outline 只有一份，改色即替换，
 * 不会出现两条框套在一起的重复观感。
 * 选择器必须 **3 个类**才压得过既有的 `.chip.chip-sel` / `.gedit-key.chip-sel`
 * （2 个类）；写成 2 个类会被后者按同权重、后出现顺序覆盖掉。 */
.chip.chip-sel.flash-hold-err,
.gedit-key.chip-sel.flash-hold-err { outline-color: var(--err); }
/* 布局里的按键也可以是**黄色**（与 `.chip-sel` 的 --sel 选中框同色）：
 * 用于「使用数」弹窗跳过来时 —— 那只是"带你到用过它的那个键"，
 * 不是出错，同色系光晕比红光自然得多。
 * 黄不需要 outline 覆盖（没有两色相争），光晕叠在黄框外圈即可。
 * 点击后摘掉本类 → 光晕消失，按键保持选中黄框（选择状态照旧）。 */
.flash-hold.flash-hold-sel { animation-name: flash-hold-sel; border-color: var(--sel); }
@keyframes flash-hold {
  0%, 100% { box-shadow: 0 0 0 3px var(--accent); }
  50% { box-shadow: 0 0 0 7px var(--accent); }
}
@keyframes flash-hold-err {
  0%, 100% { box-shadow: 0 0 0 3px var(--err); }
  50% { box-shadow: 0 0 0 7px var(--err); }
}
@keyframes flash-hold-sel {
  0%, 100% { box-shadow: 0 0 0 3px var(--sel); }
  50% { box-shadow: 0 0 0 7px var(--sel); }
}

/* ---------------- 预览 ---------------- */
.preview-panel > summary { font-size: 15px; }
.preview-toolbar {
  display: flex; gap: 12px; flex-wrap: wrap; align-items: center;
  padding: 10px 14px 0;
}
.layout-tabs { display: flex; gap: 4px; flex-wrap: wrap; }
.pill {
  padding: 3px 12px; border-radius: 999px; border: 1px solid var(--border);
  background: #23262c; color: var(--text-dim); cursor: pointer; font-size: 12.5px;
}
.pill.active { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
/* 浅色下 .pill.active 的蓝底必须赢回浅色 `.pill` 覆盖：
 * `html.site-light .pill` 权重 (0,2,1) 高过 `.pill.active` 的 (0,2,0)（多一个 html tag），
 * 不单独写这条，选中态会变成白字浮浅灰底、几乎看不见（常规/分体 pill 与布局 pills 同病）。 */
html.site-light .pill.active { background: var(--accent); border-color: var(--accent); color: #fff; }
@media (prefers-color-scheme: light) {
  html.site-auto .pill.active { background: var(--accent); border-color: var(--accent); color: #fff; }
}
.preview-toggles { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; font-size: 12.5px; color: var(--text-dim); }
/* 键盘状态开关组（Shift/组字/ASCII/停用/分体/状态标签/高度）。
 * 单独包一层是因为切到「符号面板」页时要**整组隐藏** —— 这些只对布局有意义。
 * 用 contents 让它在常规布局下不引入额外的盒（排布与从前的扁平结构一致，
 * 避免"多加一层 div 就把换行点挪了"的视觉回归）。 */
.pt-state-group { display: contents; }
.pt-state-group[hidden] { display: none; }
/* 工具栏第二行：预览几何旋钮（高度/圆角/水平间隙/垂直间隙）+ 深浅键盘。
 * 单独占一行（用户要求）—— 上面那排是"模拟键盘状态"，这里是"改预览怎么画"，
 * 混在一行会挤成一团、也不便扫读。上边距 + 一条细线把它与状态开关分开。 */
.preview-toggles-geom {
  flex-basis: 100%;
  margin-top: 2px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
}
.preview-stage { padding: 12px 14px 4px; }

/* 预览角标图例 */
.preview-legend { font-size: 12px; color: var(--text-dim); padding: 2px 14px 8px; line-height: 1.7; }
.preview-legend .lg-lp { color: #8fd0ff; }
.preview-legend .lg-hold { color: #ffd28f; }
.preview-legend .lg-popup { color: #9fe0b0; }
.preview-legend .lg-broken { color: var(--err); }
.preview-legend .lg-sel { color: var(--sel); }
/* 浅色页面上图例三色太淡（浅蓝/浅橙/浅绿都是为深底调的），加深到与键上徽标浅色值同档 */
html.site-light .preview-legend .lg-lp { color: #1f6fb8; }
html.site-light .preview-legend .lg-hold { color: #b26a00; }
html.site-light .preview-legend .lg-popup { color: #2e8b57; }
@media (prefers-color-scheme: light) {
  html.site-auto .preview-legend .lg-lp { color: #1f6fb8; }
  html.site-auto .preview-legend .lg-hold { color: #b26a00; }
  html.site-auto .preview-legend .lg-popup { color: #2e8b57; }
}

/* 预览 meta 的分体标记 */
.st-split { color: #9fe0b0; font-weight: 600; }
/* 浅色下同图例一起加深，否则白底上看不清 */
html.site-light .st-split { color: #2e8b57; }
@media (prefers-color-scheme: light) {
  html.site-auto .st-split { color: #2e8b57; }
}

.kb {
  width: 100%;
  max-width: 614px;   /* 桌面常规预览宽；恰为 .kb-split 的一半，两模式共用同一竖屏口径 unit，切换常规/分体时预览高度不跳变 */
  margin: 0 auto;
  padding: 8px;
  border-radius: 14px;
  user-select: none;
  transition: background 0.2s;
  /* 注意：不要给 max-width 加 transition。分体切回竖屏时类名立即移除，
   * 若宽度是动画收缩的，同一帧内 clientWidth 量到的还是宽值，
   * 竖屏基准宽度会被污染，导致字和行高一起变大。 */
}
/* 分体键盘一般在横屏下使用：预览放宽到占满中央编辑区（≈ content-max 去掉内外边距 = 1298-70），
 * 是常规宽的 2 倍并等比拉伸；unit 仍取常规竖屏口径，所以拉宽后高度与常规一致、切换不跳变 */
.kb-split {
  max-width: 1228px;
}
/* 键容器底色 / 内描边：主题生效时由 app.js 写 --kb-bg（= 主题 keyboardColor 或
 * altKeyboardColor，随 key_border_enabled 联动）与 --kb-inset=none（清掉下面这圈
 * 写死的 1px 内描边，否则主题底色上会多一圈固定色）。
 * 未导入主题时两个变量都不存在 → var() 回落原色值，外观与从前逐像素一致。 */
.kb-dark { background: var(--kb-bg, #191a1e); box-shadow: var(--kb-inset, inset 0 0 0 1px #26282d); }
.kb-light { background: var(--kb-bg, #cfd2d7); box-shadow: var(--kb-inset, inset 0 0 0 1px #b9bdc4); }
.kb-section { display: flex; flex-direction: column; }

/* ---------------- text_editor 专用预览 ----------------
 * Foxy 的 text_editor 不是一行普通键盘：ly0 充满原键盘容器，顶部是 ky0 光标触控板，
 * 底部才是布局文件提供的一行按键。触控板自身四周还有 8dp 内缩，提示文字由 App 固定绘制。 */
.kb-text-editor-frame {
  width: 100%; min-height: 0; display: flex; flex-direction: column;
}
.kb-text-editor-touchpad-area {
  flex: 1 1 auto; min-height: 0; display: flex;
}
.kb-text-editor-touchpad {
  flex: 1 1 auto; min-width: 0; min-height: 0; margin: 8px;
  border-radius: 6px; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 0;
  padding: 8px; text-align: center; overflow: hidden;
}
.kb-dark .kb-text-editor-touchpad {
  background: var(--kb-key-bg-fn, #313439);
  color: var(--kb-key-fg-fn, #f2f3f5);
}
.kb-light .kb-text-editor-touchpad {
  background: var(--kb-key-bg-fn, #d6d9de);
  color: var(--kb-key-fg-fn, #22242a);
}
.kb-text-editor-hint-main,
.kb-text-editor-hint-sub {
  display: block; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; line-height: 1.2;
}
.kb-text-editor-hint-main { opacity: 0.35; }
.kb-text-editor-hint-sub { opacity: 0.25; }
.kb-text-editor-bottom {
  flex: 0 0 auto; min-height: 0; width: 100%; display: flex;
}
.kb-text-editor-bottom > .kb-section { width: 100%; min-height: 0; }
.kb-key-preview-only { cursor: default; }
/* 槽位对齐：行内无 gap，槽按 weight 分整行宽，键在槽内左右各留间隙
 * （见 buildRowsSection）。不同键数的行同权重点边界重合。
 *
 * ⚠️ 行/网格容器**不带自己的上下外边距**：App 的键间距是「每键四周内缩」
 * （v40.setPadding + InsetDrawable，见 foxy-render-spec.md §2.7），
 * 所以上下相邻键的可见间隙由两侧键各自的 margin 合成（= 2×间隙设置）。
 * 早先这里写死 `margin-bottom: 5px` 属"固定间隙"时代的遗留 —— 它会在
 * 按键内缩之外**再加一份**，使间距大于设置值、与手机不一致。 */
.kb-row { display: flex; }
.kb-slot { display: flex; min-width: 0; }
.kb-slot > .kb-key { flex: 1 1 auto; min-width: 0; }
/* 网格：gap 由 JS 按「按键外观」设置写入（同样取 2×设置值，见 buildGridSection），
 * 这里不写死，避免与配置值打架。 */
.kb-grid { display: grid; }
.kb-empty { color: var(--text-dim); text-align: center; padding: 30px 10px; font-size: 13px; }

.kb-key {
  position: relative;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  cursor: pointer;
  transition: filter 0.06s;
}
.kb-dark .kb-key { background: var(--kb-key-bg, #52555b); color: var(--kb-key-fg, #f2f3f5); box-shadow: var(--kb-key-shadow, 0 1px 0 rgba(0,0,0,0.45)); }
.kb-dark .kb-key.kt-function { background: var(--kb-key-bg-fn, #313439); }
.kb-dark .kb-key.kt-action { background: var(--kb-key-bg-action, #3d6fb4); }
.kb-light .kb-key { background: var(--kb-key-bg, #ffffff); color: var(--kb-key-fg, #22242a); box-shadow: var(--kb-key-shadow, 0 1px 1px rgba(0,0,0,0.22)); }
.kb-light .kb-key.kt-function { background: var(--kb-key-bg-fn, #d6d9de); }
.kb-light .kb-key.kt-action { background: var(--kb-key-bg-action, #a8c8f2); }
/* ⚠️ 按下态**不能**用亮度滤镜。App 端按下是 StateListDrawable 换背景色
 * （state_pressed 分支用 pressedColor，见 foxy-render-spec.md §2.6，v40.java:276-291），
 * 而不是把原色提亮。早先这里是 `filter: brightness(1.35)`，叠在主题 pressed 行内色
 * 之上，导致**显示值 ≠ 配置值**（用户配 #FF0000，屏幕上是提亮后的粉红）—— 已删除。
 * 按下时的背景色完全由 app.js 的 applyKeyColors 写入。 */
/* hover 提亮**仅为本网页的交互反馈**，手机端没有 hover 概念、App 无此效果。
 * 所以系数压到很小（1.06），避免盖掉配置色观感；也绝不作用于按下态
 * （`.kb-key.pressed` 在下面、同权重后出现者胜 → 按下时 filter 恒为 none）。 */
.kb-key:hover { filter: brightness(1.06); }
/* 按下态：不加任何滤镜，颜色由 applyKeyColors 写 background。
 * 显式写 filter: none 是为了盖掉上面 hover 的提亮（悬停时按下也不能变亮）。 */
.kb-key.pressed { filter: none; }
.kb-key.kb-key-broken { outline: 2px dashed var(--err); outline-offset: -2px; }
.kb-key.kb-key-sel { outline: 2px solid var(--sel); outline-offset: -1px; }
/* 键边框：是否画、画什么，由 app.js 的 applyKeyColors 按**两个开关组合**决定
 * （foxy-render-spec.md §2.4，v40.java:196-206）：
 *   B=keyBorderEnabled   true  → 键面内缩 + 1dp 边框 / 描边；false → 无边框无描边，纯色圆角块
 *   C=keyStrokeEnabled   true  → 用描边（setStroke 1dp）；false → 用实线边框
 * 本类只负责「边框宽度 1dp」这一半；**开关关掉时根本不会加上这个类**。
 * ⚠️ C（描边）默认 false（xw0.java:56），且编辑器目前**没有**描边开关的 UI ——
 * 预览一律按 C=false 的实线边框画（与 App 默认一致）；将来加开关时
 * 在这里补「描边」变体即可，不要再让边框无条件出现。 */
.kb-key-hasborder { border-style: solid; border-width: 1px; }
.kb-spacer { background: transparent !important; box-shadow: none !important; cursor: default; pointer-events: none; }

/* ⭐ 键文字**必须单行**（nowrap + 裁切），不能让它折行。
 *
 * 依据 App：键文字是 `z9`（`extends TextView`，`z9.smali` 类声明），
 * `v40.smali:233-237` 只设 `setScaleMode(Ly9;->a)`，而 `Ly9` 的常量顺序是
 * None / Horizontal / Proportional / Height —— `a` 就是 **None**（不缩放）；
 * `z9.onMeasure`（`z9.smali:4989+`）用 `Paint$FontMetrics` 的 top/bottom 算高，
 * 即**恒为一行**。整条键文字路径上 `setSingleLine`/`setMaxLines`/`setEllipsize`
 * 出现次数为 **0**（实测 grep），所以 App 既不让键文字换行、也不缩字。
 *
 * 早先这里只写 `overflow: hidden`：inline 元素下它压不住换行，于是
 * "计算模式/日期输入/日期计算/数字转换"这类 4 字标签在 numpad 里折成两行，
 * 不但互相压叠（浏览器实测：4 个键的文字块高出键框 16.8px），
 * 还反过来把网格行顶高（见 app.js 的 FE.gridColTemplate 注释 —— 那是同一
 * 根因的两个表现）。改成单行 + 省略号后，文字块 74.4×42.6 落在键框 77.5×51.6 内，
 * 溢出归零，与 App 的「一行、放不下就裁」一致。
 *
 * ⚠️ 不要改成 `white-space: normal` 或给键加 `min-height`：那会让行高随文字变化，
 * 而 App 的行高是纯几何（`fo.smali`：`5.0f / rows` 或 rowHeights 按比例）。
 * `display: block` 是 `text-overflow: ellipsis` 生效的前提。 */
.kb-label {
  font-weight: 500; pointer-events: none; text-align: center;
  display: block; max-width: 96%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kb-icon-wrap { pointer-events: none; display: inline-flex; }
.kb-icon { width: 100%; height: 100%; }
/* 图标色跟随**键的主文字色**（App 端 v40.e 用 setColorFilter 给图标上同一种色，
 * foxy-render-spec.md §1.4）。这里必须用 currentColor：早先写死 #e8eaee/#33363d，
 * 优先级高于父级行内 color，主题键文字色（或 accentColor）就传不进来。
 * 主题生效时 app.js 会把 --kb-icon-active 置成 currentColor（见 applyKeyboardCssVars）。 */
.kb-icon-wrap { color: currentColor; }
.kb-icon-active { color: var(--kb-icon-active, var(--sel)) !important; }

.kb-hint {
  position: absolute; pointer-events: none; color: #aeb4bd; line-height: 1;
  max-width: 92%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kb-light .kb-hint { color: #63676f; }
.kb-hint-up { top: 3px; left: 50%; transform: translateX(-50%); }
.kb-hint-down { bottom: 3px; left: 50%; transform: translateX(-50%); }
.kb-hint-left { left: 4px; top: 50%; transform: translateY(-50%); }
.kb-hint-right { right: 4px; top: 50%; transform: translateY(-50%); }
.kb-badge-lp {
  position: absolute; top: 2px; right: 3px; pointer-events: none;
  color: #8fd0ff; line-height: 1;
}
.kb-badge-hold { color: #ffd28f; }
.kb-light .kb-badge-lp { color: #1f6fb8; }
/* ⚠️ 长按/按住徽标（.kb-badge-lp / .kb-badge-hold）的颜色**写死且不跟主题**：
 * 它们是本编辑器的**编辑器私有标注**（index.html 图例里的"右上蓝/橙角标"），
 * 不是 Foxy 键面的一部分 —— 主题 26 色里**没有**对应字段，无处联动。
 * 因此保持写死（不要硬编主题色去"假装"联动），只保证深浅两档各自可读。 */
/* 长按弹出菜单徽标：键底中 ⌄ */
.kb-badge-popup {
  position: absolute; bottom: 1px; left: 50%; transform: translateX(-50%);
  pointer-events: none; color: #9fe0b0; line-height: 1; font-size: 11px;
}
.kb-light .kb-badge-popup { color: #2e8b57; }

/* ---------------- 区段编辑器 ---------------- */
.section-card .section-body { display: flex; flex-direction: column; gap: 8px; }
.section-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.section-title { font-size: 13px; font-weight: 600; color: #cdd2da; }
.section-tools { display: inline-flex; gap: 2px; }

.row-block {
  background: var(--card-inner);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
}
.row-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.row-title { font-size: 12.5px; font-weight: 600; color: var(--text-dim); margin-right: 4px; }

.chip-box { display: flex; flex-wrap: wrap; gap: 6px; min-height: 40px; padding: 4px; border: 1px dashed transparent; border-radius: 8px; }
.chip-box.drop-target { border-color: var(--accent); background: rgba(76, 141, 255, 0.07); }
.chip {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 52px; min-height: 42px; padding: 3px 8px; border-radius: 6px;
  background: #52555b; color: #f2f3f5; cursor: grab; border: 1px solid #5d6167;
}
.chip.chip-fn { background: #313439; border-color: #3c4046; }
.chip.chip-sel { outline: 2px solid var(--sel); outline-offset: 1px; }
.chip.chip-broken { outline: 2px dashed var(--err); outline-offset: 1px; }
.chip.dragging { opacity: 0.4; }
.chip.drop-before { box-shadow: -3px 0 0 var(--accent); }
/* 指针拖动的跟随替身（鼠标 + 触屏统一） */
.drag-ghost {
  box-shadow: 0 6px 20px rgba(0,0,0,0.5); border-radius: 6px;
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
}
.chip-add {
  background: transparent; border: 1px dashed #565b63; color: var(--text-dim);
  cursor: pointer; min-width: 44px; font-size: 13px;
}
.chip-add:hover { color: var(--accent); border-color: var(--accent); }
.chip-label { font-size: 14px; font-weight: 600; line-height: 1.2; max-width: 76px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip-sub { font-size: 10px; color: #b9bec6; font-family: var(--mono); max-width: 88px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip-badges { font-size: 10px; color: #ffb020; }

/* ---------------- 网格编辑器 ---------------- */
/* 画布仍可横向滚动（触摸板横滚、程序化、Shift+滚轮），但**隐藏原生滚动条**：
 * 下面那条自绘滚动条已经承担了这个入口，两者同时出现就是双滚动条。
 * 自绘那条鼠标也能用（拖滑块 / 点轨道），所以桌面端不损失能力。 */
.gedit-wrap {
  overflow-x: auto;
  scrollbar-width: none;          /* Firefox */
}
.gedit-wrap::-webkit-scrollbar { display: none; }   /* WebKit / Blink */

/* ---------------- 符号面板预览（.sym-pv，由 symbol-preview.js 画） ----------------
 * 两条滚动槽**隐藏原生滚动条**（用户要求：「去掉那两个网页滑动杆」，以便对齐 App 观感）。
 *
 * App 端两列各是一个 ScrollView（ly.java:66-71 / 149-153），条多时能滚，
 * 但滚动指示是 Android 那套（细、贴边、会自动淡出），不是网页这种粗白条 ——
 * 深色面板底色上一条浅色滚动条尤其扎眼，看起来就不像手机。
 *
 * 所以：**保留滚动能力，只隐藏滚动指示**（与上面 .gedit-wrap 同一套做法，
 * 桌面端仍可用滚轮 / 触控板拖动，手机端仍可用手指滑动）。
 * 两列各自可滚这条语义没变 —— test-ui 有断言钉着 overflowY === 'auto'。 */
.sym-pv-groups,
.sym-pv-cells-wrap {
  scrollbar-width: none;                        /* Firefox */
  -ms-overflow-style: none;                     /* 旧 Edge / IE */
}
.sym-pv-groups::-webkit-scrollbar,
.sym-pv-cells-wrap::-webkit-scrollbar { display: none; }   /* WebKit / Blink */

/* 网格横向滚动条（自绘）。
 *
 * 为什么需要它：`.gedit-key` 的 touch-action:none 是拖动排序的前提，于是
 * 「在键上横滑」永远是拖动、不是滚动；而 cc_grid_4 这类 48×15 全由跨距键
 * 铺满的网格（194 键 / 0 个空格）根本没有可起手的滚动面，右侧的键在窄屏上
 * 够不到。这里给网格配一条独立滚动条：滑块管横向移动、键管拖动，两者分开。
 *
 * host 用 flex 竖排，wrap 可滚、滚动条固定在下方不动 —— 放 wrap 里面会跟着
 * 内容一起滚走。 */
.gedit-host { display: flex; flex-direction: column; gap: 6px; }
.gedit-scrollbar {
  position: relative;
  height: 12px;
  border-radius: 999px;
  background: #1b1e23;
  border: 1px solid var(--border);
  /* 轨道本身不接收指针事件，只有滑块和空白区接管；避免它抢走页面滚动 */
  touch-action: pan-y;
  cursor: pointer;
}
.gedit-thumb {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  border-radius: 999px;
  background: #4a4f58;
  /* 拖动时才由 JS 设宽度与位移；初始不可见，等首帧 sync 量到尺寸再显示 */
  width: 0;
  /* 滑块是拖动目标，必须自己接管指针（父级是 pan-y） */
  touch-action: none;
}
.gedit-thumb:hover { background: #5a606a; }
.gedit-thumb:active { background: var(--accent); }
/* 不可滚动时隐藏（小网格外观完全不受影响） */
.gedit-scrollbar[hidden] { display: none; }

/* 窄屏：给右下角的浮动「回到顶部」按钮让位。
 *
 * 浮动按钮是 fixed 贴视口右缘（bottom:20px / right:12px），而网格滚动条在
 * 文档流里 —— 当网格滚到视口底部附近，两者必然在右下角重叠。实测 414px 竖屏：
 * 点轨道右端空白命中到 `button.float-bottom-right`，滚动条纹丝不动
 * （360px 时因轨道恰好错开按钮的纵向区间，所以没暴露）。
 * 这里把轨道右端缩进，避开按钮宽度 + 间隙，让「点轨道空白跳转」这个
 * 原生滚动条的习惯操作在整个窄屏区间都可用。拖滑块不受影响
 * （起点在轨道左端，本来就不与按钮重叠）。 */
@media (max-width: 980px) {
  .gedit-scrollbar { margin-right: 88px; }
}

/* ---------------- 分体（split）横幅 ---------------- */
.split-banner {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--card-inner); border: 1px solid var(--border);
  border-radius: 8px; padding: 8px 10px; margin-bottom: 10px;
}
.split-banner .pill-dim { opacity: 0.65; border-style: dashed; }

/* ---------------- 弹出菜单编辑 ---------------- */
.popup-state-row { display: flex; align-items: flex-start; gap: 8px; margin: 6px 0; flex-wrap: wrap; }
.popup-state-name { font-size: 12.5px; font-weight: 600; color: var(--text-dim); min-width: 40px; padding-top: 8px; }
.popup-cands { flex: 1 1 260px; }
.popup-cand { position: relative; }
.popup-cand .chip-label { max-width: 96px; }
.popup-cand .cand-idx { position: absolute; top: -6px; left: -6px; font-size: 9px; background: #4a4f58; color: #cfd3da; border-radius: 999px; padding: 0 4px; }
.popup-missing { margin: 8px 0; display: flex; flex-direction: column; gap: 6px; }
/* 主按键对话框里内嵌的弹出菜单整键编辑（与键定义卡片同构） */
.popup-section .popup-key-editor .section-head { margin-bottom: 6px; }

/* 弹出气泡预览：整体跟随上面键盘颜色。
 * · 舞台背景用键盘底色（.kb-dark/.kb-light 容器同款色值）；
 * · 气泡/候选随明暗主题；模拟按键额外套 keyType 配色 + colors 覆盖。 */
.pp-stage { padding: 18px 12px 12px; display: flex; justify-content: center; border-radius: 8px; }
/* 舞台背景 = 键盘容器底色（与 .kb-dark / .kb-light 同款）。
 * ⚠️ 这里**保持写死色值**：弹出菜单那侧（popup-editor.js）已按 foxy-render-spec.md §7
 * 给舞台写内联主题底（resolveKeyboardColor，含 key_border_enabled 联动），
 * 行内优先级更高；若这里再套 --kb-bg 变量就是重复建模、两处互相牵制。 */
.pp-stage.kb-dark { background: #191a1e; }
.pp-stage.kb-light { background: #cfd2d7; }
.pp-wrap { display: flex; flex-direction: column; align-items: center; position: relative; }
.pp-bubble {
  display: flex; align-items: stretch; gap: 4px; position: relative;
  background: #f4f5f7; color: #22242a;
  border-radius: 10px; padding: 6px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
  margin-bottom: 14px; min-height: 44px;
}
/* 气泡背景随键盘主题 */
.pp-stage.kb-dark .pp-bubble { background: #2b2d31; color: #f2f3f5; }
.pp-stage.kb-light .pp-bubble { background: #ffffff; color: #22242a; box-shadow: 0 4px 16px rgba(0,0,0,0.18); }
/* 气泡下的小三角必须**跟气泡同色**：`background: inherit` 对伪元素取的是
 * 起源元素（.pp-bubble）的计算值，所以无论气泡色来自
 *   · popup-editor.js 写的行内主题色（popupBackgroundColor，foxy-render-spec.md §7.1），
 *   · 还是下面 .pp-stage.kb-dark/.kb-light 两档 CSS，
 * 三角都会自动一致。早先写死 #f4f5f7，气泡一变它就"露馅"（明显对不上）。
 * ⚠️ 因此**不要再给 ::after 单独写 background 的档位规则** —— 那会盖掉 inherit。 */
.pp-bubble::after {
  content: ''; position: absolute; bottom: -4px; left: 50%; transform: translateX(-50%) rotate(45deg);
  width: 10px; height: 10px; background: inherit; border-radius: 2px;
  /* 三角不单独投影：气泡那圈模糊阴影是舞台层的观感处理，三角自带阴影会与气泡错位 */
  box-shadow: none;
}
.pp-cand {
  display: flex; align-items: center; justify-content: center;
  min-width: 34px; padding: 2px 8px; border-radius: 6px; background: #ffffff; color: #22242a;
  font-size: 15px; cursor: default;
}
/* 候选是「键」：随键盘主题套 LETTER 键色（与 .kb-*.kb-key 同款色值） */
.pp-stage.kb-dark .pp-cand { background: #52555b; color: #f2f3f5; }
.pp-stage.kb-light .pp-cand { background: #ffffff; color: #22242a; box-shadow: 0 1px 1px rgba(0,0,0,0.18); }
.pp-cand-first { min-width: 52px; min-height: 40px; font-size: 22px; font-weight: 600; }
.pp-empty-hint { color: #888; font-size: 12.5px; align-self: center; padding: 4px 10px; }
.pp-key {
  min-width: 120px; height: 44px; border-radius: 8px;
  background: #52555b; color: #f2f3f5; display: flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 600; position: relative; z-index: 1;
}
/* 模拟按键的 keyType 配色（随键盘主题；colors 覆盖由 FE.applyKeyColors 以行内样式写入，优先级更高） */
.pp-stage.kb-light .pp-key { background: #ffffff; color: #22242a; box-shadow: 0 1px 1px rgba(0,0,0,0.22); }
.pp-stage.kb-light .pp-key.kt-function { background: #d6d9de; }
.pp-stage.kb-light .pp-key.kt-action { background: #a8c8f2; }
.pp-stage.kb-dark .pp-key.kt-function, .pp-key.kt-function { background: #313439; }
.pp-stage.kb-dark .pp-key.kt-action, .pp-key.kt-action { background: #3d6fb4; }
.gedit {
  display: grid;
  gap: 5px;
  min-width: 320px;
  margin-top: 6px;
}
.gedit-cell {
  min-height: 46px;
  border-radius: 6px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  position: relative;
}
.gedit-empty {
  border: 1px dashed #4a4f57; color: var(--text-dim); cursor: pointer; font-size: 15px;
}
.gedit-empty:hover { border-color: var(--accent); color: var(--accent); }
.gedit-key {
  background: #52555b; color: #f2f3f5; cursor: pointer; border: 1px solid #5d6167;
}
.gedit-key.chip-fn { background: #313439; }
.gedit-key.chip-sel { outline: 2px solid var(--sel); outline-offset: 1px; }
.gedit-key.chip-broken { outline: 2px dashed var(--err); outline-offset: 1px; }
.gedit-key:hover { filter: brightness(1.15); }
.gedit-key { touch-action: none; }
.gedit-cell.gedit-drop { outline: 2px solid var(--accent); outline-offset: -2px; background: rgba(76, 141, 255, 0.18); }
.gedit-label { font-size: 15px; font-weight: 600; line-height: 1.2; }
.gedit-sub { font-size: 10px; color: #b9bec6; font-family: var(--mono); max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gedit-span {
  position: absolute; top: 2px; right: 3px; font-size: 10px; color: var(--sel);
}

/* 布局编辑里可点击编辑的按键（.chip 行区段 / .gedit-key 网格区段）跟随预览键盘配色。
 * 两层叠加，与预览 .kb-key 同款语义：
 * · 基础色（主题/keyType）走 CSS 类 —— .theme-light 下覆盖为浅色；
 * · 该键 colors 覆盖由 applyEditorKeyColors 以行内样式写入（见 app.js），优先级更高。
 * 深浅两档都显式写死（不用"默认基础值"偷懒）：整站另有 site-light 浅色主题，
 * 一旦整站变浅、键盘仍是深色，裸基础值会被整站浅变量带跑，只有显式规则才钉得住。 */
.theme-light .chip { background: #ffffff; color: #22242a; border-color: #c9ccd2; }
.theme-light .chip.chip-fn { background: #d6d9de; border-color: #c2c6cc; }
.theme-light .chip-sub { color: #63676f; }
.theme-light .chip-add { border-color: #c2c6cc; }
.theme-light .gedit-key { background: #ffffff; color: #22242a; border-color: #c9ccd2; }
.theme-light .gedit-key.chip-fn { background: #d6d9de; }
.theme-light .gedit-sub { color: #63676f; }
.theme-light .gedit-empty { border-color: #c2c6cc; }
.theme-dark .chip { background: #52555b; color: #f2f3f5; border-color: #5d6167; }
.theme-dark .chip.chip-fn { background: #313439; border-color: #3c4046; }
.theme-dark .chip-sub { color: #b9bec6; }
.theme-dark .gedit-key { background: #52555b; color: #f2f3f5; border-color: #5d6167; }
.theme-dark .gedit-key.chip-fn { background: #313439; }
.theme-dark .gedit-sub { color: #b9bec6; }

/* ⭐ 主题生效时，编辑区按键跟随**键盘配色变量**（由 app.js 写到 #layout-sections）。
 * 上面那批写死规则**保留不动**（test-ui.js 有源码级正则断言钉着它们，防整站浅色带跑），
 * 这一组是**同权重后置**规则：变量不存在（未导入主题）时 var() 回落同一色值 →
 * 外观逐像素不变；变量存在（主题生效）时才真正跟随键盘主题。
 * 目的：键盘预览变了、旁边的编辑区按键不再"各说各话"。 */
.theme-light .chip, .theme-light .gedit-key { background: var(--kb-key-bg, #ffffff); color: var(--kb-key-fg, #22242a); }
.theme-dark .chip, .theme-dark .gedit-key { background: var(--kb-key-bg, #52555b); color: var(--kb-key-fg, #f2f3f5); }
.theme-light .chip.chip-fn, .theme-light .gedit-key.chip-fn { background: var(--kb-key-bg-fn, #d6d9de); }
.theme-dark .chip.chip-fn, .theme-dark .gedit-key.chip-fn { background: var(--kb-key-bg-fn, #313439); }

/* 布局编辑容器（行块 / 网格画布 / 网格滚动条）跟随预览键盘底色。
 * 行块 ≈ 预览 .kb（自带 padding，底色整块可见）；网格 .gedit 本身无 padding，
 * 间隙透出的是父级 .gedit-wrap，所以底色打在 wrap 上；滚动条轨道/滑块同步键盘底，
 * 否则深色轨道在浅灰画布下过扎眼。色值全部取自本文件已有（.kb-dark/.kb-light）。
 * 深浅两档都显式写死：行块底色基础值是 var(--card-inner)，会跟整站 site-light 变浅，
 * 深色键盘时必须显式钉回 #191a1e，否则"浅色整站 + 深色键盘"下容器跟丢。 */
.theme-light .row-block { background: #cfd2d7; border-color: #b9bdc4; }
.theme-light .row-block .row-title { color: #63676f; }
.theme-light .row-block .mini-label { color: #63676f; }
/* 行头 ↑ ↓ ✕：底色与行块底同色（#cfd2d7），只留边框+灰字，融进行块背景；
 * hover 才浮出白底。边框不能省，否则浅底上"白成一片、看不见边"。 */
.theme-light .row-block .icon-button { color: #5f656e; border-color: #a8adb5; background: #cfd2d7; }
.theme-light .row-block .icon-button:hover { color: #1c1e22; border-color: #8a8f98; background: #ffffff; }
.theme-light .gedit-wrap { background: #cfd2d7; border-radius: 8px; }
.theme-light .gedit-scrollbar { background: #b9bdc4; border-color: #c2c6cc; }
.theme-light .gedit-thumb { background: #63676f; }
.theme-light .gedit-thumb:hover { background: #4a4f58; }
/* 拖放目标高亮在浅底上 0.07 几乎看不见，与 .gedit-drop 的 0.18 对齐 */
.theme-light .chip-box.drop-target { background: rgba(76, 141, 255, 0.18); }
/* 行块/网格设置里的输入框、列/行数字框同步变浅（与浅色键面同款白色），
 * 否则深色小框嵌在浅灰行块里过重。影响面只限 .theme-light 之内。 */
.theme-light .row-block .mini-input { background: #ffffff; color: #22242a; border-color: #c2c6cc; }
.theme-light .row-block .mini-input::placeholder { color: #8a8f98; }
.theme-light .section-body > .form-row .mini-input { background: #ffffff; color: #22242a; border-color: #c2c6cc; }
.theme-light .section-body > .form-row .mini-input::placeholder { color: #8a8f98; }
/* 深色键盘钉死：底色/边框/文字全部显式写死，不随整站变量走 */
.theme-dark .row-block { background: #191a1e; border-color: #26282d; }
.theme-dark .row-block .row-title { color: #9aa0a8; }
.theme-dark .row-block .mini-label { color: #9aa0a8; }
/* 行头 ↑ ↓ ✕：底色与深色行块底同色，只留边框+灰字；background 必须显式写，
 * 否则将来整站浅色的 button 白底规则（tag 选择器）会透进来，白底贴深底行块上。 */
.theme-dark .row-block .icon-button { color: #9aa0a8; border-color: #34383f; background: #191a1e; }
.theme-dark .row-block .icon-button:hover { color: #e6e8ec; border-color: #4a4f58; background: #262a31; }
/* ⚠️ 行块 / 网格画布（.row-block / .gedit-wrap）的底色**暂不接 --kb-bg**：
 * test-ui.js:3022-3023 有源码级正则断言要求这里显式写死 #191a1e（防整站浅色带跑），
 * 改成 var() 会与既有断言冲突。该面板由弹出菜单那侧的代理统一处理。 */
.theme-dark .gedit-wrap { background: #191a1e; border-radius: 8px; }
.theme-dark .gedit-scrollbar { background: #1b1e23; border-color: #34383f; }
.theme-dark .gedit-thumb { background: #4a4f58; }
.theme-dark .gedit-thumb:hover { background: #5a606a; }
.theme-dark .row-block .mini-input { background: #17191d; color: #e6e8ec; border-color: #34383f; }
.theme-dark .section-body > .form-row .mini-input { background: #17191d; color: #e6e8ec; border-color: #34383f; }

/* ---------------- 变体行 / 手势行 ---------------- */
.variant-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 4px 0; border-bottom: 1px dashed #31353c; }
.variant-row:last-child { border-bottom: none; }
.gesture-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px dashed #31353c; flex-wrap: wrap; }
.gesture-row:last-child { border-bottom: none; }
.gesture-name { font-size: 12.5px; min-width: 92px; color: #cdd2da; font-weight: 600; }
.gesture-sum { font-size: 12px; color: var(--text-dim); flex: 1 1 120px; min-width: 0; word-break: break-all; }
/* 浅色下手势名（点击/滑动/长按…）加深：#cdd2da 在白底上对比度只有 1.5，几乎隐形 */
html.site-light .gesture-name { color: #1c1e22; }
@media (prefers-color-scheme: light) {
  html.site-auto .gesture-name { color: #1c1e22; }
}

/* ---------------- 按键定义列表 ---------------- */
.def-item {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: var(--card-inner); border: 1px solid var(--border); border-radius: 8px;
  padding: 8px 12px; margin: 6px 0; flex-wrap: wrap;
}
.def-item.col { flex-direction: column; align-items: stretch; }
.def-main { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.def-name { font-family: var(--mono); color: var(--accent); cursor: pointer; font-size: 13.5px; word-break: break-all; }
.def-label { font-weight: 600; }
.def-badges { font-size: 12px; color: var(--text-dim); }
.def-tools { display: flex; gap: 6px; }

/* 整行可点即进入编辑（按键定义页）。行内按钮会 stopPropagation 排除。
 * 键盘可达性由行自带 tabindex + keydown 承担（见 app.js 注释）。 */
.def-item.def-row-click { cursor: pointer; }
.def-item.def-row-click:hover { border-color: #4a4f58; background: #24272e; }
.def-item.def-row-click .def-tools { cursor: default; }

/* ---------------- 定义列表的工具条（按键定义 / 动作 / 宏 共用） ----------------
 * 三个列表外观与行为完全一致，改这里三处同时生效。
 * 结构：.def-toolbar > 若干 .def-tool-group；每组 = 输入框 + 按钮（同一功能）。
 * 桌面：两组并排一行；手机竖屏：每组各占一行，且**组内输入框与按钮仍在同一行**。 */
.def-toolbar {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  margin: 8px 0;
}
.def-tool-group {
  display: flex; gap: 6px; align-items: center;
  flex: 1 1 auto; min-width: 0;
}
/* 输入框吃掉剩余宽度，按钮保持自然宽度不被压扁。
 * ⚠️ 搜索框外面包了一层 .search-wrap（为了把 ✕ 清空按钮定位在框内右侧），
 * 所以尺寸规则要**同时**覆盖「直接子元素」与「wrap 内的 input」两种情况 ——
 * 只写 `.def-tool-group > .mini-input` 的话，包起来之后那条规则就失效了
 * （input 不再是 group 的直接子元素），输入框会缩成默认宽度。 */
.def-tool-group > .mini-input,
.search-wrap > .mini-input { flex: 1 1 auto; min-width: 0; width: auto; }
.def-tool-group > .mini-button { flex: 0 0 auto; }
@media (min-width: 641px) {
  /* 桌面上限宽，两组靠左，不要拉满整行显得松散 */
  .def-tool-group { flex: 0 1 auto; }
  .def-tool-group > .mini-input,
  .search-wrap { width: min(260px, 100%); }
  .search-wrap > .mini-input { width: auto; }
}
@media (max-width: 640px) {
  /* 手机竖屏：搜索一行、新建一行，各自的输入框与按钮同处一行 */
  .def-tool-group { flex: 1 1 100%; }
  .def-tool-group > .mini-input,
  .search-wrap > .mini-input { width: auto; }
}
/* 搜索框 + 框内 ✕：wrap 负责占位，✕ 绝对定位在输入框右内侧 */
.search-wrap { position: relative; display: flex; flex: 1 1 auto; min-width: 0; }
/* 给 ✕ 留出空间，否则长文字会压到按钮下面 */
.search-wrap > .mini-input { padding-right: 26px; }
.search-clear {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 18px; height: 18px; padding: 0; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: transparent; color: var(--text-dim); font-size: 12px;
}
.search-clear:hover { background: #2f333a; color: var(--text); }
.search-clear:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }
/* 过滤生效时的提示行（匹配 N / 总数） */
.def-match-hint {
  font-size: 12px; color: var(--text-dim);
  padding: 2px 0 4px;
}

/* ---------------- 使用情况对话框 ---------------- */
.usage-summary {
  font-size: 13px; font-weight: 600; margin-bottom: 8px;
  padding-bottom: 8px; border-bottom: 1px solid var(--border);
}
.usage-list { display: flex; flex-direction: column; gap: 4px; max-height: 52vh; overflow-y: auto; }
.usage-item {
  display: flex; align-items: center; gap: 8px;
  font-size: 12.5px; font-family: var(--mono); text-align: left;
  color: var(--text-dim); background: #1b1e23;
  border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 10px; word-break: break-all;
}
/* 可跳转的条目做成按钮外观，鼠标悬停明确提示可点 */
.usage-item.usage-jump {
  cursor: pointer; color: var(--text);
  background: var(--card-inner);
}
.usage-item.usage-jump:hover { border-color: var(--accent); background: #2a3140; }
.usage-item.usage-jump:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }
.usage-jump-mark { margin-left: auto; flex: 0 0 auto; font-size: 11.5px; color: var(--accent); }
/* 「被引用」与「引用」两节都在时各矮一点，避免弹窗总高超过屏幕 */
.usage-list-compact { max-height: 30vh; }
/* 第二节（引用）的小标题：与第一段列表拉开距离，明确是另一组信息 */
.usage-section-title {
  font-size: 12.5px; font-weight: 600; color: var(--text);
  margin: 14px 0 6px; padding-top: 10px; border-top: 1px solid var(--border);
}
/* 出现位置（tap / swipe.down / 步骤2…）：靠右、次要色，不抢名字的注意力 */
.usage-where { margin-left: auto; flex: 0 1 auto; font-size: 11px; color: var(--text-dim); }
/* 引用目标已不存在（悬空引用）：明确标红，别让用户点了没反应 */
.usage-missing { margin-left: auto; flex: 0 0 auto; font-size: 11.5px; color: var(--err); }

/* ---------------- 动作与宏：图形化编辑 ---------------- */
.def-item.def-gui { gap: 8px; }
.action-def-editor { display: flex; flex-direction: column; gap: 4px; }
.action-def-editor .action-editor { background: #1b1e23; }

/* 可折叠条目（动作与宏）：默认全部折叠，点摘要行展开。
 * 注意 .def-item 是 display:flex，这里必须覆盖成默认流式 —— <details> 的
 * 折叠靠 summary 作首个子元素，flex 容器下会与 .def-item 的布局规则打架。 */
.def-item.def-collapsible { display: block; padding: 0; }
.def-summary {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; cursor: pointer; min-width: 0;
  border-radius: 8px; list-style: none;   /* 去掉默认三角，用自定义 caret */
}
.def-summary::-webkit-details-marker { display: none; }
.def-summary:hover { background: #262a31; }
.def-summary:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }
.def-summary-caret {
  flex: 0 0 auto; color: var(--text-dim); font-size: 11px;
  transition: transform 0.12s; display: inline-block;
}
details[open] > .def-summary > .def-summary-caret { transform: rotate(90deg); }
/* 摘要行里名字可长、徽标可长：让徽标占满剩余空间并可截断，删除按钮钉在右侧 */
.def-summary .def-name { flex: 1 1 auto; min-width: 0; }
.def-summary .def-badges { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.def-summary .def-tools { flex: 0 0 auto; margin-left: auto; }
/* 展开后的编辑器区域：左边缩进，与摘要行的 caret 对齐 */
.def-item.def-collapsible > :not(.def-summary) { padding: 0 12px 10px 30px; }

/* 宏步骤：每步一行；窄屏自动把工具栏折到下一行 */
.macro-steps { display: flex; flex-direction: column; gap: 6px; }
.macro-step {
  display: flex; align-items: flex-start; gap: 8px;
  background: #1b1e23; border: 1px solid var(--border); border-radius: 8px;
  padding: 8px 10px; flex-wrap: wrap;
}
.macro-step.dragging { opacity: 0.55; border-color: var(--accent); }
.macro-step.drop-gap { border-color: var(--sel); box-shadow: 0 0 0 2px rgba(255, 176, 32, 0.35); }
.macro-step-handle {
  cursor: grab; color: var(--text-dim); font-size: 15px; line-height: 1.5;
  padding: 0 2px; user-select: none; flex: 0 0 auto;
}
.macro-step-handle:active { cursor: grabbing; }
.macro-step-index {
  flex: 0 0 auto; min-width: 20px; text-align: center;
  font-family: var(--mono); font-size: 12px; color: var(--text-dim);
  background: #262a31; border-radius: 4px; padding: 2px 4px; line-height: 1.4;
}
.macro-step-main { flex: 1 1 320px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.macro-step-tools { display: flex; gap: 2px; flex: 0 0 auto; align-items: center; }
.macro-step-raw {
  display: block; font-size: 11.5px; color: var(--text-dim);
  word-break: break-all; white-space: pre-wrap; margin-top: 2px;
}
/* 步骤里的下拉框不必像主表单那样宽 */
.macro-step .mini-select { max-width: 100%; }
.macro-step .action-editor { padding: 6px; }
.macro-steps .status { margin: 2px 0; }

@media (max-width: 640px) {
  .macro-step-main { flex: 1 1 100%; }
  .macro-step-tools { flex: 1 1 100%; justify-content: flex-end; }
}

/* ---------------- JSON 编辑器 ----------------
 * ⚠️ width 必须扣掉卡片的左右 margin：`details.card > *:not(summary)` 给直接子元素
 * 左右各加了 14px margin，而 box-sizing:border-box 的 100% 不含 margin，
 * 直接写 100% 右边会溢出 14px，又被卡片的 overflow:hidden 裁掉 ——
 * 右缘滚动条与右下缩放把手看不见。textarea 是 replaced element，
 * width:auto 会按 cols 固有宽度收缩（实测只剩 177px），不能用。 */
.json-editor {
  display: block;
  width: calc(100% - 28px);
  min-height: 320px;
  background: #14161a;
  color: #d6dae0;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.55;
  resize: vertical;
  tab-size: 2;
  margin: 8px 0;
}
.json-editor.small { min-height: 0; margin: 4px 0; }

/* ---------------- JSON 问题提醒面板 ---------------- */
.json-issues {
  display: none;
  font-size: 12.5px;
  margin: 8px 0;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  background: #1d2025;
  color: var(--text-dim);
  line-height: 1.7;
}
.json-issues.warn,
.json-issues.error,
.json-issues.ok { display: block; }
.json-issues.warn { border-left-color: var(--warn); background: rgba(226, 163, 63, 0.10); color: #f0d9ab; }
.json-issues.error { border-left-color: var(--err); background: rgba(238, 107, 100, 0.10); color: #f4bcb8; }
.json-issues.ok { border-left-color: var(--ok); background: rgba(72, 194, 106, 0.10); color: #a9e2bb; }
.json-issues-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
/* 片段编辑器（动作 / 宏 / 按键原始 JSON）用的紧凑版 */
.json-issues.small { font-size: 12px; padding: 6px 10px; margin: 6px 0; }
.snippet-editor { display: flex; flex-direction: column; gap: 2px; margin-top: 4px; }

/* ---------------- 对话框 ---------------- */
dialog.editor-dialog {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card);
  color: var(--text);
  padding: 0;
  width: min(560px, calc(100vw - 40px));
  max-height: calc(100vh - 60px);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.55);
}
dialog.editor-dialog.wide { width: min(720px, calc(100vw - 40px)); }
dialog.editor-dialog::backdrop { background: rgba(5, 6, 8, 0.6); backdrop-filter: blur(2px); }
.editor-form { display: flex; flex-direction: column; max-height: calc(100vh - 60px); }
.dialog-titlebar {
  padding: 10px 16px; border-bottom: 1px solid var(--border); background: #23262c;
  border-radius: 12px 12px 0 0;
}
.dialog-titlebar h3 { margin: 0; font-size: 15px; }
.dialog-body { padding: 12px 16px; overflow-y: auto; }
.dialog-toolbar {
  padding: 10px 16px; border-top: 1px solid var(--border); margin: 0 !important;
  justify-content: flex-end; background: #1d2025; border-radius: 0 0 12px 12px;
}

/* ---------------- 浮动工具（右上撤销/重做、右下回到顶部） ----------------
 * 位置：贴着**中央操作区的外缘**，而不是视口边缘 —— 钉在视口上的话屏幕越宽
 * 离内容越远，鼠标得多跑一截。做法是用一层与内容列同宽（--content-max）、
 * 同样居中、同样左右内边距（--content-pad-x）的 fixed 定位层，
 * 按钮挂在这层的右边缘上，于是无论窗口多宽都紧贴内容列外侧。
 * 显隐由 app.js 的 updateFloatTools 管（滚出顶栏后才出现，避免与顶栏同款按钮重叠）。
 * 注意 [hidden] { display: none !important } 已在文件顶部，能盖住这里的 display。 */
.float-layer {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  /* 只用来定位，不吃鼠标事件；按钮自身在下面重新开启 */
  pointer-events: none;
  z-index: 40;
}
.float-rail {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 var(--content-pad-x);
  height: 100%;
  position: relative;
}
/* 右上：撤销 / 重做；右下：回到顶部。
 * ⚠️ 用 left 定位，**不要改用 right**：
 *   · right: 0 会把按钮的**右**缘钉在内容列边上，按钮本体向左伸出去 → 压到卡片上；
 *   · 同时给 left 与 right 且宽度 auto → 按钮会被左右拉伸。
 * 这里让按钮的**左**缘落在内容列右缘之外：left: 100% 是内容列（rail 的 padding box）
 * 右缘，减去内边距回到"卡片右缘"，再加 8px 间隙 —— 于是按钮整体位于内容列外侧的
 * 页边距里，间隙恒为 8px、与按钮实际宽度无关（不需要写死按钮宽度）。 */
.float-top-right { position: absolute; top: 12px; left: calc(100% - var(--content-pad-x) + 8px); }
.float-bottom-right { position: absolute; bottom: 20px; left: calc(100% - var(--content-pad-x) + 8px); }
.float-rail > * { pointer-events: auto; }
.float-group { display: flex; gap: 6px; }
/* 浮在内容之上：加投影让按钮从背景里"浮"出来，并保持圆角一致 */
.float-group .mini-button,
.float-bottom-right {
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  border-radius: 8px;
}
.float-group .mini-button:focus-visible,
.float-bottom-right:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 窄屏兜底：内容列外侧的页边距放不下按钮，按钮会越过视口右缘被裁切，
 * 此时退回贴视口右缘（此宽度下页边距本就很小，贴视口≈贴内容）。
 * 断点 ≈ 内容列 1298px + 两侧各约 170px（按钮组宽 + 间隙 + 余量）。 */
@media (max-width: 1638px) {
  .float-top-right { position: fixed; top: 12px; left: auto; right: 12px; }
  .float-bottom-right { position: fixed; bottom: 20px; left: auto; right: 12px; }
}
/* 手机竖屏：main 的内边距收窄，变量同步收窄，浮动层才能继续与内容对齐
 * （宽屏公式里用到 --content-pad-x）。 */
@media (max-width: 640px) {
  :root { --content-pad-x: 10px; }
}

/* ---------------- 内建提示 / 确认 / 输入框（FE.uiAlert / uiConfirm / uiPrompt） ----------------
 * 替代浏览器 alert / confirm / prompt：不阻塞主线程、样式与编辑器统一。
 * 这几个对话框刻意做得紧凑（宽度跟随 .editor-dialog 的默认宽度），
 * 因为它们只承载一两行文字或一个输入框，不该占满屏幕。 */
.ui-dialog-body { font-size: 13.5px; line-height: 1.65; }
/* 消息里的 \n 被拆成多个 .ui-dialog-msg，逐行留一点间距 */
.ui-dialog-msg + .ui-dialog-msg { margin-top: 4px; }
.ui-dialog-input { width: 100%; }
.ui-dialog-error {
  min-height: 18px; font-size: 12.5px; color: var(--err); margin-top: 4px;
}
/* 确认框的「确定」在右（工具栏已 justify-content: flex-end），
 * 危险操作由调用方传 danger 加 .danger 类；这里只保证最小宽度一致 */
.ui-dialog-ok, .ui-dialog-cancel { min-width: 72px; }

.picker-list { max-height: 46vh; overflow-y: auto; border: 1px solid var(--border); border-radius: 8px; margin-top: 8px; }
.picker-group { padding: 6px 12px 2px; font-size: 12px; color: var(--text-dim); font-weight: 700; background: #1d2025; position: sticky; top: 0; }
.picker-item {
  display: flex; align-items: center; gap: 10px; padding: 6px 12px; cursor: pointer;
  border-top: 1px solid #262a31;
}
.picker-item:hover { background: #2a2e36; }
.picker-item.active { background: #2c3a55; }
.picker-item code { min-width: 150px; }
.picker-label { flex: 1; font-weight: 600; }
.picker-kind { font-size: 11px; color: var(--text-dim); }

.gesture-area { border: 1px solid var(--border); border-radius: 8px; padding: 10px; margin: 8px 0; background: var(--card-inner); }
.action-editor { border: 1px dashed var(--border); border-radius: 8px; padding: 8px; background: #1d2025; }
.action-fields { display: flex; flex-direction: column; gap: 2px; }
.meta-checks { display: inline-flex; gap: 8px; }

/* ---------------- 页脚 ---------------- */
.footnote { text-align: center; color: var(--text-dim); font-size: 12px; padding: 12px 20px 24px; }
.footnote code { font-size: 11px; }

/* ================================================================
 * 主题页 / 符号面板页
 * 主题、符号面板与弹出菜单一样是**另一类文档**，标签间用间距分隔。
 * ================================================================ */
.tab.tab-theme { margin-left: 22px; }
.tab.tab-symbols { margin-left: 0; }

/* 槽位配色表单：26 个字段排成自适应多列，窄屏自动回落单列。
 * 用 grid + minmax 而不是固定列数 —— 色值输入框宽度固定（.color-input），
 * 列数由容器宽度决定，避免 PC 上过宽、手机上挤成一条。 */
.th-color-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 4px 12px;
  margin: 6px 0;
}
.th-color-grid .form-row.form-inline { margin: 0; }
/* ⚠️ `flex: 1 1 100%`（不是 `1 1 auto`）—— 标签必须**恒定独占整行**。
 *
 * 用 `auto` 时标签按内容宽度参与计算：26 个字段里标签长短悬殊（`candidateBarColor`
 * 的中文注释长、`accentColor` 只有「强调色」三个字），短标签省出的空间会让
 * `.color-input`(150px) + `清除` 按钮挤上**同一行**，于是那一行变成
 * 「标签 + 框」而 `清除` 掉到下一行 —— 与其余 25 行的「标签一行、框和按钮一行」
 * 明显不一致（用户实机截图指出）。
 * `100%` 的 flex-basis 在 `flex-wrap: wrap` 下保证标签独占一行，
 * 输入框与按钮必然成组换到第二行，26 行排版彻底统一。
 * 短标签因此会右侧留白 —— 这正是想要的"对齐"，且与 keyTypes 网格共用同一外观。 */
.th-color-grid .mini-label { flex: 1 1 100%; min-width: 0; }

/* 字段名很长（keyHintTextBottomColor 等），允许换行显示完整名，不省略 */
.th-field-label { overflow-wrap: anywhere; }

/* keyTypes 分组卡片：三类（LETTER / FUNCTION / ACTION）各一块 */
.th-keytype-block {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  margin: 8px 0;
  background: var(--card-inner);
}
.th-keytype-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 4px;
}
.th-keytype-head code { font-family: var(--mono); font-size: 13px; color: var(--text); }
.th-keytype-missing { font-size: 12px; color: var(--text-dim); }

/* 主题预览槽位提示条：明确区分"主题槽位"与"预览深浅开关" */
.th-slot-hint { font-size: 12.5px; color: var(--text-dim); margin: 4px 0; }
.th-slot-hint code { font-family: var(--mono); }

/* 符号分组：组名 + 条目 chips（symbol-editor.js 使用，容器先备好） */
.sym-group-block {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  margin: 8px 0;
  background: var(--card-inner);
}
.sym-entries { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.sym-entry {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; min-height: 30px; padding: 2px 7px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-soft); font-size: 15px; line-height: 1.3;
  overflow-wrap: anywhere;
}

/* ================================================================
 * 竖屏手机优化（仅 ≤640px 生效，不影响 PC / 平板横屏布局）
 * 目标：更大的触摸目标、更省空间的排版、拖动时不误触滚动。
 * ================================================================ */
@media (max-width: 640px) {
  /* 收窄外边距，把宝贵的横向空间留给内容。
   * 用变量而非硬编码 —— .float-rail 靠同一个变量对齐，浮动按钮才能继续贴住内容外缘。 */
  main { padding: 10px var(--content-pad-x) 32px; }
  .topbar { padding: 10px 12px 8px; }
  .topbar h1 { font-size: 18px; }
  .topbar h2 { font-size: 11.5px; }
  details.card > *:not(summary) { margin-left: 10px; margin-right: 10px; }
  details.card > summary, .panel > summary { padding: 11px 12px; font-size: 14.5px; }

  /* 标签页：整行铺满、加大点按区域 */
  .tabs { gap: 4px; }
  .tab { flex: 1 1 auto; text-align: center; padding: 10px 8px; font-size: 13.5px; }
  .tab.tab-popup, .tab.tab-theme, .tab.tab-symbols { margin-left: 0; }

  /* 工具栏按钮更大更易点，输入框占满一行 */
  .toolbar { gap: 6px; }
  button, .mini-button { padding: 9px 14px; font-size: 14px; }
  .icon-button { padding: 7px 12px; font-size: 15px; }
  .mini-input, .mini-select { padding: 8px 10px; font-size: 14px; }
  .mini-input.wide, .mini-select.wide { width: 100%; }
  .preview-toggles { gap: 8px 12px; font-size: 13px; }
  .preview-toggles label.check, .preview-toggles .mini-label { min-height: 32px; }

  /* 预览键盘占满宽度，方便查看效果 */
  .preview-toolbar { gap: 8px; padding: 10px 12px 0; }
  .preview-stage { padding: 10px 8px 4px; }
  .kb { max-width: 100% !important; }

  /* 行按键 chip：更大的触摸目标；拖动时禁用浏览器手势，避免误触滚动 */
  .chip-box { gap: 8px; min-height: 52px; padding: 6px; }
  .chip {
    min-width: 60px; min-height: 52px; padding: 5px 10px;
    touch-action: none;   /* 让指针拖动接管，不被页面滚动抢走 */
  }
  .chip-label { font-size: 15px; max-width: 88px; }
  .chip-sub { font-size: 11px; }
  .chip-add { min-width: 52px; min-height: 52px; font-size: 15px; }

  /* 拖动提示条：整块编辑区顶部提示一次，告诉用户可直接按住拖动 */
  #layout-sections::before {
    content: '提示：按住按键可拖动排序，拖到目标位置松手';
    display: block; font-size: 12px; color: var(--text-dim);
    background: var(--card-inner); border: 1px dashed var(--border);
    border-radius: 8px; padding: 7px 10px; margin-bottom: 8px;
  }

  /* 网格编辑：格子更大更好点 */
  .gedit-cell { min-height: 54px; }
  .gedit-wrap { -webkit-overflow-scrolling: touch; }

  /* 行编辑头部换行更紧凑 */
  .row-head { gap: 6px; }
  .row-head .mini-label { flex: 1 1 40%; }
  .row-head .mini-input { flex: 1 1 auto; min-width: 0; }

  /* 表单两列在窄屏堆叠为单列 */
  .form-grid-2 { grid-template-columns: 1fr; }

  /* JSON 编辑框在手机上给足高度 */
  .json-editor { min-height: 180px; }

  /* 对话框接近全屏，操作区按钮更大 */
  dialog.editor-dialog, dialog.editor-dialog.wide { width: calc(100vw - 16px); max-width: calc(100vw - 16px); }
  .dialog-toolbar button, .dialog-toolbar .mini-button { padding: 10px 16px; }
}
