/* ============================================================
   FORM_UTILITY archetype — 共享 composition 层（Phase 20A2B）

   消费者（本轮仅此一页）：tools/fx/  以及它的 8 份 locale 派生页。
   下一轮机械迁 translator / qrcode 时，它们只要挂上这些 class 即可。

   ⚠️ 这一层**只做 composition**：它不定义 token、按钮基样式、输入框基样式、
      Product Page 外壳或 header 基样式。视觉值一律来自
      tokens.css · yq-ui.css · workspace-ui.css · product-page.css · tool-landing.css。

   archetype 解剖（Founder 在 20A2A-R2 批准的信息关系）：
     Tool Hero        .pp-hero
     Main Surface     .yqw-surface / .fu-surface
       Summary Card   .fu-summary          ← 当前换算：一个容器，两个逻辑子区域
         Input        .fu-input-region       Amount ／ From ↔ To
         Result       .fu-result-region      Result ／ 主结果 ／ 汇率 ／ 动作
       Trend Card     .fu-trend-card       ← 独立、全宽、横向舒展
       Feedback       .fu-feedback
     Editorial        （FX 没有 —— FX_CONTENT_EXPANSION = BACKLOG）

   ⚠️ 两个子区域之间**没有竖线**：区分只许来自空间、层级、Result 的轻 tint 与 padding。
      R1 的「Control Pane 左 / Result + Chart 右」已被 Founder 否决，不要改回去。
   ============================================================ */

/* ── Shell：流式，服从 product-page.css 的页面 gutter 权威 ─────
   （改造前是 `.container { width: 520px; margin: auto }` —— 1440 与 1024
     实测同为 520px，完全不响应 viewport。） */
html.is-standalone body.fu-page { display: block; padding: 0; overflow-y: auto; }

/* ── 容器宽度：FORM_UTILITY 比 FILE_PROCESSOR 窄一档 ─────────────
   --fu-max 是 **FORM_UTILITY 专属**的产品框宽度，共享的 --yqw-max-width(1200)
   一个字节没动 —— 文件网格要宽，表单不要。
   ⚠️ 1140 已经接近**硬下限**：两列成立需要
        Summary 内容宽 ≥ 728(货币对) + 20(gap) + 280(Result) = 1028
      而 Summary 内容宽 = 容器宽 − 54(surface padding+border) − 32(summary padding)
      → 容器 ≥ 1114。再窄就只有两条路：让币种 label 截断，或者 1440 上也堆叠。
      两条都不可接受，所以"容器缩小"到此为止。
   Hero 与 Surface 共用同一个上限，左右边缘才在同一条线上。 */
.fu-shell {
  --fu-max: 1140px;
  width: 100%;
  max-width: calc(var(--fu-max) + 2 * var(--pp-inline));
  margin: 0 auto;
  padding-inline: var(--pp-inline);
  padding-bottom: 8px;
}
html.is-standalone .fu-shell {
  margin: 0 auto; padding-left: var(--pp-inline); padding-right: var(--pp-inline);
}
.fu-hero { max-width: calc(var(--fu-max) + 2 * var(--pp-inline)); }
.fu-shell .pp-hero { padding-top: 40px; padding-bottom: 24px; }
@media (max-width: 640px) { .fu-shell .pp-hero { padding-top: 28px; padding-bottom: 18px; } }

/* assurance 行搬进 Hero：文案逐字未改（fx.trust），只换位置。
   ⚠️ FX 是 G6 §9 的 B 类，**不能**套用 common.trust_local。 */
.fu-shell .fu-trust, .pp-hero .fu-trust {
  max-width: none; margin: 14px 0 0; padding: 0;
  text-align: left; font-size: 13px; line-height: 1.6; color: var(--yq-text-3);
}

/* §「容器高度减到 70%」：748 → 517 的账在四处，不是把某一块砍到底 ——
   surface padding 40→26 · 区块 gap 20→14 · Summary 内边距 26→16 · 图表 330→190。 */
.fu-surface { gap: 14px; padding: 26px; }

/* ════════════ Summary Card：当前换算 ════════════
   §3：Input 与 Result 是**同一个容器里的两个逻辑子区域**，
   不是一根线劈开的两块。区分只许来自 空间 / 层级 / 轻微 tint / padding。 */
