/* ==========================================================================
   批次35 · 表单弹窗排版样式（前缀 b35f-）
   文件: public/b35-forms.css    自包含, 无 JS 依赖, 无 @import / 无外部字体图片

   【这个文件是干什么的】
     为「添加单码」「批量生成单码」「卡密模板编辑」三个弹窗表单统一排版:
     两列字段网格、字段标签/控件/单位/说明小字、顶部信息提示条、区块小标题、
     上下两块之间的细分隔线, 以及下半块(卡号生成设置)的「收起 / 展开」。
     宿主: public/app.js 的 modal({title, bodyHtml, footerHtml, wide:true})
     —— bodyHtml 落进 .modal > .body, 按钮行由 .modal > footer 提供, 本文件不管页脚。

   【契约类名清单（冻结, 不得改名; HTML 由另一代理按此编写）】
     .b35f           表单根容器(整体字号/行距/上下留白)
     .b35f-grid      两列网格(<=900px 降为单列)
     .b35f-row       一个「标签 + 控件」单元(标签在上、控件在下)
     .b35f-row.full  横跨两列(用于 textarea / 备注 / 描述)
     .b35f-lb        字段标签文字(内部可含 .req 红星与 .b35f-q 问号)
     .b35f-q         标签后的「?」浅灰小圆圈
     .b35f-help      字段下方更紧凑的灰色说明小字
     .b35f-unit      输入框右侧的单位文字块(图3 的「元」「天」)
     .b35f-inline    一行内放多个控件(如 数字输入 + 单位)
     .b35f-note      顶部信息提示条(图2)
     .b35f-sec       区块小标题(图1/图3 的「单码自定义参数」「备注」)
     .b35f-divider   图3 上下两块之间的细分隔线
     .b35f-lower     图3 下半块(卡号生成设置)容器
     .b35f-collapsed 加在 .b35f-lower 或其父层上 → 下半块收起
     .b35f-toggle    居中的「收起 ^」按钮
     .b35f-ro        只读/派生字段外观(disabled 的输入框或下拉)
     .b35f-hint-ph   「请先选择软件」式引导占位
     .b35f-host      (可选)加在弹窗宿主上, 做宽度微调

   【本文件的自我约束】
     · 选择器全部限定在 .b35f / .b35f-host 命名空间内, 绝不外溢到宿主应用;
       不使用任何裸元素选择器。
     · .inp / .sel / .txa / .req / .help / .btn 等既有通用类只作为后代「复用」,
       从不在本文件里单独重定义(仅允许在 .b35f 内部做必要的局部修正)。
     · 不新增 CSS 变量: 只用 style.css :root 已有的设计变量
       (--radius / --border / --border-2 / --fg / --fg-dim / --fg-dim2 /
        --blue / --blue-soft), 变量未覆盖处写字面量。
     · 不用 !important; 过渡统一 .14s; 无预处理器语法。
   ========================================================================== */

/* --------------------------------------------------------------------------
   图1/2/3 公共: 表单根容器 —— 统一字号、行距、正文色与上下留白
   布局口径: 每个 .b35f-row 内「标签在上、控件在下」(与宿主 .frow > label.lb 的
   既有习惯一致), 这样行内无论再追加 .b35f-help / .b35f-inline / 提示文字,
   都会自然向下堆叠, 不会像「左标签右控件」的固定列网格那样被挤到错误的格子里。
   -------------------------------------------------------------------------- */
.b35f {
  font-size: 13px;
  line-height: 1.5;
  color: var(--fg);
}

/* 首元素不带上外边距, 留白交给 .modal > .body 的 18px 内边距 */
.b35f > .b35f-note:first-child,
.b35f > .b35f-sec:first-child,
.b35f > .b35f-grid:first-child,
.b35f > .b35f-lower:first-child {
  margin-top: 0;
}

/* --------------------------------------------------------------------------
   图1/2/3 公共: 两列字段网格
   列间距 18px / 行间距 14px(字段纵向间距保持在 14-16px 口径内);
   窄屏(<=900px)降为单列, 见文件末尾的媒体查询。
   -------------------------------------------------------------------------- */
.b35f-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px 18px;
  align-items: start;
}

/* --------------------------------------------------------------------------
   图1/2/3 公共: 一个「标签 + 控件」单元
   min-width:0 是必需的: 网格项默认 min-width:auto, 而 .inp 是 width:100%,
   不加会在窄列里把网格撑破。
   -------------------------------------------------------------------------- */
