/*
 * 主题：暗色为默认，[data-theme="light"] 覆盖成亮色。
 * theme=system 时由 app.js 判断 prefers-color-scheme 后写到 data-theme 上。
 *
 * 不透明度：body 背景用 rgba(--bg-rgb, --ui-alpha)，--ui-alpha 由设置里的
 * 不透明度决定（100% = 完全不透明，观感与以前一致）；窗口本身在
 * tauri.conf.json 里开了 transparent，所以调低时能真的透出桌面。
 */
:root {
  color-scheme: dark;
  /* 灰阶与远程管理（hub.html）同一套：暗色 base 2,6,23 / card 14,18,35 */
  --bg-rgb: 2, 6, 23;
  --panel: #0e1223;
  --border: #242b41;
  --border-strong: #334155;
  /* 字体灰阶整体提亮一档：调低窗口不透明度后，文字会落在被桌面透过来的背景上，
     原来的 #a5b0c2 / #6f7d92 一淡就糊成一片，现在每一档都留足对比度。 */
  --text: #f8fafc;
  --text-dim: #94a3b8;
  --text-faint: #64748b;
  --text-fainter: #475569;
  --input-bg: #141a2e;
  /* 按钮/链接色走 --accent 系列（调色板选的强调色，app.js 派生并写入；
     未写入时回退到内置靛蓝，与 hub 同色） */
  --btn: var(--accent, #6366f1);
  --btn-hover: var(--accent-hover, #7070f2);
  --btn-secondary: #334155;
  --btn-secondary-hover: #42536e;
  --btn-text: #ffffff;
  --btn-secondary-text: #e2e8f0;
  /* 选中系统的那一抹绿（勾 + 边框 + 底色） */
  --ok: #22c55e;
  --ok-border: #16a34a;
  --ok-bg: #0f2a1a;
  --warn: #f59e0b;
  --link: var(--accent-link, #7f82f4);
  --link-hover: var(--accent-link-hover, #9296f8);
  --port-border: #6b3a1f;
  --port-bg: #2a1b13;
  --port-strong: #f0a98a;
  --port-text: #d8b18a;
  --pending-border: #6b4a1f;
  --pending-bg: #2a2113;
  --pending-text: #d8b98a;
  --danger: #b4442f;
  --danger-text: #fff3ee;
  /* 直接落在半透明窗体上的文字（标题 / 状态 / 提示）用的描边 */
  --text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
  --font:
    "Segoe UI", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC",
    "Source Han Sans SC", "WenQuanYi Micro Hei", system-ui, sans-serif;
  font-family: var(--font);
  color: var(--text);
}

/*
 * 亮色主题。
 *
 * 这里的每个灰阶都比「看起来够淡」更暗一档：白底上小字（12–13px 的说明文案、
 * 「最近请求」列表）如果用 #8b95a3 这类浅灰，对比度只有 2 出头，正文基本看不清。
 * 现在四档的对比度依次约 19 / 12 / 10 / 8，既分层又能读。
 */
[data-theme="light"] {
  color-scheme: light;
  /* 与远程管理亮色同一套：base 244,246,250 / card #ffffff / line 226,232,240 */
  --bg-rgb: 244, 246, 250;
  --panel: #ffffff;
  --border: #e2e8f0;
  --border-strong: #cbd5e1;
  --text: #0f172a;
  --text-dim: #5a6476;
  --text-faint: #6d798c;
  --text-fainter: #8a94a6;
  --input-bg: #fbfcfe;
  --btn: var(--accent, #4f46e5);
  --btn-hover: var(--accent-hover, #4338ca);
  --btn-secondary: #e2e8f0;
  --btn-secondary-hover: #d3dbe6;
  --btn-text: #ffffff;
  --btn-secondary-text: #1e293b;
  --ok: #16a34a;
  --ok-border: #22c55e;
  --ok-bg: #eaf7ee;
  --warn: #b45309;
  --link: var(--accent-link, #4338ca);
  --link-hover: var(--accent-link-hover, #3730a3);
  --port-border: #e2b48a;
  --port-bg: #fdf4ea;
  --port-strong: #a85a15;
  --port-text: #7a4a1c;
  --pending-border: #e6cf94;
  --pending-bg: #fdf8e8;
  --pending-text: #7a5a1c;
  --danger: #c74a33;
  --danger-text: #ffffff;
  /* 亮色下反着来：用一层浅描边托住深色文字 */
  --text-shadow: 0 1px 2px rgba(255, 255, 255, 0.9);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  /* 内嵌模式窗口只有 460 宽；分栏模式（③④）在下面另给 640 的下限 */
  min-width: 360px;
  /* 首帧前不可见：页面重载（窗口呼出时的唤醒钩子会 reload）时，HTML 初始态是
     「正在扫描…」+ 空列表，直接画出来会「闪一下旧页面」。等首份快照渲染完
     由 app.js 给 <html data-ready> 再显示 —— 用户只看到成品。
     遮罩底色与 body 同源，避免白闪。 */
  opacity: 0;
  background: rgba(var(--bg-rgb), var(--ui-alpha, 1));
  color: var(--text);
  /* 窗口竖着切成两块：左右双栏（.app-columns，各自可滚） + 底部提示条
   * （钉在窗口最下方）。提示条**不浮在内容上**，而是占自己那一格 ——
   * 它属于窗口、不属于某一栏。 */
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}

/* 首帧渲染完成后才显示：页面重载（窗口呼出时的唤醒钩子会 reload）期间，
   HTML 初始态是「正在扫描…」+ 空列表，直接画出来就是「闪一下旧页面」。
   app.js 在首份快照渲染完时给 <html data-ready>，这里才让它出现。 */
html[data-ready] body {
  opacity: 1;
  transition: opacity 80ms ease-out;
}

/* 左栏（左半区）：原主窗口的全部内容，自己一个滚动容器 */
main {
  display: grid;
  /* 单列网格：minmax(0,1fr) 让子项可以收缩到 0 —— grid 项默认 min-width:auto，
   * 长路径/token 会把列撑到内容宽度，出现横向滚动条（用户要求去掉） */
  grid-template-columns: minmax(0, 1fr);
  /* 行不许拉伸：本容器是 flex 撑满整高的 grid，内容比容器矮时 align-content 的
   * 默认值 normal=stretch 会把 header / tabs / 正文行按剩余空间等比例拉高。
   * GRUB 独立模式（④）把 GRUB 整块搬到右栏后左栏内容变短，就正好触发 ——
   * 表现是页签上方一大片空白（用户报的正是这个）。start = 行保持自然高度、贴顶。 */
  align-content: start;
  gap: 20px;
  padding: 24px;
  /* 正文自己滚，提示条永远钉在窗口底边 */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

/* ---------- 界面栏目页签 ----------
 * 主窗口内容分成两个栏目：系统引导（主功能，rEFInd + GRUB 同页）和
 * 基本设置（应用设置 + 远程控制）。切换纯前端，localStorage 记忆。 */
.tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border, rgba(128, 140, 160, 0.35));
}
.tab {
  appearance: none;
  border: 0;
  background: none;
  color: var(--btn-secondary-text, #e7ebf2);
  font: inherit;
  font-weight: 600;
  padding: 8px 16px;
  cursor: pointer;
  position: relative;
  opacity: 0.72;
}
/* 选中态下划线：设计稿（pc-redesign-v2）的圆角短杠形态，颜色跟随调色板 */
.tab::after {
  content: "";
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: -1px;
  height: 2px;
  border-radius: 999px;
  background: transparent;
}
.tab:hover {
  opacity: 1;
}
.tab.active {
  color: var(--accent, var(--btn, #2f7fe0));
  opacity: 1;
}
.tab.active::after {
  background: var(--accent, var(--btn, #2f7fe0));
}
.tab-panel {
  display: grid;
  gap: 20px;
  min-width: 0;
}
.tab-panel[hidden],
.tab-panel:not(.active) {
  display: none;
}
/* 浏览器直开（静态预览）时的提示横幅：Tauri API 不可用，只能看不能操作 */
.browser-banner {
  position: sticky;
  top: 0;
  z-index: 50;
  padding: 8px 16px;
  font-size: 13px;
  background: var(--warn, #f0b073);
  color: #1c1c1c;
  text-align: center;
}

/* 横幅里「更换 token」的小按钮：无边框文字样式，颜色压深保证黄底可读 */
.browser-banner .banner-link {
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 12px;
  background: none;
  color: #4a3000;
  text-decoration: underline;
  cursor: pointer;
}

/* token 模态框：首次访问 / 401 时要求输入令牌 */
.token-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
}

/* display:flex 会压过 [hidden] 的 UA 样式，显式补一条（和 .grub-empty-state
   同一个坑）：不然 JS 里 overlay.hidden = true 只是改了属性，弹窗永远关不掉 ——
   连接成功/点「暂不连接」后它都还盖在屏幕上，手机端整个页面跟着被锁死。 */
.token-modal-overlay[hidden] {
  display: none;
}

.token-modal {
  width: min(380px, 92vw);
  padding: 22px 24px;
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  background: var(--panel);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
}

.token-modal h2 {
  margin: 0 0 10px;
  font-size: 17px;
  color: var(--text);
}

.token-modal p {
  margin: 0 0 12px;
  font-size: 13px;
  line-height: 1.7;
  color: var(--text-dim);
}

.token-modal input {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--input-bg);
  color: var(--text);
  font-size: 14px;
}

.token-modal input:focus {
  outline: none;
  border-color: var(--btn);
}

.token-modal .token-error {
  color: var(--danger-text);
  background: var(--danger);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 12px;
}

/* 「显示 token」勾选框：密码框打什么都看不见，手机上很容易漏打/被输入法改掉 */
.token-modal .token-reveal {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  font-size: 12px;
  color: var(--text-dim);
  cursor: pointer;
  user-select: none;
}

.token-modal .token-reveal input {
  width: auto;
  margin: 0;
  padding: 0;
  accent-color: var(--btn);
}

.token-modal .token-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  margin-top: 14px;
}

/* 远程模式横幅里的 token 输入行（web 端连接表单） */
.browser-banner input {
  margin: 0 6px;
  padding: 3px 8px;
  width: 220px;
  max-width: 40vw;
  font: inherit;
  color: #1c1c1c;
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid rgba(0, 0, 0, 0.25);
  border-radius: 5px;
}

.browser-banner button {
  padding: 3px 14px;
  font: inherit;
  font-weight: 600;
  color: #1c1c1c;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(0, 0, 0, 0.25);
  border-radius: 5px;
  cursor: pointer;
}

/* 横幅里的「传输方式」下拉：跟着 token 输入框的观感走（黄底上要压深字色） */
.browser-banner select {
  margin: 0 4px;
  padding: 3px 6px;
  font: inherit;
  font-size: 12px;
  color: #1c1c1c;
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid rgba(0, 0, 0, 0.25);
  border-radius: 5px;
}

/* 下拉刷新时跟着手指走的那颗小药丸；刷新中固定在顶部显示「正在刷新…」。
   z-index 压过横幅（50），免得被黄条盖住。 */
.pull-refresh {
  position: fixed;
  top: 0;
  left: 50%;
  z-index: 60;
  padding: 6px 14px;
  font-size: 12px;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  pointer-events: none;
  will-change: transform;
}

.browser-banner button:hover {
  background: rgba(255, 255, 255, 0.85);
}

/* ---------- 窗口最下方的提示条（左右双栏之外） ----------
 * 以前所有操作回执都写在正文最末尾的 #message，用户不滚到底根本看不到
 * （「勾选了某个系统之后没反应」就是这个原因）。后来挪成 fixed 遮罩，但视觉上
 * 仍像是正文的一部分 —— 现在把它做成窗口自己的**独立底栏**：
 * 和 .app-columns 平级、不盖住内容、有内容才占位，没内容整条收起。 */
.status-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 14px;
  border-top: 1px solid var(--border-strong);
  background: var(--panel);
  box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.28);
  font-size: 12px;
  line-height: 1.5;
}

.status-bar.hidden {
  display: none;
}

.status-bar-icon {
  flex: 0 0 auto;
  font-size: 13px;
  line-height: 1.5;
}

.status-bar-text {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--text);
  word-break: break-word;
  white-space: pre-wrap;
}

/* 失败回执整体染成暖色，一眼能和「已成功」区分开 */
.status-bar.error {
  border-top-color: var(--warn);
  background: var(--port-bg);
}

.status-bar.error .status-bar-text,
.status-bar.error .status-bar-icon {
  color: var(--warn);
}

.status-bar.ok .status-bar-icon {
  color: var(--ok);
}

.status-bar-close {
  flex: 0 0 auto;
  padding: 0 4px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--text-faint);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}

.status-bar-close:hover {
  background: var(--btn-secondary);
  color: var(--text);
}

h1 {
  margin: 0 0 8px;
  font-size: 20px;
}

#status {
  margin: 0;
  color: var(--text-dim);
  font-size: 14px;
}

/*
 * 这两个元素**不在卡片里**（标题、状态），直接落在半透明的窗体背景上。
 * 给它们加一层主题色的描边，不透明度调到 50% 时文字也还能立住。
 */
h1,
#status {
  text-shadow: var(--text-shadow);
}

/* ---------- 下次启动：醒目大字（问题 7） ---------- */
.next-boot {
  display: grid;
  gap: 2px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  border-left: 4px solid var(--ok);
  border-radius: 10px;
  background: var(--panel);
}

.next-boot small {
  color: var(--text-dim);
  font-size: 12px;
  letter-spacing: 0.08em;
}

.next-boot strong {
  color: var(--ok);
  /* 级联全链动辄几十个字，24px 太占地方（用户反馈「过大」），收到 16px */
  font-size: 16px;
  line-height: 1.3;
}

.next-boot.unknown strong {
  color: var(--text-faint);
}

/* ---------- 系统列表：勾 / 名称 / 按钮 三列，列宽固定所以天然左对齐 ---------- */
#systems {
  display: grid;
  gap: 10px;
}

.system-card {
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}

.system-card.active {
  border-color: var(--ok-border);
  background: var(--ok-bg);
}

.system-card.unavailable {
  opacity: 0.45;
}

/* 勾占独立一列：选中就显示绿勾、没选中就空着，其他列不会因此错位 */
.system-check {
  color: var(--ok);
  font-size: 18px;
  font-weight: 700;
  text-align: center;
}

.system-name {
  display: grid;
  gap: 3px;
  justify-items: start;
  text-align: left;
}

.system-name strong {
  font-size: 15px;
}

.system-name small {
  color: var(--text-faint);
}

/* ============ 单窗口 + 四种展示模式（body[data-grub-mode] 驱动） ============
 * ① inline 内嵌：窗口 460 宽，GRUB 区块嵌在主流程里（跟老版一致）；
 * ② wide 加宽：窗口 932 宽，还是单栏主流程，列表铺两列；
 * ③ list 列表独立：窗口 932、左右分栏，只有系统列表搬进右栏，配置留左栏；
 * ④ pane 全独立：窗口 932、左右分栏，GRUB 全部内容都在右栏。
 * 两栏等宽：同一个 flex 基准 0 + 同一份 grow，窗口多宽都各占一半。
 * 中缝 .pane-gap 是纯空白长条（12px）；③④ 两栏各自一个滚动容器
 * （左 = <main>，右 = .pane-body）→ 滚动条互不影响。 */
.app-columns {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
}

/* 两栏等宽的唯一来源：都从基准 0 开始长。（main 自己那条 flex: 1 1 auto
 * 是给它做 body 子项时用的老规则，这里必须用更高优先级按同一基准压平，
 * 否则基准 auto 会让左栏按内容宽度比右栏宽。） */
.app-columns > .pane {
  flex: 1 1 0;
  min-width: 0;
}

.pane-gap {
  /* 纯空白：不设背景、不画边框，就是窗口底色的一条缝 */
  flex: 0 0 12px;
}

.pane-grub {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* 模式 ①② 右栏与中缝整体隐藏，左栏独占整窗；③④ 才亮出来 */
.pane-grub,
.pane-gap {
  display: none;
}

body[data-grub-mode="list"] .pane-grub,
body[data-grub-mode="pane"] .pane-grub {
  display: flex;
}

body[data-grub-mode="list"] .pane-gap,
body[data-grub-mode="pane"] .pane-gap {
  display: block;
}

/* 分栏模式给窗口一个体面的最小宽度（内嵌/加宽模式仍是全局的 360） */
body[data-grub-mode="list"],
body[data-grub-mode="pane"] {
  min-width: 640px;
}

/* 右栏顶部：原来独立窗口那一行（标题 + 元信息 + 回执）。
 * 左右内边距跟左栏 main 的 24px 对齐，两栏内容的左边缘才会齐平。 */
.grub-pane-head {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  padding: 24px 24px 0;
}

.grub-pane-head strong {
  font-size: 14px;
}

/* GRUB 独立模式（④）：.grub-head（圆标 GRUB 区 + h2 GRUB 启动项）整行搬进
   右栏当标识，pane-head 那行孤零零的「GRUB」strong 就成了重复 —— 藏掉，
   右栏标识与左栏 rEFInd 区完全同构（用户定案 2026-10-13）。③ 列表独立
   模式列表单独搬过去、.grub-head 留在左栏，这行仍是右栏唯一的标题，保留。 */
body[data-grub-mode="pane"] .grub-pane-head {
  display: none;
}

/* ③④ 右栏背板 = 卡片（用户定案 2026-10-13）：pane-body 本身是滚动容器，
   把原来的 24px 外边距从 padding 挪成 margin，容器直接套上与 .grub/.refind
   同一套边框/底色/圆角 —— 右栏看起来就是一块和 rEFInd 区一样的卡片。
   ④ 里 pane-head 藏了，卡片顶距用 margin-top 24px 对齐左栏（卡片顶对齐，
   内部都是 14px 内边距，两栏圆标行自然同一水平线）。 */
body[data-grub-mode="list"] .pane-body,
body[data-grub-mode="pane"] .pane-body {
  margin: 12px 24px 24px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}

body[data-grub-mode="pane"] .pane-body {
  margin-top: 0;
}

/* ④ 的 .grub-head 不参与滚动（用户定案 2026-10-13）：applyGrubLayout 把它
   搬出滚动容器、钉在卡片上方 —— 圆标/标题/EFI·条目数·同步时间常驻，
   只有卡片内容滚。sticky 方案在 grid 子项上会被约束在自己的网格区里、
   滚过头照样走，不可靠，已弃。内边距：顶 24px 与左栏首行同高，
   左右 24px 与卡片同缩进，底 10px 隔开卡片。 */
body[data-grub-mode="pane"] .pane-grub > .grub-head {
  padding: 24px 24px 10px;
}

.grub-pane-head small {
  color: var(--text-faint);
  font-size: 11px;
  word-break: break-all;
}

/* 切换启动项的回执行（原来在独立窗口的标题栏里） */
.grub-pane-status {
  min-height: 14px;
  color: var(--ok);
}

.grub-pane-status.fail {
  color: var(--warn);
}

/* 右栏唯一那个滚动容器：GRUB 的全部内容都在这里滚（列表不另开一层滚动条） */
.pane-body {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  gap: 10px;
  padding: 12px 24px 24px;
  overflow-y: auto;
  overflow-x: hidden;
}

/* GRUB 启动项条目：grub.cfg 里动辄几十个（本机 77 个），字号比上面「可启动系统」
 * 小一号。列表不再限高 —— 右栏整条就是它的滚动区。 */
/* ---------- GRUB 启动项：和上面三个系统并列的第二套机制 ----------
 * grub.cfg 里动辄几十个条目（本机 77 个），字号整体比上面小一号。
 * 模式 ①②③ 它是一个带边框的面板（在左栏主流程里 / ③ 时配置部分留左栏）；
 * 模式 ④ 整块搬进右栏后只剩空壳，直接藏掉（右栏由 .pane-body 自己排版）。
 * .refind（rEFInd 区）与它同一块背板：边框/底色/圆角/内边距完全一致
 * （用户定案 2026-10-13：两个区域的背板统一）。 */
.grub,
.refind {
  display: grid;
  gap: 10px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}

/* rEFInd 区 / GRUB 区的分界横隔（用户定案 2026-10-13）：一道加粗的浅色
 * 长线，让「上面是 rEFInd、下面是 GRUB」一眼分开。GRUB 独立右栏（模式 ④）
 * 时分隔线还留在左栏就成了一条悬空的线 —— 跟 #grub 一起藏掉。 */
.boot-divider {
  border: none;
  height: 4px;
  margin: 6px 0;
  border-radius: 2px;
  background: linear-gradient(
    to right,
    var(--border-strong),
    var(--border) 60%,
    transparent
  );
}

body[data-grub-mode="pane"] .boot-divider {
  display: none;
}

/* 引导配置须知：os-prober / EFI 顺序 / Windows 分区标志 —— 用户不配这些
 * GRUB 列表里就只有自己，所以常驻可见（小字，不抢主流程的视线）。
 * min-width:0 + anywhere 断行是硬要求：GRUB_DISABLE_OS_PROBER=false 这种
 * 无空格长参数在 460px 内嵌列里不许横向溢出——一溢出就把整个内嵌区撑宽
 * （用户 2026-10-13 报的「内嵌列表宽度被改」就是这么来的，不是布局改了）。 */
.grub-advice {
  display: block;
  min-width: 0;
  overflow-wrap: anywhere;
  padding: 8px 10px;
  border: 1px dashed var(--border-strong);
  border-radius: 8px;
  color: var(--text-dim);
  font-size: 11px;
  line-height: 1.7;
}

.grub-advice strong {
  display: block;
  margin-bottom: 2px;
  color: var(--text);
}

.grub-advice ul {
  margin: 0;
  padding-left: 16px;
}

.grub-advice code {
  padding: 0 3px;
  border-radius: 3px;
  background: var(--border);
  font-size: 10px;
  overflow-wrap: anywhere;
}

/* 区块标志（用户定案 2026-10-13）：rEFInd 区 / GRUB 区各挂一枚小圆标，
 * 结构完全同构 —— 圆标 + 标题一行（.grub-title-row 两边共用）。
 * GRUB 的那枚在 .grub-head 里，随头部一起搬进右栏（GRUB 独立模式下右栏
 * 照样有标志）；rEFInd 的那枚固定在左栏顶部。 */
.zone-tag {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--panel);
  color: var(--text-dim);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  white-space: nowrap;
}

.grub-title-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  /* 在 .grub-head（flex）里不许被右侧状态小字挤窄 —— 挤窄会把标题折到
     圆标下一行，rEFInd 区一行、GRUB 区两行就不一致了（grid 里此属性无效，
     不影响 rEFInd 区）。 */
  flex: none;
}

/* 电源三键贴着应用名同一行、右对齐（用户定案 2026-10-13） */
.header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.header-row h1 {
  margin: 0;
}

/* Linux 系统卡上的问号徽标：hover / 键盘聚焦时浮现 09_anyboot 说明 */
.system-help {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  margin-left: 4px;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  color: var(--text-dim);
  font-size: 10px;
  line-height: 1;
  cursor: help;
  vertical-align: middle;
}

.system-help:hover,
.system-help:focus {
  color: var(--text);
  border-color: var(--text-dim);
}

body[data-grub-mode="pane"] #grub {
  display: none;
}

.grub-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

/* 两区标题同行规格（用户定案 2026-10-13）：字号/边距必须完全一致 ——
   上一轮删 .systems-head 后 rEFInd 的 h2 失去这条规则、回落浏览器默认
   1.5em + 默认 margin，字号偏大还把卡片顶部留白撑开（用户报的正是这个）。
   .grub-head h2 单列一条：GRUB 独立模式下 .grub-head 整体搬进右栏
   .pane-body，不再是 .grub 的后代，`.grub h2` 够不着它（同坑第二次）。 */
.grub h2,
.refind h2,
.grub-head h2 {
  margin: 0;
  font-size: 14px;
}

.grub-head small {
  color: var(--text-faint);
  font-size: 11px;
  text-align: right;
  /* 挤的时候小字自己折行，别把标题行顶成两行 */
  min-width: 0;
  overflow-wrap: anywhere;
}

.grub-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.grub-row label {
  font-size: 12px;
  color: var(--text-dim);
}

.grub-row input {
  flex: 1 1 180px;
  min-width: 0;
  padding: 7px 10px;
  font-size: 12px;
}

.grub-row select {
  flex: 1 1 180px;
  min-width: 0;
  padding: 7px 10px;
  font-size: 12px;
}

/* 独立窗口已改为「独立区域」（同一窗口里的右栏），原来那条说明文字的样式一并消失 */

.grub-row button {
  padding: 7px 11px;
  font-size: 12px;
}

/* 勾选行（挂载 ESP）：整行可点，勾选框和文字同一基线 */
.grub-check {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-dim);
  cursor: pointer;
}

