/* ============================================================
   ui-v2.css — 结构层
   只负责「什么在哪、多大、怎么排」，一个具体颜色都不写死：
   每一处颜色都是 var(--token)，由 theme.css 按主题填值。

   ── 这一版删掉了什么，为什么 ──────────────────────────────
   上一版这个文件里有将近一半是【连接页】的旧实现：.boot-sequence（开机字样）、
   .gate-cable / .hero-cable / .cable-plug（SVG 数据线）、.gate-orbit、
   .gate-scan、.cinematic-device、.cable-stage……这些是三代连接页的遗骸，
   没有任何一个还挂在 DOM 上，但它们的规则还在生效，而且【优先级还比新写的
   连接页高】（.gate-device img 这种 0,1,1 的复合选择器打赢 .hero-mouse
   这种 0,1,0 的）。上一版为此付出的代价是：图片被 object-fit:contain 悄悄
   缩到实际尺寸的 68%，肉眼几乎看不出来，是靠量 getBoundingClientRect 才发现的。

   结构层不再拥有连接页。连接页的全部样式归 connect.css，选择器前缀统一
   用 .gate- / .connect-，不可能再和结构层撞车。

   ── 主题切换的落点 ────────────────────────────────────────
   两套主题共用一个变量名表（--bg / --surface / --line / --well / --glass…），
   值在 theme.css 里按 [data-theme] 各填一份。所以这个文件不需要知道
   现在是深色还是浅色 —— 也不允许知道。
   ============================================================ */

:root{
  --bg:#08090c;
  --surface:rgba(255,255,255,.035);
  --surface-2:rgba(255,255,255,.062);
  --line:rgba(255,255,255,.078);
  --line-hi:rgba(255,255,255,.155);
  --text:#f4f5f7;
  --muted:#898f9d;
  --dim:#5b606b;
  --lime:#c9ff3d;
  --lime-ink:#121806;
  --orange:#ff6a3d;
  --well:rgba(0,0,0,.28);
  --well-2:rgba(0,0,0,.4);
  --glass:rgba(8,9,12,.88);
  --glass-2:rgba(8,9,12,.92);
  --panel:#111318;
  --grid-line:rgba(255,255,255,.018);
  --stage:#0a0b11;
  --stage-2:#131620;
  --on-stage:#f4f5f7;
  --on-stage-dim:#898f9d;
  --card-grad:linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,.012) 42%,rgba(255,255,255,.02));
  --card-base:rgba(16,18,23,.94);
  --hi:rgba(255,255,255,.07);
  --hi-edge:rgba(255,255,255,.02);
  --sh-card:0 1px 2px rgba(0,0,0,.55),0 18px 40px -22px rgba(0,0,0,.9);
  --sh-raise:0 2px 4px rgba(0,0,0,.6),0 28px 60px -24px rgba(0,0,0,.95);
  --font:"Bahnschrift","Segoe UI","Microsoft YaHei",sans-serif;
  --mono:"Cascadia Code",Consolas,monospace;
  --r-ctl:8px;
  --r-card:13px;
  --r-pill:999px;
  --ease-out:cubic-bezier(.22,.68,0,1);
  --ease-io:cubic-bezier(.32,.72,0,1);
}

*{box-sizing:border-box}
html{color-scheme:dark}
body{
  margin:0;min-height:100vh;
  background:var(--bg);color:var(--text);
  font:14px/1.5 var(--font);
}
button,select,input{font:inherit}
button{border:0;cursor:pointer;color:inherit}
h1,h2,p{margin:0}

/* ---------- 顶栏 ---------- */
/* 没有 border-bottom。原来那条 1px 的 --line 会在页面顶端横着割一道，
   连接页上尤其明显 —— 那里整片都是空的，唯一的一条水平线就成了视线焦点。
   顶栏和下面的场要连成一片，分割交给背景色的差别去做，不交给线。
   （连接页那层连背景色都不留，见 connect.css 末尾的 .topbar 覆盖。） */
