/* style.css — 4G-wifi 蓝牙配置页
 *
 * 设计取向：深色 + 单一强调色，克制的层次。页面分三个清晰区块——
 * 设备状态条 / 认证 / WiFi+终端工作区。终端是最重的内容，给最大空间；
 * WiFi 列表收窄高度让两块同屏可见。所有交互态用色彩区分，不用阴影堆叠。
 * 保留原有 class 名（app.js 依赖），只改视觉。
 */

:root {
  --bg:        #0a0d16;
  --bg-soft:   #11151f;
  --card:      #161a26;
  --card-hi:   #1c2130;
  --line:      #262c3d;
  --line-hi:   #353d54;
  --fg:        #e8eaf2;
  --fg-dim:    #b4bacd;
  --muted:     #79809b;
  --accent:    #5b8cff;
  --accent-hi: #7aa3ff;
  --ok:        #3ddc8c;
  --warn:      #f5b342;
  --err:       #f46e6e;
  --radius:    12px;
  --radius-sm: 8px;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
               "Microsoft YaHei", Roboto, sans-serif;
  background: var(--bg);
  background-image:
    radial-gradient(900px 500px at 100% -10%, rgba(91,140,255,.07), transparent 60%),
    radial-gradient(700px 400px at -10% 110%, rgba(61,220,140,.05), transparent 60%);
  background-attachment: fixed;
  color: var(--fg);
  line-height: 1.5;
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
}

/* ---- header ---- */
header {
  max-width: 46rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 0;
}
header h1 {
  margin: 0 0 .35rem;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -.01em;
  display: flex; align-items: center; gap: .6rem;
}
.header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  flex-wrap: wrap;        /* on narrow phones the status wraps below the title */
}
/* Status chip in the header — sits right of the title on desktop, wraps on
 * mobile. Slightly more prominent than the old bottom-of-page status. */
.status-top {
  flex-shrink: 0;
  align-self: center;        /* explicit: align to header-row's vertical center */
  margin: 0 !important;      /* override the later-defined .status base rule's
                              * margin-top:1rem — .status and .status-top have
                              * equal specificity (0,1,0), and .status is
                              * defined AFTER .status-top, so it wins by order.
                              * !important here is the reliable override.
                              * This was the real cause of "chip 上面大 margin":
                              * the .status base rule's 1rem top was winning. */
  padding: .3rem .6rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 999px;   /* pill chip */
  font-size: .82rem;
  line-height: 1.2;
}
header h1 .logo {
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, var(--accent), #6f5bff);
  border-radius: 9px;
  font-size: 1rem;
}
.hint { color: var(--muted); font-size: .85rem; margin: 0 0 .4rem; max-width: 38rem; }

main {
  max-width: 46rem;
  margin: 0 auto;
  padding: 1.25rem 1.25rem 2rem;
}

/* ---- generic card / fieldset ---- */
fieldset {
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: var(--radius);
  padding: 1.1rem 1.1rem 1.2rem;
  margin: 0 0 1rem;
  transition: border-color .15s, opacity .15s;
}
fieldset:disabled { opacity: .45; }
legend {
  color: var(--fg-dim);
  padding: 0 .5rem;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
}

/* ---- inputs & buttons ---- */
label { display: block; margin: .5rem 0 .25rem; color: var(--fg-dim);
         font-size: .85rem; }
input[type="text"], input[type="password"] {
  width: 100%;
  padding: .65rem .8rem;
  background: var(--bg-soft);
  border: 1px solid var(--line);
  color: var(--fg);
  border-radius: var(--radius-sm);
  font-size: 1rem;
  font-family: inherit;
  transition: border-color .15s, box-shadow .15s;
}
input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(91,140,255,.18);
}
button {
  background: var(--accent);
  color: #fff;
  border: 0;
  border-radius: var(--radius-sm);
  padding: .65rem 1.15rem;
  font-size: .95rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background .15s, transform .05s;
}
button:hover:not(:disabled) { background: var(--accent-hi); }
button:active:not(:disabled) { transform: translateY(1px); }
button:disabled { opacity: .4; cursor: not-allowed; }
.btn-secondary {
  background: transparent;
  color: var(--fg-dim);
  border: 1px solid var(--line-hi);
}
.btn-secondary:hover:not(:disabled) { background: var(--card-hi); border-color: var(--line-hi); }