.fu-summary {
  /* ── archetype 默认几何（§11.1）────────────────────────────────
     ⚠️ 这里只许放**三页都成立**的默认值。字段宽走平台标准 168
        （authority = workspace-ui.css `--yqw-field-w`，g80/g81 守着）。
     ⚠️ FX 的 290px **不是** FORM_UTILITY 的默认，是 FX 这一页的取舍 ——
        它住在下面的 `.fu-summary--pair` 里。20A2B 把它写在 `.fu-summary`
        上，当时没有第二个成员所以看不出来；translator / qrcode 进来之后
        那就是一颗"下一页加一行 pair-row 就静默继承 290"的雷。 */
  --fu-field: var(--yqw-field-w);
  --fu-input-min: 320px;
  --fu-result-min: 320px;
  /* Result 列的**上限**：FX 的结果是一个数，给它 400 就够了；
     translator 的结果是一整块文本，要和左边等宽 —— 所以这是个参数，
     不是 archetype 的固定事实。 */
  --fu-result-max: 400px;
  --fu-sum-gap: 20px;

  border: 1px solid var(--yq-border);
  border-radius: var(--yq-r-card);
  background: var(--yq-bg-page);
  padding: 16px;
}

/* ── FX：currency pair（From ↔ swap ↔ To）──────────────────────
   下面这段整体是 **FX-specific decision**，不是 archetype 契约。
   ── 币种字段宽：338 → 290（Founder V3：左边缩短、右边加宽）──────
     这不是"放宽一个阈值"，是拿实测分布做的一次**明码取舍**。
     9 locale × 32 币种 = 288 个组合，**逐个真的选中再量**可见 span 有没有溢出：
       字段 338 → 截断 0 / 288      字段 290 → 截断 2 / 288
     那两条都是 AED（最长的币名）：
       es「dírham de los Emiratos Árabes Unidos (AED)」
       fr「dirham des Émirats arabes unis (AED)」
     其余 286 个组合零截断，菜单里也始终是全名。
     ⚠️ 这里的数字刻意不用星号强调：一对强调星号后面紧跟斜杠，
        就凑成了注释结束符，注释会在那里就地结束，后面的声明被 CSS 的
        错误恢复整块吃掉 —— 实测表现是 1440 上 Summary 莫名其妙变成堆叠。
        CLAUDE.md 记过这条；本轮写这段注释时又踩了一次，所以留在这儿。
     省下来的 2×48 = 96px 全部给了 Result —— 它现在要装下
     `MOP 10,000,000,000.00`（100 亿 + 2 位小数，32 种币里最宽的一种）。 */
.fu-summary--pair {
  --fu-pair-field: 290px;
  --fu-pair-gap: 12px;
  --fu-swap: 36px;
  /* Input Region 装得下 From + swap + To 的最小宽 */
  --fu-input-min: calc(2 * var(--fu-pair-field) + 2 * var(--fu-pair-gap) + var(--fu-swap));
  /* Result 下限 = 最坏字串在**断点处的最小字号**下的渲染宽 + 左右 padding：
     342.8 × (24/30) + 32 ≈ 306 → 取 320 留余量。 */
  --fu-result-min: 320px;
}
.fu-sum-grid {
  display: grid;
  grid-template-columns: minmax(var(--fu-input-min), 1fr) minmax(var(--fu-result-min), var(--fu-result-max));
  gap: var(--fu-sum-gap);
  align-items: stretch;
}

/* 断点由**真实最小内容宽**推导，不是被文案触发（§7）：
   两列成立需要 summary 内容宽 ≥ input-min + gap + result-min
                            = 640 + 20 + 320 = 980px。
   ⚠️ 字段收窄之后这个门槛跟着降了，所以两列能撑到更窄的视口 ——
      具体数值**逐视口实测**后写在下面，不是算个近似值就写上去。
   ⚠️ 堆叠**不是退化**：Result 仍紧邻当前换算，Trend 仍独立在下方。 */
/* ⚠️ 这个数是从 **FX 自己的** 最小内容宽推出来的，所以它跟着
   `.fu-summary--pair` 走，不是 archetype 的通用断点。
   别的成员各有各的最小内容宽，各自量、各自声明（§11.1）。 */