.topbar{
  height:72px;padding:0 36px;
  display:flex;align-items:center;justify-content:space-between;
  background:var(--glass);
  backdrop-filter:blur(18px);
  position:sticky;top:0;z-index:20;
}
.brand{display:flex;align-items:center;gap:10px;color:var(--text);text-decoration:none;font-weight:700;font-size:17px;letter-spacing:.03em}
.brandmark{width:30px;height:30px;display:grid;place-items:center;background:var(--lime);color:var(--lime-ink);font:700 15px var(--mono)}
.brand small{font:500 10px var(--mono);letter-spacing:.17em;color:var(--muted);margin-left:7px}
/* 顶栏三个孩子（品牌 / 连接状态 / 主题切换）走 space-between，
   但主题切换必须贴在【最右】。给 .connection 一个 margin-left:auto
   把这一组整体推到右边，主题切换跟在它后面自然就是最后一个。 */
.connection{display:flex;align-items:center;gap:11px;color:var(--muted);font-size:12px;margin-left:auto}
.connection + .theme-switch{margin-left:16px}
.connection #statusDot{width:7px;height:7px;border-radius:50%;background:var(--dim);transition:background .3s var(--ease-out),box-shadow .3s var(--ease-out)}
.connection.connected #statusDot{background:var(--lime)}
.battery{display:flex;align-items:center;gap:6px;padding:4px 0 4px 10px;border:0;border-left:1px solid var(--line);background:transparent;color:inherit;cursor:pointer}
.battery[hidden]{display:none}
.battery:disabled{cursor:wait;opacity:.72}
.battery>i{position:relative;width:22px;height:10px;padding:1px;border:1px solid var(--line-hi);border-radius:3px}
.battery>i::after{content:"";position:absolute;right:-3px;top:3px;width:2px;height:4px;border-radius:0 2px 2px 0;background:var(--line-hi)}
.battery>i b{display:block;width:var(--battery,0%);height:100%;border-radius:1px;background:var(--accent);transition:width .35s var(--ease-out)}
.battery output{min-width:31px;font:600 11px var(--mono);color:var(--muted)}
.outline-button{padding:9px 15px;background:transparent;border:1px solid var(--line-hi);margin-left:6px;transition:border-color .26s var(--ease-out),color .26s var(--ease-out),background .26s var(--ease-out)}

/* ---------- 连接页占位 ----------
   .device-gate 是连接页的固定画布，尺寸和层高归结构层；
   它内部长什么样全部归 connect.css。
   注意：这里不能给 display —— #deviceGate[hidden] 要靠 UA 的
   [hidden]{display:none} 生效，一旦写了 display:grid 就再也藏不掉了。 */
.device-gate{position:fixed;z-index:18;inset:72px 0 0;overflow:hidden}

/* ---------- 配置页外壳 ---------- */
.app-shell{max-width:1580px;margin:auto;padding:38px 36px 122px}
.app-shell,body:not(.device-connected) footer{
  visibility:hidden;opacity:0;
  transition:opacity .5s var(--ease-out),visibility 0s linear .5s;
}
.device-connected .app-shell,.device-connected footer{
  visibility:visible;opacity:1;
  transition:opacity .6s var(--ease-out) .1s,visibility 0s;
}
/* 连接页出现前立刻摘掉配置页。这里不能保留 .5s 淡出：gate 会先显示，
   淡出期间两页就会短暂叠在一起。 */
.device-disconnected .app-shell,.device-disconnected footer{visibility:hidden;opacity:0;transition:none}

.command-bar{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:24px}
.eyebrow{font:600 11px var(--mono);letter-spacing:.16em;color:var(--lime)}
.command-bar h1{font-size:30px;letter-spacing:-.04em;margin-top:5px}
.profiles{display:flex;align-items:center;gap:14px}
.profiles>span{font:500 12px var(--font);color:var(--muted);letter-spacing:.02em}
.profiles>div{display:flex;background:var(--well);border:1px solid var(--line);padding:3px}
.profiles button{min-width:80px;padding:9px 13px;background:transparent;color:var(--muted);font-size:13px;transition:background .26s var(--ease-out),color .26s var(--ease-out)}
.profiles button.active{background:var(--text);color:var(--bg)}

/* 控制台 = 左导航 + 舞台。
   改版前是「三行卡片」：产品图 | 按键面板 → 性能 | 灯效 → 固件，四块内容
   一次性全铺在首屏上，一眼看过去全是控件、没有主次。现在收成一列导航，
   一次只放一块面板出来。

   导航列宽写死 236px 而不是用 fr：它是导航不是内容，宽度跟着视口涨会让
   1fr 的舞台在宽屏上白白少一截。sticky 是为了滚到底部时它还在 —— 面板比
   导航高得多，不钉住的话切功能要先滚回顶部。 */