.grub-check input[type="checkbox"] {
  width: 15px;
  height: 15px;
  margin: 0;
  padding: 0;
  flex: 0 0 auto;
  accent-color: var(--btn);
}

.grub-check:has(input:disabled) {
  cursor: not-allowed;
  opacity: 0.55;
}

/* 分区识别结果：FAT32 / fs-uuid / 挂载状态，一行小字 */
.grub-part {
  flex: 1 1 100%;
  color: var(--text-faint);
  font-size: 11px;
  line-height: 1.5;
  word-break: break-all;
}

.grub-part b {
  color: var(--ok);
  font-weight: 600;
}

.grub-part i {
  color: var(--warn);
  font-style: normal;
}

.grub-hint {
  margin: 0;
  color: var(--text-faint);
  font-size: 11px;
  line-height: 1.5;
}

/* 本区操作的回执一律走窗口底部那条独立提示条（见 .status-bar），
 * 这一区里只保留快照带来的「硬错误」提示。#grub-error 装的是快照里的错。 */
.grub-error {
  margin: 0;
  color: var(--warn);
  font-size: 11px;
  white-space: pre-wrap;
}

.grub-list {
  /* 原来直接是 grid；为了「选中项置顶固定」（问题1）改成 block 容器：
   * 置顶行是它的直接子块（CSS sticky 钉在滚动容器顶部，怎么滚都不动），
   * 真正的条目网格挪进 .grub-list-body —— grid 项的 sticky 只能在自己的
   * 格子里挪，出不了那一行，所以置顶行不能参与 grid 流。 */
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--input-bg);
}