@media (max-width: 1234px) {
  .fu-summary--pair .fu-sum-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── Input Region ──────────────────────────────────────────── */
.fu-input-region { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.fu-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.fu-label {
  font-size: var(--yqw-t3-size); font-weight: var(--yqw-t3-weight);
  line-height: var(--yqw-t3-lh); color: var(--yq-text-2);
}
.fu-input-region input[type="number"] {
  width: 100%; height: var(--yqw-h-select); padding: 0 12px;
  font-family: var(--yq-font); font-size: 15px; color: var(--yq-text-1);
  background: var(--yq-bg-page);
  border: 1px solid var(--yq-border); border-radius: var(--yq-r-ctl); outline: none;
  transition: border-color var(--yq-dur), box-shadow var(--yq-dur);
}
.fu-input-region input[type="number"]:hover { border-color: var(--yq-border-strong); }
.fu-input-region input[type="number"]:focus {
  border-color: var(--yq-primary); box-shadow: 0 0 0 3px var(--yq-focus-ring);
}
/* §9：空态显示浅色 0。DOM value 仍为空 —— 计算触发、缓存、输入真相都不变。 */
.fu-input-region input[type="number"]::placeholder { color: var(--yq-text-3); opacity: 1; }

/* From ↔ To 明显是一组：并排 + 中间 swap，swap 与两个字段**底边对齐**
   （§19：垂直居中于 pair 行、视觉属于 currency pair，不再漂在分隔线上）。 */
.fu-pair-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: var(--fu-pair-gap);
  align-items: end;
}
.fu-swap {
  width: var(--fu-swap); height: var(--yqw-h-select); flex: none;
  border: 1px solid var(--yq-border); border-radius: var(--yq-r-ctl);
  background: var(--yq-bg-page); color: var(--yq-text-2);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; transition: background var(--yq-dur), color var(--yq-dur);
}
.fu-swap:hover { background: var(--yq-bg-quiet-hover); color: var(--yq-text-1); }
.fu-swap:focus-visible { outline: 2px solid var(--yq-primary); outline-offset: 2px; }
.fu-swap svg { width: 16px; height: 16px; }

/* 窄屏：From / Swap / To 竖排（§8 的移动端顺序，§19 允许 swap 单独占一行）。
   ⚠️ 这个断点也是**算出来的**，不是"手机就竖排"：
      并排时每个字段 = (pair 行宽 − swap 36 − 2×gap) / 2，
      要 ≥ 332.4（R1 实测的零截断下限）就需要 pair 行 ≥ 725px，
      字段收窄到 290 之后 pair 行的下限变成 640px，断点随之下移。
      逐视口实测：880 → 290.8 ✓ / 876 → 289.0 ✗，所以断点取 879。
      竖排之后字段反而变成满宽，所以窄屏上的可读性是**变好**的。 */
/* 879 同理：由 FX 的 pair 行推出来的 */
@media (max-width: 879px) {
  .fu-pair-row { grid-template-columns: minmax(0, 1fr); justify-items: stretch; }
  .fu-swap { justify-self: center; width: 44px; }
  /* 竖排时方向关系是上下的，图标跟着转 90°，不然读起来仍像左右对调 */
  .fu-swap svg { transform: rotate(90deg); }
}
/* 390 上页面 gutter 只剩 20px，卡片内边距也要收，
   否则字段窄到连**默认选中**的 label 都装不下（实测最长 182.4 + chrome 45 = 227.4）。 */
@media (max-width: 640px) {
  .fu-summary { padding: 12px; }
  .fu-result-region { padding: 12px 14px; }
  .fu-input-region { gap: 12px; }
}

/* ── translator：source ↔ result 是**一对对称的工作面**（20A2C1）────
   Founder 的裁定：待翻译文本与翻译结果是非常明确的对称关系 ——
   左边是正常输入控件、右边被包进另一个浅灰容器里，那读起来就成了
   「输入是控件 / 输出是次级对象」，关系是错的。
   所以这一页：**一个工作容器 → toolbar → 两个对称 surface → 动作**。
   ⚠️ 外层 `.yqw-surface` 已经是那个工作容器，Summary 自己**不再画卡**，
      否则就是"卡里套卡"（20A2C 第一版实测就是这个形状）。 */
.fu-summary--text {
  border: 0;
  background: transparent;
  padding: 0;
  /* 三个动作的公共宽：实测 9 locale × 3 label = 27 条，最宽
     vi 的 "Sao chép văn bản" = 109.3px；+2×14 padding +2×1 border = 140
     → 取 144 留一点余量。⚠️ 这个宽度**只属于 translator 的动作组**，
     不提升成全站 Button token。 */
  --tl-action-w: 144px;
  --tl-text-h: 320px;
}
/* 两列**完全等宽**：不是 minmax(内容最小宽)，那会让两边因为内容不同而不等。 */
.fu-summary--text .fu-sum-grid {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  align-items: stretch;
}
@media (max-width: 830px) {
  .fu-summary--text .fu-sum-grid { grid-template-columns: minmax(0, 1fr); }
}
/* ⚠️ 两列都**没有**自己的 padding / 背景 / 边框：它们只是排版列。
   一旦哪一侧多一层 chrome，两个文本框的 top/bottom 就再也对不齐了。 */
.fu-summary--text .fu-input-region,
.fu-summary--text .fu-result-region {
  display: flex; flex-direction: column; gap: 12px; min-width: 0;
  padding: 0; background: transparent; border: 0; border-radius: 0;
}
/* 两块文本面：同底色、同边框、同内边距、同高度。
   readonly 只由 cursor 与语义属性表达，**不靠整块浅灰**降级成次级区域。 */
.fu-summary--text .fu-textarea {
  flex: 1 1 auto;
  min-height: var(--tl-text-h);
  background: var(--yq-bg-page);
}
.fu-summary--text .fu-textarea[readonly] {
  background: var(--yq-bg-page);
  cursor: default;
}
/* 动作行：两列各一行，基线对齐由"文本框等高 + 按钮等高"自然得到 */
.fu-summary--text .fu-actions { gap: 10px; flex: none; }
/* 三个动作：同宽 · 同高 · 同圆角 · 同字号。
   主次**只由颜色表达**，不再靠尺寸（Founder §6）。 */
.fu-summary--text .fu-actions .yqw-btn {
  width: var(--tl-action-w);
  height: 42px;
  padding: 0 14px;
  font-size: var(--yqw-t3-size);
  font-weight: 500;
  border-radius: var(--yq-r-ctl);
}
/* ⚠️ disabled 一律**中性灰**。共享层的 primary disabled 是浅蓝
   （--yqw-primary-disabled-bg），Founder 明确否决："像能点又像不能点"。
   这里用的是既有中性令牌，**不新增任何色值**。 */
.fu-summary--text .fu-actions .yqw-btn:disabled {
  background: var(--yq-bg-quiet);
  border-color: var(--yq-border);
  color: var(--yq-text-3);
  opacity: 1;
}
/* ⚠️ 请求进行中**保持** primary blue（20A2C1B §1.1）。
   上面那条中性 disabled 是给"没东西可翻译"的空态用的；请求中同样是
   disabled（阻止重复提交），但它不是"不可用"，是"正在工作" ——
   让它闪成灰色是 Founder 明确否决的那个 blue → gray → blue。
   ⚠️ 这里**不新增第二套请求状态机**：`.loading` 仍由既有的 setLoading() 挂，
      只是多一条视觉规则盖住 disabled 的灰。 */
.fu-summary--text .fu-actions .yqw-btn--primary.loading:disabled {
  background: var(--yqw-primary);
  border-color: var(--yqw-primary);
  color: #fff;
  opacity: 1;
  cursor: progress;
}
@media (max-width: 640px) {
  /* 窄屏三个按钮**仍然彼此等宽**（各自占满列宽），只是不再是那个定值 ——
     390 上两个 144 并排放不下，硬挤就会截断，而截断是 0 容忍的。 */
  .fu-summary--text { --tl-action-w: 100%; --tl-text-h: 200px; }
  .fu-summary--text .fu-actions { flex-direction: column; align-items: stretch; }
}
/* 目标语言是**这一次任务的设置**，属于整块 Summary 而不属于某一侧 ——
   放在网格之上，两块文本的顶边因此天然对齐，它也不破坏左右对称。 */
.fu-task-row { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }

/* ── qrcode：左操作 · 右预览（20A2C3，Owner 已确认的最终设计）────────
   Desktop = Left Control Pane + Right Preview Pane，同一张工作卡里。
   ⚠️ 这一段**只属于 qrcode**（.fu-summary--media）。fx 的 --pair、
      translator 的 --text 一个字节都不许碰 —— 三页共用这张表，
      靠修饰符物理隔离。
   ⚠️ 外层 .yqw-surface 已经是那张工作卡，Summary 自己**不再画卡**：
      20A2C 的形态是 surface → summary 边框 → stage → qr-box 三四层，
      Founder 看的第一眼就是"卡里套卡"。 */
.fu-summary--media {
  border: 0;
  background: transparent;
  padding: 0;
  --qr-gap: 32px;
}
/* 两栏**等宽**：左 = 操作，右 = 结果。不是 minmax(内容最小宽)，
   那会让两边因为内容不同而不等。 */
.fu-summary--media .fu-sum-grid {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--qr-gap);
  align-items: stretch;
}
/* 纵向切换点由"右栏还放得下一张像样的预览"推出来（实测写在 g86 [R]）：
   两栏成立需要右栏内容宽 ≥ 预览显示尺寸 + 两侧留白。 */
