/* content-data.jsx — UX-writing / content layer (the third pillar: Tokens · Components · Content).
   Three light arrays, factories mirror data.jsx's t()/a():
     • GUIDES    — prose voice/tone & English-copy norms (Markdown, rendered by MarkdownDoc; no editor)
     • GLOSSARY  — bilingual word-list (preferred term ↔ meaning, with avoid[] for inclusive language)
     • COPY      — i18n-grade string registry; ONE shape covers nav / button / tooltip / empty-state /
                   error / toast / system-message / icon-label, optionally linked to a component & product.
   Bilingual content lives in plain en/zh fields (NOT routed through tr() — tr() is for UI chrome).
   Loads after components-data.jsx (uses PRODUCTS / COMPONENT_MAP) and before ui.jsx. In-memory only. */

/* orthogonal axes (kept as plain enums, like STATUS_META):
   SURFACE  = where the string appears   ·  GLOSSARY CATEGORY = vocabulary bucket
   product  = cross-product scope ('all' = system-wide)  ·  status reuses the token lifecycle */
const COPY_SURFACES = ['nav','button','label','placeholder','tooltip','helptext','empty-state','error','toast','system-message','icon-label'];
const SURFACE_LABEL = {
  nav:{en:'Navigation',zh:'导航'}, button:{en:'Button',zh:'按钮'}, label:{en:'Label',zh:'标签'},
  placeholder:{en:'Placeholder',zh:'占位符'}, tooltip:{en:'Tooltip',zh:'提示'}, helptext:{en:'Help text',zh:'帮助文字'},
  'empty-state':{en:'Empty state',zh:'空状态'}, error:{en:'Error',zh:'错误'}, toast:{en:'Toast',zh:'轻提示'},
  'system-message':{en:'System message',zh:'系统消息'}, 'icon-label':{en:'Icon label',zh:'图标文字'},
};
function surfaceLabel(s){ const m = SURFACE_LABEL[s] || { en:s, zh:s }; return tr(m.en, m.zh); }