.console{display:grid;grid-template-columns:236px minmax(0,1fr);gap:20px;align-items:start}
.rail{display:flex;flex-direction:column;gap:3px;position:sticky;top:86px;border-right:1px solid var(--line);padding-right:16px}
.rail-item{
  position:relative;display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;align-items:center;
  padding:12px 13px;background:transparent;border:0;border-radius:var(--r-ctl);
  color:var(--muted);text-align:left;
  transition:background .24s var(--ease-out),color .24s var(--ease-out);
}
.rail-item i{display:flex;flex:none;opacity:.55;transition:opacity .24s var(--ease-out),color .24s var(--ease-out)}
.rail-item svg{width:18px;height:18px}
.rail-item b{display:block;font-size:14px;font-weight:600;letter-spacing:-.01em}
/* --dim 是「仅装饰，禁止正文」的那一级（深 3.3:1 / 浅 2.8:1，都不到 AA 的
   4.5:1）。这一行是 9px 的字，属于正文，用 --muted。 */
.rail-item small{display:block;font:500 12px var(--font);letter-spacing:0;color:var(--muted);margin-top:4px}
.rail-item:hover{background:var(--well);color:var(--text)}
.rail-item:hover i{opacity:1}
.rail-item[aria-selected=true]{background:var(--well);color:var(--text)}
.rail-item[aria-selected=true] i{opacity:1;color:var(--accent)}
.rail-item[aria-selected=true]::before{
  content:"";position:absolute;left:-17px;top:50%;transform:translateY(-50%);
  width:2px;height:20px;background:var(--accent);border-radius:2px;
}

.stage{min-width:0}
/* 一次只显示一块。hidden 必须显式压掉 display —— .panel-split 是 grid，
   自带的 display 会盖过 hidden 的 display:none。
   焦点圈不用在这里写：theme.css 有一条 :where(button,…,[tabindex]):focus-visible
   的全局规则，面板带 tabindex 已经被它盖住。 */
.panel[hidden]{display:none}
/* 按键面板是唯一的两栏：左边产品图（决定整块高度），右边按键配置跟着等高。
   其余三块单栏铺满舞台。 */
.panel-split{display:grid;grid-template-columns:minmax(340px,.76fr) minmax(0,1.24fr);gap:18px}
.surface,.device-view{border:1px solid var(--line)}

/* ---------- 设备舞台 ---------- */
/* 高度不再写死：行高由图片（下面那条 clamp）决定，栅格默认 stretch 会把右边的
   按键面板拉到同高。以前那两个同心圆和十字轴线删掉了 —— 鼠标放大之后它们
   大半压在机身底下，剩下的圆弧又和热点环抢视线。 */
.device-view{padding:24px;position:relative;display:grid;grid-template-rows:auto 1fr auto;overflow:hidden}
.device-heading,.section-title{display:flex;align-items:flex-start;justify-content:space-between}
.device-heading h2{font-size:35px;letter-spacing:-.045em;margin-top:2px}
.ready-state{font:600 11px var(--mono);letter-spacing:.12em;color:var(--muted);display:flex;align-items:center;gap:8px}
.ready-state i{width:6px;height:6px;background:var(--lime)}
.mouse-canvas{min-height:0;position:relative;display:grid;place-items:center;margin:14px 0}
/* 盒子由图片撑出来，长宽比读 --ar（key-panel.js 在 load 之后按 naturalWidth/Height
   写进去；CSS 里的 .57 只是首帧兜底）。热点层的百分比坐标因此严格对应图片本身，
   而不是对应带留白的画布 —— 这是对齐的全部前提。 */
.mouse-frame{position:relative;height:clamp(288px,44vh,418px);aspect-ratio:var(--ar,.57)}
.mouse-frame img{display:block;width:100%;height:100%;will-change:transform,opacity}
.hotspots{position:absolute;inset:0}
/* 空心环【套住】烤进画面的编号，不是盖住 —— 编号本身就是标签，环里必须是透的。
   读坐标的误差约 ±1% 图宽（≈3px），盖不严就会露出半个数字。
   静息态保持很淡：7 个环同时发亮会盖过产品图本身。 */
