/* ============================================================================
 * site.css —— 公开面 · 组件层（**生产**，`/static/site.css`）
 *
 * 配套：`app/static/tokens.css`（**令牌层，先加载**）
 *      本文件**一个令牌都不定义** —— 只写组件。
 *      ⚠️ 令牌在这一层重新定义一次 = 两份会各自漂移的真理，而「哪份是对的」
 *         没人知道。`check_tokens.py` 判的是令牌层，看不见这里。
 *
 * ⚠️ 本文件是**唯一一份**：公开面静态样板（`_design/public/`）直接链它
 *    （`../../app/static/site.css`，与 tokens.css 同款做法），不另拷一份。
 *    另拷一份只有两个下场：改版式要改两处、迟早漂移，而**没有任何判据会发现两份不一样**。
 *    ⇒ 样板要做实验，加**覆盖层**（那正是 `brands.css` 的形态），别复制本文件。
 *
 * 规矩（来自《网站设计安排 v1》§1.1 / §3）：
 *   1. 零外链 —— 字体/图标/CDN 都会把平台品牌漏到代理站点上（C2）
 *   2. `--brand` 只作用于**三处**：主按钮底 / 品牌强调块 / 强调描边。
 *      其余一律走 `--accent` 与中性色 —— 品牌色撒得越开，对比度风险面越大
 *   ★ 由此推出一条**本层自定的硬规矩**：`--brand` **只做底色与描边，不做文字色**。
 *     理由：色板里有浅色品牌（`--preset-sky` / `--preset-amber`）配**深字**，
 *     这时 `--brand-700` 当文字压在浅底上不够 4.5 ——
 *     浅底上的品牌色文字只能拿 `--ink`（实测 amber 系 `#1F2933` 压 `#FFF8EC` 极高）。
 *     品牌感由**底色的品牌调**提供，不由字色提供。写文字色一律 `--ink` / `--ink-soft` / `--muted`。
 *     （同族的历史事故：金色 `#D4AF37` 白底当文字只有 2.10:1，是量 computed style 才发现的。）
 *   3. 正文基准 13.5px 是**量出来的**，不是"看着小"就调大
 *   4. 层级靠描边不靠阴影（商务场景的阴影一重就显廉价）
 *
 * 类名：本层定义 97 个，六页实际用到 **89** 个 —— 比《设计安排》§2.1 的 60–80 预算多 9 个。
 *   ⚠️ 这 9 个恰好就是 `.mock-*` 那一族（mock / mock-bar / mock-body / mock-btn /
 *      mock-code / mock-dot / mock-drop / mock-row / mock-tr）。它是用 CSS 画的
 *      「界面截图」占位 ⇒ 净 80 个，正好顶着预算上沿。
 *      留着这条记录，是因为"预算超了"和"预算超在那一族上"是两回事，
 *      而单看那个 89 是看不出来的。
 *
 *   ★ 这一族**不是**脚手架，进生产后**刻意留着**（P1 搬模板时定的事，见下）：
 *     它们出现在 index / bid / inspect 三页的营销段落里，是"产品长什么样"的示意。
 *     换成真 `<img>` 的代价是**每张图都要为每个品牌重出**（截图上带平台配色 = C2 直接破防），
 *     而 CSS 画的图**跟着 `--brand` 自动变色**。⇒ 白标场景下，CSS 画的示意图
 *     比位图更省事、也更难漏品牌。真接 API 截图时再回来还这笔债。
 *     ⚠️ 因此**不要**因为"这是 mock"就把这一族删掉 —— 生产模板里有 30 处在用它们
 *        （`grep -c 'mock-' app/web/templates/*.html`），删了页面会塌成一片空白，
 *        而**没有任何判据**会报红（几何判据量的是"有没有越界"，空白不越界）。
 * ==========================================================================*/


/* ---------------------------------------------------------------- 基础 */
*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; }
/* ⚠️ 竖排撑满 + main 吃余量 = **页脚永远贴在底边**。
   不加这两行的后果在 `auth.html` 上肉眼可见：内容只有 700px 高，
   页脚就停在 700px 处，底下空出来的一整块页底比页脚还显眼——
   自动化判据一条都不会响（没有越界、没有溢出、对比度全绿）。 */