/* ---- COPY: the string-registry spine ---- */
function cp(key, surface, en, zh, extra) {
  const e = extra || {};
  return { key, surface, en, zh,
    context: e.context || '', contextZh: e.contextZh || '',
    componentId: e.componentId || null, iconId: e.iconId || null, themes: e.themes || (e.product ? [e.product] : ['all']),
    good: e.good || '', goodZh: e.goodZh || '', bad: e.bad || '', badZh: e.badZh || '',
    vars: e.vars || [], tags: e.tags || [surface], status: e.status || 'published' };
}
const COPY = [
  cp('nav.overview', 'nav', 'Overview', '总览', { componentId:'menu', product:'all', context:'Top-level landing for a product area — the at-a-glance summary.', contextZh:'某产品域的顶层着陆页 —— 一眼概览。', good:'Overview', bad:'Home / Dashboard / Index', tags:['nav'] }),
  cp('button.save', 'button', 'Save', '保存', { componentId:'button', product:'all', context:'Commits the current edits.', contextZh:'提交当前编辑。', good:'Save', bad:'OK / Submit / Done', good_note:'imperative verb' }),
  cp('button.discard', 'button', 'Discard', '放弃', { componentId:'modal', product:'all', context:'Reverses unsaved changes — pairs with a confirm dialog.', contextZh:'放弃未保存的修改 —— 配合确认弹窗。', good:'Discard', bad:'Cancel / Delete' }),
  cp('modal.discard.title', 'system-message', 'Discard changes?', '放弃修改?', { componentId:'modal', product:'all', context:'Confirm-dialog title. Title states the consequence as a question; 3–5 words.', contextZh:'确认弹窗标题。以疑问句点明后果,3–5 字。', good:'Discard changes?', bad:'Are you sure you want to discard all of your unsaved changes?' }),
  cp('table.empty.title', 'empty-state', 'No data yet', '暂无数据', { componentId:'table', product:'all', context:'Shown when a data table has zero rows.', contextZh:'数据表格无任何行时显示。', good:'No data yet', bad:'Error: 0 results returned' }),
  cp('table.empty.body', 'helptext', 'Connect a source or adjust your filters to see rows here.', '接入数据源或调整筛选条件,即可在此查看数据。', { componentId:'table', product:'all', context:'Empty-state body — tells the user the next action.', contextZh:'空状态正文 —— 告诉用户下一步该做什么。' }),
  cp('input.search.placeholder', 'placeholder', 'Search by name or ID', '按名称或 ID 搜索', { componentId:'input', product:'all', context:'Search field placeholder — name what is searched, not just “Search”.', contextZh:'搜索框占位符 —— 说明搜索范围,而不只是「搜索」。', good:'Search by name or ID', bad:'Search…' }),
  cp('message.network.error', 'error', 'Connection lost. Retrying…', '连接已断开,正在重试…', { componentId:'message', product:'all', context:'Inline/banner message when the live connection drops.', contextZh:'实时连接断开时的内联/横幅消息。', good:'Connection lost. Retrying…', bad:'Oops! Something went wrong 😬', tags:['error'] }),
  cp('message.saved.toast', 'toast', 'Changes saved', '已保存', { componentId:'message', product:'all', context:'Success toast after a save. Past tense, no exclamation.', contextZh:'保存成功后的轻提示。用完成态,不加感叹号。', good:'Changes saved', bad:'Success!!!' }),
  cp('badge.overflow', 'label', '99+', '99+', { componentId:'badge', product:'all', context:'Count badge caps at 99+ to protect layout.', contextZh:'计数徽标超过 99 显示 99+,保护布局。', vars:['{count}'] }),
  cp('tooltip.export', 'tooltip', 'Export current view as CSV', '将当前视图导出为 CSV', { componentId:'button', product:'all', context:'Tooltip on the export action — say the format & scope.', contextZh:'导出操作的提示 —— 说明格式与范围。', good:'Export current view as CSV', bad:'Export' }),
  cp('tabs.more', 'label', 'More', '更多', { componentId:'tabs', product:'all', context:'Overflow tab when tabs exceed the row.', contextZh:'标签超出一行时的溢出入口。' }),
  cp('forge.run.recipe', 'button', 'Run recipe', '运行配方', { componentId:'button', product:'forge', context:'Forge HMI — starts a process recipe on a tool. “Run”, not “Start/Execute”.', contextZh:'Forge HMI —— 在设备上运行工艺配方。用「运行」,不用「开始/执行」。', good:'Run recipe', bad:'Start / Execute / Go' }),
  /* icon-label: the single source of truth for every icon's standard text label.
     The front-end icon library reads these via copyForIcon(iconId) — editing here updates the icon drawer. */
  cp('iconlabel.bell', 'icon-label', 'Notifications', '通知', { iconId:'icon.alert.bell', context:'Standard label & meaning for the bell icon.', contextZh:'铃铛图标的标准文字与含义。' }),
  cp('iconlabel.export', 'icon-label', 'Export', '导出', { iconId:'icon.action.export', context:'Standard label for the download/export icon.', contextZh:'下载/导出图标的标准文字。' }),
  cp('iconlabel.alert', 'icon-label', 'Alert', '告警', { iconId:'icon.status.alert', context:'Status icon for an active alert condition.', contextZh:'表示存在告警状态的状态图标。', good:'Alert', bad:'Warning / Error' }),
  cp('iconlabel.online', 'icon-label', 'Online', '在线', { iconId:'icon.status.online', context:'Status icon for a connected, healthy entity.', contextZh:'表示已连接且正常的状态图标。', good:'Online', bad:'Up / Active' }),
  cp('iconlabel.gauge', 'icon-label', 'Gauge', '仪表', { iconId:'icon.data.gauge', context:'Data icon for a single live measurement.', contextZh:'表示单一实时测量值的数据图标。' }),
  cp('iconlabel.trend', 'icon-label', 'Trend', '趋势', { iconId:'icon.data.chart-line', context:'Data icon for a value over time (line chart).', contextZh:'表示数值随时间变化(折线)的数据图标。' }),
  cp('iconlabel.distribution', 'icon-label', 'Distribution', '分布', { iconId:'icon.data.chart-bar', context:'Data icon for a categorical distribution (bar chart).', contextZh:'表示分类分布(柱状)的数据图标。' }),
  cp('iconlabel.signal', 'icon-label', 'Signal', '信号', { iconId:'icon.data.signal', context:'Data icon for connection / signal strength.', contextZh:'表示连接/信号强度的数据图标。' }),
  cp('iconlabel.temperature', 'icon-label', 'Temperature', '温度', { iconId:'icon.sensor.temperature', context:'Sensor icon for a temperature reading.', contextZh:'表示温度读数的传感器图标。' }),
  cp('iconlabel.vibration', 'icon-label', 'Vibration', '振动', { iconId:'icon.sensor.vibration', context:'Sensor icon for a vibration reading.', contextZh:'表示振动读数的传感器图标。' }),
  cp('iconlabel.chip', 'icon-label', 'Chip / Wafer', '芯片 / 晶圆', { iconId:'icon.equipment.chip', context:'Equipment icon for a chip or wafer.', contextZh:'表示芯片或晶圆的设备图标。' }),
  cp('iconlabel.datastore', 'icon-label', 'Data store', '数据存储', { iconId:'icon.equipment.database', context:'Equipment icon for a database / data store.', contextZh:'表示数据库/数据存储的设备图标。', good:'Data store', bad:'DB' }),
];
const COPY_MAP = Object.fromEntries(COPY.map(c => [c.key, c]));
function copyForComponent(id) { return COPY.filter(c => c.componentId === id); }
/* the canonical icon-label copy entry for an icon asset id (single source of truth) */
function copyForIcon(iconId) { return COPY.find(c => c.surface === 'icon-label' && c.iconId === iconId) || null; }