.hotspot,.hotspot-off{
  position:absolute;width:9.5%;aspect-ratio:1;
  transform:translate(-50%,-50%);border-radius:50%;
}
.hotspot{
  border:1px solid rgba(var(--accent-rgb),.30);
  background:transparent;padding:0;cursor:pointer;
  transition:border-color .24s var(--ease-out),background .24s var(--ease-out),box-shadow .24s var(--ease-out);
}
/* 图上画了编号、但板载没有对应槽位的那一颗：虚线空环，点不动（也故意不给
   .hotspot 类，免得被 selectKey 按下标算进去）。 */
.hotspot-off{border:1px dashed var(--line-hi);cursor:default}
.hotspot:hover{border-color:var(--accent);background:rgba(var(--accent-rgb),.12);box-shadow:0 0 0 4px rgba(var(--accent-rgb),.09)}
.hotspot.active{
  border-color:var(--accent);background:rgba(var(--accent-rgb),.16);
  box-shadow:0 0 0 3px rgba(var(--accent-rgb),.22),0 0 20px 3px rgba(var(--accent-rgb),.26);
}
.slot-strip{display:grid;grid-template-columns:repeat(7,1fr);gap:5px;padding:0 0 14px}
.slot-strip button{min-width:0;padding:7px 4px;border:1px solid var(--line);border-radius:7px;background:var(--well);color:var(--muted);text-align:center;transition:border-color .2s var(--ease-out),background .2s var(--ease-out),color .2s var(--ease-out)}
.slot-strip b{display:block;font:650 13px var(--mono);color:var(--text)}
.slot-strip span{display:block;margin-top:2px;font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.slot-strip button:hover{border-color:var(--line-hi);color:var(--text)}
.slot-strip button.active{border-color:var(--accent);background:rgba(var(--accent-rgb),.11);color:var(--accent-text)}
.slot-strip button.active b{color:var(--accent-text)}
.model-switch{display:grid;grid-template-columns:repeat(3,1fr);border-top:1px solid var(--line)}
.model-switch button{background:transparent;color:var(--muted);padding:14px 10px;border-right:1px solid var(--line);text-align:left;font-weight:600;transition:background .26s var(--ease-out),color .26s var(--ease-out)}
.model-switch button:last-child{border-right:0}
.model-switch button small{font:500 11px var(--mono);color:var(--muted);margin-right:9px}
.model-switch button.active{color:var(--text)}

/* ---------- 卡片 ---------- */
.performance-card,.key-panel,.lighting-card,.firmware-card{padding:22px}
.section-title{margin-bottom:17px}
.section-title h2,.firmware-copy h2{font-size:19px;letter-spacing:-.02em;margin-top:3px}
.section-title>span{font-size:12px;color:var(--muted)}

.dpi-list{display:grid;grid-template-columns:repeat(5,1fr);gap:7px}
.dpi-item{border:1px solid var(--line);background:var(--well);padding:11px;transition:border-color .26s var(--ease-out),background .26s var(--ease-out)}
.dpi-item header{display:flex;justify-content:space-between;font:500 11px var(--mono);color:var(--muted);margin-bottom:6px}
.dpi-item input[type=checkbox]{accent-color:var(--lime)}
.dpi-item input[type=number]{width:100%;background:none;border:0;outline:0;color:var(--text);font:600 17px var(--mono)}

.sensor-row{display:grid;grid-template-columns:1fr 1fr 1.35fr;gap:10px;margin-top:14px}
.sensor-row label>span{display:flex;justify-content:space-between;color:var(--muted);font-size:12px;margin-bottom:7px}
.sensor-row select,.field select{
  width:100%;height:38px;padding:0 32px 0 11px;
  color:var(--text);background:var(--well);border:1px solid var(--line);
  outline:none;appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--dim) 50%),linear-gradient(135deg,var(--dim) 50%,transparent 50%);
  background-position:calc(100% - 14px) 16px,calc(100% - 10px) 16px;
  background-size:4px 4px;background-repeat:no-repeat;
  transition:border-color .26s var(--ease-out);
}
.debounce-field input{width:100%;accent-color:var(--lime)}
#debounceOut{color:var(--text);font:500 12px var(--mono)}

/* ---------- 按键面板 ---------- */
/* 左列功能 + 右列参数。两列之间只留一条发丝线 —— 面板里再套一层卡片边框
   会和 .surface 的外框打架，变成「框里的框」。 */