.b35f-row {
  display: flex;
  flex-direction: column;
  gap: 6px;          /* 标签到控件 6px */
  min-width: 0;
}

/* 图1「备注」/ 图3「描述」: 整行跨两列 */
.b35f-row.full {
  grid-column: 1 / -1;
}

/* --------------------------------------------------------------------------
   图1/2/3 公共: 字段标签
   .req(红星)沿用宿主样式, 这里只把它与文字的间距收窄(标签本身已用 gap 排版)。
   -------------------------------------------------------------------------- */
.b35f-lb {
  display: flex;
  align-items: center;
  gap: 5px;
  min-height: 18px;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--fg);
}

.b35f-lb .req {
  margin-left: 0;
}

/* --------------------------------------------------------------------------
   图1: 「登录设备数限制(卡)」后面那个「?」小圆图标
   不可点, 但一眼能看懂是「说明」: 浅灰小圆圈 + 灰色问号。
   -------------------------------------------------------------------------- */
.b35f-q {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border: 1px solid var(--border-2);
  border-radius: 50%;
  background: #f7f9fc;
  color: var(--fg-dim2);
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0;
  cursor: help;
  user-select: none;
}

/* --------------------------------------------------------------------------
   图1/图3 公共: 字段下方的灰色说明小字(比宿主 .frow .help 更紧凑)
   纵向间距由 .b35f-row 的 6px gap 提供, 所以这里不再加 margin-top。
   -------------------------------------------------------------------------- */
.b35f-help {
  font-size: 12px;
  line-height: 1.55;
  color: var(--fg-dim);
}

/* --------------------------------------------------------------------------
   图3: 输入框右侧的单位文字块(「元」「天」)
   基础形态是「独立小色块」: 与 .inp/.sel 同为 36px 高, 靠 .b35f-inline 的
   align-items:center 与之并排对齐; 不依赖「紧贴拼接」, 所以哪怕 HTML 里
   单位块没有紧跟在输入框后面, 外观也不会坏。
   -------------------------------------------------------------------------- */
.b35f-unit {
  flex: 0 0 auto;
  align-self: center;
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #f7f9fc;
  color: var(--fg-dim);
  font-size: 12.5px;
  line-height: 1;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   图3: 一行里放多个控件(数字输入 + 单位 / 区间两个输入框)
   .inp 在宿主里是 width:100%, 进了 flex 行必须交回宽度控制权, 否则不收缩。
   -------------------------------------------------------------------------- */
.b35f-inline {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.b35f-inline > .inp,
.b35f-inline > .sel {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
}

/* --------------------------------------------------------------------------
   批次47 需求⑤ + 批次48 需求①: 「归属分类」= 主分类 + 子分类(必要时再加一个三分类)的联动下拉;
   批次48 起它与「计费类型」**同处一行** —— 一行三格: 主分类 / 子分类 / 计费类型。
     · 行仍是既有的 .b35f-row.full(跨满两列网格), 不新增网格类;
     · 容器由批次47 的 flex(两个 .sel 各 flex:1 1 0 等分)改成**固定 3 列网格**:
         - minmax(0,1fr) ⇒ 三格严格等宽, 且**允许收缩到 0**: select 的固有宽度不会把网格
           撑破, 整行因此永远不出现横向滚动;
         - 固定 3 列(而不是 flex 的"按子项个数等分") ⇒ #gCategorySub2 以后真被放开显示时
           自己占第 4 格换行, **不会**把「计费类型」挤成 1/4 宽;
         - gap 8px 与 .b35f-inline 同一口径。
     · 每格 = .b35f-cats-col(标签 + 控件 的纵向单元), 三格各有自己的小标签。
   -------------------------------------------------------------------------- */
.b35f-cats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  align-items: start;
  min-width: 0;
}

.b35f-cats-col {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

/* .sel/.inp 在宿主里是 width:100%(style.css), 进了网格列必须交回宽度控制权,
   否则控件在窄列里不收缩、把整行撑出横向滚动。 */
.b35f-cats-col > .sel,
.b35f-cats-col > .inp {
  width: 100%;
  min-width: 0;
}

/* 三格各自的小标签: 比这一行的主标签(.b35f-lb「归属分类*」)小一号、颜色更淡 */
.b35f-sublb {
  display: block;
  min-height: 16px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--fg-dim);
}

/* --------------------------------------------------------------------------
   图2: 顶部「批量制卡任务将在后台执行……」信息提示条
   浅蓝底 + 左侧蓝色竖条 + 略深的蓝字。
   刻意用普通块流(而非 flex+gap): 提示文案通常是一整句话, 中间可能夹 <b> 或
   .tag; 若做成 flex 容器, 裸文本与行内元素会各自成为匿名 flex 项, gap 会在句中
   凭空撑出空隙。块流则怎么夹行内元素都正常。
   -------------------------------------------------------------------------- */
.b35f-note {
  display: block;
  margin: 0 0 14px;
  padding: 10px 13px;
  border: 1px solid #c7ddff;
  border-left: 3px solid var(--blue);
  border-radius: var(--radius);
  background: var(--blue-soft);
  color: #1e40af;
  font-size: 12.5px;
  line-height: 1.75;
}

/* --------------------------------------------------------------------------
   图1「单码自定义参数」/ 图3「卡号生成设置」等区块小标题
   左对齐、比正文略重、上方留白 16px。
   -------------------------------------------------------------------------- */
.b35f-sec {
  margin: 16px 0 10px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--fg);
}