@media (max-width: 830px) {
  .fu-summary--media .fu-sum-grid { grid-template-columns: minmax(0, 1fr); --qr-gap: 24px; }
}
/* 左栏 = Control Board：Input Panel → Style Panel → Action Panel，顺序固定（§4）。
   20A2C3C（Owner）：左侧要像一个被设计过的操作面板，整体高度与右侧预览面板相当。
   高度来自**三块面板本身**（边框 + 内边距），不是块与块之间的空白：
     面板间距              16px
     Input / Style 面板    padding 18px（Style 下方 20px，行下留白略大于上方）
     Action 面板           padding 19/20px，更舒展；Generate ↔ 次级按钮 16px
     label ↔ 控件          10px（Style 12px）
   面板的边框语言与右侧预览面板相同（1px --yq-border、--yq-r-card、白底）。
   面板直接挂在三个既有分组元素上 —— 没有多套一层 wrapper。
   ⚠️ 合法退役：20A2C3B 的"栏盒贴顶取内容高度、剩余留白在下方"已被 Owner 更正：
      现在左栏与预览面板等高。多出的几像素只由 Action 面板吸收（flex-grow），
      并且它的内容**整组居中**，按钮之间的距离不会被拉开。
   ⚠️ 仍然禁止 space-between / 块间大空白。 */
