/* ==========================================================================
   产品溯源查询页 · verify.css
   依赖 style.css 中的设计令牌（色彩 / 圆角 / 阴影 / 字体 / 容器 / 按钮）
   只新增本页专属类，不覆盖 index.html 的任何既有规则
   编写方式：移动优先，min-width 断点 640 / 768 / 1024
   ========================================================================== */

/* ---------- 1. 本页局部令牌 ---------- */
body.page-verify {
  --ok-ink:    #153A2B;              /* 正品 · 主文字  在 ok-bg 上 ≈ 11.6:1 */
  --ok-ink-2:  #2A6248;              /* 正品 · 副文字  ≈ 6.4:1 */
  --ok-bg:     #EDF3EE;
  --ok-line:   rgba(30, 77, 57, .20);

  --bad-ink:   #7A2C24;              /* 异常 · 主文字  在 bad-bg 上 ≈ 8.2:1 */
  --bad-ink-2: #9C4034;              /* 异常 · 副文字 */
  --bad-bg:    #FBEDEA;
  --bad-line:  rgba(122, 44, 36, .20);

  --bad-solid: #B4453A;              /* 异常 · 实心圆底，白字 ≈ 5.2:1 */

  /* 认证金：只在「这是正品」这一层意思上用，全页出现不超过三处。
     用深版 #7A5F2A 而不是品牌暖金 #C6A97F —— 后者在 ok-bg 上只有 2.1:1，
     做正文色读不出来；#7A5F2A 在 ok-bg 上 ≈ 5.3:1，可读且仍是金调。
     #C6A97F 只用于 3px 装饰色带的收尾，不承载任何文字。 */
  --ok-gold:   #7A5F2A;
  --ok-gold-l: #C6A97F;

  background: var(--paper);
}

/* ---------- 2. 页面骨架 ---------- */
.qv-main {
  padding-top: 104px;                              /* 固定头部 80 + 呼吸 */
  padding-bottom: clamp(64px, 9vw, 120px);
}
@media (min-width: 768px) {
  .qv-main { padding-top: 148px; }
}

/* 本页正文用窄栏聚焦（880），但头部页脚仍走全站 1320 栅格。
   写成 .container.qv-shell 而不是 .qv-shell：两者与 .container 同为单类选择器，
   只靠引入顺序取胜太脆，加一层提高权重更稳。 */
.container.qv-shell { max-width: 880px; }

/* ---------- 3. 页头区 ---------- */
.qv-head { margin-bottom: clamp(26px, 4vw, 40px); }

.qv-title {
  font-size: clamp(30px, 8vw, 44px);
  line-height: 1.16;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--ink);
}
.qv-lead {
  margin-top: 16px;
  max-width: 58ch;
  font-size: 15px;
  line-height: 1.9;
  color: var(--ink-3);
}
@media (min-width: 768px) {
  .qv-lead { font-size: 16.5px; }
}

/* ---------- 4. 状态舞台 ---------- */
.qv-stage > * { display: none; }

.qv-stage[data-state="idle"]    .qv-ask,
.qv-stage[data-state="bad"]     .qv-ask,
.qv-stage[data-state="ok"]      .qv-out,
.qv-stage[data-state="bad"]     .qv-out { display: block; }

/* pending 单独列，因为它是 flex（下面几行）—— 早先这里也写着 display:block，
   被下一行同特异性覆盖，是一句永远不生效的死声明。留着只会让人以为它有作用。 */
.qv-stage[data-state="pending"] .qv-pending { display: flex; }
.qv-stage[data-state="bad"] .qv-ask { margin-top: 22px; }

/* 面板通用外观 */
.qv-panel {
  background: var(--white);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-md);
}

/* ---------- 5. 查询表单 ---------- */
.qv-ask { padding: clamp(24px, 5vw, 40px); }

.qv-ask__title {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--ink);
}
.qv-ask__desc {
  margin-top: 8px;
  font-size: 14px;
  line-height: 1.85;
  color: var(--ink-3);
}

.qv-form { margin-top: 22px; }

.qv-field { display: grid; gap: 12px; }