/* GRUB 空状态：还没识别出任何 GRUB 信息（没填 fs-uuid、也没识别过目录）
 * 时替代一片空白的引导卡。虚线框 = 「这里是空的、等你来填」。 */
.grub-empty-state {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
  margin-bottom: 10px;
  padding: 14px;
  border: 1px dashed var(--border-strong);
  border-radius: 8px;
}

.grub-empty-state strong {
  font-size: 13px;
}

.grub-empty-state small {
  color: var(--text-faint);
  line-height: 1.5;
}

/* display:flex 会压过 [hidden] 的 UA 样式，显式补一条 */
.grub-empty-state[hidden] {
  display: none;
}

.grub-list-body {
  display: grid;
  gap: 4px;
}

/* 选中的启动项置顶固定（问题1）：钉在最近滚动容器顶部（①② 是 .grub-list
 * 自己，③④ 是右栏 .pane-body），其余行从它底下滚过。实底背景（--ok-bg）
 * 保证透不出底下的行。 */
.grub-entry.is-pinned {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--ok-bg);
}

/* 滚动条归属跟展示模式走：
 * ①② 列表限高 + 自己滚（HEAD 行为，配置在下面继续滚主流程）；
 * ③④ 右栏 .pane-body 是滚动容器，列表放开高度跟着它滚 —— 两栏滚动条独立。 */