/* ---- auth field ---- */
#auth-field label { display: flex; flex-direction: column; }
#auth-field input { margin-top: .25rem; }
#auth-field { display: flex; flex-wrap: wrap; gap: .75rem 1rem; align-items: flex-end; }
#auth-field > label { flex: 1 1 14rem; margin: 0; }
#auth-field > button { height: fit-content; margin-bottom: 0; }

/* ---- status row ---- */
.status {
  display: flex; align-items: center; gap: .55rem;
  font-size: .85rem; color: var(--muted);
  margin: 1rem .25rem 0;
}
.dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--muted); display: inline-block; flex-shrink: 0;
  transition: background .2s, box-shadow .2s;
}
.dot.s0, .dot.s4, .dot.s6, .dot.s7 { background: var(--err); box-shadow: 0 0 8px rgba(244,110,110,.6); }
.dot.s1 { background: var(--ok); box-shadow: 0 0 8px rgba(61,220,140,.6); }
.dot.s2 { background: var(--warn); box-shadow: 0 0 8px rgba(245,179,66,.6); }
.dot.s3 { background: var(--ok); box-shadow: 0 0 8px rgba(61,220,140,.6); }

.actions { display: flex; justify-content: flex-end; margin-top: .25rem; }
.actions button + button { margin-left: .5rem; }
.busy-hint {
  color: var(--warn); font-size: .85rem; margin-top: .6rem;
}
.banner {
  background: rgba(244,110,110,.1); border: 1px solid rgba(244,110,110,.35);
  color: var(--err); padding: .75rem 1rem; border-radius: var(--radius-sm);
  font-size: .85rem; margin-bottom: 1rem;
}
.banner a { color: var(--accent-hi); }
.hidden { display: none; }
/* .hidden must beat the display:flex on .modal-overlay / .wifi-status below. */
.modal-overlay.hidden,
.wifi-status.hidden { display: none !important; }

/* ---- device wifi status bar ---- */
.wifi-status {
  display: flex; align-items: center; gap: .8rem;
  background: linear-gradient(135deg, var(--card), var(--card-hi));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .85rem 1rem;
  margin-bottom: 1rem;
}
.ws-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0;
         transition: background .2s, box-shadow .2s; }
.ws-dot.on { background: var(--ok); box-shadow: 0 0 10px var(--ok); }
.ws-dot.off { background: var(--muted); }
.ws-text { display: flex; flex-direction: column; line-height: 1.35;
           min-width: 0; flex: 1; }
.ws-ssid { font-weight: 600; font-size: .95rem; overflow: hidden;
           text-overflow: ellipsis; white-space: nowrap; }
.ws-ip { color: var(--muted); font-size: .8rem; font-family: ui-monospace, "SF Mono", Menlo, monospace; }

/* ---- tabs (WiFi / Hotspot) ---- */
.tabs {
  display: flex;
  gap: 0;
  margin-bottom: .9rem;
  border-bottom: 1px solid var(--line);
}
.tab {
  background: transparent;
  color: var(--muted);
  border: 0;
  border-bottom: 2px solid transparent;
  padding: .55rem .9rem;
  font-size: .9rem;
  font-weight: 600;
  border-radius: 0;
  cursor: pointer;
  transition: color .15s, border-color .15s;
}
.tab:hover:not(.locked) { color: var(--fg-dim); }
.tab.active { color: var(--fg); border-bottom-color: var(--accent); }
.tab.locked { opacity: .4; cursor: not-allowed; }
.tab-panel.locked { opacity: .45; pointer-events: none; }