.key-panel{display:grid;grid-template-rows:auto 1fr auto}
.key-panel-body{display:grid;grid-template-columns:200px minmax(0,1fr);gap:20px;min-height:0}
.fn-list{display:flex;flex-direction:column;gap:3px;border-right:1px solid var(--line);padding-right:18px}
.fn-item{
  position:relative;display:flex;align-items:center;gap:11px;
  padding:11px 12px;background:transparent;border:0;border-radius:var(--r-ctl);
  color:var(--muted);font-size:14px;text-align:left;
  transition:background .24s var(--ease-out),color .24s var(--ease-out);
}
.fn-item i{display:flex;flex:none;opacity:.62;transition:opacity .24s var(--ease-out),color .24s var(--ease-out)}
.fn-item svg{width:17px;height:17px}
.fn-item:hover{background:var(--well);color:var(--text)}
.fn-item:hover i{opacity:1}
.fn-item[aria-checked=true]{background:var(--well);color:var(--text)}
.fn-item[aria-checked=true] i{opacity:1;color:var(--accent)}
.fn-item[aria-checked=true]::before{
  content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);
  width:2px;height:17px;background:var(--accent);border-radius:2px;
}

.fn-detail{display:flex;flex-direction:column;min-width:0;padding-top:2px}
/* 两条 margin-top:auto 把「标题 + 参数」推到面板中部、把状态条钉在底部。
   面板的高度是被左边那张产品图撑出来的，参数少的功能（默认按键 / 停止宏）
   本来会全挤在顶部，下面空一大片。 */
.fn-main{margin-top:auto;min-width:0}
.fn-head h3{font-size:15px;letter-spacing:-.015em}
.fn-head p{font-size:13px;color:var(--muted);margin-top:6px;line-height:1.65;text-wrap:pretty;max-width:58ch}
.fn-body{margin-top:16px}
/* 下拉框和说明文字都限宽：参数列本身接近 900px，一个 900px 宽的下拉框
   读起来像坏掉了。多余的地方留给留白，不留给控件。 */
.field{max-width:420px}
.field>span{display:block;font:500 12px var(--font);letter-spacing:.02em;color:var(--muted);margin-bottom:7px}
/* 触发方式那两句话。放在下拉框下面，跟着选项换 —— 只写「左键触发」四个字，
   用户按下去没反应时没法自查。 */
.fn-tip{font-size:12px;color:var(--muted);line-height:1.65;margin-top:8px;text-wrap:pretty;max-width:46ch}
.fn-warning{margin:0 0 16px;padding:11px 13px;border:1px solid rgba(var(--accent-rgb),.28);border-radius:var(--r-ctl);background:rgba(var(--accent-rgb),.08);font-size:12px;line-height:1.6;color:var(--muted)}
.fn-warning strong{color:var(--text);font-weight:650}
.field+.field{margin-top:11px}
.fn-none{font-size:13px;color:var(--muted)}
.fn-note{font-size:13px;color:var(--muted);line-height:1.7;padding:13px;border:1px dashed var(--line-hi);border-radius:var(--r-ctl);max-width:42ch}
/* 「写入预览」：面板下方的等宽字节串。它和真正发出去的包共用一张 BYTES 表，
   所以这行既解释了「这个选项到底改变了什么」，也不会和实现对不上。 */
.fn-foot{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin-top:auto;padding-top:13px;border-top:1px solid var(--line);
  font-size:12px;color:var(--muted);
}
.fn-bytes{font-family:var(--mono);font-weight:600;color:var(--accent-text);letter-spacing:.14em}
.key-hint{font-size:12px;color:var(--muted);margin-top:12px}

/* 映射目标：6 选 1。用按钮而不是下拉 —— 只有 6 个选项，全摊开比点开一层再选快。 */
.target-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:7px}
.target{
  padding:11px 8px;border:1px solid var(--line);background:var(--well);
  border-radius:var(--r-ctl);color:var(--muted);font-size:13px;
  transition:border-color .24s var(--ease-out),background .24s var(--ease-out),color .24s var(--ease-out);
}
.target:hover{border-color:var(--line-hi);color:var(--text)}
.target.on{border-color:var(--accent);background:rgba(var(--accent-rgb),.11);color:var(--text)}