body[data-grub-mode="inline"] .grub-list,
body[data-grub-mode="wide"] .grub-list {
  max-height: 320px;
  overflow-y: auto;
}

/* 模式 ② 加宽：窗口 932、单栏，树铺成两列（原来那条按视口判的 @media
 * 在四模式下行不通 —— ③④ 视口同样 ≥700 但右栏只有一半宽，所以改按模式判） */
body[data-grub-mode="wide"] .grub-list-body {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 4px 16px;
}

body[data-grub-mode="wide"] .grub-list {
  max-height: calc(100vh - 380px);
}

.grub-empty {
  padding: 6px 4px;
  color: var(--text-faint);
  font-size: 11px;
}

/* 每一行本身就是「点一下就切过去」的按钮：左名称、右标签 */
.grub-entry {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 9px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  text-align: left;
}

/*
 * 树形层级：顶层条目（组标题，如「Arch Linux 的高级选项」）和子条目要一眼分得开。
 * 顶层用主题色 + 加粗 + 左侧色条，子条目缩进并挂一条竖直的引导线 ——
 * grub.cfg 里动辄几十项、还夹着 submenu，光靠文字前缀根本看不出谁属于谁。
 */
.grub-entry.is-group {
  margin-top: 4px;
  border-left: 3px solid var(--btn);
  background: var(--input-bg);
}