.fu-summary--media .fu-input-region { gap: 16px; justify-content: flex-start; }
.fu-summary--media .qr-panel {
  border: 1px solid var(--yq-border);
  border-radius: var(--yq-r-card);
  background: var(--yq-bg-page);
  padding: 18px;
}
.fu-summary--media .qr-panel--input { gap: 10px; }
.fu-summary--media .qr-panel--style { gap: 12px; padding-bottom: 20px; }
.fu-summary--media .qr-panel--action {
  display: flex; flex-direction: column; justify-content: center; gap: 16px;
  /* 纵向 19：三块面板的自然高度（≈374）必须**略小于**预览面板的 376 ——
     大于它就会把右侧预览也撑高（实测 20 时是 376.4，预览跟着变成 376.4）。
     差出来的 1–2px 由下面的 flex-grow 补上，内容整组居中。 */
  padding: 19px 20px;
}
@media (min-width: 831px) {
  .fu-summary--media .qr-panel--action { flex: 1 1 auto; }
}
/* 窄屏：面板内边距收到 14，配合页面里色块间距 7，10 枚色块在 390 下仍是一行
   （296 − 2×14 − 2 = 266 ≥ 10×20 + 9×7 = 263），不再折成 8+2 */
@media (max-width: 640px) {
  .fu-summary--media .qr-panel,
  .fu-summary--media .qr-panel--style,
  .fu-summary--media .qr-panel--action { padding: 14px; }
}

/* 右栏只是排版列，**没有**自己的 tint / padding / 边框 ——
   可见的那一层是它里面的预览面（.fu-media-stage）。 */
.fu-summary--media .fu-result-region {
  display: flex; min-width: 0;
  padding: 0; background: transparent; border: 0; border-radius: 0;
}
.fu-summary--media .fu-result-region > .fu-stage { flex: 1 1 auto; min-width: 0; }

/* Generate：左栏**整宽**，唯一的 Primary（46px tier，既有 primary 蓝） */
.fu-summary--media .qr-generate { width: 100%; }

/* 三个次级动作：同宽 · 同高 · 同圆角 · 同字号，全部 secondary。
   ⚠️ 这里刻意**只放三个**：诊断用的 Export logs 不进这一行，
      否则解锁后它会变成第四个同宽按钮，打破"三个"的关系。 */