.qv-input {
  width: 100%;
  min-width: 0;
  padding: 15px 20px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 16px;                 /* 必须 ≥16px：低于此值 iOS 聚焦时会自动放大页面 */
  letter-spacing: .14em;
  text-transform: uppercase;       /* 批次号统一大写，避免用户大小写混输 */
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease),
              background var(--dur) var(--ease);
}
.qv-input::placeholder {
  font-family: var(--sans);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-3);
  opacity: .8;                     /* 再低就掉到 4:1 以下，示例码会看不清 */
}
.qv-input:hover { border-color: rgba(18, 23, 21, .18); }
.qv-input:focus {
  outline: none;
  background: var(--white);
  border-color: var(--moss-500);
  box-shadow: 0 0 0 4px rgba(76, 124, 96, .14);
}
.qv-input[aria-invalid="true"] {
  border-color: var(--bad-solid);
  box-shadow: 0 0 0 4px rgba(180, 69, 58, .12);
}

.qv-field .btn { width: 100%; }

.qv-hint {
  margin-top: 14px;
  font-size: 13px;
  line-height: 1.8;
  color: var(--ink-3);
}
.qv-hint b { color: var(--ink-2); font-weight: 600; }

.qv-error {
  margin-top: 12px;
  font-size: 13px;
  line-height: 1.7;
  color: var(--bad-ink);
  display: none;
}
.qv-error.is-on { display: block; }

@media (min-width: 640px) {
  .qv-field { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
  .qv-field .btn { width: auto; padding-inline: 34px; }
}

/* ---------- 6. 查询中 ---------- */
.qv-pending {
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 64px 24px;
  font-size: 14px;
  color: var(--ink-3);
}
.qv-spinner {
  width: 18px;
  height: 18px;
  flex: none;
  border-radius: 50%;
  border: 2px solid rgba(76, 124, 96, .20);
  border-top-color: var(--forest-700);
  animation: qvSpin .8s linear infinite;
}
@keyframes qvSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .qv-spinner { animation-duration: 2.4s; }
}

/* ---------- 6·5 英文微标签（全页统一语言） ----------
   中文是主、英文是影：英文只在中文标题右侧跟一个「小、淡、全大写、大字距」的标签。
   四类标题共用同一套排版，所以放在这里统一定义，不散落到各段。
   ⚠️ 三处标题的原有 letter-spacing / text-transform 会被标签覆盖，
      别在别处再给 .vgroup__title-en 加字距，否则两处打架。 */
.vgroup__title,
.vchain__title,
.vcard__label,
.qv-kicker,
.qv-ask__title {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 3px 10px;
}

/* 标签前缀用 8px 短横，不用中点「·」——
   页脚法务条已经用中点做分隔符，两处撞脸会让「·」的语义变模糊 */
.vgroup__title-en,
.qv-mini-en {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .2em;
  line-height: 1.4;
  text-transform: uppercase;
  color: var(--ink-3);
}
.vgroup__title-en::before,
.qv-mini-en::before {
  content: "";
  width: 8px;
  height: 1px;
  flex: none;
  background: currentColor;
  opacity: .45;
}

/* ---------- 7. 结果卡 ---------- */
.vcard { overflow: hidden; }

/* 卡头是一条「认证横幅」，三层信息由虚到实：
     ① 顶端 3px 渐变色带 —— 仪式感，绿 → 苔绿 → 金，只此一处用满三色
     ② 英文微标签行  —— AUTHENTICITY VERIFIED · JIYUAN AGRI-TECH
     ③ 主体行        —— 印章 + 中文结论 + 英文影子 + 右侧徽章
   背景用「极淡径向光 + 纵向渐变」而不是纯色块，
   左上角那点白光让头部比卡片身体亮一档，卡片就有了方向感。 */