.grub-entry.is-group .grub-entry-name {
  color: var(--link);
  font-weight: 700;
}

/*
 * 组头是 disabled 的按钮（它没有 selector，点不了）。通用的 button:disabled 会给它
 * 套 0.55 的透明度，看起来像「不可用」——但它是分层标题，不该发灰。
 */
.grub-entry.is-group:disabled {
  opacity: 1;
  cursor: default;
}

/* 子条目：缩进一格 + 一条竖直引导线（用 inset box-shadow 画，不吃布局宽度） */
.grub-entry.is-child {
  padding-left: 26px;
  box-shadow: inset 12px 0 0 -11px var(--border-strong);
}

.grub-entry.is-child .grub-entry-name {
  color: var(--text-dim);
}

/* 更深一层（submenu 套 submenu）再缩进一格 */
.grub-entry.depth-2 {
  padding-left: 42px;
}

.grub-entry.depth-2,
.grub-entry.depth-3,
.grub-entry.depth-4 {
  box-shadow: inset 28px 0 0 -27px var(--border-strong);
}

/* 顶层组里的「组名」本身也能点（切过去 = 用它的默认项），把它写得像标题一点 */
.grub-entry.is-group:hover:not(:disabled) {
  border-color: var(--border-strong);
}

.grub-entry.active {
  border-color: var(--ok-border);
  background: var(--ok-bg);
}

.grub-entry-name {
  overflow: hidden;
  font-size: 12px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.grub-entry.active .grub-entry-name {
  color: var(--ok);
  font-weight: 600;
}

.grub-entry-tag {
  color: var(--text-fainter);
  font-size: 10px;
  white-space: nowrap;
}

/* 组内的子条目数量小标（顶层组行右侧） */
.grub-entry-count {
  color: var(--text-fainter);
  font-size: 10px;
  white-space: nowrap;
}

/* ---------- 安装 / 卸载的执行过程 ---------- */
.grub-log {
  display: grid;
  gap: 6px;
}

.grub-log summary {
  color: var(--text-dim);
  font-size: 12px;
  cursor: pointer;
}

.grub-log.ok summary {
  color: var(--ok);
}

.grub-log.fail summary {
  color: var(--warn);
}

.grub-log pre {
  max-height: 220px;
  margin: 0;
  padding: 9px 11px;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--input-bg);
  color: var(--text-dim);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-all;
}

.grub-script {
  display: grid;
  gap: 8px;
}

.grub-script summary {
  color: var(--text-dim);
  font-size: 12px;
  cursor: pointer;
}

.grub-script small {
  color: var(--text-faint);
  font-size: 11px;
  line-height: 1.5;
}

.grub-script code {
  padding: 0 3px;
  border-radius: 4px;
  background: var(--input-bg);
  font-size: 11px;
}

.grub-script textarea {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  background: var(--input-bg);
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  line-height: 1.5;
  resize: vertical;
}

button {
  border: 0;
  border-radius: 7px;
  padding: 9px 13px;
  background: var(--btn);
  color: var(--btn-text);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
}