/* ---------- 灯效 ---------- */
.lighting-card{display:grid;grid-template-columns:minmax(300px,.92fr) minmax(320px,1.08fr);gap:34px;align-items:center}
.lighting-card .section-title{grid-column:1/-1;margin-bottom:0}
.light-preview{--light-color:#e8963a;position:relative;min-height:300px;display:grid;place-items:center;isolation:isolate;overflow:hidden;border-right:1px solid var(--line)}
.light-preview::before{content:"";position:absolute;width:220px;aspect-ratio:1;border-radius:50%;background:radial-gradient(circle,color-mix(in srgb,var(--light-color) 28%,transparent),transparent 68%);filter:blur(8px);opacity:1}
.light-preview i{position:absolute;width:164px;height:238px;border:1px solid color-mix(in srgb,var(--light-color) 36%,var(--line));border-radius:78px 78px 92px 92px;background:linear-gradient(160deg,var(--surface-2),var(--well));box-shadow:inset 0 1px 0 var(--hi),0 24px 50px -28px color-mix(in srgb,var(--light-color) 55%,transparent)}
.light-preview i::before{content:"";position:absolute;left:50%;top:24px;width:2px;height:62px;transform:translateX(-50%);background:var(--light-color);box-shadow:0 0 16px 3px var(--light-color)}
.light-preview span{position:absolute;width:118px;height:16px;border-radius:50%;bottom:36px;background:color-mix(in srgb,var(--light-color) 42%,transparent);filter:blur(9px)}
.light-preview b{position:relative;margin-top:116px;font:600 12px var(--mono);letter-spacing:.24em;color:var(--light-color)}
.light-controls{display:grid;gap:26px;max-width:480px;padding-right:22px}
.light-color{display:flex;align-items:center;justify-content:space-between;gap:24px;padding-bottom:22px;border-bottom:1px solid var(--line)}
.light-color span{display:block}
.light-color b{display:block;font-size:14px;color:var(--text)}
.light-color small{display:block;margin-top:6px;font-size:12px;color:var(--muted)}
.light-color input{width:54px;height:42px;padding:4px;border:1px solid var(--line);border-radius:10px;background:var(--well);cursor:pointer}
.light-color input::-webkit-color-swatch-wrapper{padding:0}
.light-color input::-webkit-color-swatch{border:0;border-radius:6px}
.light-note{font-size:12px;color:var(--muted);line-height:1.6}
.brightness-field{display:block}
.brightness-field>span{display:flex;justify-content:space-between;color:var(--muted);font-size:12px;margin-bottom:9px}
.brightness-field input{width:100%;accent-color:var(--lime)}

/* ---------- SOF 锁相 ---------- */
.sof-lock{margin-top:22px;padding:16px 18px;display:grid;grid-template-columns:1fr auto auto;align-items:center;gap:12px;border:1px solid var(--line);border-radius:var(--r-ctl);background:var(--well);cursor:pointer}
.sof-lock span b{display:block;font-size:13px;color:var(--text)}
.sof-lock span small{display:block;margin-top:4px;font-size:12px;line-height:1.55;color:var(--muted)}
.sof-lock input{position:absolute;opacity:0;pointer-events:none}
.sof-lock i{position:relative;width:42px;height:24px;border-radius:999px;background:var(--well-2);border:1px solid var(--line-hi);transition:background .22s var(--ease-out)}
.sof-lock i::after{content:"";position:absolute;width:18px;height:18px;left:2px;top:2px;border-radius:50%;background:var(--muted);transition:transform .22s var(--ease-out),background .22s var(--ease-out)}
.sof-lock input:checked+i{background:rgba(var(--accent-rgb),.18);border-color:var(--accent)}
.sof-lock input:checked+i::after{transform:translateX(18px);background:var(--accent)}
.sof-lock output{min-width:28px;font-size:12px;color:var(--muted)}

/* ---------- 固件 ---------- */
.firmware-card{display:flex;align-items:center;justify-content:space-between;gap:24px}
.firmware-copy p{font-size:13px;color:var(--muted);margin-top:6px}
.firmware-actions{display:flex;gap:8px}
.file-button{min-width:132px;text-align:left;padding:10px 13px;background:var(--well);border:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;transition:border-color .26s var(--ease-out),background .26s var(--ease-out),color .26s var(--ease-out)}
.file-button span{font-size:12px;color:var(--muted)}
.file-button b{font-size:13px}

/* ---------- 底栏 ---------- */
footer{
  position:fixed;z-index:15;bottom:0;left:0;right:0;height:84px;padding:0 36px;
  display:flex;align-items:center;justify-content:space-between;
  background:var(--glass-2);backdrop-filter:blur(18px);border-top:1px solid var(--line);
}
.notice{display:flex;align-items:center;gap:10px;color:var(--muted);font-size:12px}
.notice>span{width:22px;height:22px;border:1px solid var(--line-hi);display:grid;place-items:center;font:600 12px var(--mono);color:var(--lime)}
.actions{display:flex;gap:8px}
.text-button{background:transparent;color:var(--muted);padding:12px 18px;transition:color .26s var(--ease-out)}
.apply-button{
  min-width:180px;padding:13px 15px;background:var(--lime);color:var(--lime-ink);font-weight:700;
  display:flex;align-items:center;justify-content:space-between;
  transition:background .26s var(--ease-out),box-shadow .26s var(--ease-out);
}
.apply-button b{font-size:18px}
.apply-button:disabled{opacity:.45;cursor:wait}

.legacy-hooks{display:none!important}

#toast{
  position:fixed;z-index:40;top:88px;left:50%;transform:translate(-50%,-8px);opacity:0;pointer-events:none;
  padding:12px 18px;background:var(--text);color:var(--bg);font-weight:600;font-size:12px;
  transition:opacity .2s var(--ease-out),transform .2s var(--ease-out);
}
#toast.show{opacity:1;transform:translate(-50%,0)}