body.site{
  margin:0; background:var(--bg); color:var(--ink); min-height:100vh;
  display:flex; flex-direction:column;
  font:var(--fs-base)/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI",
       "Microsoft YaHei","PingFang SC","Hiragino Sans GB",sans-serif;
  /* ★ T6-04：**任何一串没有空格的文字都不许撑破视口**。
     中文靠字间断行本来不会溢出，但页面上有大量**外部数据**：
     客服邮箱 / 订单号 / 品牌名 / 阶段码 —— 它们可以是 60~80 个字符的英文或数字，
     浏览器默认 `overflow-wrap:normal` 不会在词中间断，于是一串 464px 的邮箱
     在 390px 屏上直接把页面撑到 480px。
     `anywhere` 而非 `break-word`：前者同时参与 min-content 计算，
     弹性/网格子项才**真的挤得动**（后者只在排版时断，子项的最小宽度仍按整串算）。 */
  overflow-wrap:anywhere;
}
body.site > main{ flex:1 0 auto; }
h1,h2,h3,p,ul,ol,figure{ margin:0; }
a{ color:var(--accent); text-decoration:none; }
a:hover{ text-decoration:underline; }
img{ max-width:100%; display:block; }
.sr-only{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;
}
:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--r-xs); }


/* ---------------------------------------------------------------- 外壳 */
/* 内容列：1120 是设计定的读数，别"顺手"改成 1200 */
.wrap{ max-width:var(--w-wide); margin:0 auto; padding:0 var(--sp-6); }
.wrap.read{ max-width:var(--w-read); }
/* 阅读列：**左沿与页面基线对齐**，只把右边收窄。
   ⚠️ 它和 `.wrap.read` **不是一回事** —— 后者是 `margin:0 auto`（整块居中）。
      同一页里混用两者，那一个 section 会**整块平移**（1280 下差 100px），
      而几何判据一条都不响（不越界、不重叠、不溢出）。
      本次是看图才发现的：`inspect@1000` 的「常被问到的」比上面几个标题右移了 37px。
      `check_design.py` 的 L1 就是拿这条钉住的 —— 量的是 `.wrap` 的**盒左沿**，
      不是标题文字的左沿（文字左沿会被 `text-align:center` 带跑）。 */
.read-col{ max-width:var(--w-read); }

.head{
  position:sticky; top:0; z-index:20; background:var(--surface);
  border-bottom:1px solid var(--line);
}
.head-in{
  max-width:var(--w-wide); margin:0 auto; padding:0 var(--sp-6);
  height:var(--topbar-h); display:flex; align-items:center; gap:var(--sp-6);
  /* ⚠️⚠️ **不换行**是这里的硬规矩，不是"默认值恰好如此"：顶栏是 height 写死的
     单行条，内容一旦折行，子块会比顶栏还高、从它里面戳出来 —— 而 `.head-in`
     自己量不出来（它恒等于 58），所以这件事**只能量子块**（判据 H1）。 */
  flex-wrap:nowrap;
}
/* ⚠️ 品牌名长短是**代理数据**（代理完全可以叫"深圳市中建智远国际工程技术咨询
   有限责任公司"），所以弹性必须落在它身上：flex-shrink:1 + min-width:0 让它
   **可以被挤**，挤到装不下就在 `.brand-name` 上出省略号。
   这里原来写的是 `flex:0 0 auto`（不许挤），后果实测过（601px 档、10 字品牌名）：
   被挤的是**导航与按钮** —— 导航项折成"一行一个汉字"、高 77px，从 58px 的顶栏里
   戳出来，而**横向溢出还是 0** ⇒ 所有现成判据全绿，只有看图才看得见。
   `min-width:0` 不能省：名字上有 white-space:nowrap，它的自动最小尺寸就是
   整串文字宽，不显式归零的话这个块根本挤不动。 */