.fu-summary--media .qr-secondary {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
.fu-summary--media .qr-secondary .yqw-btn {
  width: 100%; min-width: 0;
  height: var(--yqw-h-select);
  padding: 0 10px;
  font-size: var(--yqw-t3-size); font-weight: 500;
  border-radius: var(--yq-r-ctl);
}
/* 不可用 = **中性灰**，不是共享层那个浅蓝 disabled（translator 那轮
   Founder 明确否决过"像能点又像不能点"）。用的都是既有中性令牌。 */
.fu-summary--media .qr-secondary .yqw-btn:disabled {
  background: var(--yq-bg-quiet);
  border-color: var(--yq-border);
  color: var(--yq-text-3);
  opacity: 1;
}
/* 诊断入口解锁后也只是一个安静的小按钮，不抢三个动作的位置 */
.fu-summary--media .qr-diag { align-self: flex-start; }

/* 窄屏三个次级动作的排法由 **9 locale 实测**决定（g86 [R] 守）：
   390 下三列每格只剩 ~82px，"Export PNG" / "Exporter en PNG" 必然截断 ——
   截断是 0 容忍的，所以改成一列，三个按钮**仍然彼此同尺寸**。 */
@media (max-width: 640px) {
  .fu-summary--media .qr-secondary { grid-template-columns: minmax(0, 1fr); gap: 10px; }
}

/* ── Right Preview Pane：极简预览面 ─────────────────────────────
   白底 · 细边框 · 圆角 · 大而稳定 · QR 居中 · 四周留足 quiet zone。
   不要说明文案、不要 tips、不要 tinted 灰底。 */
.fu-media-stage {
  --fu-stage-pad: 28px;
  --qr-display: 320px;
  position: relative;
  display: flex; align-items: center; justify-content: center;
  min-height: calc(var(--qr-display) + 2 * var(--fu-stage-pad));
  /* 预览显示尺寸要跟着这张面的**真实内容宽**收缩（1024 右栏只有 ~390），
     所以它是一个 container，画布用 cqw 取宽。 */
  container-type: inline-size;
  overflow: hidden;
}
@media (max-width: 640px) {
  .fu-media-stage { --fu-stage-pad: 12px; min-height: 0; }
}

/* ── 通用表单控件（20A2C：translator / qrcode 进来之后才需要）────
   ⚠️ 这一组只做 presentation：尺寸走平台标准 token，**不带任何业务语义**。
   ⚠️ 高度一律 `--yqw-h-select`（g80 的 CANONICAL_STANDARD_CONTROL_HEIGHT），
      宽度默认 100%；需要"标准窄字段"的用 `.fu-field--std`。 */
.fu-input-region input[type="text"],
.fu-result-region input[type="text"]:not(.result-input) {
  width: 100%; height: var(--yqw-h-select); padding: 0 12px;
  font-family: var(--yq-font); font-size: 15px; color: var(--yq-text-1);
  background: var(--yq-bg-page);
  border: 1px solid var(--yq-border); border-radius: var(--yq-r-ctl); outline: none;
  transition: border-color var(--yq-dur), box-shadow var(--yq-dur);
}
.fu-input-region input[type="text"]:hover { border-color: var(--yq-border-strong); }
.fu-input-region input[type="text"]:focus {
  border-color: var(--yq-primary); box-shadow: 0 0 0 3px var(--yq-focus-ring);
}
.fu-input-region input[type="text"]::placeholder { color: var(--yq-text-3); opacity: 1; }

/* 标准窄字段：平台标准宽，**不是** FX 的 290（§11.1） */
.fu-field--std { max-width: var(--fu-field); }

/* 文本工作区：source / result 两块长文本。
   §5.2：**高度由自己决定，不被对面那块的内容撑扁或压矮** ——
   所以两边都是 `min-height` + `resize: vertical`，
   result 出现只填内容，不参与高度竞争。 */
.fu-textarea {
  width: 100%;
  min-height: var(--fu-text-h, 260px);
  padding: 12px;
  font-family: var(--yq-font); font-size: 15px; line-height: 1.6;
  color: var(--yq-text-1); background: var(--yq-bg-page);
  border: 1px solid var(--yq-border); border-radius: var(--yq-r-ctl);
  outline: none; resize: vertical;
  /* 滚动条必须看得见（V0.0.1228 全站规范） */
  scrollbar-gutter: stable;
  transition: border-color var(--yq-dur), box-shadow var(--yq-dur);
}
.fu-textarea:hover:not([readonly]) { border-color: var(--yq-border-strong); }
.fu-textarea:focus { border-color: var(--yq-primary); box-shadow: 0 0 0 3px var(--yq-focus-ring); }
.fu-textarea::placeholder { color: var(--yq-text-3); opacity: 1; }
.fu-textarea[readonly] { background: transparent; }
@media (max-width: 640px) { .fu-textarea { --fu-text-h: 180px; } }

/* 动作行：次级动作横排，窄屏换行不溢出（§8） */
.fu-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.fu-actions--end { justify-content: flex-end; }

/* Primary CTA：46px tier，宽度自适应内容（不拉通） */
.fu-cta { align-self: flex-start; }
@media (max-width: 640px) { .fu-cta { align-self: stretch; width: 100%; } }

/* ── Result Region ─────────────────────────────────────────────
   §20/§21：**没有**竖线、没有把 Result 框起来的完整边框。
   区分只来自：轻微 tint + 更大的字 + 更宽松的 padding。 */
.fu-result-region {
  display: flex; flex-direction: column; gap: 8px; min-width: 0;
  padding: 14px 16px;
  border-radius: var(--yq-r-card);
  background: var(--yq-bg-sidebar);
}
.fu-result-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.fu-result-actions { position: relative; display: flex; align-items: center; gap: 8px; }
.fu-result-label {
  font-size: var(--yqw-t3-size); font-weight: var(--yqw-t3-weight); color: var(--yq-text-2);
}
.fu-primary { position: relative; margin-top: 2px; }
/* §22：主结果是 Summary Card 最强的视觉点之一，但**不超过页面 H1**
   （H1 是 clamp(28px, 1.2vw+14px, 32px) → 上限 32；这里上限 34，
   字重 680 vs 650，量级相当而不过头）。
   ⚠️ 元素仍是那个 readonly <input id="resultInput">：复制读的就是它的 .value。 */
.fu-primary .result-input {
  width: 100%; height: auto; padding: 0;
  font-family: var(--yq-font);
  font-size: clamp(24px, 1.9vw, 30px); font-weight: 680; line-height: 1.15;
  letter-spacing: -0.02em; color: var(--yq-text-1);
  background: transparent; border: none; outline: none; box-shadow: none;
}
/* §10：空态 Result 看见的是 `0 <目标币种>`，不是横线。
   它是 **placeholder**——内部 result state 一如既往为空，没有伪造一次换算。 */
.fu-primary .result-input::placeholder { color: var(--yq-text-3); font-weight: 680; opacity: 1; }

/* 窄屏上 `MOP 10,000,000,000.00` 这种 21 字的串在 24px 下要 271px，
   而 390 的 Result 只有 242px 可用 —— 实测会截断。所以移动端字号跟着视口收敛。
   上限 22px 仍是卡片里最大的字；下限 14px 让 320 那一档也装得下（4.6vw 时 320 那档实测仍差 2px，收到 4.5vw）。
   ⚠️ 刻意**不用**省略号糊过去：把结果截断等于给了用户一个错数字。
   ⚠️ 这条必须排在上面那条基础字号**之后** —— 两者同特异度(0,2,0)，
      靠 source order 决胜。第一版我把它写进了文件前半段的移动端块里，
      实测 390 上字号纹丝不动仍是 24px。 */
@media (max-width: 640px) {
  .fu-primary .result-input { font-size: clamp(14px, 4.5vw, 22px); }
}

.fu-rate-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: auto; }
.fu-rate-row .result-info { margin: 0; font-size: 13.5px; color: var(--yq-text-2); }
.fu-iconbtn {
  width: 32px; height: 32px; flex: none;              /* MINI 档 */
  border: 1px solid var(--yq-border); border-radius: var(--yq-r-sm);
  background: var(--yq-bg-page); color: var(--yq-text-2);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  transition: background var(--yq-dur), color var(--yq-dur);
}
.fu-iconbtn:hover { background: var(--yq-bg-quiet-hover); color: var(--yq-text-1); }
.fu-iconbtn:focus-visible { outline: 2px solid var(--yq-primary); outline-offset: 2px; }
.fu-iconbtn:disabled { opacity: .55; cursor: default; }
.fu-iconbtn svg { width: 15px; height: 15px; }