.b35f-sec:last-child {
  margin-bottom: 0;
}

/* 下半块里的标题紧跟分隔线, 不再重复留白 */
.b35f-lower > .b35f-sec:first-child {
  margin-top: 0;
}

/* --------------------------------------------------------------------------
   图3: 上半块(名称/面值/售价/描述)与下半块(卡号生成设置)之间的细分隔线
   可写成 <hr class="b35f-divider"> 或 <div class="b35f-divider">, 两种都成立。
   -------------------------------------------------------------------------- */
.b35f-divider {
  height: 1px;
  margin: 14px 0;
  border: 0;
  background: var(--border);
}

/* --------------------------------------------------------------------------
   图3: 下半块(卡号生成设置)容器
   本身只是个普通包裹层, 纵向间距由上面的 .b35f-divider 提供。
   -------------------------------------------------------------------------- */
.b35f-lower {
  display: block;
  min-width: 0;
}

/* --------------------------------------------------------------------------
   图3: 下半块收起 —— 自洽实现
   ① 推荐把 .b35f-collapsed 加在「同时包住 .b35f-divider 与 .b35f-lower」的
      那层(即 .b35f-lower 的父层): 下面两条 > 规则一起生效, 下半块与分隔线
      同时 `display:none`(瞬时收起, 无高度动画, 不抖动)。
   ② 若 .b35f-collapsed 直接加在 .b35f-lower 自身, 下半块同样收起;
      但分隔线是它的「前一个兄弟」, 纯 CSS 选不到前兄弟, 故用 :has() 从外层
      反向命中。不支持 :has() 的浏览器(Chrome<105)只会残留那条 1px 分隔线,
      下半块本身仍会正确收起 —— 属可接受的降级。
   -------------------------------------------------------------------------- */
.b35f .b35f-collapsed > .b35f-divider,
.b35f .b35f-collapsed > .b35f-lower {
  display: none;
}

.b35f .b35f-lower.b35f-collapsed {
  display: none;
}

.b35f:has(.b35f-lower.b35f-collapsed) .b35f-divider {
  display: none;
}

/* --------------------------------------------------------------------------
   图3: 居中的「收起 ^」按钮
   无边框、无底色, 平时只是不显眼的灰字, hover 才变蓝。
   箭头用 ::after 画(右边框 + 下边框组成的角): 默认旋转 -135° 朝上(^);
   带 .is-open(下半块已收起)时旋转 45° 朝下(v)。
   居中方式: width:max-content + margin:auto。若浏览器不支持 max-content,
   width 退回 auto(整行宽), 但 justify-content:center 仍让文字居中 —— 等价降级。
   -------------------------------------------------------------------------- */
.b35f-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: max-content;
  max-width: 100%;
  margin: 12px auto 0;
  padding: 4px 12px;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--fg-dim);
  font-size: 12.5px;
  line-height: 1.4;
  cursor: pointer;
  user-select: none;
  transition: color .14s, background-color .14s;
}

.b35f-toggle::after {
  content: "";
  width: 6px;
  height: 6px;
  margin-bottom: 3px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-135deg);
  transition: transform .14s, margin .14s;
}