.brand{ display:flex; align-items:center; gap:var(--sp-2); flex:0 1 auto; min-width:0; }
.brand:hover{ text-decoration:none; }
/* 品牌标：**底色 + 配对 ink**，不做线条描出来的彩色图标。
   理由同文件头第 2 条 —— 浅色品牌画成线条压在白色页头上不够 3:1（非文本元素门限）。
   做成「底块」之后，`.btn-primary` 那一套配对规则原样适用，代理换色也换不出事故。
   真站点上这里换成代理上传的 logo；**没有就给文字品牌名**（§3.3 回落规则）。
   ⚠️ 真 logo 那一路（`<img>`）必须 `object-fit:contain`：盒子是**写死的 26×26**，
      不声明的话默认 `fill` 会把代理传的任意比例图**拉伸变形** —— 而这件事
      在尺寸上完全量不出来（盒子永远 26×26），只有 `object-fit` 看得见（判据 H3）。
      取 contain 不取 cover：宁可两侧留白，也不裁掉代理 logo 的内容。 */
.brand-logo{
  width:26px; height:26px; flex:0 0 auto; border-radius:var(--r-sm);
  background:var(--brand); color:var(--brand-ink);
  font-size:var(--fs-sm); font-weight:700; line-height:1;
  display:flex; align-items:center; justify-content:center;
}
img.brand-logo{ object-fit:contain; }
/* 名字过长 → 省略号：**不折行、不撑破顶栏**。⚠️ 这几条要成套写 ——
   少了 nowrap 会折行（顶栏就那么高），少了 overflow 看不见省略号，
   少了 min-width:0 挤不动（理由见 `.brand` 那段）。
   被省略的是**顶栏这一处**；完整品牌名仍在 <title> 与页脚（页脚是 wrap 布局，
   长名字多占一行，不影响别的）。 */
.brand-name{
  font-size:var(--fs-lg); font-weight:650; color:var(--ink); letter-spacing:.3px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0;
}
/* ⚠️ 不许被挤：导航项被压缩会折成"一行一个汉字"，而顶栏高是写死的（见 `.brand`）。 */
.nav{ display:flex; align-items:center; gap:var(--sp-6); margin-left:var(--sp-4); flex:0 0 auto; }
.nav a{ color:var(--ink-soft); font-size:var(--fs-base); padding:4px 0; border-bottom:2px solid transparent; }
.nav a:hover{ color:var(--ink); }
/* ⚠️ 「当前页」**不用品牌色写文字**，用品牌色画下划线 —— 见文件头第 2 条规矩。 */
.nav a[aria-current="page"]{ color:var(--ink); font-weight:640; border-bottom-color:var(--brand); }
/* ⚠️ 同上：两个按钮（`white-space:nowrap`）被挤会折成两行，而顶栏高是写死的。 */
/* ⚠️ T6-04：登录后的**显示名是用户数据**，可以是一整串没有空格的邮箱。
   原来这里是 `flex:0 0 auto`（不许挤）⇒ 390px 档上它与品牌名互不相让，
   整条顶栏被顶出视口。改成可挤之后，名字走省略号、按钮保持原尺寸：
   "退到看不见"的只能是名字，不能是「退出」这个动作。 */
.head-cta{ margin-left:auto; display:flex; align-items:center; gap:var(--sp-3); flex:0 1 auto; min-width:0; }
.head-cta .link-quiet{
  color:var(--ink-soft); min-width:0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.head-cta form{ flex:0 0 auto; }

.foot{
  margin-top:var(--sp-8); border-top:1px solid var(--line);
  background:var(--surface);
}
.foot-in{
  max-width:var(--w-wide); margin:0 auto; padding:var(--sp-6);
  display:flex; flex-wrap:wrap; gap:var(--sp-5) var(--sp-8);
  color:var(--muted); font-size:var(--fs-sm);
}
.foot-in a{ color:var(--muted); }
/* ⚠️ T6-04：客服串是**代理数据**，可以是一整串没有词间空格的邮箱。
   `margin-left:auto` 让它贴右；不显式给 `min-width:0` 时，flex 子项的最小宽度
   是整串的 max-content（实测 463.7px）⇒ 390px 档把整页撑到 480px。
   断行规则由 body 的 `overflow-wrap:anywhere` 继承，这里只负责"允许被挤"。 */
.foot-note{ margin-left:auto; min-width:0; max-width:100%; }


/* ---------------------------------------------------------------- 按钮 */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-2);
  height:38px; padding:0 var(--sp-5); border-radius:var(--r-md);
  border:1px solid transparent; font-size:var(--fs-md); font-weight:600;
  line-height:1; cursor:pointer; white-space:nowrap;
}
.btn:hover{ text-decoration:none; }
.btn-primary{ background:var(--brand); color:var(--brand-ink); }
.btn-primary:hover{ background:var(--brand-700); }
.btn-ghost{ background:var(--surface); color:var(--ink); border-color:var(--line-strong); }
.btn-ghost:hover{ background:var(--accent-50); border-color:var(--accent); color:var(--accent-on-soft); }
.btn-sm{ height:31px; padding:0 var(--sp-4); font-size:var(--fs-base); }
.btn-lg{ height:46px; padding:0 var(--sp-7); font-size:var(--fs-lg); }
.btn-row{ display:flex; gap:var(--sp-3); flex-wrap:wrap; align-items:center; }