.vcard__head {
  position: relative;
  padding: clamp(28px, 4.6vw, 38px) clamp(22px, 4vw, 34px) clamp(22px, 4vw, 30px);
}
.vcard__head::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
}
.vcard--ok .vcard__head {
  background:
    radial-gradient(128% 148% at 0% 0%, rgba(255, 255, 255, .94) 0%, rgba(255, 255, 255, 0) 62%),
    linear-gradient(180deg, #F4F8F5 0%, var(--ok-bg) 100%);
  border-bottom: 1px solid var(--ok-line);
}
.vcard--ok .vcard__head::before {
  background: linear-gradient(90deg, var(--forest-700) 0%, var(--moss-500) 40%, var(--ok-gold-l) 100%);
}
.vcard--bad .vcard__head {
  background:
    radial-gradient(128% 148% at 0% 0%, rgba(255, 255, 255, .94) 0%, rgba(255, 255, 255, 0) 62%),
    linear-gradient(180deg, #FDF3F1 0%, var(--bad-bg) 100%);
  border-bottom: 1px solid var(--bad-line);
}
.vcard--bad .vcard__head::before {
  background: linear-gradient(90deg, var(--bad-solid) 0%, #CF8A7E 58%, #E3C3A6 100%);
}

/* 英文微标签行：小字号 + 大字距 + 全大写，是「高级感」最省力的一招，
   也最容易滥用 —— 全页只出现在卡头、分组标题、链路标题、小节标题四处。
   gap 用 6px 10px 让它窄屏能自然折行，不撑破卡片 */
.vcard__eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .22em;
  line-height: 1.4;
  text-transform: uppercase;
}
.vcard--ok  .vcard__eyebrow { color: var(--ok-ink-2); }
.vcard--bad .vcard__eyebrow { color: var(--bad-ink-2); }
.vcard__eyebrow-sep {
  width: 16px;
  height: 1px;
  flex: none;
  background: currentColor;
  opacity: .45;
}
/* 窄屏放不下「标签 — 品牌」一行时，flex 会折行，
   而那条 16px 短横会孤零零挂在第一行行尾（像漏了个字）。
   直接把短横去掉，让品牌自然落到第二行——看起来是刻意分两行，不是断掉 */
@media (max-width: 479px) {
  .vcard__eyebrow-sep { display: none; }
}

.vcard__row {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-top: 16px;
}

/* 印章 = 圆底 + 1px 金环 + 一圈 7px 的极淡扩散环 + 投影。
   金环是「认证」这层意思的全部来源，比再加一个金色徽章克制得多 */
.vcard__seal {
  width: 56px;
  height: 56px;
  flex: none;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
}
.vcard--ok .vcard__seal {
  background: radial-gradient(120% 120% at 32% 22%, #2F7154 0%, var(--forest-700) 62%);
  box-shadow:
    0 0 0 1px rgba(198, 169, 127, .62),
    0 0 0 7px rgba(30, 77, 57, .07),
    0 10px 22px -12px rgba(14, 42, 30, .55);
}
.vcard--bad .vcard__seal {
  background: radial-gradient(120% 120% at 32% 22%, #C4574A 0%, var(--bad-solid) 62%);
  box-shadow:
    0 0 0 1px rgba(180, 69, 58, .30),
    0 0 0 7px rgba(180, 69, 58, .07),
    0 10px 22px -12px rgba(122, 44, 36, .50);
}
.vcard__seal svg { width: 28px; height: 28px; }

.vcard__text { min-width: 0; }

.vcard__verdict {
  font-size: clamp(21px, 4.8vw, 26px);
  line-height: 1.24;
  font-weight: 700;
  letter-spacing: .02em;
}
.vcard--ok  .vcard__verdict { color: var(--ok-ink); }
.vcard--bad .vcard__verdict { color: var(--bad-ink); }

/* 中文结论的「英文影子」：字号小、字距大，起分隔与提气的作用。
   全页唯一用 --ok-gold 的文字，出现一次就够 */
.vcard__verdict-en {
  margin-top: 5px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--ok-gold);
}
.vcard--bad .vcard__verdict-en { color: var(--bad-ink-2); }

.vcard__sub {
  margin-top: 9px;
  font-size: 13.5px;
  line-height: 1.7;
}
.vcard--ok  .vcard__sub { color: var(--ok-ink-2); }
.vcard--bad .vcard__sub { color: var(--bad-ink-2); }

/* 徽章：从「实心色块」改成「描边 + 内勾」，让它退到印章之后当配角。
   头部已经有色带、金环、金色小标三处高饱和，再来一块实心绿会打架 */
.vcard__badge {
  margin-left: auto;
  flex: none;
  align-self: flex-start;
  display: none;
  align-items: center;
  gap: 5px;
  padding: 6px 13px 6px 11px;
  border-radius: var(--r-pill);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .1em;
  white-space: nowrap;
}
.vcard__badge svg { width: 12px; height: 12px; flex: none; }
.vcard--ok .vcard__badge {
  color: var(--forest-700);
  background: rgba(30, 77, 57, .07);
  border: 1px solid rgba(30, 77, 57, .26);
}
.vcard--bad .vcard__badge {
  color: var(--bad-solid);
  background: rgba(180, 69, 58, .07);
  border: 1px solid rgba(180, 69, 58, .26);
}
@media (min-width: 640px) { .vcard__badge { display: inline-flex; } }

/* 印章「落定」：一次轻微回弹，只跑一次。
   卡片整体已有 qvRise 上浮，印章再单独滚一下，视线才会先落在结论上 */
.qv-stage[data-state="ok"]  .vcard__seal,
.qv-stage[data-state="bad"] .vcard__seal {
  animation: qvSeal .62s var(--ease-out) .12s both;
}
@keyframes qvSeal {
  0%   { opacity: 0; transform: scale(.5) rotate(-14deg); }
  62%  { opacity: 1; transform: scale(1.07) rotate(3deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .qv-stage[data-state="ok"] .vcard__seal,
  .qv-stage[data-state="bad"] .vcard__seal { animation: none; }
}

.qv-stage[data-state="ok"]  .vcard { animation: qvRise .5s var(--ease-out) both; }
.qv-stage[data-state="bad"] .vcard { animation: qvRise .5s var(--ease-out) both; }
@keyframes qvRise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .qv-stage[data-state="ok"] .vcard,
  .qv-stage[data-state="bad"] .vcard { animation: none; }
}

/* ---------- 8. 字段区 ---------- */
/* ---------- 8·1 字段分组 ---------- */
.vgroups {
  padding: 4px clamp(22px, 4vw, 34px) 14px;
}
.vgroup + .vgroup {
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--line-2);
}
.vgroup__title {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .16em;
  color: var(--moss-500);
}

.vcard__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  padding: 0;
}
.vcard__grid > div {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 15px 0;
  border-top: 1px solid var(--line-2);
  min-width: 0;
}
/* 分隔线画在每项的「上边」而不是下边：
   两列栅格里若按 :last-child / :nth-last-child 去掉末行线，
   条目数为奇数时会出现「左列有线、右列没线」的错位（5 项的产品信息就是奇数）。
   改成「除第一行外每行都有上边线」，与条目数奇偶无关，永远左右对称 */
.vcard__grid > div:first-child { border-top: 0; }
.vcard__grid dt {
  flex: none;
  width: 78px;
  font-size: 13px;
  letter-spacing: .04em;
  color: var(--ink-3);
}
.vcard__grid dd {
  min-width: 0;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--ink);
  word-break: break-word;
  /* 采集地区是四段地址（「贵州省 · 遵义市 · 桐梓县 · 狮溪镇」），
     在手机上（值宽约 215px）必然折行。默认折法是「贵州省 · 遵义市 · 桐梓县 ·」
     /「狮溪镇」——第一行末尾吊着一个孤零零的分隔点，看着像被截断了。
     balance 让两行长度接近，折点落在词与词之间，读起来是「一行地址分两行」而不是「断掉」。
     只对会折行的值起作用，单行值无副作用；不支持的浏览器退回默认折法，不影响可读性。 */
  text-wrap: balance;
}
.qv-mono {
  font-family: var(--mono);
  font-size: 14.5px;
  letter-spacing: .07em;
}

/* 正文中夹带的编码不许折行：SZ-C-2609-01-10 断成两行会读不出是一个编号 */
.qv-ask__desc .qv-mono,
.vcard__sub .qv-mono { white-space: nowrap; }

@media (min-width: 640px) {
  .vcard__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(28px, 5vw, 56px); }
  /* 两列时第一行是前两项，它们才贴着组标题 */
  .vcard__grid > div:nth-child(-n+2) { border-top: 0; }
  /* 奇数项分组（质量责任 5 项）的末项会独占左半格、右半格空着，
     它的上边线也只画半条 —— 那半格空洞和半截线就是「不协调」的来源。
     选到它的条件是「既是末项、又落在左列」：末项 :last-child，
     落在左列即序号为奇数 :nth-child(odd)。两项一夹，无需 JS 参与，
     也不会误伤偶数项分组（产品信息 6 项，末项是第 6 个 = 偶数，不命中）。
     单列布局下 grid-column:1/-1 等价于 auto，无副作用。 */
  .vcard__grid > div:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* ---------- 9. 溯源链路（工序刻度轴） ---------- */
/* 一条**有终点的刻度轴**，对应原来最扎眼的四个毛病：
     ① 轴线 1px 浅灰 → 2px 品牌绿，节点加白描边「钉」在轴上。
        原来线细到几乎看不见，节点像互不相干的孤立圆点。
     ② 节点一模一样 → 末节点换成深金大点 + 金色外环，末段轴线由绿渐金。
        这条链是有终点的（成品入库），原来完全没传达。
     ③ 名称/日期层级扁平 → 名称 13.5/600、日期降到 11.5px mono 的 --ink-3。
        原来 mono 日期数字比工序名还抢眼，主次颠倒。
     ④ 标题一行左重右空 → ::after 拉一条细线撑满，标题从「一行小字」变成小节头。
   数据契约不变（仍是 chain[].name / date），末步无日期照旧不渲染 <time>。

   ⚠️ **工序数已从 6/7 步涨到 9 步**（原料采集→…→成品入库，见 verify.js），
   横向排布的宽度需求随之翻倍，所以宽屏下结果卡做了「破格加宽」，
   见本文件末尾 @media (min-width:1024px) 里 `#qvOut > .vcard--ok` 那段。
   列宽是算出来的、不是试出来的：日期自然宽 64px、最长的是
   「02-20→09-03」70px，右内边距 14px → 每列至少要有 84px 才不折行。 */
/* margin-top 从 34px 收到 0。
   原来「末行字段 → 标题」实测 99px（15 行内边距 + 14 分组内边距 + 34 margin + 34 padding），
   在 870px 高的卡里占掉 11%，读起来像一段空洞——尤其质量责任组最后一行是
   「存样编号」独占整行、本身就很轻，下面再接近 100px 留白，整块会显得飘。
   这里 border-top 已经明确画出了分节，再叠 34px 外边距是重复表达。
   收到 0 之后：文字到分隔线 29px、分隔线到标题 34px，分节依然清楚但不空。 */
.vchain {
  margin-top: 0;
  padding: clamp(26px, 4vw, 34px) clamp(22px, 4vw, 34px) clamp(28px, 4vw, 36px);
  border-top: 1px solid var(--line-2);
}
.vchain__title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--moss-500);
}
/* 标题右侧那条细线：flex:1 1 0 + min-width:0 让它在窄屏自动收成 0 宽，
   既不留半截残线、也不会因为放不下而换行，所以不需要媒体查询。
   align-self 必须写：父级是 align-items:baseline，而 ::after 没有文字基线，
   不覆盖的话它会贴着行底走，和标题不在同一条视觉线上。 */