/* 错误：语义一字未改（§25 / R1 §25），只换视觉。
   flex:none ⇒ 出现时把下面的内容**推下去**，不吃 Trend 的高度。 */
.fu-error {
  flex: none; margin: 0;
  padding: 10px 12px; border-radius: var(--yq-r-sm);
  border: 1px solid var(--yq-danger); color: var(--yq-danger);
  background: color-mix(in srgb, var(--yq-danger) 7%, transparent);
  font-size: 13px; line-height: 1.5;
}

/* ════════════ Trend Card：独立、全宽、横向舒展 ════════════
   §13：「按日期的趋势图应该宽，不应该窄而高」。
   宽度 = Main Surface 满宽；高度比 R1 的 280 再增加。 */
/* Secondary Stage 的**卡片外壳**是三页共享的 primitive；
   各成员只改自己的内容尺寸，不再各画一张卡（§11 禁同义 class）。 */
.fu-stage {
  border: 1px solid var(--yq-border);
  border-radius: var(--yq-r-card);
  background: var(--yq-bg-page);
  padding: var(--fu-stage-pad, 10px);
}
.fu-trend-card { --fu-trend-h: 190px; }
@media (max-width: 1234px) { .fu-trend-card { --fu-trend-h: 175px; } }
@media (max-width: 640px)  { .fu-trend-card { --fu-trend-h: 165px; --fu-stage-pad: 8px; } }
.fu-chart { width: 100%; height: var(--fu-trend-h); margin: 0; flex: none; }