button:hover:not(:disabled) {
  background: var(--btn-hover);
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

button.secondary {
  background: var(--btn-secondary);
  color: var(--btn-secondary-text);
}

button.secondary:hover:not(:disabled) {
  background: var(--btn-secondary-hover);
}

/*
 * 卡片化的分区换成了带底色的卡片：以前 .directory / .settings 只有一条上边框，
 * 里面的小字直接落在**半透明的窗体背景**上 —— 不透明度一调低，字就压在桌面上，看不清。
 * 现在正文全部落在不透明的 --panel 上，窗体再怎么透也不影响读字。
 */
.directory {
  display: grid;
  gap: 8px;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}

label {
  font-size: 13px;
  color: var(--text-dim);
}

input {
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  padding: 10px 12px;
  background: var(--input-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}

/* 电源操作现在摆在系统列表**上方**：右对齐贴着列表右边，不再需要上边距 */
.system-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}

.settings {
  display: grid;
  gap: 12px;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}

.settings h2 {
  margin: 0;
  font-size: 15px;
}

.setting {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* token 那一行内容多：标题在上、两排控件在下的竖排 */
.setting-block {
  display: grid;
  /* 隐式列不会自己撑满：不写死 minmax(0, 1fr)，整行会缩到 max-content（实测
     771 的容器里只占 293），两排控件就被挤成「复制」掉行、「应用」掉行。 */
  grid-template-columns: minmax(0, 1fr);
  justify-items: stretch;
  gap: 8px;
}

label.setting {
  cursor: pointer;
}

.setting-text {
  display: grid;
  gap: 2px;
}

.setting-text strong {
  color: var(--text);
  font-size: 14px;
}

.setting-text small {
  color: var(--text-faint);
  font-size: 12px;
}

.setting input[type="checkbox"] {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  padding: 0;
  accent-color: var(--btn);
}

select {
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  padding: 8px 10px;
  background: var(--input-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
}

.setting :disabled {
  opacity: 0.55;
}

.setting-state {
  color: var(--text-fainter);
}

.link {
  justify-self: start;
  padding: 0;
  border: none;
  background: none;
  color: var(--link);
  font-size: 12px;
  text-decoration: underline;
}

.link:hover:not(:disabled) {
  background: none;
  color: var(--link-hover);
}

.hidden {
  /* !important：.pending / .port-occupant 也是 display:grid（同优先级），
     它们写在后面就会把这条覆盖掉，导致该藏的卡片一直显示 */
  display: none !important;
}

.remote-inline {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 6px;
}

.port-occupant {
  display: grid;
  gap: 6px;
  padding: 10px 12px;
  border: 1px solid var(--port-border);
  border-radius: 10px;
  background: var(--port-bg);
}

.port-occupant strong {
  font-size: 13px;
  color: var(--port-strong);
}

.port-occupant small {
  color: var(--port-text);
  font-size: 12px;
}

.port-occupant .actions {
  display: flex;
  gap: 8px;
}

button.danger {
  background: var(--danger);
  color: var(--danger-text);
}

button.danger:hover:not(:disabled) {
  filter: brightness(1.12);
}

.remote-inline input[type="text"] {
  width: 116px;
  padding: 7px 9px;
}

.remote-inline input[type="checkbox"] {
  width: 17px;
  height: 17px;
  padding: 0;
  accent-color: var(--btn);
}

.remote-inline input[type="range"] {
  width: 140px;
  padding: 0;
  border: 0;
  background: none;
  accent-color: var(--btn);
}

/* 锁定窗口大小后滑条停用：压暗 + 禁用光标，一眼能看出「现在改不了」
 * （浏览器默认的 disabled 外观在深色主题下不够明显，这里显式给） */
.remote-inline input[type="range"]:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* 调色板入口框：整个框就渲染成选中的颜色（WebView2 默认外观带灰边和
   内边距，看起来像没生效；这里把原生 swatch 铺满整个框） */
.remote-inline input[type="color"] {
  width: 46px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: none;
  cursor: pointer;
}

.remote-inline input[type="color"]::-webkit-color-swatch-wrapper {
  padding: 2px;
}

.remote-inline input[type="color"]::-webkit-color-swatch {
  border: none;
  border-radius: 4px;
}

#http-bind {
  width: 116px;
}

#sound-file-path {
  width: 200px;
}

/* token 自定义输入框宽一点，免得看不到自己填了什么 */
#token-input {
  width: 220px;
  font-family: ui-monospace, "Cascadia Mono", "JetBrains Mono", monospace;
}

.pending {
  display: grid;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid var(--pending-border);
  border-radius: 10px;
  background: var(--pending-bg);
}

.pending strong {
  font-size: 14px;
}

.pending small {
  color: var(--pending-text);
  font-size: 12px;
}

.remote-hint,
.remote-log {
  display: grid;
  gap: 6px;
}

/*
 * 「远程怎么调」的示例框和「最近请求」的列表框是上下挨着的两块，
 * 之前一个是描边卡片、一个是裸列表，看着像来自两个软件 —— 现在统一质感：
 * 同样的描边、圆角、底色、字色、字号。只有内边距不同（列表要给序号留位置）。
 */
.remote-hint pre,
#http-requests {
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--input-bg);
  color: var(--text-dim);
  font-size: 11px;
}

.remote-hint pre {
  padding: 10px 12px;
  white-space: pre-wrap;
  word-break: break-all;
}

#http-requests {
  max-height: 150px;
  /* 左边多留一点给序号 */
  padding: 10px 12px 10px 26px;
  overflow-y: auto;
}

#http-requests li {
  margin-bottom: 4px;
}

#http-requests:empty::after {
  content: "暂无远程请求";
  color: var(--text-fainter);
  font-size: 11px;
}
/* ============ 贴边隐藏的小白条 ============
   主窗口贴边隐藏后，窗口缩成一条固定细缝，白条 #dock-strip 是**独立渲染**
   的白色条，长度/厚度/不透明度全由前端 CSS 变量控制，与窗口几何无关。
   贴边态下窗口本身（html/body 背景 + main 内容）要**全部隐藏**，只留白条
   —— 否则用户看到的就是「窗口缩成的一条」而不是独立白条。 */
/* 贴边态：html/body 背景透明、内容隐藏，只露白条 */
html {
  background: transparent;
}
body.dock-top,
body.dock-bottom,
body.dock-left,
body.dock-right {
  background: transparent;
}
/* 贴边态把整个双栏 + 底栏都藏掉（左栏是 main、右栏是 .pane-grub，
 * 底栏是 .status-bar —— 三个都要，漏一个就会在 40px 的缝里露出残影） */
