onboarding 链路已走完（L-core 四份全文 + mockup/code 任务命中的 L-ref：AGENTS 硬规则表+Mockup Gate、code-conventions 起手必读段+R2/R3/R6/R7/R14/R15/R19、design-process M22/M48/M49/M50/Scope Handling/State-Completeness、domain-tvu 全文、figma-component-catalog、tokens 活源、canonical 组件活源）。下面先给判定与依据，再给产物。

## 1. 意图归类（R7 起手必判）

**intent classified as 场景 2.2（已有产品增量 · 已有页面加新 element）**，Task Entry Mode = **US-3**。
→ 视觉真源 = 页面既有惯例优先；local 没有的取 TVU 库（场景 2 是 element-level 决策，不是禁用 TVU）。
→ **不借机修既有合规债**（R6/M21 澄清：pre-design-system 不视为 bug）。

## 2. Phase 0 元素→组件映射（R0，逐条对活源实证）

| 元素 | Figma 库组件（catalog 实证） | code 活源 | 状态 |
|---|---|---|---|
| 顶部导航页签 | `Tab List` Type=Line + `Tab/Item` Active/Green/Line | `src/canonical/TabList.vue`·`TabItem.vue` | ✅ 不新增页签 |
| Type of source 三选一 | `radio` `9f07746f…`（status=on/off × enable） | `src/components/Radio/Radio.vue` | ✅ |
| 选择行↔内容区分隔 | **库内无 divider 组件**（catalog 2026-08-03 实测收口）→ 1px 线绑 `Color Type/Line/Light Divider` | `--line-light` | ✅ 按库范式 |
| 来源卡片 | 无卡片组件 → `Background/Layer_3` + `Radius/M` | `--bg-layer3`/`--r-m` | ✅ |
| 卡片状态 pill | `Badge` `4db5246d…` Rectangle×Color×Tag | `src/canonical/Badge.vue` | ✅ M3 配色 |
| 目标接收端 / NDI 下拉 | `select box/filled` `ca2ff89d…` size=M | `src/components/Select/Select.vue` | ✅ 只画收起态 |
| NDI 文本字段 | `input box/filled` `1f513914…` | `src/components/Input/Input.vue` | ✅ |
| 表单行 | `Form Item` `1923:50705` Label Width=200px/Dynamic | `src/components/FormItem/FormItem.vue` | ✅ |
| Apply / Rescan | `Button/dark M` `12b38c8b…` | `src/components/Button/Button.vue` | ✅ |
| 源离线告知 | `Message` `1408:17153` warning×L | `src/components/Message/Message.vue` | ✅ 设备侧谱系（M12） |
| 图标 | `arrow/dropdown`·`message/warning-2`·`action/close` | `src/icons/catalog/generated/*` | ✅ 逐字取自库，零自画 |

**关键实证收获**：库里本来就有 `input/ndi`、`input/hdmi`、`input/sdi`、`indicator/ndi`、`output/ndi` —— NDI 在这套设计系统里不是新概念，只是这个页面还没暴露它。（本次未在选择行加图标：现网那两项是纯文字，加图标属于改既有视觉，不在增量范围。）

## 3. 三处必须说明的判断

1. **控件选型用 radio，没换成分段控件。** catalog 里两条 Don't-use 互指（`radio` 说"切换内容面板用 Tab/Item"、`Tab/Item` 说"分段控件就用它"），但现网这一项带 `Type of source:` 字段标签、是表单取值形态；把 radio 换成 segmented 属于"替换已有 local 控件"= 场景 4.4，**必须 owner 显式发起**，不能夹在增量里做。若现网实为分段控件，换成 `Tab/Item Active/Green/Filled` 即可 —— 但要注意 catalog 记的 `CANONICAL-F93`：未选态原生 fill `#262626` 与内容区底色对比度实测 1.013、不可辨。
2. **HDMI/SDI/USB 帧的内容区画成"既有区域"标注带，没有编内容。** 我拿不到现网页面（本会话无 Figma MCP、无法跑 `pnpm pattern:lookup`），编三张本地输入卡片就是把不知道的产品状态伪造成看起来合理的样子。Scope Handling 要求"本期不动 X ≠ 删/隐藏 X"，所以该区保持可见、占同样几何、只是显式标注不改动 —— 这样分隔线与选择行的间距关系在三态里都能核。
3. **NDI 表单字段是我按 NDI 协议定的**（源发现 / Discovery server / Group / Bandwidth / 目标接收端），需求里没给字段清单。`目标接收端` 与 IP Source 卡片同名同组件（M49 语义一致性），提交动词沿用现网的 `Apply`（Content Contract：同一概念只用一个词，不引入 `Update`/`OK`）。

另外多给了一帧边界态（NDI 源离线）—— M12 硬要求"外部/异步事件不得静默 no-op"，而 NDI 源是网络上随时会消失的东西。前进路径没做成"通知里塞按钮"（`Message` 无按钮、设备侧不混用 `Notification` 谱系），而是让 NDI source 字段进 error 态 + 旁边本就有的 `Rescan` 承接。

## 4. 本轮没跑成的机检（如实登记）

`audit:no-hardcoded-design-tokens`、`audit:consumer-code`（R14/R19/R9 探针）、`pattern:lookup` 都跑不了 —— 本会话 Bash 停用。补偿做法：颜色/间距/字阶全部走 `var()`，文件里唯一出现 hex 的地方是顶部 token 定义块，那是 `src/tokens/variables.css` dark 段的**逐字副本**（值一位没改）；`position:absolute` 只用了一处并带了 affordance 注释。