/* ---------------------------------------------------------------- 首屏 */
.hero{ padding:var(--sp-8) 0 var(--sp-7); }
/* ⚠️ 这里原本有一行 `.hero .wrap{ max-width:var(--w-wide); }` —— 已删。
   它在五个页面上**是空转的**（那些页的 hero 里就是 `.wrap`，本来就 1120），
   唯一真正起作用的地方是 `help.html`：那一页整页是 `.wrap read`（920 的阅读列），
   而 `.hero .wrap` 与 `.wrap.read` **特异性相同、源序更后** ⇒ 静默把 hero 顶回 1120。
   症状是 help 页的 hero 比正文宽出 200px、左沿差 100px，而
   越界 / 重叠 / 溢出 / 对比度**一条都不响**（`check_design.py` 的 L1 抓的就是它）。
   一条"只在别处生效、且生效方式是破例"的规则，删掉比留着好。 */
.hero-eyebrow{
  display:inline-block; margin-bottom:var(--sp-4); padding:3px var(--sp-3);
  background:var(--brand-soft); color:var(--ink);
  border-radius:var(--r-full); font-size:var(--fs-sm); font-weight:600;
}
/* 38px 是 --fs-4xl，设计定的。中文标题别再往上堆 */
.hero-title{ font-size:var(--fs-4xl); line-height:1.25; font-weight:700; letter-spacing:-.2px; }
.hero-sub{ margin-top:var(--sp-5); font-size:var(--fs-lg); color:var(--ink-soft); max-width:var(--ch); }
.hero-actions{ margin-top:var(--sp-6); }
.hero-note{ margin-top:var(--sp-4); font-size:var(--fs-sm); color:var(--muted); }
.hero.centered{ text-align:center; }
.hero.centered .hero-sub{ margin-left:auto; margin-right:auto; }
.hero.centered .btn-row{ justify-content:center; }


/* ---------------------------------------------------------------- 区块 */
.section{ padding:var(--sp-7) 0; }
.section.alt{ background:var(--surface); border-top:1px solid var(--line); border-bottom:1px solid var(--line); }
.section-title{ font-size:var(--fs-2xl); font-weight:680; letter-spacing:-.1px; }
.section-sub{ margin-top:var(--sp-3); color:var(--ink-soft); max-width:var(--ch); }
.section-head{ margin-bottom:var(--sp-6); }

.grid{ display:grid; gap:var(--sp-5); }
.grid.c2{ grid-template-columns:repeat(2,minmax(0,1fr)); }
.grid.c3{ grid-template-columns:repeat(3,minmax(0,1fr)); }
/* ⚠️ 这里**没有** `.grid.c3` 的中间档 —— 这是量出来的，不是省事。
   原先写着 `@media (max-width:1000px){ .grid.c3{ 2 列 } }`，理由是一句想当然的
   「三列在窄屏挤，塌两列好读」。1000px 下逐页看图：`.grid.c3` 里**都是 3 张卡**，
   塌成 2 列 ⇒ 第 3 张独占一行、右边空出小半屏（定价页约 470×300 的空洞）。
   而**没有一条判据会响**：不越界、不重叠、不溢出，落差聚类因为第 3 张独自成组被跳过。
   接缝宽度的"什么都没有"是这一族最典型的形态（历史事故：右栏 268px 的洞）。
   ⇒ 3 列一路留到 820 再塌成一列。821px 时每张卡约 239px，实测放得下。 */