.vchain__title::after {
  content: "";
  flex: 1 1 0;
  align-self: center;
  height: 1px;
  min-width: 0;
  background: var(--line-2);
}

.vline {
  margin-top: 24px;
  padding: 0;
  list-style: none;
  display: grid;
}

/* 纵向（默认）：工序名在左、日期右对齐 —— 像一份台账记录。
   比原来「名称一行、日期再一行」每步省约 15px，六步合计省近 90px。 */
.vstep {
  position: relative;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding: 0 0 20px 30px;
}
.vstep:last-child { padding-bottom: 0; }

/* 轴线：每段从本节点连到下一节点 */
.vstep::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 17px;
  bottom: 0;
  width: 2px;
  border-radius: 1px;
  background: rgba(30, 77, 57, .30);
}
.vstep:last-child::before { display: none; }

/* 节点：12px 实心 + 3px 白描边。
   白描边的作用是让圆点「压」在轴线上，而不是被轴线穿过去。 */
.vstep__dot {
  position: absolute;
  left: 0;
  top: 5px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--forest-600);
  box-shadow: 0 0 0 3px var(--white);
}
.vstep__name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
}
.vstep__date {
  flex: none;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .02em;
  white-space: nowrap;
  color: var(--ink-3);
}

/* 终点节点：末步「成品入库」是这页的当前落点（所以它没有日期）。
   做成一枚「金印」：**暖金实心 + 3px 白间隙 + 2px 深金外环**。
   两个坑都踩过：
     · 先用深金 #7A5F2A 做实心 → 在一排饱和绿点旁边读起来是「土褐色」不是金；
     · 再用柔光晕 rgba(198,169,127,.40) 做外圈 → 半透明白底上糊成一团脏影。
   所以内芯用暖金 #C6A97F（够亮、明确是金），外环用深金（给足轮廓对比）。
   left/top 都要跟着改：圆点从 12px 长到 16px，中心必须仍落在轴线中心（横向 y=6 / 纵向 x=6），
   只改 top 不改 left 会横竖任一方向偏 2px。 */
