/* 表单控件 —— 全部自绘，前台和后台共用这一份。
 *
 * **为什么不是「原生的丑」这么简单。**
 * macOS / iPadOS 在窗口不是 key window 的时候会把原生表单控件重绘成灰色，
 * 而且这层重绘盖得过 `accent-color`。于是切到别的窗口时，速度和节拍器音量
 * 两个滑块变灰，而播放按钮、走带已播段、底轨选中态纹丝不动 —— 因为那些是
 * CSS 自绘的。**方向是让原生控件别再变，不是让别的也跟着变。**
 *
 * **为什么是一个文件而不是两份。**
 * `style.css` 和 `admin.css` 各写各的正是漂移的源头 —— 后台的 accent 曾经
 * 停在配色系统之前的 `#4d9cff` 上很久（notes/LOG.md 2026-09-10）。
 * 这里只用两边都定义了的 token（--accent / --line / --panel / --fg / --dim /
 * --faint / --sunk / --accent-ink），所以两边引同一份就行，颜色跟着各自的主题走。
 *
 * **自绘要自己补回原生白送的那些东西**：键盘可达、`:focus-visible` 的可见焦点、
 * `:disabled` 的状态、以及足够大的点击区（触屏 44px 那条）。
 * 下面每一处都写了。
 */

/* 作者写的 `display` 赢得过 UA 的 `[hidden] { display: none }`，无论特指度
   —— 所以任何设了 display 的样式表都要自己把它兜回来，否则带 hidden 的元素
   藏不住。这个项目为这件事付过一次：后台的登录框曾经永远盖在控制台上面。 */
[hidden] { display: none !important; }

/* ---- 滑块 -------------------------------------------------------------
 * 两套伪元素都要写：WebKit 一套、Firefox 一套，且**不能合并成一条选择器**
 * —— 任何一个引擎认不出其中一个，整条规则都会被丢掉。 */
input[type="range"] {
  -webkit-appearance: none; appearance: none;
  height: 22px; width: 108px; background: transparent; cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 4px; border-radius: 999px; background: var(--sunk, #2a2f3a);
  border: 1px solid var(--line);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px; margin-top: -7px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--panel);
  box-shadow: 0 1px 3px rgba(0,0,0,.35);
}
input[type="range"]::-moz-range-track {
  height: 4px; border-radius: 999px; background: var(--sunk, #2a2f3a);
  border: 1px solid var(--line);
}
input[type="range"]::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--panel);
  background: var(--accent); box-shadow: 0 1px 3px rgba(0,0,0,.35);
}
input[type="range"]:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--accent); outline-offset: 2px; }
input[type="range"]:focus-visible::-moz-range-thumb { outline: 2px solid var(--accent); outline-offset: 2px; }
input[type="range"]:disabled { opacity: .45; cursor: default; }

/* ---- 复选框 / 单选 ---------------------------------------------------- */
input[type="checkbox"], input[type="radio"] {
  -webkit-appearance: none; appearance: none;
  width: 17px; height: 17px; flex: none; margin: 0; cursor: pointer;
  border: 1.5px solid var(--line); background: var(--panel);
  border-radius: 5px; position: relative; display: inline-grid; place-content: center;
  transition: background .12s, border-color .12s;
}
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:checked, input[type="radio"]:checked {
  background: var(--accent); border-color: var(--accent);
}
/* 勾用两条边画，不用字形 —— 字形会跟着字体缺字变成豆腐块。 */
input[type="checkbox"]:checked::after {
  content: ""; width: 4px; height: 8px; margin-top: -2px;
  border: solid var(--accent-ink, #fff); border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
input[type="radio"]:checked::after {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent-ink, #fff);
}
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
input[type="checkbox"]:disabled, input[type="radio"]:disabled { opacity: .45; cursor: default; }

/* ---- 下拉 -------------------------------------------------------------
 * 只统一外观，不自绘弹出层：自绘下拉要自己处理键盘、触屏和屏幕阅读器，
 * 成本远大于收益，而原生弹出层在移动端反而更好用。箭头自己画。 */
select {
  -webkit-appearance: none; appearance: none;
  font: inherit; color: var(--fg); background-color: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r, 8px);
  padding: 7px 30px 7px 10px; cursor: pointer; min-height: 34px;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 14px center, right 9px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
select:disabled { opacity: .5; cursor: default; }

/* ---- 数字框 -----------------------------------------------------------
 * 去掉那对上下小箭头：它们在两个引擎里长得不一样，而且小到点不准。 */
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ---- 文字类输入框（兜底层）---------------------------------------------
 * 后台原来只在 `.lbox` / `.codeform` / `table` / `.oBar` 这几个上下文里写了
 * 输入框样式，落在这些之外的就是原生的白底黑字，在深色控制台里非常扎眼
 * （套餐价格和天数那两个就是）。这一段是给它们兜底的基线。
 *
 * 🔴 **它必须输给任何别人写的规则，所以放进 `@layer`。**
 * 原来这里靠的是「前台 style.css 有一条 `input:not(…):not(…):not(…)`，特指度
 * 更高，所以前台不受影响」—— 那是把意图写在注释里，靠一条别的规则碰巧比它高
 * 来实现的。`web/tests/specificity.mjs` 一路查过来，看见的就是「controls.css
 * 这几条在前台一条都没生效」，它没法从注释里读出这是故意的。
 * 分层规则输给任何未分层的规则，与特指度和先后无关 —— 意图这才成了 CSS 本身
 * 说的话，而不是注释里的一句承诺。
 * （`@layer` 要 Safari 15.4；项目里已经用了 20 处 `color-mix()`，那是 16.2，
 * 门槛本来就更高，所以这条不额外抬门槛。） */
@layer fallback {
  input[type="text"], input[type="number"], input[type="password"],
  input[type="search"], input[type="email"], input[type="url"], input[type="tel"] {
    font: inherit; color: var(--fg); background: var(--panel);
    border: 1px solid var(--line); border-radius: var(--r, 8px);
    padding: 7px 10px; min-height: 34px;
  }
  input[type="text"]:focus-visible, input[type="number"]:focus-visible,
  input[type="password"]:focus-visible, input[type="search"]:focus-visible,
  input[type="email"]:focus-visible, input[type="url"]:focus-visible,
  input[type="tel"]:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 1px;
  }
}

/* ---- 文本域 ----------------------------------------------------------- */
textarea {
  font: inherit; color: var(--fg); background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r, 8px);
  padding: 9px 11px; resize: vertical; min-height: 76px; width: 100%;
}
textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ---- 选文件 -----------------------------------------------------------
 * 原生的 file input 在中文界面里是唯一一处英文（Choose File / No file chosen），
 * 而且它的外观完全不可控。做法：原生 input 藏在 label 里（**不是
 * `display:none`** —— 那样键盘就找不到它了），外面套一个自己的按钮，
 * 文件名由 JS 写进 `.fileName`。 */