@media (max-width:820px){
  .grid.c2,.grid.c3{ grid-template-columns:minmax(0,1fr); }
  .wrap,.head-in,.foot-in{ padding-left:var(--sp-4); padding-right:var(--sp-4); }
  .hero-title{ font-size:var(--fs-3xl); }
  /* ⚠️ T6-04 复验（`_selfcheck_render` 的 601 档 + 21 字品牌名变体）：601–820
     这一段导航**是可见的**，而 `.nav` 与 `.head-cta` 都不许被挤（折行会戳破
     写死高度的顶栏）⇒ 能伸缩的只剩品牌名，它挤到 0 之后还差 20px。
     修法是把两处间距各收一档（`30 → 16`），⛔ 不是让导航折行、也不是
     `overflow-x:hidden`（那是藏，不是修）。601 那一档正是 `.nav` 刚从
     `display:none` 回来、又还没到 820 的接缝，只有它撑不住。 */
  .head-in{ gap:var(--sp-4); }
  .nav{ gap:var(--sp-4); }
  .head-cta{ gap:var(--sp-2); }
}
@media (max-width:600px){
  .head-in{ gap:var(--sp-3); }
  .nav{ display:none; }   /* 窄屏收起导航，入口在页脚与首屏按钮里都有 */
}
/* 390 档的内边距：`.form-card` 在 820 档仍是 42px 的左右内边距，
   358px 的可用宽度里被它吃掉 84px ⇒ 输入框只剩 274px。
   不是溢出（`min-width:0` 之后不会），但一行验证码已经挤到不能看。
   只在内边距**比内容还占地方**的宽度上收一档，桌面档一个字不动。 */
@media (max-width:480px){
  .form-card{ padding:var(--sp-6); }
  .section{ padding:var(--sp-6) 0; }
  .hero{ padding:var(--sp-7) 0 var(--sp-6); }
}


/* ---------------------------------------------------------------- 卡片 */
.card{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-lg); padding:var(--sp-6); box-shadow:var(--sh-1);
}
.card.tight{ padding:var(--sp-5); }
.card-h{ font-size:var(--fs-xl); font-weight:660; }
.card-sub{ margin-top:var(--sp-2); color:var(--ink-soft); }
.card-b{ margin-top:var(--sp-4); }
.card-edge{ border-left:3px solid var(--brand); }


/* ---------------------------------------------------------------- 三步 */
.steps{ display:grid; gap:var(--sp-5); grid-template-columns:repeat(3,minmax(0,1fr)); }
@media (max-width:820px){ .steps{ grid-template-columns:minmax(0,1fr); } }
.step{ display:flex; gap:var(--sp-4); }
.step-n{
  flex:0 0 auto; width:26px; height:26px; border-radius:var(--r-full);
  background:var(--brand-soft); color:var(--ink);
  font-size:var(--fs-base); font-weight:700;
  display:flex; align-items:center; justify-content:center;
}
.step-t{ font-size:var(--fs-md); font-weight:640; }
.step-d{ margin-top:var(--sp-1); color:var(--ink-soft); font-size:var(--fs-base); }


/* ---------------------------------------------------------------- 清单 */
.list{ list-style:none; padding:0; display:grid; gap:var(--sp-3); }
.list li{ position:relative; padding-left:var(--sp-6); color:var(--ink-soft); }
.list li::before{
  content:""; position:absolute; left:2px; top:.55em;
  width:9px; height:5px; border-left:2px solid var(--brand);
  border-bottom:2px solid var(--brand); transform:rotate(-45deg);
}
.list li b{ color:var(--ink); font-weight:640; }


/* ---------------------------------------------------------------- 截图占位 */
/* 样板上用 CSS 画的"界面截图"。真页面换成 <img> —— 这一块只是为了让
   版式关系看得见，别把它当成最终视觉。 */