```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Device Local Settings — Type of source 三选一（新增 NDI）</title>
<style>
/* ============================================================================
   1) TVU DESIGN SYSTEM TOKENS
   逐字复制自 src/tokens/variables.css 的 dark (:root) 段，只取本页用到的子集，
   值一位未改。正规工程做法是构建期把 variables.css 原样拷成 tvu-tokens.css 再
   <link>（code-conventions §R2.1 copyDesignTokensPlugin）；本文件为单文件交付，
   故内联同一份值。深色是设计系统默认，本页仅交付 dark（domain-tvu §M4）。
   ========================================================================== */
:root{
  /* Neutrals */
  --color-grey-8:#595959;
  /* Brand / status */
  --brand:#2fb54e; --brand-hover:#41c760; --brand-disable:#1a652c; --brand-match:#0b2b13;
  --red:#ea4233; --red-hover:#ed5b4e;
  --orange:#f68512; --orange-bg:#fff3db;
  --blue:#3892f3; --blue-bg:#e1eefd;
  /* Text */
  --text-heading:#ffffff; --text-body:#f8f8f8; --text-2:#cccccc; --text-tips:#9e9e9e;
  --text-placeholder:#7b7b7b; --text-disabled:#595959; --text-primary-btn:#f8f8f8;
  /* Icon */
  --icon-active:#f8f8f8; --icon-default:#9e9e9e; --icon-placeholder:#7b7b7b;
  /* Surface */
  --bg-layer1:#141414; --bg-layer2:#1f1f1f; --bg-layer3:#262626; --bg-layer4:#353535;
  --bg-grey-btn-en:#434343; --bg-grey-btn-hv:#595959;
  /* Line */
  --line-border:#595959; --line-deep:#353535; --line-light:#434343;
  --select-disabled-border:var(--color-grey-8);
  /* Spacing */
  --sp-xxs:4px; --sp-xs:8px; --sp-s:12px; --sp-m:16px; --sp-l:24px; --sp-xl:32px; --sp-xxl:40px;
  /* Radius */
  --r-xs:2px; --r-s:4px; --r-m:8px; --r-l:12px; --r-xl:16px;
  /* Breakpoints */
  --bp-md:960px; --bp-xl:1280px;
  /* Font */
  --font-family-en:-apple-system, BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,'Fira Sans',
    'Droid Sans','Helvetica Neue',sans-serif;
  --font-family-zh:'PingFang SC','Microsoft YaHei',Helvetica,Arial,sans-serif;
  --font-family-base:var(--font-family-en),var(--font-family-zh);
  /* Text styles */
  --text-style-tips:400 12px/16px var(--font-family-base);
  --text-style-body:400 14px/21px var(--font-family-base);
  --text-style-pop-title:600 16px/24px var(--font-family-base);
  --text-style-headline-2:600 18px/27px var(--font-family-base);
  --text-style-headline-1:600 20px/30px var(--font-family-base);
  /* Component-variant tokens */
  --radio-circle-size:16px; --radio-circle-border-width:1.2px; --radio-selected-dot-size:6px;
  --badge-circle-size:24px;
  --form-item-row-height:32px;
  --form-item-label-width-200:200px;
  --form-item-root-width-single-line:638px;
  --prompt-message-l-max-width:480px;
  /* Derived aliases */
  --input-filled-bg:var(--bg-layer4);
  --green-bg:var(--brand-match);
  --radio-default-border:var(--text-tips);
  --radio-selected-dot:var(--bg-layer1);
  --prompt-info-text:#3892f3;
  --prompt-warning-text:#f68512;
}

/* ============================================================================
   2) 文档外壳（注释层 —— 不是产品 UI；语言用中文，产品帧内一律英文单语，
      code-conventions §R16 / mockup-conventions §M33 字符串边界）
   ========================================================================== */
*{box-sizing:border-box}
html,body{margin:0}
body{
  background:var(--bg-layer1);
  color:var(--text-body);
  font:var(--text-style-body);
  -webkit-font-smoothing:antialiased;
}
.doc{max-width:var(--bp-xl);margin:0 auto;padding:var(--sp-xl) var(--sp-l) var(--sp-xxl)}
.doc__title{margin:0 0 var(--sp-xs);font:var(--text-style-headline-1);color:var(--text-heading)}
.doc__sub{margin:0 0 var(--sp-l);font:var(--text-style-body);color:var(--text-tips)}
.doc__block{
  background:var(--bg-layer2);
  border:1px solid var(--line-deep);
  border-radius:var(--r-m);
  padding:var(--sp-m) var(--sp-l);
  margin-bottom:var(--sp-l);
}
.doc__block h2{margin:0 0 var(--sp-xs);font:var(--text-style-pop-title);color:var(--text-heading)}
.doc__block ul{margin:0;padding-left:var(--sp-l);color:var(--text-2)}
.doc__block li{margin:var(--sp-xxs) 0}
.doc__block li b{color:var(--text-body);font-weight:600}
.doc__meta{color:var(--text-tips);font:var(--text-style-tips)}
.frame-caption{
  display:flex;align-items:baseline;gap:var(--sp-xs);flex-wrap:wrap;
  margin:var(--sp-xl) 0 var(--sp-xs);
}
.frame-caption__no{
  font:var(--text-style-tips);color:var(--brand);
  border:1px solid var(--brand);border-radius:var(--r-xs);
  padding:1px var(--sp-xxs);
}
.frame-caption__name{font:var(--text-style-pop-title);color:var(--text-heading)}
.frame-caption__note{font:var(--text-style-tips);color:var(--text-tips)}
.doc table{width:100%;border-collapse:collapse;font:var(--text-style-tips)}
.doc th,.doc td{
  text-align:left;padding:var(--sp-xs) var(--sp-s);
  border-bottom:1px solid var(--line-deep);vertical-align:top;
}
.doc th{color:var(--text-tips);font-weight:600;white-space:nowrap}
.doc td{color:var(--text-2)}
.doc td code{color:var(--text-body)}
.table-scroll{overflow-x:auto}
code{font-family:var(--font-family-base);color:var(--text-body)}

/* ============================================================================
   3) 产品帧外壳（设备本地设置页）
   ========================================================================== */
.frame{
  background:var(--bg-layer1);
  border:1px solid var(--line-deep);
  border-radius:var(--r-m);
  overflow:hidden;
}

/* 顶部导航条 —— 既有 chrome，本轮零改动（不新增页签） */
.device-nav{
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-l);
  padding-inline:var(--sp-l);
  background:var(--bg-layer2);
  border-bottom:1px solid var(--line-deep);
}
.device-nav__id{
  flex:0 0 auto;font:var(--text-style-tips);color:var(--text-tips);
  font-variant-numeric:tabular-nums;
}

/* Tab List Type=Line —— mirror src/components/Tab/TabList.vue */
.tvu-tab-list{display:flex;align-items:stretch;overflow-x:auto;margin-bottom:-1px}
.tvu-tab-list__items{display:inline-flex;align-items:center;gap:var(--sp-l)}
/* Tab/Item Type=Line —— mirror src/components/Tab/TabItem.vue */
.tvu-tab-item{
  display:inline-flex;align-items:center;gap:var(--sp-xs);
  height:32px;padding:0 var(--sp-m);
  font:var(--text-style-body);color:var(--text-tips);
  cursor:pointer;white-space:nowrap;user-select:none;
  border-bottom:1px solid transparent;
  transition:color .15s,border-color .15s;
}
.tvu-tab-item:hover{color:var(--text-2)}
/* Property 2 = Green（Active/Green/Line）：White 变体的 active 下边线是
   --line-light #434343，在深色导航上几乎不可辨，故本页取 Green 变体 */
.tvu-tab-item--active{color:var(--brand);border-bottom-color:var(--brand);font-weight:700}

/* 页面内容区 —— 顶部导航下方不再放重复的 page-title strip（catalog §Top bar Don't use for） */
.page{padding:var(--sp-l)}

/* ============================================================================
   4) 改动 ①：Type of source 选择行（二选一 → 三选一互斥）
   ========================================================================== */
.source-type{
  display:flex;align-items:center;gap:var(--sp-l);flex-wrap:wrap;
  min-height:var(--form-item-row-height);
}
.source-type__label{flex:0 0 auto;font:var(--text-style-body);color:var(--text-tips)}
.source-type__options{display:flex;align-items:center;gap:var(--sp-xl);flex-wrap:wrap}

/* radio —— mirror src/components/Radio/Radio.vue（dark theme=on） */
.tvu-radio{
  display:inline-flex;align-items:center;gap:var(--sp-xs);
  height:21px;cursor:pointer;user-select:none;
  font:var(--text-style-body);color:var(--text-body);
}
.tvu-radio__circle{
  flex-shrink:0;
  width:var(--radio-circle-size);height:var(--radio-circle-size);
  border-radius:50%;
  border:var(--radio-circle-border-width) solid var(--radio-default-border);
  background:transparent;
  display:flex;align-items:center;justify-content:center;
  transition:border-color .15s,background .15s;
}
.tvu-radio:hover .tvu-radio__circle{border-color:var(--text-2)}
.tvu-radio[aria-checked="true"] .tvu-radio__circle,
.tvu-radio[aria-checked="true"]:hover .tvu-radio__circle{
  background:var(--brand);border-color:var(--brand);
}
.tvu-radio__dot{
  width:var(--radio-selected-dot-size);height:var(--radio-selected-dot-size);
  border-radius:50%;background:var(--radio-selected-dot);
}
.tvu-radio:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:var(--r-xs)}

/* ============================================================================
   5) 改动 ③：选择行与内容区之间的视觉分隔
   本库内没有独立的分隔线 component（figma-component-catalog 2026-08-03 实测
   收口：三组关键词检索均无命中）。库范式 = 1px 线 + 绑 Line 样式，此处绑
   Color Type/Line/Light Divider（--line-light）。
   ========================================================================== */
.section-divider{
  height:1px;border:0;margin:var(--sp-m) 0 var(--sp-l);
  background:var(--line-light);
}

/* ============================================================================
   6) 库控件（全部 mirror canonical / base 活源，无自画）
   ========================================================================== */

/* Button/dark M —— mirror src/components/Button/Button.vue（size=M, radius=square）*/
.tvu-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-xxs);
  height:32px;padding-inline:16px;
  border:1px solid transparent;border-radius:var(--r-s);
  font-family:var(--font-family-base);font-size:14px;font-weight:400;line-height:21px;
  white-space:nowrap;cursor:pointer;user-select:none;
  transition:background .15s,color .15s,border-color .15s;
}
.tvu-btn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.tvu-btn--fill-green{background:var(--brand);color:var(--text-primary-btn)}
.tvu-btn--fill-green:hover{background:var(--brand-hover)}
/* status=disable：filling/green → bg --brand-disable / text --text-tips */
.tvu-btn--fill-green[disabled]{
  background:var(--brand-disable);color:var(--text-tips);cursor:not-allowed;
}
.tvu-btn--ghost-green{background:transparent;color:var(--brand);border:1.2px solid var(--brand)}
.tvu-btn--ghost-green:hover{background:var(--green-bg)}
.tvu-btn--ghost-gray{background:transparent;color:var(--text-2);border:1.2px solid var(--bg-grey-btn-en)}
.tvu-btn--ghost-gray:hover{background:var(--bg-grey-btn-en)}

/* input box/filled —— mirror src/components/Input/Input.vue（variant=filled, size=m）*/
.tvu-input{
  display:block;width:100%;height:32px;
  padding:var(--sp-xs) var(--sp-s);
  background:var(--input-filled-bg);
  border:1px solid transparent;border-radius:var(--r-s);
  color:var(--text-body);
  font-size:14px;font-family:var(--font-family-base);
  outline:none;transition:border-color .15s;
}
.tvu-input::placeholder{color:var(--text-placeholder)}
.tvu-input:hover,.tvu-input:focus{border-color:var(--brand)}

/* select box/filled —— mirror src/components/Select/Select.vue（filled, size=m）
   本页只画收起态；展开后的选项面板是另一个组件 Drop down List/Select Type=Radio */
.tvu-select{position:relative;display:block;width:100%}
.tvu-select__field{
  display:flex;align-items:center;width:100%;height:32px;
  padding:0 var(--sp-xl) 0 var(--sp-s);
  background:var(--bg-layer4);
  border:1px solid transparent;border-radius:var(--r-s);
  color:var(--text-body);
  font-size:13px;font-family:var(--font-family-base);
  cursor:pointer;outline:none;transition:border-color .15s;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.tvu-select__field:hover,.tvu-select__field:focus-visible{border-color:var(--brand)}
.tvu-select--placeholder .tvu-select__field{color:var(--text-placeholder)}
.tvu-select--error .tvu-select__field{border-color:var(--red)}
/* overlay anchored to the field box —— 箭头是装饰层、不参与布局流（§R14 例外，已注明 affordance）*/
.tvu-select__arrow{
  position:absolute;right:12px;top:50%;transform:translateY(-50%);
  pointer-events:none;color:var(--text-tips);display:flex;align-items:center;
}

/* Form Item —— mirror src/components/FormItem/FormItem.vue（Theme=Dark）*/
.tvu-form-item{--fi-label:var(--text-tips);color:var(--text-body)}
.tvu-form-item__main{display:flex;gap:var(--sp-xs);align-items:flex-start}
.tvu-form-item__label{
  flex:0 0 auto;display:inline-flex;align-items:center;gap:var(--sp-xxs);
  width:var(--form-item-label-width-200);
  min-height:var(--form-item-row-height);padding-block:var(--sp-xs);
  font:var(--text-style-body);color:var(--fi-label);
}
.tvu-form-item__optional{color:var(--text-placeholder)}
.tvu-form-item__content{
  display:flex;flex:1 1 auto;min-width:0;flex-direction:column;gap:var(--sp-xxs);
}
.tvu-form-item__message{margin:0;font:var(--text-style-tips);color:var(--fi-label)}
.tvu-form-item__message--error{color:var(--red)}
/* Layout = 2 lines（窄容器 / 卡片内），Label Width = Dynamic */
.tvu-form-item--two-lines .tvu-form-item__main{flex-direction:column;gap:0}
.tvu-form-item--two-lines .tvu-form-item__label{
  width:auto;min-height:0;padding-block:0 var(--sp-xxs);
}
.tvu-form-item--two-lines .tvu-form-item__content{width:100%}

/* Badge Type=Rectangle —— mirror src/canonical/Badge.vue */
.tvu-badge{
  display:inline-flex;align-items:center;justify-content:center;
  height:var(--badge-circle-size);padding-inline:var(--sp-xs);
  border:1px solid transparent;border-radius:var(--r-xl);
  font:var(--text-style-tips);white-space:nowrap;
}
/* domain-tvu §M3 广播 ON AIR 惯例：Live=红 / Preview·Standby=绿 / Inactive=灰 */
.tvu-badge--red-filled{background:var(--red);color:var(--text-primary-btn)}
.tvu-badge--green-line{border-color:var(--brand);color:var(--brand)}
.tvu-badge--neutral-line{border-color:var(--line-deep);color:var(--text-body)}

/* Message —— mirror src/components/Message/Message.vue（size=L，无按钮，点击关闭）
   设备侧顶部通知走 Message set，不混用 web 侧 Notification set（domain-tvu §M12 谱系隔离）*/
.tvu-message{
  display:flex;align-items:center;gap:var(--sp-xs);
  padding:var(--sp-s) var(--sp-m);border-radius:var(--r-s);
  max-width:var(--prompt-message-l-max-width);
}
.tvu-message--warning{background:var(--orange-bg);color:var(--prompt-warning-text)}
.tvu-message--info{background:var(--blue-bg);color:var(--prompt-info-text)}
.tvu-message__icon{flex:0 0 auto;width:16px;height:16px;display:inline-flex;margin-top:2px}
.tvu-message__copy{
  flex:1 1 auto;min-width:0;margin:0;
  font:var(--text-style-body);color:inherit;
}
.tvu-message__close{
  flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:16px;height:16px;padding:0;border:0;background:transparent;
  color:var(--icon-default);cursor:pointer;
}
.tvu-message__close:hover{color:var(--icon-active)}

/* ============================================================================
   7) 内容区 A：既有来源卡片网格（IP Source）—— 结构沿用，本轮零改动
   ========================================================================== */
.source-grid{
  display:grid;gap:var(--sp-m);
  grid-template-columns:repeat(auto-fill,minmax(300px,1fr));
}
.source-card{
  display:flex;flex-direction:column;gap:var(--sp-s);
  padding:var(--sp-m);
  background:var(--bg-layer3);
  border:1px solid var(--line-deep);
  border-radius:var(--r-m);
}
.source-card__head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-xs)}
.source-card__name{
  font:var(--text-style-body);font-weight:600;color:var(--text-heading);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.source-card__preview{
  position:relative;
  aspect-ratio:16/9;
  display:flex;align-items:center;justify-content:center;
  background:linear-gradient(180deg,var(--bg-layer2),var(--bg-layer1));
  border:1px solid var(--line-deep);border-radius:var(--r-s);
  overflow:hidden;
}
.source-card__preview-meta{
  font:var(--text-style-tips);color:var(--text-tips);
  font-variant-numeric:tabular-nums;
}
.source-card__kv{display:flex;align-items:baseline;gap:var(--sp-xs);font:var(--text-style-tips)}
.source-card__k{flex:0 0 auto;color:var(--text-tips)}
.source-card__v{
  color:var(--text-body);font-variant-numeric:tabular-nums;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.source-card__actions{display:flex;justify-content:flex-end;margin-top:var(--sp-xxs)}

/* ============================================================================
   8) 内容区 B：NDI 专属表单（本轮新增）
   ========================================================================== */
.ndi-panel{display:flex;flex-direction:column;gap:var(--sp-m);max-width:var(--form-item-root-width-single-line)}
.ndi-form{display:flex;flex-direction:column;gap:var(--sp-s)}
.field-row{display:flex;gap:var(--sp-xs);align-items:flex-start}
.field-row .tvu-select{flex:1 1 auto;min-width:0}
.field-row .tvu-btn{flex:0 0 auto}
/* 动作按钮右缘与控件右缘对齐（domain-tvu §M17 几何一致性核查项）*/
.ndi-actions{display:flex;justify-content:flex-end;padding-top:var(--sp-xs)}

/* ============================================================================
   9) 既有区域标注带（Scope Handling：本期不动 X ≠ 删 / 隐藏 X）
   ========================================================================== */
.unchanged{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--sp-xs);text-align:center;
  min-height:264px;padding:var(--sp-xl) var(--sp-m);
  background:var(--bg-layer2);
  border:1px dashed var(--line-border);border-radius:var(--r-m);
}
.unchanged__title{font:var(--text-style-body);font-weight:600;color:var(--text-2)}
.unchanged__body{margin:0;max-width:52ch;font:var(--text-style-tips);color:var(--text-tips)}

.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}
</style>
</head>
<body>

<!-- ── 图标 sprite：三个图标的 path 逐字取自 TVU 库活源，未自画（code-conventions §R1/§R15）
     · arrow/dropdown   ← src/icons/catalog/generated/arrow.ts
     · message/warning-2 ← src/icons/catalog/generated/message.ts
     · action/close      ← src/icons/raw.ts (IconClose)
     注：message/warning-2 的 fill 是库导出时被解析成的字面 hex #F68512（等于
     --prompt-warning-text 的值）。这是 FIGMA_AS_SOURCE_OF_TRUTH 已登记的合法
     divergence `icon-variable-resolved-to-hex`，故保留原样、不改库资产。 ── -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
  <symbol id="i-arrow-dropdown" viewBox="0 0 16 16">
    <path d="M3.05146 6.20128C3.27161 5.95374 3.65086 5.93145 3.89853 6.15148L7.86707 9.67727C7.94284 9.74458 8.05698 9.74458 8.13274 9.67727L12.1013 6.15148C12.349 5.93145 12.7282 5.95374 12.9484 6.20128C13.1685 6.44883 13.1462 6.82788 12.8985 7.04791L8.39841 11.046C8.17112 11.2479 7.8287 11.2479 7.60141 11.046L3.10129 7.04791C2.85362 6.82788 2.83131 6.44883 3.05146 6.20128Z" fill="currentColor"/>
  </symbol>
  <symbol id="i-message-warning-2" viewBox="0 0 16 16">
    <path d="M8 1C4.13438 1 1 4.13438 1 8C1 11.8656 4.13438 15 8 15C11.8656 15 15 11.8656 15 8C15 4.13438 11.8656 1 8 1ZM7.9999 4C8.33127 4 8.5999 4.26863 8.5999 4.6V9C8.5999 9.33137 8.33127 9.6 7.9999 9.6C7.66853 9.6 7.3999 9.33137 7.3999 9V4.6C7.3999 4.26863 7.66853 4 7.9999 4ZM7.9999 12C7.66853 12 7.3999 11.7314 7.3999 11.4C7.3999 11.0686 7.66853 10.8 7.9999 10.8C8.33127 10.8 8.5999 11.0686 8.5999 11.4C8.5999 11.7314 8.33127 12 7.9999 12Z" fill="#F68512"/>
  </symbol>
  <symbol id="i-action-close" viewBox="0 0 16 16">
    <path d="M12.5758 13.4247C12.8101 13.659 13.19 13.659 13.4243 13.4247C13.6586 13.1903 13.6586 12.8104 13.4243 12.5761L8.84898 8.00082L13.4248 3.42543C13.6591 3.19113 13.6591 2.81123 13.4248 2.5769C13.1905 2.34258 12.8106 2.34256 12.5763 2.57687L8.00045 7.15229L3.42429 2.57613C3.18997 2.34181 2.81007 2.34181 2.57576 2.57613C2.34145 2.81044 2.34145 3.19034 2.57576 3.42465L7.15188 8.00078L2.57596 12.5763C2.34163 12.8106 2.34161 13.1905 2.57592 13.4248C2.81022 13.6592 3.19012 13.6592 3.42445 13.4249L8.00041 8.84931L12.5758 13.4247Z" fill="currentColor"/>
  </symbol>
</svg>

<div class="doc">

  <h1 class="doc__title">Device Local Settings — Type of source 增量设计</h1>
  <p class="doc__sub">
    二选一 → 三选一互斥（新增 NDI）· 内容区按选中项切换 · 选择行与内容区加分隔 · 顶部导航不新增页签
  </p>

  <section class="doc__block">
    <h2>本轮改动（4 项）</h2>
    <ul>
      <li><b>① Type of source 扩为三选一互斥</b>：新增第三项 <code>NDI</code>，控件仍是既有的 radio 互斥组，不换控件形态。</li>
      <li><b>② 内容区随选中项切换</b>：<code>IP Source</code> 保持既有来源卡片网格（预览 / IP / 目标接收端 / Apply）；<code>NDI</code> 切换为 NDI 专属表单。</li>
      <li><b>③ 选择行与内容区之间加一条 1px 分隔线</b>，绑 <code>Color Type/Line/Light Divider</code>。</li>
      <li><b>④ 顶部导航零改动</b>：页签集合、顺序、命名全部沿用现网，NDI 不作为导航项出现。</li>
    </ul>
  </section>

  <section class="doc__block">
    <h2>判定与边界</h2>
    <ul>
      <li><b>任务性质</b>：已有产品 · 已有页面加新 element（增量）。视觉真源 = 页面既有惯例优先，页面没有的才取 TVU 库；不借机改既有区域。</li>
      <li><b>控件选型</b>：沿用 radio 互斥组。该项带 <code>Type of source:</code> 字段标签、是表单取值形态；换成分段控件属于「替换既有控件」，须单独发起、不夹在本次增量里。</li>
      <li><b>HDMI/SDI/USB 内容区</b>：本轮不改动，帧 1 中以「既有区域」标注带占位并保持可见 —— 不动 ≠ 删除或隐藏，该区提供空间参照与风格基线。实装时按现网原样保留。</li>
      <li><b>NDI 字段集</b>：按 NDI 协议能力定为 源发现 / Discovery server / Group name / Bandwidth / 目标接收端。<code>Target receiver</code> 与 IP Source 卡片同一概念、同一组件。</li>
      <li><b>提交动词</b>：沿用页面既有的 <code>Apply</code>，两个内容区一致，不引入 <code>Update</code> / <code>OK</code> / <code>Save</code> 等同义词。</li>
      <li><b>Apply 启用条件</b>：目标接收端已选 且 源在线。任一不满足 → Apply 为 <code>status=disable</code> 真变体（见帧 2 第三张卡与帧 3b）。</li>
      <li><b>主题</b>：设备操控面按 dark 单主题交付，不出浅色对照。</li>
    </ul>
  </section>

  <!-- ═════════════════════════════ 帧 1 ═════════════════════════════ -->
  <div class="frame-caption">
    <span class="frame-caption__no">帧 1 / 3</span>
    <span class="frame-caption__name">Type of source = HDMI/SDI/USB</span>
    <span class="frame-caption__note">既有选项 · 内容区本轮不改动</span>
  </div>

  <div class="frame" lang="en">
    <!-- 顶部导航（既有 · 零改动）；页签名称沿用现网，本轮不新增 / 不改名 -->
    <div class="device-nav">
      <nav class="tvu-tab-list" role="tablist" aria-label="Device settings sections">
        <div class="tvu-tab-list__items">
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Status</div>
          <div class="tvu-tab-item tvu-tab-item--active" role="tab" aria-selected="true" tabindex="0">Source</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Encoder</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Network</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">System</div>
        </div>
      </nav>
      <div class="device-nav__id">RPS One &middot; 10.0.0.42</div>
    </div>

    <div class="page">
      <!-- ① 三选一互斥选择行 -->
      <div class="source-type">
        <span class="source-type__label" id="f1-src-label">Type of source:</span>
        <div class="source-type__options" role="radiogroup" aria-labelledby="f1-src-label">
          <span class="tvu-radio" role="radio" aria-checked="true" tabindex="0">
            <span class="tvu-radio__circle"><span class="tvu-radio__dot"></span></span>
            HDMI/SDI/USB
          </span>
          <span class="tvu-radio" role="radio" aria-checked="false" tabindex="-1">
            <span class="tvu-radio__circle"></span>
            IP Source
          </span>
          <span class="tvu-radio" role="radio" aria-checked="false" tabindex="-1">
            <span class="tvu-radio__circle"></span>
            NDI
          </span>
        </div>
      </div>

      <!-- ③ 选择行与内容区的视觉分隔 -->
      <hr class="section-divider">

      <!-- 内容区：既有 HDMI/SDI/USB 面板，本轮不改动 -->
      <div class="unchanged" lang="zh-CN">
        <div class="unchanged__title">既有内容区 · 本轮不改动</div>
        <p class="unchanged__body">
          HDMI / SDI / USB 本地输入面板沿用现网页面原样，本次增量未触碰该区。
          此处保持同等几何以核对选择行、分隔线与内容区的间距关系。
        </p>
      </div>
    </div>
  </div>

  <!-- ═════════════════════════════ 帧 2 ═════════════════════════════ -->
  <div class="frame-caption">
    <span class="frame-caption__no">帧 2 / 3</span>
    <span class="frame-caption__name">Type of source = IP Source</span>
    <span class="frame-caption__note">既有选项 · 内容区沿用既有来源卡片网格（预览 / IP / 目标接收端 / Apply）</span>
  </div>

  <div class="frame" lang="en">
    <div class="device-nav">
      <nav class="tvu-tab-list" role="tablist" aria-label="Device settings sections">
        <div class="tvu-tab-list__items">
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Status</div>
          <div class="tvu-tab-item tvu-tab-item--active" role="tab" aria-selected="true" tabindex="0">Source</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Encoder</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Network</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">System</div>
        </div>
      </nav>
      <div class="device-nav__id">RPS One &middot; 10.0.0.42</div>
    </div>

    <div class="page">
      <div class="source-type">
        <span class="source-type__label" id="f2-src-label">Type of source:</span>
        <div class="source-type__options" role="radiogroup" aria-labelledby="f2-src-label">
          <span class="tvu-radio" role="radio" aria-checked="false" tabindex="-1">
            <span class="tvu-radio__circle"></span>
            HDMI/SDI/USB
          </span>
          <span class="tvu-radio" role="radio" aria-checked="true" tabindex="0">
            <span class="tvu-radio__circle"><span class="tvu-radio__dot"></span></span>
            IP Source
          </span>
          <span class="tvu-radio" role="radio" aria-checked="false" tabindex="-1">
            <span class="tvu-radio__circle"></span>
            NDI
          </span>
        </div>
      </div>

      <hr class="section-divider">

      <div class="source-grid">

        <!-- 卡片 1：在播 -->
        <article class="source-card">
          <header class="source-card__head">
            <h3 class="source-card__name">IP Source 1</h3>
            <span class="tvu-badge tvu-badge--red-filled">Live</span>
          </header>
          <div class="source-card__preview">
            <span class="source-card__preview-meta">1920&times;1080 &middot; 59.94p</span>
          </div>
          <div class="source-card__kv">
            <span class="source-card__k">IP</span>
            <span class="source-card__v">10.0.0.51:5000</span>
          </div>
          <div class="tvu-form-item tvu-form-item--two-lines">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" id="f2c1-rx-label">Target receiver</label>
              <div class="tvu-form-item__content">
                <div class="tvu-select">
                  <div class="tvu-select__field" role="combobox" aria-expanded="false"
                       aria-labelledby="f2c1-rx-label" tabindex="0">Receiver A &mdash; Newsroom</div>
                  <span class="tvu-select__arrow"><svg width="16" height="16" aria-hidden="true"><use href="#i-arrow-dropdown"/></svg></span>
                </div>
              </div>
            </div>
          </div>
          <div class="source-card__actions">
            <button type="button" class="tvu-btn tvu-btn--ghost-green">Apply</button>
          </div>
        </article>

        <!-- 卡片 2：待播 -->
        <article class="source-card">
          <header class="source-card__head">
            <h3 class="source-card__name">IP Source 2</h3>
            <span class="tvu-badge tvu-badge--green-line">Standby</span>
          </header>
          <div class="source-card__preview">
            <span class="source-card__preview-meta">1280&times;720 &middot; 50p</span>
          </div>
          <div class="source-card__kv">
            <span class="source-card__k">IP</span>
            <span class="source-card__v">10.0.0.52:5000</span>
          </div>
          <div class="tvu-form-item tvu-form-item--two-lines">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" id="f2c2-rx-label">Target receiver</label>
              <div class="tvu-form-item__content">
                <div class="tvu-select">
                  <div class="tvu-select__field" role="combobox" aria-expanded="false"
                       aria-labelledby="f2c2-rx-label" tabindex="0">Receiver B &mdash; Studio 2</div>
                  <span class="tvu-select__arrow"><svg width="16" height="16" aria-hidden="true"><use href="#i-arrow-dropdown"/></svg></span>
                </div>
              </div>
            </div>
          </div>
          <div class="source-card__actions">
            <button type="button" class="tvu-btn tvu-btn--ghost-green">Apply</button>
          </div>
        </article>

        <!-- 卡片 3：从未配置（目标接收端未选 → Apply 为 disable 真变体） -->
        <article class="source-card">
          <header class="source-card__head">
            <h3 class="source-card__name">IP Source 3</h3>
            <span class="tvu-badge tvu-badge--neutral-line">Inactive</span>
          </header>
          <div class="source-card__preview">
            <span class="source-card__preview-meta">No signal</span>
          </div>
          <div class="source-card__kv">
            <span class="source-card__k">IP</span>
            <span class="source-card__v">10.0.0.53:5000</span>
          </div>
          <div class="tvu-form-item tvu-form-item--two-lines">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" id="f2c3-rx-label">Target receiver</label>
              <div class="tvu-form-item__content">
                <div class="tvu-select tvu-select--placeholder">
                  <div class="tvu-select__field" role="combobox" aria-expanded="false"
                       aria-labelledby="f2c3-rx-label" tabindex="0">Select a receiver</div>
                  <span class="tvu-select__arrow"><svg width="16" height="16" aria-hidden="true"><use href="#i-arrow-dropdown"/></svg></span>
                </div>
                <p class="tvu-form-item__message">Never configured. Set a receiver to enable this source.</p>
              </div>
            </div>
          </div>
          <div class="source-card__actions">
            <button type="button" class="tvu-btn tvu-btn--fill-green" disabled>Apply</button>
          </div>
        </article>

      </div>
    </div>
  </div>

  <!-- ═════════════════════════════ 帧 3 ═════════════════════════════ -->
  <div class="frame-caption">
    <span class="frame-caption__no">帧 3 / 3</span>
    <span class="frame-caption__name">Type of source = NDI</span>
    <span class="frame-caption__note">新增选项 · 内容区切换为 NDI 专属表单</span>
  </div>

  <div class="frame" lang="en">
    <div class="device-nav">
      <nav class="tvu-tab-list" role="tablist" aria-label="Device settings sections">
        <div class="tvu-tab-list__items">
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Status</div>
          <div class="tvu-tab-item tvu-tab-item--active" role="tab" aria-selected="true" tabindex="0">Source</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Encoder</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Network</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">System</div>
        </div>
      </nav>
      <div class="device-nav__id">RPS One &middot; 10.0.0.42</div>
    </div>

    <div class="page">
      <div class="source-type">
        <span class="source-type__label" id="f3-src-label">Type of source:</span>
        <div class="source-type__options" role="radiogroup" aria-labelledby="f3-src-label">
          <span class="tvu-radio" role="radio" aria-checked="false" tabindex="-1">
            <span class="tvu-radio__circle"></span>
            HDMI/SDI/USB
          </span>
          <span class="tvu-radio" role="radio" aria-checked="false" tabindex="-1">
            <span class="tvu-radio__circle"></span>
            IP Source
          </span>
          <span class="tvu-radio" role="radio" aria-checked="true" tabindex="0">
            <span class="tvu-radio__circle"><span class="tvu-radio__dot"></span></span>
            NDI
          </span>
        </div>
      </div>

      <hr class="section-divider">

      <div class="ndi-panel">
        <div class="ndi-form">

          <!-- NDI source：下拉 + Rescan（发现动作） -->
          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" id="f3-ndi-src-label">NDI source</label>
              <div class="tvu-form-item__content">
                <div class="field-row">
                  <div class="tvu-select">
                    <div class="tvu-select__field" role="combobox" aria-expanded="false"
                         aria-labelledby="f3-ndi-src-label" tabindex="0">STUDIO-A (OBS Studio)</div>
                    <span class="tvu-select__arrow"><svg width="16" height="16" aria-hidden="true"><use href="#i-arrow-dropdown"/></svg></span>
                  </div>
                  <button type="button" class="tvu-btn tvu-btn--ghost-gray">Rescan</button>
                </div>
                <p class="tvu-form-item__message">3 sources found on this network &middot; last scan 12:04:31</p>
              </div>
            </div>
          </div>

          <!-- Discovery server（可选） -->
          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" for="f3-disc">
                Discovery server <span class="tvu-form-item__optional">(Optional)</span>
              </label>
              <div class="tvu-form-item__content">
                <input class="tvu-input" id="f3-disc" type="text" placeholder="e.g. 10.0.0.5">
                <p class="tvu-form-item__message">Leave empty to use mDNS auto-discovery.</p>
              </div>
            </div>
          </div>

          <!-- Group name（可选） -->
          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" for="f3-group">
                Group name <span class="tvu-form-item__optional">(Optional)</span>
              </label>
              <div class="tvu-form-item__content">
                <input class="tvu-input" id="f3-group" type="text" placeholder="Public">
                <p class="tvu-form-item__message">The default group is used when empty.</p>
              </div>
            </div>
          </div>

          <!-- Bandwidth -->
          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" id="f3-bw-label">Bandwidth</label>
              <div class="tvu-form-item__content">
                <div class="tvu-select">
                  <div class="tvu-select__field" role="combobox" aria-expanded="false"
                       aria-labelledby="f3-bw-label" tabindex="0">Highest</div>
                  <span class="tvu-select__arrow"><svg width="16" height="16" aria-hidden="true"><use href="#i-arrow-dropdown"/></svg></span>
                </div>
                <p class="tvu-form-item__message">Lowest uses the NDI proxy stream for constrained networks.</p>
              </div>
            </div>
          </div>

          <!-- Target receiver：与 IP Source 卡片同一概念、同一组件 -->
          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" id="f3-rx-label">Target receiver</label>
              <div class="tvu-form-item__content">
                <div class="tvu-select">
                  <div class="tvu-select__field" role="combobox" aria-expanded="false"
                       aria-labelledby="f3-rx-label" tabindex="0">Receiver A &mdash; Newsroom</div>
                  <span class="tvu-select__arrow"><svg width="16" height="16" aria-hidden="true"><use href="#i-arrow-dropdown"/></svg></span>
                </div>
              </div>
            </div>
          </div>

        </div>

        <div class="ndi-actions">
          <button type="button" class="tvu-btn tvu-btn--fill-green">Apply</button>
        </div>
      </div>
    </div>
  </div>

  <!-- ═════════════════════════════ 帧 3b ═════════════════════════════ -->
  <div class="frame-caption">
    <span class="frame-caption__no">帧 3b</span>
    <span class="frame-caption__name">Type of source = NDI &middot; 所选源离线</span>
    <span class="frame-caption__note">
      外部事件不得静默 no-op：检测 → 告知（Message warning）→ 前进路径（字段 error 态 + Rescan）
    </span>
  </div>

  <div class="frame" lang="en">
    <div class="device-nav">
      <nav class="tvu-tab-list" role="tablist" aria-label="Device settings sections">
        <div class="tvu-tab-list__items">
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Status</div>
          <div class="tvu-tab-item tvu-tab-item--active" role="tab" aria-selected="true" tabindex="0">Source</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Encoder</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">Network</div>
          <div class="tvu-tab-item" role="tab" aria-selected="false" tabindex="-1">System</div>
        </div>
      </nav>
      <div class="device-nav__id">RPS One &middot; 10.0.0.42</div>
    </div>

    <div class="page">
      <div class="source-type">
        <span class="source-type__label" id="f3b-src-label">Type of source:</span>
        <div class="source-type__options" role="radiogroup" aria-labelledby="f3b-src-label">
          <span class="tvu-radio" role="radio" aria-checked="false" tabindex="-1">
            <span class="tvu-radio__circle"></span>
            HDMI/SDI/USB
          </span>
          <span class="tvu-radio" role="radio" aria-checked="false" tabindex="-1">
            <span class="tvu-radio__circle"></span>
            IP Source
          </span>
          <span class="tvu-radio" role="radio" aria-checked="true" tabindex="0">
            <span class="tvu-radio__circle"><span class="tvu-radio__dot"></span></span>
            NDI
          </span>
        </div>
      </div>

      <hr class="section-divider">

      <div class="ndi-panel">
        <!-- Message status=warning size=L（设备侧谱系，无按钮，点击关闭） -->
        <div class="tvu-message tvu-message--warning" role="status">
          <span class="tvu-message__icon"><svg width="16" height="16" aria-hidden="true"><use href="#i-message-warning-2"/></svg></span>
          <p class="tvu-message__copy">NDI source &ldquo;STUDIO-A&rdquo; is no longer on the network.</p>
          <button type="button" class="tvu-message__close" aria-label="Close">
            <svg width="16" height="16" aria-hidden="true"><use href="#i-action-close"/></svg>
          </button>
        </div>

        <div class="ndi-form">

          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" id="f3b-ndi-src-label">NDI source</label>
              <div class="tvu-form-item__content">
                <div class="field-row">
                  <div class="tvu-select tvu-select--error">
                    <div class="tvu-select__field" role="combobox" aria-expanded="false"
                         aria-labelledby="f3b-ndi-src-label" aria-invalid="true" tabindex="0">STUDIO-A (OBS Studio)</div>
                    <span class="tvu-select__arrow"><svg width="16" height="16" aria-hidden="true"><use href="#i-arrow-dropdown"/></svg></span>
                  </div>
                  <button type="button" class="tvu-btn tvu-btn--ghost-gray">Rescan</button>
                </div>
                <p class="tvu-form-item__message tvu-form-item__message--error">
                  Source not found on the network. Rescan, or pick another source.
                </p>
              </div>
            </div>
          </div>

          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" for="f3b-disc">
                Discovery server <span class="tvu-form-item__optional">(Optional)</span>
              </label>
              <div class="tvu-form-item__content">
                <input class="tvu-input" id="f3b-disc" type="text" placeholder="e.g. 10.0.0.5">
                <p class="tvu-form-item__message">Leave empty to use mDNS auto-discovery.</p>
              </div>
            </div>
          </div>

          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" for="f3b-group">
                Group name <span class="tvu-form-item__optional">(Optional)</span>
              </label>
              <div class="tvu-form-item__content">
                <input class="tvu-input" id="f3b-group" type="text" placeholder="Public">
                <p class="tvu-form-item__message">The default group is used when empty.</p>
              </div>
            </div>
          </div>

          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" id="f3b-bw-label">Bandwidth</label>
              <div class="tvu-form-item__content">
                <div class="tvu-select">
                  <div class="tvu-select__field" role="combobox" aria-expanded="false"
                       aria-labelledby="f3b-bw-label" tabindex="0">Highest</div>
                  <span class="tvu-select__arrow"><svg width="16" height="16" aria-hidden="true"><use href="#i-arrow-dropdown"/></svg></span>
                </div>
              </div>
            </div>
          </div>

          <div class="tvu-form-item">
            <div class="tvu-form-item__main">
              <label class="tvu-form-item__label" id="f3b-rx-label">Target receiver</label>
              <div class="tvu-form-item__content">
                <div class="tvu-select">
                  <div class="tvu-select__field" role="combobox" aria-expanded="false"
                       aria-labelledby="f3b-rx-label" tabindex="0">Receiver A &mdash; Newsroom</div>
                  <span class="tvu-select__arrow"><svg width="16" height="16" aria-hidden="true"><use href="#i-arrow-dropdown"/></svg></span>
                </div>
              </div>
            </div>
          </div>

        </div>

        <div class="ndi-actions">
          <!-- 源不在线 → Apply 走 status=disable 真变体，不用 opacity 凑 -->
          <button type="button" class="tvu-btn tvu-btn--fill-green" disabled>Apply</button>
        </div>
      </div>
    </div>
  </div>

  <!-- ═════════════════════════════ 交付表 ═════════════════════════════ -->
  <div class="frame-caption"><span class="frame-caption__name">元素 → TVU 库组件对照（dev handoff）</span></div>
  <section class="doc__block">
    <div class="table-scroll">
      <table>
        <thead>
          <tr>
            <th>页面元素</th>
            <th>Figma 库组件 / 变体</th>
            <th>code 对应</th>
            <th>本轮</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>顶部导航页签</td>
            <td><code>Tab List</code> Type=Line + <code>Tab/Item</code> Property1=Active/Normal, Property2=Green, Type=Line</td>
            <td><code>TabList</code> / <code>TabItem</code></td>
            <td>不改 · 不新增页签</td>
          </tr>
          <tr>
            <td>Type of source 三选一</td>
            <td><code>radio</code> <code>9f07746f…</code>（dark theme=on × status=on/off × enable=yes）</td>
            <td><code>Radio</code></td>
            <td><b>改</b>：二选一 → 三选一，新增 NDI</td>
          </tr>
          <tr>
            <td>选择行 ↔ 内容区分隔</td>
            <td>库内无 divider 组件 → 1px 线绑 <code>Color Type/Line/Light Divider</code></td>
            <td><code>--line-light</code></td>
            <td><b>新增</b></td>
          </tr>
          <tr>
            <td>来源卡片容器</td>
            <td>无卡片组件 → <code>Background/Layer_3</code> + <code>Radius/M</code> + <code>Line/Deep Divider</code> 描边</td>
            <td><code>--bg-layer3</code> / <code>--r-m</code> / <code>--line-deep</code></td>
            <td>不改</td>
          </tr>
          <tr>
            <td>卡片状态 pill</td>
            <td><code>Badge</code> <code>4db5246d…</code> Type=Rectangle × Color × Tag<br>Live=Red/Filled · Standby=Green/Line · Inactive=Neutral/Line</td>
            <td><code>Badge</code></td>
            <td>不改（广播 ON AIR 配色）</td>
          </tr>
          <tr>
            <td>目标接收端 / NDI 各下拉</td>
            <td><code>select box/filled</code> <code>ca2ff89d…</code> size=M, status=Value/Placeholder, UX=default/error<br>展开面板 = <code>Drop down List/Select</code> Type=Radio（本页只画收起态）</td>
            <td><code>SelectBoxFilled</code> / <code>DropDownListSelect</code></td>
            <td>NDI 侧新增，形态与卡片一致</td>
          </tr>
          <tr>
            <td>NDI 文本字段</td>
            <td><code>input box/filled</code> <code>1f513914…</code> size=M</td>
            <td><code>InputBoxFilled</code></td>
            <td><b>新增</b></td>
          </tr>
          <tr>
            <td>表单行（label + 控件 + hint / error）</td>
            <td><code>Form Item</code> <code>1923:50705</code><br>1 line + Label Width=200px（NDI 表单）· 2 lines + Dynamic（卡片内）· Theme=Dark · Status=Normal/Error</td>
            <td><code>FormItem</code></td>
            <td>NDI 侧新增</td>
          </tr>
          <tr>
            <td>Apply / Rescan</td>
            <td><code>Button/dark M</code> <code>12b38c8b…</code><br>Apply=filling/green（卡片内 ghost/green）· Rescan=ghost/gray 1 · 不可提交时 status=disable</td>
            <td><code>Button</code></td>
            <td>NDI 侧新增；沿用既有 <code>Apply</code> 措辞</td>
          </tr>
          <tr>
            <td>源离线告知</td>
            <td><code>Message</code> <code>1408:17153</code> status=warning × size=L（无按钮）</td>
            <td><code>Message</code></td>
            <td><b>新增</b>（设备侧谱系，不用 web 侧 Notification）</td>
          </tr>
          <tr>
            <td>图标</td>
            <td><code>arrow/dropdown</code> · <code>message/warning-2</code> · <code>action/close</code></td>
            <td><code>src/icons/catalog/generated/*</code></td>
            <td>path 逐字取自库，零自画</td>
          </tr>
        </tbody>
      </table>
    </div>
  </section>

  <section class="doc__block">
    <h2>实装须知</h2>
    <ul>
      <li><b>颜色 / 间距 / 字阶零硬编码</b>：全部走 <code>var(--*)</code>。文件顶部 token 定义块是 <code>src/tokens/variables.css</code> dark 段的逐字副本，值未改；接入工程时删掉该块、改为引用真源（构建期拷 <code>tvu-tokens.css</code>），真源只留一份。</li>
      <li><b>IP / 端口 / 时间戳不用等宽字体</b>：设计系统刻意没有 monospace token，数据文本走 <code>--font-family-base</code> + <code>font-variant-numeric: tabular-nums</code>。</li>
      <li><b>本页控件是 HTML 静态镜像</b>，API 以 <code>src/canonical/*.vue</code> 为准；实装直接消费 canonical 组件，不要照抄本页的 class 结构。</li>
      <li><b>下拉展开态未画</b>：面板是独立组件 <code>Drop down List/Select</code> Type=Radio，键盘 roving focus 已内置，不要自撸列表。</li>
      <li><b>三个待现网校准点</b>：① 顶部页签的实际名称与顺序 ② HDMI/SDI/USB 内容区的实际内容 ③ 选择行控件在现网究竟是 radio 还是分段控件。三处本页都按「沿用现网、不改动」处理，拿到现网页面即可替换，不影响本轮四项改动的结构。</li>
      <li><b>本轮未执行的机检</b>（只读会话，无法运行命令）：<code>audit:no-hardcoded-design-tokens</code> · <code>audit:consumer-code</code>（R14 / R19 / R9 探针）· <code>pattern:lookup</code>。合库前请补跑。</li>
    </ul>
  </section>

</div>
</body>
</html>
```

补一句：产物里 `position:absolute` 只用于下拉箭头这一处装饰层，已按 R14 注明 affordance；`selected` 态没有走 R19 的 rail+fill+chrome 三层，因为选中语义落在 radio 自身的 canonical dot 上（R19 管的是列表项 / row / tab / card 的选中态，不覆盖表单 radio）。