.vstep:last-child .vstep__dot {
  left: -2px;
  top: 3px;
  width: 16px;
  height: 16px;
  background: var(--ok-gold-l);
  box-shadow:
    0 0 0 3px var(--white),
    0 0 0 5px var(--ok-gold);
}
.vstep:last-child .vstep__name { color: var(--ok-ink); }

/* 通往终点的那一段轴线由绿转金，视线被带向终点 */
.vstep:nth-last-child(2)::before {
  background: linear-gradient(180deg, rgba(30, 77, 57, .30) 0%, rgba(198, 169, 127, .85) 100%);
}

/* 入场的逐级点亮：0.05s 一档，让人顺着读完整条链。
   用 both 填充，延迟期间保持起始态（透明），不会先闪一下再动。 */
.qv-stage[data-state="ok"] .vstep {
  animation: qvStep .5s var(--ease-out) both;
}
@media (prefers-reduced-motion: reduce) {
  .qv-stage[data-state="ok"] .vstep { animation: none; }
}
.qv-stage[data-state="ok"] .vstep:nth-child(1) { animation-delay: .26s; }
.qv-stage[data-state="ok"] .vstep:nth-child(2) { animation-delay: .31s; }
.qv-stage[data-state="ok"] .vstep:nth-child(3) { animation-delay: .36s; }
.qv-stage[data-state="ok"] .vstep:nth-child(4) { animation-delay: .41s; }
.qv-stage[data-state="ok"] .vstep:nth-child(5) { animation-delay: .46s; }
.qv-stage[data-state="ok"] .vstep:nth-child(6) { animation-delay: .51s; }
.qv-stage[data-state="ok"] .vstep:nth-child(7) { animation-delay: .56s; }
.qv-stage[data-state="ok"] .vstep:nth-child(8) { animation-delay: .61s; }
/* 第 9 步是终点（成品入库）。显式写出来而不是留给默认 0s ——
   少写一条的话它会在动画开始的瞬间第一个亮起，整条链的点亮顺序就反了。
   步数 6 / 7 / 9 都写过一遍，多的两条对少步数的链路无害。 */