.b35f-toggle:hover {
  color: var(--blue);
  background: var(--blue-soft);
}

.b35f-toggle.is-open {
  color: var(--blue);
}

.b35f-toggle.is-open::after {
  margin-bottom: 0;
  margin-top: 3px;
  transform: rotate(45deg);
}

/* 键盘可达性: 只在键盘聚焦时显示焦点环 */
.b35f-toggle:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 1px;
}

/* --------------------------------------------------------------------------
   图1: 只读 / 派生字段(这一项由其它选择决定, 不能手填)
   宿主 style.css 里没有 .inp:disabled 规则, 所以禁用态完全由这里表达:
   灰底 + 浅描边 + 次要文字色 + not-allowed, 既要「明显不可编辑」, 又要
   「内容看得清」, 不能做成像坏掉了(不用 opacity 压暗, 不用虚线)。
   两种写法都支持: 类直接加在控件上, 或加在包裹控件的那层上。
   -------------------------------------------------------------------------- */
.b35f .b35f-ro:disabled,
.b35f .b35f-ro[readonly] {
  border-color: var(--border);
  background: #f4f6fa;
  color: var(--fg-dim);
  cursor: not-allowed;
}

.b35f .b35f-ro .inp:disabled,
.b35f .b35f-ro .sel:disabled,
.b35f .b35f-ro .inp[readonly],
.b35f .b35f-ro .sel[readonly] {
  border-color: var(--border);
  background: #f4f6fa;
  color: var(--fg-dim);
  cursor: not-allowed;
}

/* 只读字段里的 placeholder 也要看得清(比正常 placeholder 深一档) */
.b35f .b35f-ro .inp:disabled::placeholder,
.b35f .b35f-ro .inp[readonly]::placeholder {
  color: var(--fg-dim2);
}

/* --------------------------------------------------------------------------
   图1: 「请先选择软件」式引导占位 —— 字段还没到能填的时候
   做成一个「空槽」: 与 .inp 同高 36px 的浅底虚线框 + 次要灰字,
   一眼能看出「这里将来会有内容, 但此刻还不能填」。
   -------------------------------------------------------------------------- */