body.dock-top main,
body.dock-bottom main,
body.dock-left main,
body.dock-right main,
body.dock-top .app-columns,
body.dock-bottom .app-columns,
body.dock-left .app-columns,
body.dock-right .app-columns,
body.dock-top .status-bar,
body.dock-bottom .status-bar,
body.dock-left .status-bar,
body.dock-right .status-bar {
  display: none;
}
/* 停靠缝的「零竞态」兜底：后端 dock 是 hide→缩几何→show，show 出来那一帧
 * viewport 已经只有缝那么厚（40px 逻辑像素量级），而「docked=true」广播还在
 * IPC 路上、body.dock-* 类没加上 —— 之前那一帧显示的是主界面在缝里的切片，
 * 透明窗口后就是肉眼可见的「主界面残留一角再消失」。正常窗口最小 360x560，
 * 不可能窄/矮到 100px 以下；viewport 一旦小于这个值必然是停靠缝，按 viewport
 * 尺寸直接藏内容 —— 同步于渲染，不依赖事件时序。 */
@media (max-height: 100px), (max-width: 100px) {
  html,
  body {
    background: transparent;
  }
  main,
  .app-columns,
  .status-bar {
    display: none;
  }
}
/* dock 前的「清屏」帧：停靠流程是 hide→缩几何→show，但 macOS 窗口服务器
 * 在窗口重新出现的第一帧会先摆出**隐藏前缓存的最后一帧**——上面那个媒体
 * 查询只管新绘的帧，管不住这帧旧缓冲（主界面切片直接浮在桌面上，就是
 * 「残影子」）。修法：后端 dock 在窗口**还可见**时先给 html 加 dock-pending，
 * 让 WebView 把最后一帧刷成全透明，再 hide。缓存帧 = 全透明 → show 无旧内容。
 * 类由后端 eval 添加/移除，undock 时移除。 */
html.dock-pending,
html.dock-pending body {
  background: transparent;
}
html.dock-pending main,
html.dock-pending .app-columns,
html.dock-pending .status-bar {
  display: none;
}
#dock-strip {
  position: fixed;
  display: none;
  /* 颜色走调色板设置（--dock-strip-color，app.js 从快照写入），默认白 */
  background: var(--dock-strip-color, #ffffff);
  opacity: var(--dock-strip-opacity, 1);
  z-index: 9999;
  pointer-events: none;
}
/* 贴底边：白条贴窗口底（屏幕底），水平居中，长=窗口宽百分比，高=厚度 */
body.dock-bottom #dock-strip {
  display: block;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: var(--dock-strip-length, 100%);
  height: var(--dock-strip-thickness, 6px);
}
/* 贴顶边：白条贴窗口顶（屏幕顶） */
body.dock-top #dock-strip {
  display: block;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: var(--dock-strip-length, 100%);
  height: var(--dock-strip-thickness, 6px);
}
/* 贴左缘：白条贴窗口左（屏幕左），垂直居中，长=窗口高百分比，宽=厚度 */
body.dock-left #dock-strip {
  display: block;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  height: var(--dock-strip-length, 100%);
  width: var(--dock-strip-thickness, 6px);
}
/* 贴右缘：白条贴窗口右（屏幕右） */
body.dock-right #dock-strip {
  display: block;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: var(--dock-strip-length, 100%);
  width: var(--dock-strip-thickness, 6px);
}

/* ============ 热区可视化层 ============
   贴边态下把「鼠标移入即呼出」的触发带画出来，调试用：默认全透明 + 无边框
   （--hotzone-opacity 默认 0、--hotzone-border-width 默认 0），用户从设置里
   打开底色或边框才可见。厚度是「热区设置厚度 − 贴边缝隙」（app.js 算好写入，
   已 clamp 到窗口槽位 40px 内），贴屏幕边一侧铺开 —— 与后端热区判定矩形的
   可见部分严格对齐。 */
#hotzone {
  position: fixed;
  display: none;
  background: var(--hotzone-color, #ff0000);
  opacity: var(--hotzone-opacity, 0);
  border: var(--hotzone-border-width, 0px) solid var(--hotzone-border-color, #ff0000);
  box-sizing: border-box;
  /* 白条画在上面（9999），热区是背景参考层 */
  z-index: 9998;
  pointer-events: none;
}
body.dock-bottom #hotzone {
  display: block;
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--hotzone-thickness, 40px);
}
body.dock-top #hotzone {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  height: var(--hotzone-thickness, 40px);
}
body.dock-left #hotzone {
  display: block;
  left: 0;
  top: 0;
  bottom: 0;
  width: var(--hotzone-thickness, 40px);
}
body.dock-right #hotzone {
  display: block;
  right: 0;
  top: 0;
  bottom: 0;
  width: var(--hotzone-thickness, 40px);
}

/* ---------- 移动端适配（网页控制台在手机浏览器上使用） ----------
 * 断点 720px：③④ 的左右分栏改为上下堆叠（滚动权交给 .app-columns）；
 * 设置行改「文字 | 控件」两列网格防长文案挤压；输入控件 16px 防 iOS
 * 聚焦自动放大页面；100dvh 修正移动端地址栏收展导致的视口高度漂移。 */