/* ---- GLOSSARY: bilingual word-list (preferred ↔ avoid) ---- */
function gl(term, termZh, extra) {
  const e = extra || {};
  return { id: e.id || term.toLowerCase().replace(/[^a-z0-9]+/g, '-'), term, termZh,
    def: e.def || '', defZh: e.defZh || '', category: e.category || 'Domain',
    preferred: e.preferred !== false, avoid: e.avoid || [], related: e.related || [],
    themes: e.themes || (e.product ? [e.product] : ['all']), status: e.status || 'published' };
}
const GLOSSARY = [
  gl('Discard', '放弃', { def:'Reverses unsaved changes without saving.', defZh:'放弃尚未保存的修改,不予保存。', category:'Actions', avoid:['Cancel','Delete'], related:['save'], product:'all' }),
  gl('Save', '保存', { def:'Commits the current edits to the working set.', defZh:'将当前编辑提交到工作集。', category:'Actions', avoid:['OK','Submit'], related:['discard'], product:'all' }),
  gl('Run', '运行', { def:'Begins executing a recipe/job on equipment.', defZh:'开始在设备上执行配方/作业。', category:'Actions', avoid:['Start','Execute','Go'], product:'forge' }),
  gl('Retry', '重试', { def:'Attempts the same action again after a failure.', defZh:'失败后再次尝试同一操作。', category:'Actions', avoid:['Try again','Redo'], product:'all' }),
  gl('Overview', '总览', { def:'The at-a-glance landing for a product area.', defZh:'某产品域的一眼概览着陆页。', category:'Navigation', avoid:['Home','Dashboard','Index'], product:'all' }),
  gl('Fault', '故障', { def:'A hardware/equipment failure condition (distinct from a software “error”).', defZh:'硬件/设备的故障状态(区别于软件「错误」)。', category:'Status', avoid:['Error (for hardware)'], related:['online'], product:'all' }),
  gl('Online', '在线', { def:'Equipment is connected and reporting healthy.', defZh:'设备已连接且状态正常。', category:'Status', avoid:['Up','Active'], product:'all' }),
  gl('Idle', '空闲', { def:'Connected but not currently running a job.', defZh:'已连接但当前未运行作业。', category:'Status', avoid:['Standby','Waiting'], product:'all' }),
  gl('Wafer', '晶圆', { def:'The silicon substrate processed through fab tools.', defZh:'在 fab 设备中加工的硅基底。', category:'Equipment', product:'forge' }),
  gl('Chamber', '腔室', { def:'A sealed process compartment within a tool.', defZh:'设备内部密封的工艺腔体。', category:'Equipment', product:'forge' }),
  gl('Recipe', '配方', { def:'A saved set of process parameters for a run.', defZh:'一次运行所用的工艺参数集合。', category:'Domain', avoid:['Program','Script'], product:'forge' }),
  gl('Throughput', '产能', { def:'Units processed per unit time.', defZh:'单位时间内处理的数量。', category:'Domain', avoid:['Speed'], product:'all' }),
  /* inclusive language — enforced by the word-list, not just prose */
  gl('Allowlist', '允许列表', { def:'A set of explicitly permitted entries.', defZh:'明确允许的条目集合。', category:'Domain', avoid:['Whitelist'], product:'all' }),
  gl('Blocklist', '阻止列表', { def:'A set of explicitly denied entries.', defZh:'明确拒绝的条目集合。', category:'Domain', avoid:['Blacklist'], product:'all' }),
  gl('Primary', '主', { def:'The leading node/instance in a group.', defZh:'一组中的主导节点/实例。', category:'Domain', avoid:['Master'], related:['replica'], product:'all' }),
];
const GLOSSARY_MAP = Object.fromEntries(GLOSSARY.map(g => [g.id, g]));