.qv-stage[data-state="ok"] .vstep:nth-child(9) { animation-delay: .66s; }
@keyframes qvStep {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* 横向排列的断点从 768 提到 1024：工序已达 9 步，768px 下一列只剩约 74px，
   连「2026-02-18」这种 64px 的完整日期都放不下，所以窄屏一律走纵向。 */
@media (min-width: 1024px) {
  /* ---- 宽屏下让「正品结果卡」破格加宽 ----
     9 道工序横向排布需要约 830px 的轴线，而 .qv-shell 的阅读列只有 768px 可用
     （880 上限减去 .container 的 56px 双边内边距）。实测每列只有 77.5px，
     扣掉 14px 右内边距后剩 63.5px —— 比最长日期「02-20→09-03」所需的 70px 还少 6.5px，
     于是 9 格里 8 格的日期被挤成两行。
     这里只把结果卡向两侧各扩 72px（+144px），列距升到约 94px，每列可用约 80px，
     余量约 10px。正文、表单、帮助三块**保持不变**，仍走 880px 的阅读宽度——
     编码输入框只有十几位，跟着一起变宽只会显得空旷。
     只对 .vcard--ok 生效：查无此码的卡片内容短，没有加宽的理由。
     ⚠️ 加宽后若某列仍装不下，日期会静默折成两行（overflow-wrap:break-word 挡住了溢出，
     也就挡住了告警）——这个只有靠 tools/chain-probe.js 数行数才看得见，改列宽后务必跑一遍。 */
  #qvOut > .vcard--ok {
    width: calc(100% + 144px);
    margin-inline: -72px;
  }
  /* 轴线撑满加宽后的卡片——.vchain 的内边距不变，多出来的宽度全给 .vline */
  .vline { grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  .vstep {
    display: block;
    padding: 22px 14px 0 0;
    min-width: 0;
  }
  /* 轴改为横向：从本列节点连到下一列节点。
     right:-14px 正好接上下一列的节点（下一列节点在它自己 left:0 处） */
  .vstep::before {
    left: 6px;
    right: -14px;
    top: 5px;
    bottom: auto;
    width: auto;
    height: 2px;
    background: rgba(30, 77, 57, .26);
  }
  .vstep:nth-last-child(2)::before {
    background: linear-gradient(90deg, rgba(30, 77, 57, .26) 0%, rgba(198, 169, 127, .82) 100%);
  }
  .vstep__dot { top: 0; }
  .vstep:last-child .vstep__dot { top: -2px; }
  /* 横向每列约 94px，最长日期「02-20→09-03」占 70px，正常都在一行内。
     white-space 必须从纵向的 nowrap 改回 normal：万一哪天工序再加、
     或换个更长的日期写法，宁可换成两行也不好让文字捅出卡片。
     break-word 是「兜底不溢出」，不是「允许折行」——折行本身算缺陷，
     由 tools/chain-probe.js 的「日期行数」断言盯着（>1 行即不合格）。 */
  .vstep__date {
    display: block;
    margin-top: 4px;
    white-space: normal;
    overflow-wrap: break-word;
  }
}

/* ---------- 10. 异常态清单与操作 ---------- */
.vcard__body {
  padding: clamp(20px, 3.4vw, 28px) clamp(22px, 4vw, 34px) 0;
}
.vcard__label {
  font-size: 12px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--moss-500);
}
.vlist {
  margin-top: 14px;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}