/* ---- hotspot form ---- */
.hotspot-form { display: flex; flex-direction: column; gap: .15rem; }
.hotspot-form label { margin: .5rem 0 .25rem; }
.hotspot-form .opt-hint { color: var(--muted); font-size: .78rem; }
.hotspot-form .check-row {
  display: flex; align-items: center; gap: .5rem;
  margin: .65rem 0 .35rem; color: var(--fg-dim); font-size: .88rem;
}
.hotspot-form .check-row input { width: auto; }
.hotspot-form button { margin-top: .4rem; align-self: flex-start; }

/* ---- wifi disconnect button in status bar ---- */
.btn-wifi-dc { padding: .35rem .8rem; font-size: .82rem; flex-shrink: 0; }

/* ---- WiFi scan list ---- */
.scan-row {
  display: flex; align-items: center; gap: .75rem; margin-bottom: .5rem; flex-wrap: wrap;
}
.scan-row button { padding: .45rem .9rem; font-size: .9rem; }
.scan-hint { color: var(--muted); font-size: .8rem; }
.wifi-list {
  list-style: none; margin: .25rem 0 .4rem; padding: 0;
  max-height: 13rem; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--bg-soft);
}
.wifi-list:empty { display: none; }
.wifi-list li {
  display: flex; align-items: center; gap: .65rem;
  padding: .6rem .8rem; cursor: pointer; font-size: .9rem;
  border-bottom: 1px solid var(--line);
  transition: background .12s;
}
.wifi-list li:last-child { border-bottom: 0; }
.wifi-list li:hover { background: var(--card-hi); }
.wifi-list li.connected {
  background: rgba(61,220,140,.07);
  border-left: 3px solid var(--ok);
  padding-left: calc(.8rem - 3px);
}
.wifi-list li.saved { border-left: 3px solid var(--accent); padding-left: calc(.8rem - 3px); }
.wifi-list li.connected.saved { border-left: 3px solid var(--ok); }
.wifi-list li.connected:hover { background: rgba(61,220,140,.12); }
.wifi-name { flex: 1; min-width: 0; word-break: break-all; }
.wifi-lock { font-size: .85rem; opacity: .7; flex-shrink: 0; }
.wifi-tags { display: flex; gap: .3rem; flex-shrink: 0; }
.wifi-tag {
  font-size: .68rem; padding: 2px 7px; border-radius: 10px; white-space: nowrap;
  font-weight: 600; letter-spacing: .02em;
}
.tag-on { background: rgba(61,220,140,.18); color: var(--ok); }
.tag-saved { background: rgba(91,140,255,.18); color: var(--accent-hi); }
.tag-hotspot { background: rgba(245,179,66,.18); color: var(--warn); }
/* The device's own hotspot SSID in its own AP scan — dim it so users don't
 * mistake it for an external network to connect to. */
.wifi-list li.self-hotspot { opacity: .6; border-left: 3px solid var(--warn); }
.wifi-bars { font-family: ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: 1px; opacity: .8; flex-shrink: 0; }
.wifi-bars.b4 { color: var(--ok); }
.wifi-bars.b3 { color: var(--ok); }
.wifi-bars.b2 { color: var(--warn); }
.wifi-bars.b1 { color: var(--err); }
.wifi-bars.b0 { color: var(--muted); }

/* ---- WiFi connect modal ---- */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(5,7,12,.72);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  z-index: 100; padding: 1rem;
}
.modal {
  background: var(--card);
  border: 1px solid var(--line-hi);
  border-radius: 14px;
  padding: 1.4rem 1.4rem 1.15rem;
  width: min(92vw, 24rem);
  box-shadow: 0 20px 60px rgba(0,0,0,.5);
}
.modal h3 { margin: 0 0 .65rem; font-size: 1.05rem; font-weight: 700; }
.modal-ssid { font-weight: 600; margin-bottom: .9rem; word-break: break-all;
              color: var(--fg); }
.modal-psk-label { display: block; margin: .4rem 0; }
.modal-hint { color: var(--muted); font-size: .85rem; min-height: 1.2rem; margin: .6rem 0 .2rem; }
.modal-actions { display: flex; align-items: center; gap: .5rem; margin-top: 1rem; }
.modal-spacer { flex: 1; }