.filePick { display: inline-flex; align-items: center; gap: 10px; }
.filePick > input[type="file"] {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.filePick > .fileBtn {
  display: inline-flex; align-items: center; gap: 7px; min-height: 34px;
  padding: 0 14px; border-radius: var(--r, 8px);
  border: 1px solid var(--line); background: var(--panel); color: var(--fg);
  font: inherit; cursor: pointer;
}
.filePick > input[type="file"]:focus-visible + .fileBtn {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.filePick .fileName { color: var(--dim); font-size: 13px;
  max-width: 22em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filePick .fileName:empty::before { content: "还没有选文件"; color: var(--faint); }

/* ---- 折叠块 -----------------------------------------------------------
 * 原生那个小三角和全站任何一处都不呼应，而且两个引擎画得不一样。 */
details > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center;
  gap: 7px; min-height: 34px; color: var(--fg);
}
details > summary::-webkit-details-marker { display: none; }
details > summary::before {
  content: ""; width: 0; height: 0; flex: none;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform .14s; transform-origin: 25% 50%;
}
details[open] > summary::before { transform: rotate(90deg); }
details > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- 出了点问题，得刷新一下（js/fatal.js）-------------------------------
 * 整屏、实底，只有一个按钮。前台后台共用，所以只用两边都定义了的 token ——
 * 不用 --scrim / --red / --r（那几个只有前台有）。实底而不是半透明遮罩：
 * 这时候背后那一页已经不能用了，透出来只会让人以为还能点。
 *
 * 层级 1000：要压住一切，包括引导教程的气泡（71/72）和后台预览横幅（999）。
 * 按钮要大 —— 原来那个「重新载入」是 66×26 px、11.5px，看不到就等于没有。 */
.failWin {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: 24px; background: var(--bg); color: var(--fg);
}
.failBox {
  width: min(460px, 100%); text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 16px;
}
.failTitle { margin: 0; font-size: clamp(22px, 3.4vw, 30px); line-height: 1.3;
             font-weight: 700; color: var(--fg); text-wrap: balance; }
.failSay { margin: 0; font-size: 15px; line-height: 1.7; color: var(--dim); }
.failGo {
  min-height: 52px; min-width: 200px; padding: 0 32px; margin-top: 4px;
  font: inherit; font-size: 17px; font-weight: 600;
  color: var(--accent-ink); background: var(--accent);
  border: none; border-radius: 12px; cursor: pointer;
}
.failGo:hover { filter: brightness(1.07); }
.failGo:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.failWhy { margin: 6px 0 0; font-size: 12.5px; line-height: 1.6; color: var(--faint);
           overflow-wrap: anywhere; }
/* 「复制诊断信息」：次要按钮，别和「刷新页面」抢 —— 刷新是人要的，这个是给开发的。 */
.failDiag { min-height: 40px; margin-top: 10px; padding: 0 18px;
            font: inherit; font-size: 13.5px; color: var(--dim);
            background: transparent; border: 1px solid var(--line); border-radius: 10px;
            cursor: pointer; }
.failDiag:hover { color: var(--ink); border-color: var(--faint); }
.failDiag:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.failWhy:empty { display: none; }