.mock{
  background:var(--surface); border:1px solid var(--line-strong);
  border-radius:var(--r-md); overflow:hidden; box-shadow:var(--sh-2);
}
.mock-bar{
  height:26px; background:var(--surface-2); border-bottom:1px solid var(--line);
  display:flex; align-items:center; gap:6px; padding:0 var(--sp-3);
}
.mock-dot{ width:7px; height:7px; border-radius:var(--r-full); background:var(--line-strong); }
.mock-body{ padding:var(--sp-5); display:grid; gap:var(--sp-3); }
.mock-drop{
  border:1px dashed var(--line-strong); border-radius:var(--r-sm);
  padding:var(--sp-5); text-align:center; color:var(--muted); font-size:var(--fs-sm);
  background:var(--surface-2);
}
/* 假清单行：左边一个规则码，右边两行灰条。
   第一版这里只有五根灰条，1280px 下摊成一大片空盒子，**什么也没说**——
   「版式占位」和「看着像坏了」之间只差一个看得出结构的形状。 */
.mock-tr{ display:flex; gap:var(--sp-3); align-items:flex-start; }
.mock-tr > div{ flex:1 1 auto; display:grid; gap:6px; }
.mock-code{
  flex:0 0 auto; padding:2px 6px; border-radius:var(--r-xs);
  border:1px solid var(--line); background:var(--surface-3); color:var(--muted);
  font:700 var(--fs-xs)/1.5 ui-monospace,Consolas,"Courier New",monospace;
}
.mock-row{
  height:11px; border-radius:var(--r-xs); background:var(--surface-3);
}
.mock-row.w70{ width:70%; } .mock-row.w45{ width:45%; } .mock-row.w85{ width:85%; }
.mock-row.brand{ background:var(--brand-200); }
.mock-btn{
  justify-self:start; height:26px; padding:0 var(--sp-4); border-radius:var(--r-sm);
  background:var(--brand); color:var(--brand-ink);
  font-size:var(--fs-sm); font-weight:600; display:inline-flex; align-items:center;
}


/* ---------------------------------------------------------------- 定价 */
.price{ display:flex; flex-direction:column; }
.price.rec{ border-color:var(--brand); border-width:2px; position:relative; }
.badge{
  position:absolute; top:-11px; left:var(--sp-6);
  background:var(--brand); color:var(--brand-ink);
  font-size:var(--fs-xs); font-weight:700; letter-spacing:.5px;
  padding:3px var(--sp-3); border-radius:var(--r-full);
}
.price-amt{ font-size:var(--fs-3xl); font-weight:700; letter-spacing:-.5px; }
.price-unit{ font-size:var(--fs-base); font-weight:500; color:var(--muted); margin-left:var(--sp-1); }
.price-name{ font-size:var(--fs-md); font-weight:640; }
.price-desc{ margin-top:var(--sp-1); color:var(--muted); font-size:var(--fs-sm); }
.price-list{ margin-top:var(--sp-5); }
.price-act{ margin-top:auto; padding-top:var(--sp-5); }
.price.rec .price-act .btn{ width:100%; }


/* ---------------------------------------------------------------- 表单 */
.form-card{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg);
  padding:var(--sp-7); box-shadow:var(--sh-2); max-width:420px; margin:0 auto;
}
.field{ margin-bottom:var(--sp-5); }
.field label{ display:block; font-size:var(--fs-base); font-weight:600; margin-bottom:var(--sp-2); }
.input{
  width:100%; height:40px; padding:0 var(--sp-4);
  border:1px solid var(--line-strong); border-radius:var(--r-md);
  background:var(--surface); color:var(--ink); font-size:var(--fs-md);
  font-family:inherit;
}
.input::placeholder{ color:var(--muted-2); }
.input:focus{ outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-50); }
/* 验证码：数字之间拉开，方便一位一位念。
   ⚠️ 字距会**连 placeholder 一起拉**，于是「6 位数字」被摊成「6  位  数  字」，
   看着像坏了。占位符要单独把字距还回去（这条只有看图才发现）。 */
.input-code{ letter-spacing:6px; font-variant-numeric:tabular-nums; }
.input-code::placeholder{ letter-spacing:normal; }
.field-row{ display:flex; gap:var(--sp-3); }
/* 输入框的默认最小宽度是 `size` 个字符，flex 子项默认 `min-width:auto`
   ⇒ 窄屏（390）上"验证码 + 获取验证码"那一行会被输入框的固有宽度顶出去。
   `min-width:0` 才让它真的能缩，宽度仍由 `flex:1 1 auto` 分。 */