/* ---- GUIDES: prose voice/tone & English-copy norms (Markdown) ---- */
function gd(id, title, titleZh, extra) {
  const e = extra || {};
  return { id, title, titleZh, category: e.category || 'Foundations', order: e.order || 99,
    body: e.body || '', bodyZh: e.bodyZh || '', status: e.status || 'published', updated: e.updated || '2026-06-01' };
}
const GUIDES = [
  gd('voice-tone', 'Voice & tone', '语气与语调', { category:'Foundations', order:1,
    body: '# Voice & tone\n\nWe write like a **calm, precise engineer** — confident, never noisy.\n\n**Voice is constant. Tone shifts by surface:** an error is economical, an empty state is encouraging, an onboarding line is warm.\n\n## Principles\n- **Clear over clever.** Say the thing. Skip the wink.\n- **Calm under alarm.** The more critical the moment, the plainer the words.\n- **Active & present.** “Connection lost. Retrying…” not “A connection error has occurred.”\n\n> **Do** — Connection lost. Retrying…\n>\n> **Don’t** — Oops! Something went wrong 😬',
    bodyZh: '# 语气与语调\n\n我们的文字像一位**冷静、精确的工程师** —— 克制而笃定,绝不喧哗。\n\n**语气恒定,语调随场景变化:** 报错要简洁,空状态给鼓励,引导语则温和。\n\n## 原则\n- **清楚胜过聪明。** 把事情说清楚,别抖机灵。\n- **越紧急越平静。** 时刻越关键,用词越朴素。\n- **主动、现在时。** 用「连接已断开,正在重试…」而非「发生了一个连接错误」。\n\n> **推荐** —「连接已断开,正在重试…」\n>\n> **避免** —「哎呀,出错啦 😬」' }),
  gd('english-norms', 'English copy norms', '英文文案规范', { category:'English', order:2,
    body: '# English copy norms\n\n- **Sentence case** everywhere — buttons, titles, menus. Not Title Case, not ALL CAPS. (e.g. “Run recipe”, not “Run Recipe”.)\n- **Plain English.** Keep sentences ≤ 20 words. No jargon the operator wouldn’t use.\n- **Imperative verbs** on actions: Save, Discard, Export, Run.\n- **No trailing punctuation** on buttons or labels. Full sentences in body text get periods.\n- **Numbers:** numerals for counts (3 tools), `99+` for overflow.',
    bodyZh: '# 英文文案规范\n\n- 全部使用 **sentence case**(句首大写)—— 按钮、标题、菜单皆然,不用 Title Case,不用全大写。\n- **朴素英语。** 句子不超过 20 词,不用操作员不会说的术语。\n- 操作用**祈使动词**:Save、Discard、Export、Run。\n- 按钮/标签**不加结尾标点**;正文完整句子用句号。\n- **数字:** 计数用阿拉伯数字(3 tools),溢出用 `99+`。' }),
  gd('grammar', 'Grammar & mechanics', '语法与格式', { category:'Grammar', order:3,
    body: '# Grammar & mechanics\n\n- **Oxford comma** in lists of three or more.\n- **One space** after a period.\n- Use **“…”** (ellipsis) for in-progress states: “Saving…”, “Retrying…”.\n- Quote UI strings with curly quotes in docs, never in the product.\n- Dates: `2026-06-08` (ISO) in data, “Jun 8, 2026” in prose.',
    bodyZh: '# 语法与格式\n\n- 三项及以上列表使用 **牛津逗号**(英文)。\n- 中文标点用全角;中英混排时英文与数字两侧留半角空格。\n- 进行中的状态用 **省略号**:「保存中…」「重试中…」。\n- 日期:数据用 `2026-06-08`(ISO),正文用「2026 年 6 月 8 日」。' }),
  gd('inclusive', 'Inclusive language', '包容性表达', { category:'Inclusive', order:4,
    body: '# Inclusive language\n\nDefault to neutral, non-exclusionary terms. The [word list](#glossary) enforces the preferred forms.\n\n- **Allowlist / Blocklist** — not whitelist / blacklist.\n- **Primary / Replica** — not master / slave.\n- Avoid ability-based metaphors (“sanity check”, “blind to”).\n- Address the user as **“you”**; avoid gendered pronouns for roles.',
    bodyZh: '# 包容性表达\n\n默认使用中性、不排斥的措辞。**术语表**已将首选形式固化为规则。\n\n- 用 **Allowlist / Blocklist(允许/阻止列表)**,不用 whitelist / blacklist。\n- 用 **Primary / Replica(主/副本)**,不用 master / slave。\n- 避免基于能力的隐喻(如「sanity check」「blind to」)。\n- 以**「你」**称呼用户;角色描述避免带性别的代词。' }),
];