dialog{width:min(460px,calc(100% - 28px));padding:28px;background:var(--panel);color:var(--text);border:1px solid var(--line-hi)}
dialog::backdrop{background:rgba(0,0,0,.72);backdrop-filter:blur(5px)}
dialog h3{font-size:21px;margin:8px 0}
dialog p{color:var(--muted);font-size:12px;line-height:1.7;margin-bottom:22px}
dialog>div{display:flex;justify-content:flex-end}

/* ---------- 响应式 ---------- */
/* 导航列先塌成横排：236px 的竖列在平板宽度上会把舞台挤到 700px 以下，
   产品图一缩，印在图里的编号就糊了。横排 + 横向滚动比缩图划算。 */
@media(max-width:1180px){
  .console{grid-template-columns:1fr;gap:14px}
  .rail{
    position:static;flex-direction:row;gap:6px;overflow-x:auto;
    border-right:0;border-bottom:1px solid var(--line);padding:0 0 12px;
  }
  .rail-item{grid-template-columns:auto auto;flex:none;padding:10px 13px}
  .rail-item small{display:none}
  .rail-item[aria-selected=true]::before{
    left:50%;top:auto;bottom:-14px;transform:translateX(-50%);width:20px;height:2px;
  }
  .panel-split{grid-template-columns:1fr}
  /* 灯效双列塌为单列，预览和控制之间改用横向分隔 */
  .lighting-card{grid-template-columns:1fr;gap:20px}
  .light-preview{border-right:0;border-bottom:1px solid var(--line)}
  .light-controls{max-width:none;padding-right:0}
  .device-view{height:auto}
}
@media(max-width:760px){
  .topbar,.app-shell,footer{padding-left:14px;padding-right:14px}
  .brand small,.profiles>span,.notice{display:none}
  .command-bar{display:block}
  .profiles{margin-top:18px}
  .profiles>div{width:100%}
  .profiles button{min-width:0;flex:1}
  .device-view{padding:16px}
  .mouse-frame{height:clamp(260px,52vh,380px)}
  .rail{padding-bottom:10px;scrollbar-width:none}
  .rail::-webkit-scrollbar{display:none}
  .rail-item{padding:9px 12px}
  .rail-item b{font-size:13px}
  .rail-item[aria-selected=true]::before{bottom:-12px}
  /* 功能列改横排：200px 的侧列在手机上会把参数区挤成一条 */
  .key-panel-body{grid-template-columns:1fr;gap:14px}
  .fn-list{flex-direction:row;border-right:0;border-bottom:1px solid var(--line);padding:0 0 12px;overflow-x:auto}
  .fn-item{flex:none;padding:9px 11px}
  .fn-item[aria-checked=true]::before{left:50%;top:auto;bottom:0;transform:translateX(-50%);width:17px;height:2px}
  .target-grid{grid-template-columns:repeat(2,1fr)}
  .sensor-row{grid-template-columns:1fr}
  .dpi-list{grid-template-columns:repeat(3,1fr)}
  .firmware-card{display:block}
  .firmware-actions{margin-top:16px}
  .file-button{flex:1}
  .actions{margin-left:auto}
  .text-button{display:none}
}
@media(prefers-reduced-motion:reduce){
  *{animation:none!important;transition:none!important}
}