/* ---- interactive terminal ----
 * 46rem (~736px) 已够放 80 列 xterm，不再破开 main 的宽度限制——
 * 这样终端 fieldset 和上方 WiFi fieldset 等宽对齐。xterm 自己管 viewport
 * 和 scrollback。如果未来要更宽，改 main 的 max-width 而不是这里单破。 */
#terminal-field {
  /* xterm's fit() sizes #terminal to a fixed px width (cols × cellWidth).
   * That fixed width ignores fieldset padding constraints and was making
   * #terminal-field (643px) wider than the sibling WiFi fieldset (~600px),
   * pushing the whole page past the viewport on mobile → pinch-zoom enabled.
   * min-width:0 + overflow:hidden keep the fieldset from being stretched by
   * its own fixed-px child. */
  padding: .8rem;
  min-width: 0;
  overflow: hidden;
}
#terminal-field .term-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem; margin-bottom: .5rem; flex-wrap: wrap;
}
#terminal-field .term-row .term-label {
  color: var(--fg-dim); font-size: .85rem; font-weight: 600;
  letter-spacing: .02em; text-transform: uppercase;
}
#terminal-field .term-row .term-meta {
  color: var(--muted); font-size: .78rem;
}
#terminal {
  /* FitAddon reads getComputedStyle(#terminal).width as the canvas budget.
   * Under the global * { box-sizing: border-box }, width includes border —
   * and a 1px border on each side makes fit pick ~1-2 cols too many, so the
   * xterm canvas renders wider than the container and overflows ("宽度被挤
   * 开"). Fix: make #terminal content-box (width = pure content) and draw
   * the border as outline (not in the box model at all). Fit now reads the
   * true content width, cols match the visible area. */
  box-sizing: content-box;
  height: 24rem;
  /* 不要给 xterm 容器加 padding！xterm.js 的 .xterm-viewport 和 canvas 是
   * position:absolute 精确撑满 .xterm 的，给 #terminal 加 padding 会让
   * .xterm 内容区比容器小，viewport/canvas 和外部边框对不齐——后果是：
   * 鼠标点击落到错位区域 bash 收不到输入（移动端全黑点不进去）、wheel
   * 滚到边界事件冒泡带动外部页面、滚动条位置算错。要内边距视觉用
   * 描述符的 margin 在 .xterm 内留，或调整 xterm 的 lineHeight/letterSpacing。*/
  padding: 0;
  background: #05070d;
  outline: 1px solid var(--line);
  outline-offset: -1px;
  border-radius: var(--radius-sm);
  overflow: hidden;
}
#terminal .xterm { height: 100%; padding: 0; max-width: 100%; }

/* ---- debug log panel ----
 * 永远可见的调试日志区：终端初始化每一步 + 每条 stdout notify 都写进来，
 * 方便在没法用 DevTools 的设备上截屏排查。最多留若干行，可滚。 */
