/*
 * 壳的样式。**这里不许出现任何跟版式有关的东西** ——
 * 版式全部来自 web/assets/pages/<slug>.css（由稿子机械生成）。
 * 这个文件只解决「稿子的画布怎么落到真机视口上」这一件事。
 *
 * 为什么不复用 web/assets/fit.js：那是**看稿专用**的适配层，
 * 它把整屏 393×852 等比缩到窗口里 —— 稿子是给人并排比对的，
 * 缩完能一眼看全。真机不能这么干：稿子画布常常比手机屏高得多
 * （纠错管理 393×1388，刷题页 393×852 但内容会随题目变长），
 * 等比缩下去字会小到看不清。真机要的是：
 *   · 横向：铺满视口（稿子的 393 宽 → 100%）
 *   · 纵向：内容自然流，页面自己滚
 * 所以这里只改「画布盒子」，不动里面任何一条布局规则。
 */

html, body {
  margin: 0;
  padding: 0;
  background: var(--cl-bg-page, #fafafb);
  -webkit-text-size-adjust: 100%;
}

#app { display: flex; justify-content: center; }

/*
 * 换屏时的垫场层 —— 新屏还没准备好（控制器拿真数据去填之前会把它藏起来）时，
 * 把**上一屏**原样浮在这儿，等新屏亮相再撤。机制和三个设计决定写在 app.js
 * 的 mountScreen 上面，这里只说样式。
 *
 * ⚠ 这里**不许有任何跟版式有关的东西** —— 它里面装的是上一屏那棵原样的 DOM，
 *   外面这层只负责「铺满视口、居中、别挡事件」。多写一条就等于是我又另造了一套版式，
 *   而且浮层里的东西跟原来的位置对不上，用户会看到上一屏「跳」了一下 ——
 *   那比空白还难受。宽度的对齐靠的是同一套：.screen 自己写着
 *   `width:100%; max-width:var(--design-w,393px)`，父层只要是 flex + 居中就一致。
 *
 * 挂 body 而不是 #app：#app 是 flex 容器，多一个孩子会跟新屏并排挤在一起。
 * pointer-events:none：上一屏是**冻住的**（它的控制器已经卸了），
 *   让它接点击等于让用户点到一堆接不上的东西，那不如让他点不着。
 */
.screen-cover {
  position: fixed;
  inset: 0;
  z-index: 8000;          /* 高于 #app；低于 toast(9999) 和 devbar(9000) */
  display: flex;
  justify-content: center;
  pointer-events: none;
}

/*
 * 管理后台的侧边导航抽屉。机制和「稿子画在哪儿」写在 app/admin-nav.js 顶上，
 * 这里只说两件事：**外层这圈壳**，和**那枚汉堡的触控目标**。
 *
 * ⚠ 抽屉里的**内容**（220px 宽的白底侧边栏、九项、底部头像+退出）一条样式都不在这
 *   儿 —— 那整块是从稿子里 clone 过来的（含它自己的 CSS）。这里多写一条，
 *   就等于在代码里又抄了一份版式，稿子改了它不跟，而且不报错。
 *
 * z-index 分层：垫场层 8000（换屏用，铺满且不吃事件）< 抽屉 8100 < devbar 9000
 *   < toast 9999。抽屉必须在垫场层之上 —— 否则用户从后台某页点菜单跳转、
 *   新页在取数据时垫场层浮起来，会把刚打开的抽屉盖住。
 */
.admin-nav {
  position: fixed;
  inset: 0;
  z-index: 8100;
  pointer-events: none;   /* 交给下面两层各自收：遮罩收点击，面板收点击 */
}
.admin-nav-mask {
  position: absolute;
  inset: 0;
  pointer-events: auto;
  /* 遮罩照稿子的惯例：底色用正文色 + 45% 透明（稿子 9-38 的「遮罩」就这个写法）。
     ⚠ 不用 --op-mask：那个令牌生成时被塞了 px 单位（0.44999998807907104px），
       opacity 拿到它等于 0，遮罩会变成完全透明。 */
  background: var(--cl-text-primary, #1c1b22);
  opacity: 0;
  transition: opacity 220ms ease;   /* 跟着抽屉一起淡入，不然抽屉还在滑、后面已经暗了 */
}
.admin-nav-panel {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  display: flex;          /* 让稿子那块侧边栏的 align-self:stretch 撑满高度 */
  pointer-events: auto;
  overflow: hidden;
  /* 滑入。只动 transform，不动 left/width —— 只动 transform 才走合成层，
     在手机上不掉帧；动 left 每一帧都要重排。 */
  transform: translateX(-100%);
  transition: transform 220ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.admin-nav.is-open .admin-nav-panel { transform: translateX(0); }
.admin-nav.is-open .admin-nav-mask { opacity: 0.45; }

@media (prefers-reduced-motion: reduce) {
  .admin-nav-panel, .admin-nav-mask { transition: none; }
}

/*
 * 汉堡那枚图标的触控目标。
 *
 * 稿子把 `Icon/Menu` 画成 24×24，手指点不准（HIG 要 44）。这里用一层透明的
 * ::after 往四周长出去，**不改图标自己的尺寸** —— 改尺寸会把它在顶栏里的
 * 位置和行高一起动掉，那就不叫还原稿子了。伪元素不参与布局，长了也不挤别人。
 */
.admin-nav-trigger { position: relative; }
.admin-nav-trigger::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--sz-tap-min, 44px);
  height: var(--sz-tap-min, 44px);
  transform: translate(-50%, -50%);
}

/*
 * 稿子画布。gen.mjs 给根节点写了固定 width/height（393×1388 这种），
 * 那是**设计尺寸**，不是渲染尺寸。真机上要这么翻：
 *
 *   宽 393 → 铺满视口
 *   高 1388 → 改成视口高（100dvh）
 *
 * 高度这条是关键。设计稿那张画布是一张「照片」，它把当时那 3 张卡正好装进
 * 1388 里；真数据有 5 条、20 条，高度必须重新分配。所以：
 *   根节点 = 视口高，保持 overflow:hidden（跟稿子一样，不许整页滚）
 *   内容区 = 内部滚（见 [data-scroll]）—— 这也是稿子自己的意思：
 *            它给内容区写了 flex:1，语义就是「顶栏之外的剩余空间」，只是
 *            在固定画布下那点剩余空间不需要滚而已。
 *
 * 这样顶栏/状态栏会自然钉在顶上，只滚内容 —— 比整页滚更贴手机。
 * 宽度是唯一一处「值变了」的换算，其余布局数字全部原样。
 */
.screen {
  width: 100%;
  max-width: var(--design-w, 393px);
  overflow: hidden;
  transform: none !important;   /* 覆盖 fit.js 的缩放；真机不缩放 */
}

/* 稿子根节点：把设计高度换成视口高。js 里 view.js 的 adaptCanvas() 也会设一遍，
   这里是兜底，免得 js 没跑起来时画布高度还是 1388。 */
.screen > * { height: 100dvh !important; min-height: 0 !important; }

/*
 * 滚动容器。**这个标记不是手写的，是 view.js 从稿子的计算样式里推出来的** ——
 * 稿子里写了 flex:1 又 overflow:hidden 的那个节点，语义就是「承载剩余空间的那块」，
 * 真机上给它滚。一页只标一个（取离根最近的），不然会嵌套双滚动。
 */
[data-scroll="1"] {
  overflow-y: auto !important;
  overflow-x: hidden !important;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* 窄屏（真机）铺满，不留白边 */
@media (max-width: 480px) {
  .screen { max-width: 100%; }
}

/* 提示条：**壳自带的**，用于壳级别的报错（「控制器出错」这类，页面都还没起来）。
   页面自己的提示不走这里 —— 刷题页用稿子画的 Toast（12:74），见下面 .toast-gaozi。 */
.toast {
  position: fixed;
  left: 50%;
  bottom: 96px;
  transform: translateX(-50%);
  background: rgba(28, 27, 34, .88);
  color: #fff;
  font: 13px/1.5 var(--ff, sans-serif);
  padding: 10px 16px;
  border-radius: 10px;
  z-index: 9999;
  max-width: 80vw;
}
.toast[hidden] { display: none; }

/* ── 代码阶段规则（稿子表达不了、只能在代码里落的）─────────────
 *
 * ardot 没有「固定定位」也没有 max-height / 行数截断，
 * 所以这类东西在稿子上只能画成「它长什么样」，位置和截断得在这里补。
 * 每一条都必须写清出处，否则下一个人会把它们当成我随手写的样式。
 */

/* ① 稿子画的 Toast（12:74 的 12:157）在画布上是 absolute + left:56.5/top:400 ——
 *    那是「摆在照片里的位置」，不是真机上的位置。真机要的是浮在底部。
 *    只改位置，卡片本身的底色/圆角/阴影/字号全部来自稿子的 CSS。 */
.toast-gaozi {
  position: fixed !important;
  left: 50% !important;
  top: auto !important;
  bottom: 96px !important;
  transform: translateX(-50%);
  z-index: 9999;
}
.toast-gaozi[hidden] { display: none; }

/* ② 解析卡的折叠。ardot 存不了 -webkit-line-clamp，所以稿子上
 *    「解析卡/长文」（12:233 的 12:248）画的「4 行 + …+ 展开全部」，
 *    那个「4 行」只能在代码里落。
 *    只在 data-fold="1" 时生效 —— 短解析永远走普通的解析卡，不折。 */
[data-fold="1"] [data-name="解析"] {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
[data-fold="0"] [data-name="解析"] {
  display: block;
  -webkit-line-clamp: none;
  overflow: visible;
}

/* ③ 填空的输入框。稿子 12:190 画的是 Component/Input/Default 里一个叫「占位」的
 *    div（写着「请输入答案」）。真机上必须能真输入，所以 quiz.js 把这个槽换成了
 *    真的 <input>，**class 沿用稿子的那一份** —— 字号/颜色/行高照旧继承。
 *    这里只补三条 div 不需要、input 才需要的东西（它默认不是 flex 子项、
 *    自带边框、自带 outline）。 */
input[data-name="作答输入"] {
  flex: 1 1 0;
  min-width: 0;
  border: 0;
  background: transparent;
  outline: none;
  padding: 0;
  font: inherit;
  color: inherit;
}

/* ④ 答题卡抽屉（稿子 12:75 的 12:171）。ardot 没有「固定定位」也没有 max-height，
 *    所以那一帧画的是「抽屉长什么样、摆在哪儿」，真机上必须是浮层 + 可滚。
 *    位置和限高只能在这里落；卡片本身的底色/圆角/内边距全部来自稿子。 */
.drawer-gaozi {
  position: fixed !important;
  left: 0 !important;
  right: 0 !important;
  bottom: 0 !important;
  top: auto !important;
  max-height: 85vh;
  overflow-y: auto;
  z-index: 9000;
}
/* 遮罩也一样：稿子里它是 absolute 铺满画布，真机上要盖住整个视口 */
[data-name="遮罩"] {
  position: fixed !important;
  inset: 0 !important;
  z-index: 8999;
}

/* ⑤ 纠错弹窗（稿子 15-111 的 15:113）。跟抽屉同一个道理：ardot 没有「固定定位」，
 *    稿子画的是「弹窗长什么样、贴在底部」，真机上得钉在视口底下并允许滚动
 *    （长描述 + 六个芯片在小屏上可能超出）。
 *    卡片本身的底色/圆角/内边距/阴影全部来自稿子，这里只管位置。 */
.dialog-gaozi {
  position: fixed !important;
  left: 0 !important;
  right: 0 !important;
  bottom: 0 !important;
  top: auto !important;
  max-height: 85vh;
  overflow-y: auto;
  z-index: 9001;   /* 比抽屉(9000)高一层：纠错弹窗是从题面上开的，抽屉不该盖它 */
}

/* ⑤ 点按区补足（HIG：最小 44×44）。
 *    稿子是按「看着好看」画的，有些可点元素的绘制高度不到 44px。
 *    下面用透明伪元素往上下撑开命中区，**不动任何布局**（伪元素不占空间，
 *    也不改变绘制位置）—— 所以视觉与稿子逐像素一致，手感却够。
 *    每一条都写清是哪个节点、稿子画的多高、撑到多高。 */

/* 「展开全部」行（12:233 的 12:231）：稿子画 329×23 —— 撑到 45 高 */
[data-name="展开行"] { position: relative; }
[data-name="展开行"]::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: -11px; bottom: -11px;
}

/* 答题卡格子（12:75）：稿子画 32×32，格间距 10 ——
   上下各撑 5px 正好吃满格间距、撑到 42，且**相邻格子的命中区不重叠**
   （重叠会让点在边界上时不知道跳哪一题）。 */
[data-name^="答题卡格/"] { position: relative; }
[data-name^="答题卡格/"]::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: -5px; bottom: -5px;
}

/* ⑥ 确认弹窗（稿子 9:33「清空错题本？」、11:320「删除错题」里那个「弹窗/确认」）。
 *    同上：ardot 没有固定定位，稿子把它画在画布 41/330 处 —— 那是「摆在照片里的
 *    位置」，不是真机上的位置。真机要的是浮在视口正中（稿子 330 本来也就是居中：
 *    弹窗高约 172，852 画布居中正好是 340）。
 *    卡片本身的底色/圆角/阴影/内边距/按钮全部来自稿子，这里只落位置。
 *    遮罩的位置不用在这里写 —— 上面已经有一条 [data-name="遮罩"] 管着。 */
[data-name="弹窗/确认"] {
  position: fixed !important;
  left: 50% !important;
  top: 50% !important;
  transform: translate(-50%, -50%);
  z-index: 9000;
}

/* ⑦ 搜索框的输入位。跟 ③ 填空的输入框同一个道理：稿子画的是「占位」那个 div，
 *    真机上必须能真输入，所以控制器把它换成了真的 <input>，class 沿用稿子的那一份。
 *    这里只补 div 不需要、input 才需要的几条（它默认不撑开、自带边框和 outline）。 */
input[data-name="搜索输入"] {
  flex: 1 1 0;
  min-width: 0;
  border: 0;
  background: transparent;
  outline: none;
  padding: 0;
  font: inherit;
  color: inherit;
}

/* ⑧ 滚动容器的直接子节点，**按稿子画的高度画，不许被压扁**。
 *
 * 这条修的是一个已经发生过、而且闸门量不到的真 bug：错题本上线的版本里，
 * 顶上的分段控件实际只有 8px 高、搜索框 0px（整条看不见），
 * 但页面「无报错、卡片数对、点击有变化」—— 三条断言全是绿的。
 *
 * 原理：内容区（[data-scroll="1"]，稿子给它写的是 flex:1 min-height:0）
 * 是纵向 flex 容器。子节点里凡是**没有 flex-shrink:0** 的，都要去吸收溢出量：
 * 卡片那种稿子自带 flex-shrink:0 的完好无损，而分段控件 / 搜索框这种没带的，
 * 就被压成一条缝 —— 溢出量越大压得越狠，所以「数据一多，顶上就扁了」。
 *
 * 为什么设计稿上看不出来：稿子画布是固定 393×852 的**一张照片**，
 * 内容刚好装得下，flex 没有任何溢出可分配。ardot 里也没有「这是个滚动容器」这个概念，
 * 所以这件事只能在这里落。**这条不改任何稿子画的值** —— 每个人的高度还是稿子写的高度，
 * 改的只是「谁去吸收溢出」：从「把上面的控件压扁」改成「让内容区滚」。
 *
 * 三种线上路由都量过：纠错管理和刷题页的直接子节点稿子本来就带了 flex-shrink:0，
 * 加这条前后逐个小节点的高度一模一样（见 tools/build/squash-probe.py）。
 */
[data-scroll="1"] > * { flex-shrink: 0; }

/* ⑨ 底部抽屉（题库详情的三张：重刷确认 / 练习设置 / 题库介绍）。
 *
 * 稿子那三帧画的是「底部容器（占满画布、justify-content:flex-end）+ 面板」，
 * 但 ardot 里没有「固定定位」这个概念，它记的是 `position:absolute;top:0;height:852px`
 * —— 852 是设计画布的高，真机上就是「一个 852 高的盒子按文档流排下去」，
 * 抽屉会跑到屏幕外面去。所以这里把它钉在视口上，跟遮罩（规则③）同一个道理。
 *
 * pointer-events 那一对是必须的：容器占满全屏，不加就等于在遮罩上面盖了一层
 * 透明玻璃 —— 点面板**外面**的空白处会落到容器上，遮罩收不到那一下，
 * 「点空白关掉抽屉」就失灵了。所以容器本身不收事件，只有它的子节点（面板）收。
 *
 * ⚠ 全站只有这三帧用这个图层名（题库详情那三张抽屉），已核对。
 */
[data-name="底部容器"] {
  position: fixed !important;
  inset: 0 !important;
  height: 100dvh !important;
  z-index: 9000;
  pointer-events: none;
}
[data-name="底部容器"] > * { pointer-events: auto; }

/* ⑩ 题库详情那张概览卡上的简介，最长两行。
 *
 * 稿子画的那行样文只有一句（「国家公务员考试行测专项题库，覆盖五大模块」），
 * 而真描述可以是几百字 —— 卡片会被撑开、把进度环和三格统计挤走。
 * 按 HIG 的规矩，正文不折叠到看不见，但卡片里要收敛：这里限两行 + 省略号，
 * **全文去哪儿看**由稿子上那枚「展开」回答（它开的是「题库介绍」那张抽屉，
 * 旧前端也是这一下）。
 *
 * 为什么不在稿子里改：这是**同一条数据的两种呈现**（卡上摘要 / 抽屉全文），
 * 属于代码阶段的事，跟解析卡那条（规则②）同一个理由。
 * 判据不是 maxLines —— ardot 那个字段的默认值就是 1，全稿都写着 1，不是设计意图。
 */
[data-clamp="2"] {
  display: -webkit-box !important;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ⑪ 练习记录那一页的四样代码件（稿子里都没有，都是控制器按数据现造的）。
 *
 * 为什么会有它们：那张稿子画的是「一次练习的战果」（已完成徽标 + 已答/答对/答错），
 * 而这一页的数据源是**每题一条**的 QuestionRecord，聚合数根本没有，
 * 题干和正确答案也不在记录里（详见 routes/records.js 文件头 ①②③④⑤）。
 * 于是：汇总那行多一枚「清空」（旧前端就在那儿）、卡片里多一格题干（从错题卡借的）
 * 和一行「你的答案 / 正确答案」、翻页时多一枚「加载更多」。
 *
 * 四样都用**设计系统自己的令牌**上色，不新造颜色；徽标那两条是把稿子那枚中性徽标
 * 的两态就地改色（--cl-success-50/700、--cl-danger-50/700），
 * 因为稿子里没有可以整枚换的绿/红徽标帧。 */

/* 汇总行：稿子画的是单独一行小字，这里把它包成一行，右边挂「清空」。
 *
 * ⚠ 那对 13 / -13 不是为了好看，是给「清空」腾**44px 热区**（HIG：可点区域不小于 44）
 *   又**不让它在版式里变高**。内边距把这一行的盒子撑到 45px，等量负外边距把它在
 *   文档流里占的高度还原成 19px —— 卡片的位置跟稿子画的一模一样。
 *
 *   为什么不用规则⑤那套「::after 撑热区」：绝对定位的伪元素会把这一行的
 *   **scrollHeight 顶到 32**，而闸门把「滚动容器的子节点比自己的内容矮」判成压扁。
 *   那会是一条**假红** —— 这一行没被压扁，是热区溢到盒子外面去了。
 *   （这是真的踩到的：第一次跑闸门就是 s=error 那条红在这儿。）
 *   改成内边距之后，热区是这个盒子**自己**的尺寸：量得准，也真的点得到。
 */
[data-role="汇总行"] {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  align-self: stretch;
  flex-shrink: 0;
  padding: 13px 0;
  margin: -13px 0;
}
/* 稿子那行小字自己的类是 align-self:stretch（它在稿子里是独生子），
   包成一行之后要跟着居中，不然会贴在行的顶上都不到底。 */
[data-role="汇总行"] > [data-name="汇总"] { align-self: center; }

/* 「清空」是个纯文字按钮 —— 稿子没有这一枚（9:38 只画了确认框，没画入口）。
   高度跟着那一行撑满（45px），整块都是可点的；文字本身仍然只有 19px 高。 */
[data-role="清空"] {
  align-self: stretch;
  display: flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--cl-danger-700);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  line-height: 19px;
  cursor: pointer;
}

/* 答案行：「你的答案」按对错上色，「正确答案」恒为绿。
   两行都是 12px —— 卡片的正文是题干，这两行是注解，不该跟题干抢。 */
[data-role="答案行"] {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-self: stretch;
  flex-shrink: 0;
  font-size: var(--fs-caption);
  line-height: 19px;
}
[data-role="答案行"] [data-r="mine"] { color: var(--cl-text-secondary); }
[data-role="答案行"] [data-r="mine"][data-ok="0"] { color: var(--cl-danger-700); }
[data-role="答案行"] [data-r="mine"][data-ok="1"] { color: var(--cl-success-700); }
[data-role="答案行"] [data-r="right"] { color: var(--cl-success-700); }

/* 记录卡上那枚徽标的两态。稿子挂的是 Component/Badge/Neutral（中性灰 + 样文「已完成」），
   改的是它的底色和字色两处 —— 结构与尺寸一个字不动。 */
[data-name="记录卡片"] [data-result="ok"] { background: var(--cl-success-50) !important; }
[data-name="记录卡片"] [data-result="ok"] > * { color: var(--cl-success-700); }
[data-name="记录卡片"] [data-result="no"] { background: var(--cl-danger-50) !important; }
[data-name="记录卡片"] [data-result="no"] > * { color: var(--cl-danger-700); }

/* ⑫ 刷题报告那一页卡头上的一行（稿子里没有这一层）。
 *
 * 为什么会有它：稿子 2:1731 画的是一张**统计看板**（分段控件 + 累计卡 + 四格 + 分布卡），
 * 而这一页要的是**报告列表** —— 稿子没有画「列表里的报告卡」。卡的壳、四格、按钮行、
 * 按钮、三种文字样式**全部取自稿子**（2:1656 的四格统计卡 / 按钮行 / 主按钮，
 * 11:315 的危险按钮），只有卡头这一行是代码摆的：左边题库名 + 章节·时间，
 * 右边正确率大数字 + 「正确率」。整件事的来龙去脉见 routes/reports.js 文件头。
 *
 * ⚠ 这里只写「谁在哪儿」，不写任何字号/字色/字重 —— 那三样是稿子给的，
 *   代码拿的是稿子那三个节点的 class（n-2-1748 / n-2-1662 / n-2-1661），
 *   所以字号改了这里不用动。 */
[data-role="报告卡头"] {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
  align-self: stretch;
  flex-shrink: 0;
}
[data-role="报告卡名"] {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  flex: 1 1 0;
  min-width: 0;
}
/* 题库名和「章节 · 时间」都只占一行，长了省略。
   题库名可以很长（真库里有「2024年国家公务员录用考试行政职业能力测验」这种），
   撑开会把右边那个正确率挤出卡片 —— 全文在题库详情那一页，不在这里塞。 */
[data-role="报告卡名"] > * {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 正确率那枚大数字的基线跟「正确率」三个字对齐 —— 大数字的行高是 41px，
   居中对齐的话小字会浮在半空。 */
[data-role="报告卡率"] {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  gap: var(--sp-1);
  flex-shrink: 0;
}

/* ⑬ 登录 / 注册 / 找回密码的输入位。
 *    跟 ③（填空）、⑦（搜索框）是同一件事：稿子画的是「占位」那个 div，
 *    真机上必须能真输入，所以控制器把它换成了真的 <input>，class 沿用稿子那一份。
 *    这里只补 div 不需要、input 才需要的几条（它默认不撑开、自带边框和 outline）。
 *    ⚠ 名字没用「作答输入」那一个：那个名字在刷题页是「答题」的意思，
 *      登录页借用它会让以后看代码的人以为这一页在答题。
 *    占位字的颜色单独补一条 —— 稿子那份 class 的 color 是给「还没输入时」用的
 *    （--cl-text-secondary），而输入之后要的是值该有的颜色（--cl-text-primary，
 *    见错误帧里「值」那一格）。<input> 自己分不出这两种，得分开写。 */
input[data-name="登录输入"] {
  flex: 1 1 0;
  min-width: 0;
  border: 0;
  background: transparent;
  outline: none;
  padding: 0;
  font: inherit;
  color: var(--cl-text-primary);
}
input[data-name="登录输入"]::placeholder {
  color: var(--cl-text-secondary);
  opacity: 1;   /* Firefox 默认给占位字加透明度，会把稿子的 secondary 又调淡一档 */
}

/* ⑭ 底部胶囊导航做成液态玻璃（用户点名要的，稿子没画过这一版）。
 *
 *    认锚用图层名 `[data-name^="TabBar/"] [data-name="胶囊"]`：TabBar 的三种变体
 *    分别叫 TabBar/首页选中 · /公告选中 · /我的选中，胶囊在三种里是同一颗。
 *    稿子那颗是**纯白**（--cl-bg-card）+ 一圈中性边 + 一道紫色外投影。
 *
 *    ★ 先说量过的事实，免得看代码的人以为这里在装样子 ——
 *      「模糊」这一半**在这一版版式上一点都看不见**。
 *      胶囊坐在 95px 的 TabBar 里、正常流，它后面只有页根那一块纯色
 *      （tools/build/probe-capsule-glass.py 量过：把 backdrop-filter 打开/关掉，
 *      胶囊**内部一个像素都不差**，差异只有 1px 圆角边和图标自己的抗锯齿 ——
 *      那是图层提升带来的。元素后面是纯色，模糊一块纯色还是那块纯色）。
 *      所以现在看得见的是玻璃的**另一半**：半透明 + 上缘高光 + 底部的厚度与投影。
 *      折射那一半要**内容从它后面过**才成立，而那要动位置 —— 那是被否掉的方案。
 *      留着 backdrop-filter 是因为它才是这个材质本身的定义：改天内容真从后面过
 *      （或者胶囊挪成悬浮），它当场就是对的，不用回来补。
 *
 *    ⚠ 半透明**没有**用 color-mix 去调 --op-mask：那个令牌生成时被塞了 px 单位
 *      （0.44999998807907104px），拿它做 alpha 会变成 0（全透明）。
 *      这里直接写 rgba，色相就是 --cl-bg-card 那个白，只是透到 72%。
 *    ⚠ 稿子原来那条 inset 1px 的边**留着**，没有换成纯白亮边 ——
 *      页面底是 #FAFAFF、胶囊是白，把边去掉这颗药丸在浅色页上就没有轮廓了。
 *      玻璃是**加在稿子之上**的一层，不是把稿子改掉。
 *    ⚠ 选中项那一格（导航项/首页 之类）**不动**：用户说的是「只把胶囊做成玻璃」，
 *      实心紫的选中块照旧。 */
[data-name^="TabBar/"] [data-name="胶囊"] {
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  box-shadow:
    inset 0 0 0 1px var(--cl-border-base),        /* 稿子原有的那圈边，不动 */
    inset 0 1px 0 0 rgba(255, 255, 255, 0.95),    /* 顶光：玻璃的口沿亮边 */
    inset 0 -1px 0 0 rgba(123, 97, 255, 0.10),    /* 底缘一点点品牌色，做出厚度 */
    0 6px 20px 0 rgba(123, 97, 255, 0.16),        /* 稿子原有的外投影，不动 */
    0 1px 2px 0 rgba(28, 27, 34, 0.04);           /* 贴地一层，压住边缘的虚 */
}
/* 真机上关掉「减少动态效果」的人，也就不必为这个材质付合成的钱 */
@media (prefers-reduced-transparency: reduce) {
  [data-name^="TabBar/"] [data-name="胶囊"] {
    background: var(--cl-bg-card);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* 调试条：跟稿子无关，纯粹给我核对用（?dev=1 才出现）。
   悬浮在顶部、不参与文档流，所以不会把稿子的版式挤歪。 */
.devbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 9998;
  display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  font: 11px/1.6 var(--ff, sans-serif);
  color: #eee;
  background: rgba(28, 27, 34, .9);
  padding: 4px 8px;
}
.devbar .sep { width: 1px; height: 14px; background: #55545e; margin: 0 4px; }
.devbar button {
  font: inherit; color: #cfc6ff; background: #2b2a33;
  border: 1px solid #47454f; border-radius: 6px; padding: 1px 6px; cursor: pointer;
}
.devbar button.on { background: var(--cl-primary-600, #7B61FF); color: #fff; border-color: transparent; }