.vlist li {
  position: relative;
  padding-left: 18px;
  font-size: 14px;
  line-height: 1.7;
  color: var(--ink-2);
}
.vlist li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 9px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--sage-300);
}
.vcard__foot {
  display: grid;
  gap: 12px;
  padding: clamp(22px, 3.4vw, 30px) clamp(22px, 4vw, 34px) clamp(24px, 4vw, 32px);
}
.vcard__foot .btn { width: 100%; }
@media (min-width: 640px) {
  .vcard__foot { grid-auto-flow: column; justify-content: start; }
  .vcard__foot .btn { width: auto; }
}

/* ---------- 11. 信任条 ---------- */
.vtrust {
  margin-top: clamp(20px, 3vw, 28px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 28px;
  padding: 16px clamp(18px, 3vw, 26px);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  background: var(--paper-2);
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-3);
}
.vtrust b { color: var(--ink); font-weight: 600; }
.vtrust__domain {
  font-family: var(--mono);
  letter-spacing: .04em;
  color: var(--ink-2);
}
/* 域名后的英文标注：不装进 flex 容器（信任条本身是 flex），
   只做行内小字，窄屏跟着域名一起折行 */
.vtrust__en {
  margin-left: 7px;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--moss-500);
}

.vtrust__side { font-family: var(--mono); font-size: 12px; letter-spacing: .04em; }

/* 结果页底部的「查询其它编码」文字按钮 */
.qv-again { margin-top: 20px; text-align: center; }
.qv-link {
  border: 0;
  border-bottom: 1px solid currentColor;
  background: none;
  padding: 4px 2px;
  cursor: pointer;
  font-family: inherit;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--forest-700);
  transition: color var(--dur) var(--ease);
}
.qv-link:hover { color: var(--forest-600); }
.qv-link:focus-visible { outline: 2px solid var(--moss-500); outline-offset: 3px; border-radius: 2px; }

/* ---------- 12. 帮助区 ---------- */
.qv-help { margin-top: clamp(52px, 8vw, 96px); }

/* 小节标题：与溯源链路的 kicker 同一语言，字号小、字距大、色低饱和 */
.qv-kicker {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--moss-500);
}

.qv-help__grid { display: grid; gap: 18px; margin-top: 26px; }
.qv-help__card {
  padding: 26px 24px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  background: var(--white);
  box-shadow: var(--sh-xs);
  transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
@media (hover: hover) {
  .qv-help__card:hover { transform: translateY(-3px); box-shadow: var(--sh-sm); }
}
.qv-help__card h3 {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--ink);
}
.qv-help__card p {
  margin-top: 10px;
  font-size: 13.5px;
  line-height: 1.9;
  color: var(--ink-3);
}
@media (min-width: 768px) {
  .qv-help__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- 13. 无障碍 ---------- */
.qv-sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.qv-stage:focus { outline: none; }