/* ---- TOKEN_DOC: the "Design tokens" doc, presented like a component (5 tabs).
   NOT part of COMPONENTS — a standalone Foundations entry. Its md fields are
   editable in the backend (Tokens → Doc editor mutates these in memory; the
   front-end re-reads them). Changelog is NOT stored here — it is derived from
   CHANGE_HISTORY at render (token-kind items only). ---- */
const TOKEN_DOC = {
  name: 'Design tokens', nameZh: '设计 Token',
  summary: 'The system’s named design decisions — three tiers (primitive → semantic → component) that drive every product surface and make theming possible.',
  summaryZh: '设计系统中被命名的设计决策 —— 三层(原始 → 语义 → 组件),驱动每一个产品界面,并让换肤成为可能。',

  /* Usage — how to USE tokens (semantic-not-primitive, search by purpose, theming) */
  usageMd: '# Using tokens\n\nTokens are the only approved source of color, spacing, type, radius and elevation. Hard-coded values do not theme, do not survive a refresh, and drift out of the system.\n\n## Build with semantic & component tokens — never primitives\n- Reach for the **purpose** token: `color.text.primary`, not `color.gray.900`. `button.bg`, not `brand.accent.500`.\n- **Primitives are private.** They are the raw palette the semantic layer wraps. Using one directly couples your UI to a value and breaks under another theme or in dark mode.\n\n## Search by purpose, not value\n- Looking for the danger color? Search `danger`, not `#D63B3B`. The token name encodes intent so the same code is correct across every theme.\n- The relationship graph shows which semantic wraps which primitive — follow the chain to understand a value, don’t copy it.\n\n## Theming comes for free\n- Switching brand theme re-resolves every `themeAccent`-derived token; dark mode swaps the `modeRef` side. If you built on semantic tokens, **you wrote zero theme code.**\n- Per-brand pins live in the theme, not in your component — never fork a component to recolor it.\n\n> **Do** — `background: var(--color-bg-surface)`\n>\n> **Don’t** — `background: #FFFFFF`',
  usageMdZh: '# 如何使用 Token\n\nToken 是颜色、间距、字体、圆角与阴影的唯一合法来源。写死的值不会换肤、撑不过一次刷新,还会脱离系统漂移。\n\n## 用语义层和组件层,绝不直接用原始层\n- 取**用途**层的 token:用 `color.text.primary`,不用 `color.gray.900`;用 `button.bg`,不用 `brand.accent.500`。\n- **原始层是私有的。** 它是语义层包裹的原始色板。直接用它会把界面绑死在某个值上,换主题或进深色模式就崩。\n\n## 按用途搜,别按值搜\n- 要找危险色?搜 `danger`,别搜 `#D63B3B`。名字已经编码了意图,所以同一份代码在每个主题下都正确。\n- 关系图能看到哪个语义包了哪个原始 —— 顺着链路理解一个值,而不是把值复制走。\n\n## 换肤无需额外代码\n- 切换品牌主题会重新解析所有由 `themeAccent` 派生的 token;深色模式则切换 `modeRef` 的那一侧。只要你构建在语义层上,**就一行换肤代码都不用写。**\n- 各品牌的差异以「pin」形式存在主题里,而非组件里 —— 别为了改个颜色去 fork 组件。\n\n> **推荐** — `background: var(--color-bg-surface)`\n>\n> **避免** — `background: #FFFFFF`',

  /* Content — the NAMING rules, moved out of the former token-naming GUIDE */
  contentMd: '# Token naming\n\nNames encode taxonomy. Consistency comes from a shared **grammar + tooling**, not willpower.\n\n## Separator: `.` vs `-`\n- `.` separates **hierarchy levels**; `-` (kebab-case) joins **multi-word parts inside one level** — e.g. `color.chart-line.500`, `color.text.on-accent`.\n- The source id uses dots; the build emits CSS vars with hyphens (`--color-chart-line-500`). Never put a dot inside a segment; no spaces, camelCase, or underscores.\n\n## Grammar by tier\n**Golden rule:** primitives name the *value*, semantics name the *purpose*, component tokens name the *part*. Never bake a value into a semantic or component name.\n\n| Tier | Pattern | Examples |\n|---|---|---|\n| **Primitive** (raw value) | `category.family.step` | `color.gray.500` · `space.400` · `radius.md` |\n| **Semantic** (purpose) | `category.role.variant?.state?` | `color.text.primary` · `color.bg.surface` · `color.accent.hover` |\n| **Component** (part) | `component.part.property?.state?` | `button.bg` · `switch.on.bg` · `tabs.active.border` |\n\n**Avoid:** `color.text.gray900` (value baked into a semantic name), `button.blue.bg` (brand color hard-coded). Use `color.text.primary`, `button.bg`.\n\n## Scale convention\nPick **one** scale style per category and keep it: numeric steps for ramps (`gray.500`, `space.400`), t-shirt sizes for semantic radius/spacing (`radius.control`, `space.inset.md`). Do not mix within a category.\n\n## Renaming\nKeep the old name as an **alias for one release**, ship a `renamed` change, then deprecate. Never hard-cut.\n\n> **Tip** — the **New token** drawer assembles names from these slots and validates them. Use it instead of typing free-form ids.',
  contentMdZh: '# Token 命名规范\n\n名字是在编码一套分类。一致性靠**统一语法 + 工具**,不靠自觉。\n\n## 分隔符:`.` 还是 `-`\n- `.` 分隔**层级**;`-`(kebab)连接**同一层级内的多词** —— 如 `color.chart-line.500`、`color.text.on-accent`。\n- 源 id 用点;构建时输出的 CSS 变量用连字符(`--color-chart-line-500`)。段内不能有点,不用空格、驼峰或下划线。\n\n## 按层级的语法\n**铁律:** 原始层命名「值」、语义层命名「用途」、组件层命名「部件」。绝不把值写进语义或组件名。\n\n| 层级 | 形式 | 示例 |\n|---|---|---|\n| **原始**(原始值) | `类目.族.步` | `color.gray.500` · `space.400` · `radius.md` |\n| **语义**(用途) | `类目.角色.变体?.状态?` | `color.text.primary` · `color.bg.surface` · `color.accent.hover` |\n| **组件**(部件) | `组件.部件.属性?.状态?` | `button.bg` · `switch.on.bg` · `tabs.active.border` |\n\n**避免:** `color.text.gray900`(把值写进语义名)、`button.blue.bg`(写死品牌色)。应为 `color.text.primary`、`button.bg`。\n\n## 刻度约定\n每个类目**只用一种**刻度风格并保持一致:色阶/间距用数字步(`gray.500`、`space.400`),语义圆角/间距用 T 恤码(`radius.control`、`space.inset.md`)。同一类目内别混用。\n\n## 改名\n旧名保留**一个版本周期**作为 alias,提一条 `renamed` 变更,再废弃。别硬切。\n\n> **提示** —「新建 token」抽屉会按这些槽位拼装并校验名字。别再手敲自由 id,直接用它。',

  /* Design — rationale (why three tiers, why themeAccent) */
  designMd: '# Design rationale\n\n## Why three tiers\nA flat token set forces an impossible choice: name things by value (`blue.500` — meaningless to a reader, impossible to retheme) or by purpose (`button.bg` — but then every value is duplicated everywhere). Three tiers separate those concerns:\n\n- **Primitive** — the raw, finite palette. The *what*. Changes rarely.\n- **Semantic** — intent mapped onto primitives (`color.text.primary` → `color.gray.900`). The *why*. This is the layer products read.\n- **Component** — a part that makes its own decision (`badge.bg` is deliberately danger, not accent). The *where*. Only created when a component diverges from the semantic default.\n\nThe indirection is the point: change a primitive once and every purpose that wraps it updates; retarget a semantic and every component follows.\n\n## Why `themeAccent` instead of a fixed brand color\nEach product line has its own accent. If the accent were a plain primitive value, every theme would need a forked token set. Instead `brand.accent.*` is a **`themeAccent` token** — it resolves against the active theme’s accent scale at render time. One token graph, five brands, zero duplication. Switching theme re-resolves the accent and everything downstream (`color.accent.default` → `button.bg` → your CTA) recolors automatically.\n\n## Mode-awareness via `modeRef`\nSemantic colors that must flip for dark mode carry a `modeRef` source — a light side and a dark side, each pointing at a primitive. The resolver picks the side for the active appearance, so light/dark is a property of the *system*, not of every component.\n\n## Minimal component layer\nWe only mint a component token where a component genuinely decides something structural needs (radius, generic surfaces) reference semantics directly. No redundant 1:1 aliases — they add naming overhead without adding meaning.',
  designMdZh: '# 设计思路\n\n## 为什么要三层\n扁平的 token 集会逼出一个两难:要么按值命名(`blue.500` —— 读者看不懂,也没法换肤),要么按用途命名(`button.bg` —— 可值又会在各处重复)。三层把这两件事拆开:\n\n- **原始层** —— 原始、有限的色板。回答「是什么」。极少改动。\n- **语义层** —— 把意图映射到原始层(`color.text.primary` → `color.gray.900`)。回答「为什么」。这是产品真正读取的一层。\n- **组件层** —— 某个部件自己做的决定(`badge.bg` 故意用 danger,而非 accent)。回答「在哪里」。只有当组件偏离语义默认时才创建。\n\n这层间接正是关键:改一次原始值,所有包裹它的用途都更新;让一个语义改指向,所有组件随之跟进。\n\n## 为什么用 `themeAccent` 而非写死品牌色\n每条产品线都有自己的强调色。若强调色是普通的原始值,每个主题都得 fork 一套 token。于是 `brand.accent.*` 是一个 **`themeAccent` token** —— 渲染时按当前主题的强调色阶解析。一张 token 图,五个品牌,零重复。切换主题会重新解析强调色,下游一切(`color.accent.default` → `button.bg` → 你的 CTA)自动改色。\n\n## 用 `modeRef` 实现明暗感知\n需要在深色模式下翻转的语义色携带一个 `modeRef` 源 —— 一个浅色侧、一个深色侧,各自指向一个原始 token。解析器按当前外观挑选对应侧,于是明暗是*系统*的属性,而非每个组件的负担。\n\n## 克制的组件层\n只有当组件确实做了决定时才铸造组件 token;结构性需求(圆角、通用表面)直接引用语义层。不做冗余的 1:1 别名 —— 它们只增加命名负担,不增加含义。',
};

Object.assign(window, {
  COPY, COPY_MAP, GLOSSARY, GLOSSARY_MAP, GUIDES, TOKEN_DOC,
  cp, gl, gd, copyForComponent, copyForIcon, COPY_SURFACES, surfaceLabel,
});