/* ── 诊断入口：Export logs 默认收起，左上角 logo 连点 10 下解锁 ────
   Founder 决定（R2 之后追加）：排障按钮不该占着每个用户的完成区。
   ⚠️ 这条**取代** R2 执行令 §23「不新增 diagnostics unlock」与
      §26「不加 brand unlink」—— 后者是连带的结构性后果，不是顺手加的：
      品牌 lockup 只要还是 <a href="/">，第一下点击就导航走了，
      10 下在结构上不可能发生。FX 右上角的 "All tools" 同样指向 "/"，
      所以页面到首页的路径一条没少。
   ⚠️ 用 display:none 而不是 opacity/visibility —— 后者仍在 Tab 键路径上。 */
/* ⚠️ brand 的 cursor/user-select 归共享的 yq-brand-header.css（别写第二遍）。
   这里只留 archetype 自己的东西：诊断入口的显隐。 */
.fu-shell .fu-diag { display: none; }
.fu-shell.fu-diag-on .fu-diag { display: inline-flex; }

/* ── Feedback：Main Surface 底部一行（§23，不进 Trend Card）──── */
.fu-feedback {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  padding-top: 10px; border-top: 1px solid var(--yq-border);
}

/* ============================================================
   Header 处理（Founder 在 20A2A-R2 之后追加）
   ⚠️ 这一段与 FILE_PROCESSOR 在 archetype-file-processor.css 里的那一份
      **逐条相同**，只是作用域从 body.fp-page 换成 body.fu-page ——
      与那个已发布的先例保持一致，而不是新发明一套。
      实现轮把两份合成一个共享件是**下一轮**的事（有第二个 FORM_UTILITY
      消费者时再抽，现在抽等于为一个消费者提前抽象）。
   ⚠️ 作用域仍然收着：GLOBAL_HEADER_GOLD_ROLLOUT 仍是 BACKLOG，
      只有挂了 .fu-page 的页面会变金色。
   ⚠️ **双重包裹**：选择器与 token **都**限定在 body.fu-page 上。
      20A1B 实测过：只放开选择器不会红，因为 var() 在别的页上求值失败、
      background-image 直接变成 none。两半必须一起守。

   色值取自 logo 资产，20A1B 在真实 header 上逐 stop 量过 WCAG 对比度：
     #F2D57D → #C7A344，ink #2A2205，实测 10.97 / 8.61 / 6.58，三点都过 AA。
     （白字方案当时实测只有 1.44–2.53，远低于 AA。）
   ============================================================ */
body.fu-page {
  --fu-gold-top: #F2D57D;
  --fu-gold-mid: #E8C86C;
  --fu-gold-bot: #C7A344;
  --fu-gold-edge: #B8902F;
  --fu-gold-ink: #2A2205;
}
body.fu-page .tl-nav {
  display: inline-flex; align-items: center;
  height: 32px; padding: 0 14px;
  background: linear-gradient(180deg, var(--fu-gold-top) 0%, var(--fu-gold-bot) 100%);
  border: 1px solid var(--fu-gold-edge);
  border-radius: var(--yq-r-ctl);
  color: var(--fu-gold-ink);
  font-size: 13px; font-weight: 600;
  white-space: nowrap;
}
body.fu-page .tl-nav:hover {
  background: linear-gradient(180deg, var(--fu-gold-mid) 0%, #BE9835 100%);
  color: #221B04;
}
body.fu-page .tl-actions { gap: 10px; }
/* 品牌 lockup 不承担"回首页"（Founder 决定：落地页用户多半不是从官网来的）。
   导航交给上面那个金色按钮 —— 它同样指向 "/"。 */
body.fu-page .tl-brand { cursor: default; user-select: none; }
@media (max-width: 640px) {
  body.fu-page .tl-nav { padding: 0 12px; }
  body.fu-page .tl-actions { gap: 8px; }
}