@media (max-width: 720px) {
  html {
    -webkit-text-size-adjust: 100%;
  }

  body {
    height: 100vh;
    height: 100dvh;
  }

  /* 分栏模式（③④）改上下堆叠：左栏（main）在上、GRUB 右栏在下、
   * 中缝隐藏；整列自己滚（main / .pane-body 的 overflow 放开，高度自然） */
  .app-columns {
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
  }
  .pane-gap {
    display: none !important;
  }
  .app-columns > .pane {
    flex: 0 0 auto;
  }
  main {
    overflow-y: visible;
    padding: 14px;
    gap: 14px;
  }
  .grub-pane-head {
    padding-top: 14px;
  }
  .pane-body {
    overflow-y: visible;
    padding-bottom: 14px;
  }
  .tab-panel {
    gap: 14px;
  }
  body[data-grub-mode="list"],
  body[data-grub-mode="pane"] {
    min-width: 0;
  }

  /* 模式②的树在手机上铺回一列；①②列表限高改视口比例（原式按窗口算） */
  body[data-grub-mode="wide"] .grub-list-body {
    grid-template-columns: 1fr;
  }
  body[data-grub-mode="inline"] .grub-list,
  body[data-grub-mode="wide"] .grub-list {
    max-height: 45vh;
  }

  /* 设置行：文字一列 + 控件一列。flex 的 space-between 在窄屏会把
   * 长小字压成一坨，grid 两列让标题/说明完整换行、控件靠右不被挤 */
  .setting {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
  }

  /* 例外：token 行（setting-block）本来就是竖排结构，保持整行一列 */
  .setting.setting-block {
    grid-template-columns: minmax(0, 1fr);
  }

  /* iOS：聚焦 <16px 的输入框会自动放大页面，挪到手机上统一 16px */
  input,
  select {
    font-size: 16px;
  }

  /* ---------- SOL 远程唤醒页（#/sol）手机适配 ----------
   * SOL 的行大多是「标题在左 + 输入框/按钮组在右」，右列在窄屏里太宽：
   * 监听地址输入 200px、安装目录输入 280px（都是内联定宽）+ 两三个按钮
   * 挤在 auto 列 → 溢出/挤压歪七扭八。统一改上下堆叠：标题/状态一行，
   * 控件组自己成一行、可换行铺开。 */
  #sol-section .setting {
    grid-template-columns: minmax(0, 1fr);
    justify-items: stretch;
    gap: 8px;
  }

  /* 例外：单个勾选框的行（HTTP 服务监听）保持「文字 | 勾选框」两列 */
  #sol-section #sol-http-enabled-row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  #sol-section .remote-inline {
    flex: 1 1 auto;
    flex-wrap: wrap;
  }

  /* 行内输入框不再定宽：内联的 width / flex 用 !important 顶掉，
   * 弹性占满剩余宽度、按钮贴着排 */
  #sol-section .remote-inline input[type="text"],
  #sol-section .sol-token-line input[type="text"] {
    width: auto !important;
    flex: 1 1 140px;
    min-width: 0;
  }

  /* token 展示牌也放开最小宽度，长 token 正常断行 */
  #sol-section .sol-token-value {
    flex: 1 1 160px;
  }

  /* 两个安装按钮平铺整行（右对齐 flex-end 在窄屏里就是歪的） */
  #sol-section .actions {
    justify-content: stretch;
  }
  #sol-section .actions button {
    flex: 1 1 46%;
  }

  /* 配置编辑器同样防 iOS 聚焦缩放（textarea 不在上面的 input 规则里） */
  #sol-section textarea {
    font-size: 16px;
  }
}

/* ---------- SOL 远程唤醒标签页 ----------
 * 第三个栏目（#/sol）：sol 接收器的状态 / 监听端口 / 安装卸载 / 配置编辑。
 * 全部沿用面板既有 token（--panel / --border / --ok / --warn），不引入新色板。 */
.sol-desc {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-dim, #94a3b8);
}

/* 端口小牌：绿 = 该端口有规则且在监听；黄 = 疑点；灰 = 未监听 */
.sol-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.sol-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 12px;
  color: var(--text-dim, #94a3b8);
  background: var(--input-bg, #141a2e);
}
.sol-badge .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-faint, #64748b);
  flex: none;
}
.sol-badge.ok {
  border-color: var(--ok-border, #16a34a);
  color: var(--ok, #22c55e);
}
.sol-badge.ok .dot {
  background: var(--ok, #22c55e);
}
.sol-badge.warn {
  border-color: var(--warn, #f59e0b);
  color: var(--warn, #f59e0b);
}
.sol-badge.warn .dot {
  background: var(--warn, #f59e0b);
}

.sol-hint {
  font-size: 12px;
  color: var(--text-faint, #64748b);
  line-height: 1.5;
}

/* Token 行：上排＝当前 token（纯文字 + 复制），下排＝新 token 输入 + 生成 / 应用。
 * 不用输入框展示 token：只读文本框会有"能改"的暗示，长 token 也会被截断；
 * 纯文字 + 虚线框，点一下整段选中（user-select: all）方便手动 Ctrl+C。 */
.sol-token-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.sol-token-value {
  flex: 1 1 240px;
  min-width: 0;
  padding: 7px 10px;
  border: 1px dashed var(--border);
  border-radius: 8px;
  background: var(--input-bg, #141a2e);
  color: var(--text, #f8fafc);
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  word-break: break-all;
  user-select: all;
}
.sol-token-value.is-empty {
  color: var(--text-faint, #64748b);
}

/* 配置编辑器：等宽、暗底、与 textarea/输入框同款边框 */
.sol-config-editor {
  width: 100%;
  box-sizing: border-box;
  resize: vertical;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--input-bg, #141a2e);
  color: var(--text, #f8fafc);
  font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  tab-size: 2;
}
.sol-config-editor:focus {
  outline: none;
  border-color: var(--btn, var(--accent, #6366f1));
}
.sol-config-editor:disabled {
  opacity: 0.55;
}

/* 主色小按钮（保存配置）——面板内 secondary 为主、这一颗是唯一强调 */
button.primary-sol {
  background: var(--btn);
  color: var(--btn-text);
}
button.primary-sol:hover:not(:disabled) {
  background: var(--btn-hover);
}

/* 设置行状态文字的「出错」态：黄色提醒（沿用 --warn，与横幅一致） */
.setting-state-error {
  color: var(--warn, #f59e0b) !important;
}

/* 安装包块：与配置编辑器同款的竖排块，块之间留一拍呼吸 */
.sol-pkg-block {
  padding-top: 12px;
  border-top: 1px dashed var(--border);
}

/* 下载进度条：细轨 + 强调色填充，下载完整条隐藏 */
.sol-progress {
  display: flex;
  align-items: center;
  gap: 10px;
}
.sol-progress-track {
  flex: 1;
  height: 6px;
  border-radius: 999px;
  background: var(--input-bg, #141a2e);
  border: 1px solid var(--border);
  overflow: hidden;
}
.sol-progress-fill {
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background: var(--btn, var(--accent, #6366f1));
  transition: width 0.3s ease;
}
.sol-progress-text {
  font-size: 12px;
  color: var(--text-dim, #94a3b8);
  min-width: 64px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* 日志区：等宽、暗底、限高滚动，最新一行自动可见 */
.sol-log-block {
  padding-top: 12px;
  border-top: 1px dashed var(--border);
}
.sol-log {
  margin: 0;
  max-height: 180px;
  overflow-y: auto;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--input-bg, #141a2e);
  color: var(--text-dim, #94a3b8);
  font: 11px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: pre-wrap;
  word-break: break-all;
}
.sol-log .sol-log-line {
  display: block;
}
.sol-log .sol-log-time {
  color: var(--text-fainter, #475569);
  margin-right: 6px;
}

/* 安装目录行的小状态字（~ 展开后的真实路径提示） */
#sol-dir-state {
  font-size: 11px;
}