.b35f-hint-ph {
  display: flex;
  align-items: center;
  width: 100%;
  min-width: 0;
  height: 36px;
  padding: 0 11px;
  border: 1px dashed var(--border-2);
  border-radius: var(--radius);
  background: #fbfcfe;
  color: var(--fg-dim2);
  font-size: 12.5px;
  line-height: 1.4;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --------------------------------------------------------------------------
   (可选)弹窗宿主宽度微调
   宿主默认 .modal.wide{max-width:1020px}; 双列表单若想再宽一点,
   给 #modalHost 加上 .b35f-host 即可 —— 不加就完全不生效, 不影响其它弹窗。
   -------------------------------------------------------------------------- */
.b35f-host .modal.wide {
  max-width: 1080px;
}

/* --------------------------------------------------------------------------
   窄屏(<=900px): 两列降为单列(见 .b35f-grid 的契约要求)
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .b35f-grid {
    grid-template-columns: 1fr;
  }
}

/* --------------------------------------------------------------------------
   批次48 需求①: 「归属分类 + 计费类型」那一行的三格在窄窗口里会被挤得没法用
   (≈180px 一格), 所以与 .b35f-grid 同一口径降为**单列**竖排 ——
   宁可竖着排, 也绝不出现横向滚动、更不让控件互相挤压变形。
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {
  .b35f-cats {
    grid-template-columns: 1fr;
  }
}

/* --------------------------------------------------------------------------
   批次48 需求④:「继续添加」把新一批卡号**追加**在结果区(不覆盖上一批) ——
   各批之间给一条浅虚线, 一眼看清"结果区里有 2 批"。
   -------------------------------------------------------------------------- */
.gen-batch + .gen-batch {
  margin-top: 14px;
  padding-top: 4px;
  border-top: 1px dashed var(--border);
}

/* --------------------------------------------------------------------------
   ★ 批次49 需求: 「分类管理」右栏操作格从 5 个按钮变 6 个 —— 删掉「＋ 下级」, 原位补
   「推广配置」「教程配置」两个占位按钮(只弹 toast, 无功能)。
   背景(实测): 6 个 .btn.sm 按默认内边距(0 10px / 12.5px 字)横排约 471px, 同表其余列
   合计约 261px ⇒ 表格最少要 ~732px; 而视口 1200~1280px 时 .tblwrap 可分配宽度只有
   608~688px ⇒ 会冒出**格内横向滚动条**(5 个按钮时 1200px 就已经差 44px)。
   做法(只影响这一个格子, 不碰全局 .btn / .tbl / .nowrap):
     · 本格按钮收紧内边距与字号: 0 6px / 12px ⇒ 每个约 62px, 6 个合计约 372px;
     · 本格允许换行(覆盖 style.css 里 .nowrap 的 white-space:nowrap)。**七~十档视宽实测**
       (见 _review-b49/b49-ui.log 与 b49-ui3.log):
         1600 / 1500 / 1440px → 操作格 430px, 6 个**排成一行**(行高 56px);
         1400 / 1366px → 405.7 / 383.8px, 折成 5+1(行高 87px);
         1300 / 1280px → 341.3 / 328.5px, 折成 4+2;
         1200 / 1150px → 277 / 244.8px, 折成 3+3; 1024px → 318.2px, 折成 4+2。
         每一档都是 scrollWidth == clientWidth(**没有**格内横向滚动条), 按钮文字一个字都不截断、
         不省略, 也没有文档级横向滚动。折行只是把那一行的行高从 56px 抬到 87px, 不压别的列。
         想让 1280px 也保住一行, 单个按钮得压到 ~50px(≈10~11px 中文字号), 会小到看不清 ——
         按需求「宽屏一行 / 窄屏允许换行或收缩、绝不出格内横向滚动条」的约定, 这里选择换行。
     · biz.js 里那一列表头 width 从 340px 改成 430px: 是给自动表格布局的"想要多宽"提示,
       约等于 6 个收紧后的按钮宽度, 所以宽屏不会莫名其妙折行。
   -------------------------------------------------------------------------- */
td.ccat-ops {
  white-space: normal;
  padding-left: 8px;
  padding-right: 8px;
}
td.ccat-ops > .btn.sm {
  padding: 0 6px;
  font-size: 12px;
  margin: 2px 0;
  vertical-align: middle;
}

/* --------------------------------------------------------------------------
   ★ 批次50 需求⑬: 「部分输入框过长」—— 把原来独占一整行的一堆短字段排成
   「一行 3 个 / 一行 2 个」, 缩短表单纵向长度。
   口径(与既有 .b35f-cats 同一思路, 但断点按"侧边栏 236px 固定"重算过):
     · 三列 .b35f-cols3: 视口 <=1120px 降两列, <=900px 降单列;
     · 两列 .b35f-cols2: 视口 <=900px 降单列;
     · 视口 1120px 时三列每列实际只有 ~284px, 所以 3→2 取 1120 而不是 760。
   列宽 minmax(0,1fr) ⇒ 允许收缩到 0: select/input 的固有宽度不会把网格撑破,
   整行因此永远不出现横向滚动。每格沿用既有 .b35f-cats-col(标签 + 控件的纵向单元),
   小标签沿用 .b35f-sublb。
   ⚠ 本块必须留在文件**最后**: 上面的 @media(max-width:760px) 等规则与它同优先级,
   被排在后面的规则覆盖, 所以基础规则在前、自己的媒体查询紧随其后。
   -------------------------------------------------------------------------- */
.b35f-cols3,
.b35f-cols2 {
  display: grid;
  gap: 8px 14px;
  align-items: start;
  min-width: 0;
}

.b35f-cols3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.b35f-cols2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* 网格列里的控件交回宽度控制权(style.css 里 .inp/.sel 是 width:100%) */
.b35f-cols3 > .b35f-cats-col,
.b35f-cols2 > .b35f-cats-col {
  min-width: 0;
}

.b35f-cols3 .b35f-cats-col > .inp,
.b35f-cols3 .b35f-cats-col > .sel,
.b35f-cols2 .b35f-cats-col > .inp,
.b35f-cols2 .b35f-cats-col > .sel {
  width: 100%;
  min-width: 0;
}

/* 三/两列网格里需要横跨整行的项(例如 textarea 备注) */
.b35f-span-all {
  grid-column: 1 / -1;
  min-width: 0;
}

@media (max-width: 1120px) {
  .b35f-cols3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 900px) {
  .b35f-cols3,
  .b35f-cols2 {
    grid-template-columns: 1fr;
  }
}