.field-row .input{ flex:1 1 auto; min-width:0; }
.hint{ margin-top:var(--sp-2); font-size:var(--fs-sm); color:var(--muted); }
.form-act{ margin-top:var(--sp-6); }
.form-act .btn{ width:100%; }
.form-foot{ margin-top:var(--sp-5); text-align:center; font-size:var(--fs-sm); color:var(--muted); }


/* ---------------------------------------------------------------- 提示条 */
/* 三态里的「错误态」：文案必须含「发生了什么 + 你该点哪」（C4 判据） */
.alert{
  display:flex; gap:var(--sp-3); padding:var(--sp-3) var(--sp-4);
  border-radius:var(--r-md); border:1px solid; font-size:var(--fs-base);
  /* 错误文案里会带**用户输入/外部数据的原文**（文件名、订单号），
     它们可以是一整串没有空格的字符。见 body.site 的 overflow-wrap 说明。 */
  min-width:0;
}
.alert > div{ min-width:0; }
.alert .alert-t{ font-weight:640; }
.alert .alert-d{ color:var(--ink-soft); margin-top:2px; }
.alert.err{  background:var(--err-bg);  border-color:var(--err-line);  }
.alert.err .alert-t{ color:var(--err); }
.alert.ok{   background:var(--ok-bg);   border-color:var(--ok-line);   }
.alert.ok .alert-t{ color:var(--ok); }
.alert.info{ background:var(--info-bg); border-color:var(--info-line); }
.alert.info .alert-t{ color:var(--info); }

.pill{
  display:inline-block; padding:2px var(--sp-3); border-radius:var(--r-full);
  font-size:var(--fs-xs); font-weight:600; background:var(--surface-3); color:var(--muted);
}
.pill.ok{ background:var(--ok-bg); color:var(--ok); }
.pill.brand{ background:var(--brand-soft); color:var(--ink); }
.pill.err{ background:var(--err-bg); color:var(--err); }
.pill.warn{ background:var(--warn-bg); color:var(--warn); }


/* ---------------------------------------------------------------- 问答 */
.faq{ display:grid; gap:var(--sp-4); }
.qa{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg); padding:var(--sp-5); }
.qa-q{ font-size:var(--fs-md); font-weight:640; }
.qa-a{ margin-top:var(--sp-3); color:var(--ink-soft); }
.qa-a + .qa-a{ margin-top:var(--sp-2); }
.term{
  border-left:3px solid var(--brand); padding:var(--sp-2) 0 var(--sp-2) var(--sp-4);
  background:var(--surface-2); border-radius:0 var(--r-sm) var(--r-sm) 0;
}
.term dt{ font-weight:660; }
.term dd{ margin:var(--sp-1) 0 0; color:var(--ink-soft); }


/* ---------------------------------------------------------------- 其它 */
.dim{ color:var(--muted); font-size:var(--fs-sm); }
.nowrap{ white-space:nowrap; }
.mt-4{ margin-top:var(--sp-4); }
.mt-5{ margin-top:var(--sp-5); }
.mt-6{ margin-top:var(--sp-6); }
.center{ text-align:center; }
.split{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:var(--sp-7); align-items:center; }
@media (max-width:820px){ .split{ grid-template-columns:minmax(0,1fr); gap:var(--sp-6); } }


/* ---------------------------------------------------------------- 兜底页 */
/* `holding.html`（T1-07）：整屏垂直居中。
   ⚠️ 为什么需要单独一条：`.hero` 的 padding 是给**内容页**定的（那些页
      上下都有别的区块），单独停在兜底页上就是"三行字贴在顶边、底下空一大片" ——
      而**几何 / 对比度 / 溢出判据一条都不会响**（它们量的是越不越界，
      不是"平不平衡"）。这一条是**看图**才发现的（`_shots` 那三张）。
   ⚠️ 类名只挂在兜底页上，六页内容页不受影响（`base.html` 的 `<main>` 没有类）。 */
.holding{ min-height:100vh; display:grid; place-items:center; }
.holding .hero{ padding:var(--sp-8) 0; }