.debug-panel {
  /* Entirely hidden by default — the title row AND the log body. This is a
   * dev-only panel surfaced by tapping the main title 10×; production users
   * should never see ANY of it. (Using <details> was wrong: closing <details>
   * only hides its <pre> body, the <summary> row stayed visible.) */
  display: none;
  margin-top: .5rem;
  border: 1px dashed var(--line-hi);
  border-radius: var(--radius-sm);
  background: var(--bg-soft);
  padding: .3rem .5rem;
}
.debug-panel.shown { display: block; }
.debug-panel-head {
  cursor: default;
  color: var(--muted);
  font-size: .78rem;
  user-select: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
}
.copy-log-btn {
  font-size: .72rem;
  padding: .15rem .55rem;
  background: var(--card-hi);
  color: var(--fg-dim);
  border: 1px solid var(--line-hi);
  border-radius: 5px;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.copy-log-btn:hover { background: var(--line); }
.debug-log {
  margin: .4rem 0 0;
  max-height: 11rem;
  overflow-y: auto;
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  font-size: .72rem;
  line-height: 1.35;
  color: var(--fg-dim);
  white-space: pre-wrap;
  word-break: break-all;
  padding: .2rem 0;
}

/* ---- responsive: keep things usable on phone ---- */
@media (max-width: 36rem) {
  body { font-size: 14px; }
  header { padding: 1.4rem 1rem 0; }
  header h1 { font-size: 1.25rem; }
  main { padding: 1rem; }
  #terminal { height: 18rem; }
  .modal-actions { flex-wrap: wrap; }
}

/* ---- custom scrollbars (webkit) ---- *
 * 比原生灰色粗滚动条更贴合深色主题：细、半透明、悬停时显形。
 * 三处滚动：页面整体(wheel on body)、WiFi 列表、xterm 内部 viewport。
 * 非想（Firefox 等）走 scrollbar-width/scrollbar-color 简化版，至少配色统一。 */

/* Firefox: 一次配置所有滚动条 */
* {
  scrollbar-width: thin;
  scrollbar-color: rgba(122,163,255,.28) transparent;
}

/* WebKit (Chrome/Edge/Safari/iOS Bluefy): 逐元素精细控制 */
::-webkit-scrollbar {
  width: 9px;
  height: 9px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: rgba(122,163,255,.22);
  border-radius: 6px;
  border: 2px solid transparent;
  background-clip: padding-box;   /* 让 border 把 thumb 收窄、留出空隙更精致 */
}
::-webkit-scrollbar-thumb:hover {
  background: rgba(122,163,255,.42);
  background-clip: padding-box;
}
::-webkit-scrollbar-corner {
  background: transparent;        /* 列表 + 横向滚交叉角不留灰块 */
}

/* xterm 的 viewport 用自己的滚动条；覆盖一下颜色与页面统一。 */
.xterm-viewport::-webkit-scrollbar-thumb {
  background: rgba(122,163,255,.25);
  border-radius: 6px;
  background-clip: padding-box;
}
.xterm-viewport::-webkit-scrollbar-thumb:hover {
  background: rgba(122,163,255,.45);
  background-clip: padding-box;
}

/* 注：xterm.js 滚动条"拖拽被选中拦截"是个长期已知问题（issue #512 等），
 * 官方 workaround 是给 .xterm-viewport 提 z-index + 透明背景。但实测在
 * 安卓 Chrome 上提 z-index 会让 viewport 黑底盖住文字（即使 !important 也
 * 压不掉某些内联样式路径）→ 终端全黑。回退此修复，保留滚动条的颜色美化，
 * 拖拽问题暂时接受（滚轮滚动正常）。需要时再单独针对安卓研究。 */

/* xterm helper-textarea 在触屏设备上重定位回可视区。
 *
 * xterm.js 把它的隐藏输入 textarea 定位在 left:-9999em（xterm.css L61-77），
 * 用来捕获键盘事件。但 iOS Safari 只会为**在可视区内的**聚焦元素升起软键盘
 * ——textarea 在屏幕外时 textarea.focus() 什么也不发生，键盘根本不弹，所以
 * iOS 上点终端"打字没任何输入"。
 *
 * 修法来自 vercel-labs/wterm issue #41/#32：在触屏设备上把 textarea 移回 0,0
 * 并设 pointer-events:auto，配色全透明让它在视觉上不可见，但能被 focus。
 * 这样 iOS 点终端 → focus 透明 textarea → 软键盘弹出 → onData 触发。
 *   见 https://github.com/vercel-labs/wterm/issues/32
 *
 * 注意：这个修复让 iOS 输入可用，但安卓 GBoard 的 IME 缓冲到 Enter 的
 * composition 问题（xterm 内部 CompositionHelper 缺陷，issue #5108）应用层
 * 难完美修。 */
@media (hover: none) and (pointer: coarse) {
  .xterm .xterm-helper-textarea {
    left: 0 !important;
    top: 0 !important;
    width: 1px !important;
    height: 1px !important;
    opacity: 0;            /* 视觉不可见，但不挡 focus */
    color: transparent;
    background: transparent;
    caret-color: transparent;
  }
  /* 触屏：更窄、更隐蔽的滚动条 */
  ::-webkit-scrollbar { width: 6px; height: 6px; }
}

