/* ===== n2-inline:begin components =====
   由 rule/UI规范/inline-assets.mjs 从 设计稿/assets/components.css 生成。
   不要手改这一段：设计稿更新后重跑一次脚本即可覆盖。===== */
/* ==========================================================================
   n2one Components
   --------------------------------------------------------------------------
   只使用 tokens.css 中的变量。组件命名前缀 n2-。
   ========================================================================== */

/* ==========================================================================
   0. Base
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  font-family: var(--n2-font-family);
  font-size: var(--n2-font-body);
  line-height: var(--n2-line-body);
  font-weight: var(--n2-font-weight-regular);
  color: var(--n2-text-primary);
  background: var(--n2-bg-page);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1,
h2,
h3,
h4,
p {
  margin: 0;
}

a {
  color: var(--n2-text-link);
  text-decoration: none;
  transition: color var(--n2-duration-fast) var(--n2-ease);
}

a:hover {
  color: var(--n2-text-link-hover);
}

button {
  font-family: inherit;
}

:focus-visible {
  outline: 2px solid var(--n2-color-primary);
  outline-offset: 1px;
  border-radius: var(--n2-radius-sm);
}

.n2-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;
}

/* 滚动条 */
.n2-scroll::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
.n2-scroll::-webkit-scrollbar-thumb {
  background: rgba(0, 0, 0, 0.15);
  border-radius: var(--n2-radius-round);
}
.n2-scroll::-webkit-scrollbar-thumb:hover {
  background: rgba(0, 0, 0, 0.25);
}
.n2-scroll::-webkit-scrollbar-track {
  background: transparent;
}

/* ==========================================================================
   1. 排版
   ========================================================================== */

.n2-display {
  font-size: var(--n2-font-display);
  line-height: var(--n2-line-display);
  font-weight: var(--n2-font-weight-semibold);
}
.n2-h1 {
  font-size: var(--n2-font-h1);
  line-height: var(--n2-line-h1);
  font-weight: var(--n2-font-weight-semibold);
}
.n2-h2 {
  font-size: var(--n2-font-h2);
  line-height: var(--n2-line-h2);
  font-weight: var(--n2-font-weight-semibold);
}
.n2-h3 {
  font-size: var(--n2-font-h3);
  line-height: var(--n2-line-h3);
  font-weight: var(--n2-font-weight-semibold);
}
.n2-body {
  font-size: var(--n2-font-body);
  line-height: var(--n2-line-body);
}
.n2-caption {
  font-size: var(--n2-font-caption);
  line-height: var(--n2-line-caption);
  color: var(--n2-text-tertiary);
}
.n2-text-secondary {
  color: var(--n2-text-secondary);
}
.n2-text-tertiary {
  color: var(--n2-text-tertiary);
}
.n2-code {
  font-family: var(--n2-font-family-code);
  font-size: 13px;
  background: var(--n2-bg-hover);
  padding: 1px 6px;
  border-radius: var(--n2-radius-sm);
  color: var(--n2-text-secondary);
}

/* ==========================================================================
   2. 按钮  —— 5 类型 × 3 尺寸（docx 5.x）
   ========================================================================== */

.n2-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--n2-space-xs);
  height: var(--n2-size-control);
  padding: 0 var(--n2-space-md);
  font-size: var(--n2-font-body);
  line-height: 1;
  font-weight: var(--n2-font-weight-regular);
  white-space: nowrap;
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-base);
  background: var(--n2-bg-container);
  color: var(--n2-text-primary);
  cursor: pointer;
  transition: all var(--n2-duration-fast) var(--n2-ease);
  user-select: none;
}

.n2-btn:hover {
  border-color: var(--n2-text-link);
  color: var(--n2-text-link);
}
.n2-btn:active {
  border-color: var(--n2-color-primary-active);
  color: var(--n2-color-primary-active);
}
.n2-btn[disabled],
.n2-btn.is-disabled {
  color: var(--n2-text-quaternary);
  border-color: var(--n2-border);
  background: var(--n2-bg-disabled);
  cursor: not-allowed;
}
.n2-btn[disabled]:hover {
  color: var(--n2-text-quaternary);
  border-color: var(--n2-border);
}

/* 主按钮。主色实底托白字只有 4.10:1，靠 600 字重补偿（§2.1.1 例外条款）。 */
.n2-btn--primary {
  background: var(--n2-color-primary);
  border-color: var(--n2-color-primary);
  color: var(--n2-text-inverse);
  font-weight: var(--n2-font-weight-semibold);
}
.n2-btn--primary:hover {
  background: var(--n2-color-primary-hover);
  border-color: var(--n2-color-primary-hover);
  color: var(--n2-text-inverse);
}
.n2-btn--primary:active {
  background: var(--n2-color-primary-active);
  border-color: var(--n2-color-primary-active);
  color: var(--n2-text-inverse);
}
.n2-btn--primary[disabled],
.n2-btn--primary.is-disabled {
  background: var(--n2-bg-disabled);
  border-color: var(--n2-border);
  color: var(--n2-text-quaternary);
}

/* 虚线按钮 */
.n2-btn--dashed {
  border-style: dashed;
}

/* 文字按钮 */
.n2-btn--text {
  border-color: transparent;
  background: transparent;
  padding: 0 var(--n2-space-xs);
}
.n2-btn--text:hover {
  border-color: transparent;
  background: var(--n2-bg-hover);
  color: var(--n2-text-link);
}
.n2-btn--text[disabled] {
  background: transparent;
}

/* 危险按钮：**前台不使用**，所以这里没有 `.n2-btn--danger`。
   全站没有红色按钮 —— 表格操作列的删除 / 取消 / 停用 / 卸载与编辑 / 测试同色（`.n2-link`），
   危险操作的确认弹窗，确定按钮与其它弹窗一样是 `.n2-btn--primary`。
   红色的用途收窄为四处：必填 `*`、校验错误文字与边框、错误提示、错误状态徽标。
   设计稿里那个红底白字的 `.n2-btn--danger` 是给确认弹窗确定按钮的，本仓不采用；
   重新内联设计稿 CSS 会把它带回来，届时它是「有定义、零使用」，不要接回调用点。 */

/* 尺寸 */
.n2-btn--lg {
  height: var(--n2-size-control-lg);
  padding: 0 var(--n2-space-md);
  font-size: var(--n2-font-h3);
}
.n2-btn--sm {
  height: var(--n2-size-control-sm);
  padding: 0 var(--n2-space-xs);
  font-size: var(--n2-font-caption);
}
.n2-btn--block {
  width: 100%;
}

/* 加载态 */
.n2-btn.is-loading {
  pointer-events: none;
  opacity: 0.75;
}
.n2-spin {
  width: 12px;
  height: 12px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: n2-spin 0.7s linear infinite;
  flex: none;
}
@keyframes n2-spin {
  to {
    transform: rotate(360deg);
  }
}

/* 行内文字操作（表格操作列） */
.n2-link {
  border: none;
  background: none;
  padding: 0;
  font-size: var(--n2-font-body);
  color: var(--n2-text-link);
  cursor: pointer;
  transition: color var(--n2-duration-fast) var(--n2-ease);
}

.n2-link:hover {
  color: var(--n2-text-link-hover);
}
/* 没有 `.n2-link--danger`：行操作**全部同色** —— 删除 / 取消 / 停用 / 卸载
   与编辑 / 测试一样都是 `.n2-link` 的链接色。危险操作的分量由后续的确认弹窗承担，
   不靠颜色区分。见 UI 规范 §4.3。 */

/* ==========================================================================
   3. 表单
   ========================================================================== */

.n2-field {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xs); /* label ↔ 控件 8px */
}

.n2-field__label {
  font-size: var(--n2-font-body);
  line-height: var(--n2-line-body);
  color: var(--n2-text-primary);
}
.n2-field__label .n2-required {
  color: var(--n2-text-error);
  margin-right: var(--n2-space-xxs);
}
.n2-field__hint {
  font-size: var(--n2-font-caption);
  line-height: var(--n2-line-caption);
  color: var(--n2-text-tertiary);
}
/* 字段下方的行内操作（目前只有「AI 命名」一个）：与提示文字同一行，提示先占位、
   按钮跟在后面；没有提示时按钮自己一行。做成 flex 而不是 absolute，是为了让长提示
   换行时按钮跟着往下走，不会盖在文字上。 */
.n2-field__hintrow {
  display: flex;
  align-items: center;
  gap: var(--n2-space-2, 8px);
  flex-wrap: wrap;
}
.n2-field__error {
  font-size: var(--n2-font-caption);
  line-height: var(--n2-line-caption);
  color: var(--n2-text-error);
  display: none;
}
.n2-field.is-invalid .n2-field__error {
  display: block;
}
.n2-field[hidden] { display: none !important; }
.n2-field.is-invalid .n2-input,
.n2-field.is-invalid .n2-select {
  border-color: var(--n2-color-error);
}
.n2-field.is-invalid .n2-input:focus {
  box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.12);
}

.n2-input,
.n2-select,
.n2-textarea {
  width: 100%;
  height: var(--n2-size-control);
  padding: 0 var(--n2-space-sm);
  font-family: inherit;
  font-size: var(--n2-font-body);
  color: var(--n2-text-primary);
  background: var(--n2-bg-container);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-base);
  transition: all var(--n2-duration-fast) var(--n2-ease);
}
.n2-input::placeholder,
.n2-textarea::placeholder {
  color: var(--n2-text-quaternary);
}
.n2-input:hover,
.n2-select:hover,
.n2-textarea:hover {
  border-color: var(--n2-color-primary-hover);
}
.n2-input:focus,
.n2-select:focus,
.n2-textarea:focus {
  outline: none;
  border-color: var(--n2-color-primary);
  box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.1);
}
.n2-input[disabled],
.n2-select[disabled],
.n2-textarea[disabled] {
  background: var(--n2-bg-disabled);
  color: var(--n2-text-quaternary);
  cursor: not-allowed;
}

.n2-textarea {
  height: auto;
  min-height: 80px;
  padding: var(--n2-space-xs) var(--n2-space-sm);
  line-height: var(--n2-line-body);
  resize: vertical;
}

/* Schema 编辑器：等宽字体，行高更松，便于对齐 JSON */
.n2-textarea--code {
  font-family: var(--n2-font-family-code);
  font-size: 13px;
  line-height: 1.6;
  tab-size: 2;
}

.n2-select {
  appearance: none;
  padding-right: var(--n2-space-xl);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='rgba(0,0,0,.45)' d='M6 8.5 1.5 4h9z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--n2-space-sm) center;
  cursor: pointer;
}

/* 带前后缀的输入框 */
.n2-input-affix {
  position: relative;
  display: flex;
  align-items: center;
}
.n2-input-affix .n2-input {
  padding-left: var(--n2-space-xl);
}
.n2-input-affix__icon {
  position: absolute;
  left: var(--n2-space-sm);
  display: flex;
  color: var(--n2-text-tertiary);
  pointer-events: none;
}

/* 筛选栏：字段固定 240 × 32（CRUD 规范 3.1） */
.n2-filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--n2-space-md);
}
.n2-filter-bar .n2-field {
  width: var(--n2-size-filter);
}
/* 首行固定字段数的占位元素（IR202610120006 AC-01/AC-02）：样板页 filterConfig 声明
   firstRowCount: 2 后由渲染层插在第 2 个常用条件之后。宽度由脚本按实测算
   （n2SyncFilterFirstRowBreaks 量出「容器内容盒 − 首行字段 − 列间距」写进 flex-basis），
   本行只兜底成 0 宽：脚本没跑时退化成原来的自然折行，不会破版。height: 0 让它不占高度、
   不新增行距；字段宽度仍是 CRUD 规范 3.1 的 240 × 32，未做任何收窄。 */
.n2-filter-bar__first-row-fill {
  flex: 0 1 0;
  min-width: 0;
  height: 0;
}
.n2-filter-bar__actions {
  display: flex;
  gap: var(--n2-space-xs);
  margin-left: auto;
}
/* 查询/重置/筛选设置独立容器（US202610080018 / IR202610080016 起为引擎级默认版式，
   逐页可用 queryActionsStandalone:false 关掉）：
   常驻查询卡与表格之间、按钮组水平居中，不随【更多】抽屉收展。 */
.n2-filter-bar__actions--standalone {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--n2-space-sm);
  margin: var(--n2-space-md) 0 var(--n2-space-lg);
}
.n2-filter-bar__actions--standalone + .n2-filter-actions-hint {
  margin: calc(-1 * var(--n2-space-md)) 0 var(--n2-space-lg);
  text-align: center;
}
/* 更多按钮固定在常用条件行末尾（IR202610060002 样板页专属开关 morePinned）：
   用 margin-left:auto 钉在行尾，不与条件字段数量联动换位。 */
.n2-filter-bar__more-pinned {
  margin-left: auto;
  align-self: flex-end;
}
.n2-filter-drawer {
  margin-top: var(--n2-space-lg);
  padding-top: var(--n2-space-lg);
  border-top: 1px solid var(--n2-border-split);
  animation: n2-filter-down var(--n2-duration-base) var(--n2-ease);
}
.n2-filter-drawer[hidden] {
  display: none;
}
.n2-filter-drawer__fields {
  display: flex;
  flex-wrap: wrap;
  gap: var(--n2-space-md);
}
/* IR202610120006 AC-03：「更多条件查询」抽屉里的字段没有继承 `.n2-filter-bar .n2-field`
   的固定宽度，宽度由内部 <select>/<input> 的固有宽度决定 —— 实测只有 88/144/185px，
   select 型字段最窄（约 60px 观感），与常用区 240×32 明显不一致。
   这里补齐同一条 240×32 规则（CRUD 规范 3.1），抽屉与常用区字段宽度对齐。 */
.n2-filter-drawer__fields .n2-field {
  width: var(--n2-size-filter);
}
.n2-filter-config {
  margin-top: var(--n2-space-lg);
  padding: var(--n2-space-md);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-layout);
}
.n2-filter-config__head {
  margin-bottom: var(--n2-space-sm);
}
.n2-filter-config__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--n2-space-md);
}
.n2-filter-config__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--n2-space-md);
}
.n2-filter-config__item .n2-select {
  width: 160px;
}
.n2-filter-config__foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--n2-space-sm);
  margin-top: var(--n2-space-md);
}
.n2-query-scheme-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--n2-space-xs);
  margin-bottom: var(--n2-space-md);
}
.n2-query-scheme-tab.is-active {
  border-color: var(--n2-color-primary);
  color: var(--n2-color-primary);
}
.n2-query-scheme-menu {
  min-width: 320px;
  max-width: min(520px, calc(100vw - 48px));
  max-height: 360px;
  overflow: auto;
}
.n2-query-scheme-menu__row {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xs) var(--n2-space-sm);
}
.n2-query-scheme-menu__name {
  min-width: 0;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.n2-query-scheme-menu__actions {
  display: flex;
  gap: var(--n2-space-xxs);
}

/* ==========================================================================
   查询方案统一入口（US202610060043，仅用户故事页样板）
   一个「方案 ▾」下拉承载三组：默认查询 / 我的方案 / 操作。
   与 .n2-query-scheme-menu 的区别：那一族是「一行方案 + 三个按钮」的宽菜单，
   这一族是「一行方案 = 一个可点条目」，行内只有星标 + 名称 + 类型标签。
   ========================================================================== */
.n2-scheme-menu {
  min-width: 260px;
  max-width: min(420px, calc(100vw - 32px));
  max-height: 420px;
  overflow: auto;
}
.n2-scheme-menu__group {
  padding: var(--n2-space-xxs) 0;
}
.n2-scheme-menu__group + .n2-scheme-menu__group {
  border-top: 1px solid var(--n2-border-split);
}
.n2-scheme-menu__title {
  padding: var(--n2-space-xxs) var(--n2-space-sm);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
}
.n2-scheme-menu__item {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  width: 100%;
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border: 0;
  background: transparent;
  border-radius: var(--n2-radius-base);
  font-size: var(--n2-font-body);
  color: var(--n2-text-primary);
  text-align: left;
  cursor: pointer;
}
.n2-scheme-menu__item:hover {
  background: var(--n2-bg-hover);
}
.n2-scheme-menu__item.is-active {
  color: var(--n2-color-primary);
}
.n2-scheme-menu__star {
  flex-shrink: 0;
  width: 16px;
  color: var(--n2-color-warning);
  text-align: center;
}
.n2-scheme-menu__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.n2-scheme-menu__empty {
  padding: var(--n2-space-xs) var(--n2-space-sm);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
}
/* 加载失败态（US202610091030）：与「暂无方案」同一位置，但语气是「没拿到答案」而不是
   「一条都没有」—— 文案用危险色，右侧挂一个刷新按钮让用户自己重试。 */
.n2-scheme-menu__empty--error {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--n2-space-xs);
  color: var(--n2-color-error);
}
.n2-scheme-menu__retry {
  flex-shrink: 0;
  padding: 0 var(--n2-space-xs);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-sm);
  background: var(--n2-bg-container);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-secondary);
  cursor: pointer;
}
.n2-scheme-menu__retry:hover {
  border-color: var(--n2-color-primary);
  color: var(--n2-color-primary);
}
/* 类型标签：普通 / 高级。与 .n2-adv-scheme-item__pin 同形，颜色区分类型。 */
.n2-scheme-tag {
  flex-shrink: 0;
  padding: 1px var(--n2-space-xs);
  font-size: 12px;
  line-height: 18px;
  border-radius: var(--n2-radius-sm);
  background: var(--n2-bg-hover);
  color: var(--n2-text-secondary);
}
.n2-scheme-tag--advanced {
  background: rgba(22, 119, 255, 0.12);
  color: var(--n2-color-primary);
}
/* 当前应用方案标签（筛选栏内，方案下拉左侧） */
.n2-scheme-active {
  display: inline-flex;
  align-items: center;
  gap: var(--n2-space-xs);
  height: 32px;
  padding: 0 var(--n2-space-xs) 0 var(--n2-space-sm);
  border: 1px solid var(--n2-color-primary);
  border-radius: var(--n2-radius-base);
  background: rgba(22, 119, 255, 0.06);
  font-size: var(--n2-font-body);
  color: var(--n2-color-primary);
  white-space: nowrap;
}
/* 作者样式里的 display 会盖掉 [hidden] 的 UA 规则（同特异性、后到者赢），
   没有这一条，清掉方案后标签仍以「空胶囊」形式占位（US202610070022 验收 5）。 */
.n2-scheme-active[hidden] {
  display: none;
}
/* 「当前方案：」前缀（US202610120007 AC-06）：标签挪进扩展入口行之后加前缀，说明这个
   pill 是什么；用辅助文字色，不与方案名抢主次。 */
.n2-scheme-active__label {
  color: var(--n2-text-tertiary);
  font-size: 13px;
}
.n2-scheme-active__clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.n2-scheme-active__clear:hover {
  background: rgba(22, 119, 255, 0.16);
}
/* ===== 查询区最终形态（US202610070001，仅 schemeUnifiedEntry 样板页）=====
   三块结构自上而下：条件行（常用 + 行尾固定的两个抽屉入口）→ 抽屉容器（更多 / 高级
   两个平行抽屉，可同时展开）→ 执行容器（查询/重置/筛选设置/保存方案/方案▾ + 生效条件摘要）。
   执行容器独立于抽屉容器，抽屉收展不挪动按钮位置。 */
.n2-filter-bar__drawer-entry {
  display: flex;
  align-items: flex-end;
  gap: var(--n2-space-xs);
  margin-left: auto;
}
/* US202610070001 规格 1「入口固定第一行行尾」的落地（entryPinFirstRow 逐页开关，当前仅
   样板页用户故事页开启；其他样板化页面不传这个键，布局逐字不变）。
   只靠 flex 流做不到：.n2-filter-bar 是 flex-wrap:wrap，入口是流里最后一个 flex item，
   常用条件一折行入口就跟着落到下一行（本单根因：1280 视口下常用条件 3+2 折行，入口落到
   第二行 y=308）。所以让入口**脱离文档流**，用 absolute 钉在第一行行尾 —— 条件怎么折都
   不动它。DOM 顺序不变（入口仍是 .n2-filter-bar 的末子元素），data-list / 徽标 / 抽屉
   展开逻辑全部原样。
   右侧预留走道 padding-right = 入口宽度 216px（更多条件查询 118 + 高级查询 90 + 8 间距）：
   常用条件只按左侧剩余宽度折行，入口因此恒落在第一行，且与条件永不重叠。
   列间距 16 → 8：1280 视口下查询卡内宽 964px，预留 216 后剩 748px；3 个 240px 常用条件
   在 16px 列间距下要 752px（差 4px，入口被挤到第二行），8px 列间距下 736px 放得下。
   行间距保持 16px —— 折行后的纵向节奏与其它页一致。
   桌面（>=769px）专属：移动端（<=768px）入口由 US202610070024 的端分流方案挂进
   .n2-filter-exec__actions（流内居中换行），不套用本段的 absolute 钉法。 */
@media (min-width: 769px) {
  .n2-filter-bar--entry-pinned {
    position: relative;
    padding-right: var(--n2-filter-entry-w);
    column-gap: var(--n2-filter-pin-gap);
    row-gap: var(--n2-space-md);
  }
  /* 入口顶边 = 首行 label 行高（--n2-line-body 22px）+ label↔控件间距（--n2-space-xs 8px）
     = 首行控件顶边 —— 与首行输入框/下拉框同高对齐，而不是与整个字段盒顶端对齐。 */
  .n2-filter-bar--entry-pinned .n2-filter-bar__drawer-entry {
    position: absolute;
    top: calc(var(--n2-line-body) + var(--n2-space-xs));
    right: 0;
    margin-left: 0;
  }
  /* US202610130004-1：常用条件区与「更多条件查询」抽屉共用同一列网格。
     两区各是一个 flex-wrap 容器、字段恒 240px 宽（CRUD 规范 3.1），所以
       · 「第 n 列落在哪个 x」只由**列间距**决定：x = n × (240 + gap)；
       · 「一行放得下几列」只由**容器内容盒宽度**决定。
     入口钉首行的页两处都跟常用条件行不一样：
       · 列间距 —— 常用条件行 8px（上面那条规则：1280 视口下 3 个字段才放得下），
         抽屉却是基样式的 16px，于是抽屉第 2 列起每列右移 8px，与常用区逐列错位；
       · 内容盒 —— 常用条件行留了 216px 入口走道，抽屉按整幅内宽铺，一行多塞一列，
         「常用 3 列 / 抽屉 4 列」列数不一致，抽屉行尾也更容易剩一个孤零零的字段
         （本单 AC-01 / AC-03 报的正是这两条）。
     这里让抽屉跟常用条件行走同一套网格几何：同一个列间距 + 同一条走道，两处都读
     tokens.css 的同一个变量，将来改值也不会各自漂移。
     走道在抽屉里是纯预留（抽屉里没有入口按钮），但它正是「两区同一网格」的代价 ——
     不留走道，抽屉就比常用区宽 216px，列数必然多一个。留了走道，两区字段带的左右
     边界逐像素重合，行尾不满一行时剩下的就是网格里的空列（AC-03 的「按网格补空」）。
     行间距两区都是 16px，不另设。桌面专属：与上面那条走道规则同一个断点 ——
     移动端两区各自占满宽、列间距都是 16px，本来就对齐。 */
  .n2-filter-bar--entry-pinned ~ .n2-filter-drawers .n2-filter-drawer__fields {
    column-gap: var(--n2-filter-pin-gap);
    padding-right: var(--n2-filter-entry-w);
  }
}
/* US202610070024：移动端（<=768px）把【更多条件查询】【高级查询】从条件行行尾摘下来，
   并入下方执行按钮容器（.n2-filter-exec__actions），与 查询/重置/筛选设置/保存方案/方案▾
   一起居中换行排布（节点由 JS syncDrawerEntryPlacement 搬运，桌面搬回条件行行尾）。
   这里必须清掉 .n2-filter-bar__drawer-entry 基样式里的 margin-left:auto —— 它会在按钮行
   里把入口推到最右、破坏执行容器的居中；align-items 也从 flex-end 改回 center，与同排
   按钮对齐。定位切换靠媒体查询：>=769px 那条规则把入口 absolute 钉在条件行行尾，本段
   不设 position，移动端因此是纯流内元素，不会残留 absolute 悬浮。 */
@media (max-width: 768px) {
  .n2-filter-exec__actions > .n2-filter-bar__drawer-entry {
    margin-left: 0;
    align-items: center;
  }
}
/* ===== 查询区四层稳定布局（US202610120007；queryStableLayout 逐页开关，当前仅样板页
   用户故事页开启，其他页面不传这个键，布局逐字不变）=====
   自上而下四层，只有第三层抽屉区可收展，其余三层位置与高度恒定：
     ① 扩展入口行 .n2-filter-entry-row —— 左：当前方案标签；右：方案▾ / 更多条件查询 / 高级查询
     ② 常用条件区 .n2-filter-bar--stable —— 固定一行不折行，放不下的字段由 JS 降级进更多抽屉
     ③ 抽屉区 .n2-filter-drawers —— 两抽屉平行展开、互不关闭；收起时零占用
     ④ 操作行 .n2-filter-exec__actions —— 重置 / 筛选字段设置 / 保存为方案 / 查询 居中固定
       （第四项由 US202610130006-1 补入：保存查询方案的唯一入口）
   与 entryPinFirstRow 的 absolute 钉法相比：入口不再占用常用条件行的横向空间，常用条件行拿回
   整幅内宽（不必再留 216px 走道），入口也不会随条件折行漂移（本单根因）。 */
.n2-filter-entry-row {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: var(--n2-space-sm);
  min-height: 32px;
  margin-bottom: var(--n2-space-md);
}
.n2-filter-entry-row__right {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  margin-left: auto;
  flex-shrink: 0;
}
.n2-filter-entry-row__right .n2-filter-bar__drawer-entry {
  margin-left: 0;
  align-items: center;
}
/* 常用条件固定一行：nowrap 之后放不下的字段不再折到第二行，而是被 n2SyncCommonRowOverflow
   搬进更多抽屉（AC-02）。overflow:hidden 只作兜底，避免极端窄屏下字段溢出卡片边缘；
   条件控件是原生 select/date，弹出层在流外，不会被裁掉。
   flex: 0 0 auto 是必需的，不是美化：.n2-filter-bar .n2-field 只写了 width: 240px，
   默认 flex-shrink 是 1 —— 一旦 nowrap 且放不下，浏览器会把所有字段等比压窄「塞进一行」，
   于是脚本量到的每个字段都「恰好放得下」，降级一个都不会发生（字段还会比 CRUD 规范 3.1
   的 240×32 窄）。改成 flex: 0 0 auto（grow 0 / shrink 0 / basis auto → 240px）才既保证量出来的
   宽度是真实宽度，也保证字段尺寸合规。 */
.n2-filter-bar--stable {
  flex-wrap: nowrap;
  overflow: hidden;
  row-gap: 0;
}
.n2-filter-bar--stable .n2-field {
  flex: 0 0 auto;
}
@media (max-width: 768px) {
  /* AC-10：移动端入口行同样不换行（nowrap 已在基样式），两个抽屉上下堆叠 —— .n2-filter-drawers
     本来就是纵向排列，这里只收紧入口行的间距。 */
  .n2-filter-entry-row {
    gap: var(--n2-space-xs);
    margin-bottom: var(--n2-space-sm);
  }
}
/* ===== US202610150003-1：样板页（用户故事）查询区排版补正 =====
   作用域刻意只挂本页的 id 前缀（#usFilterBar / #usAdvDrawer / #usAdvQueryRows）：用户故事
   样板页与项目内「用户故事」页签是同一页的两个渲染入口，共用同一套 id 与同一份查询区渲染，
   两处必须逐格一致，所以一起收进来；其他列表页（ir / testCases / tables / codeRepos…）的查询区
   不匹配这里任何一条选择器，样式逐字不变（AC-04）。 */
/* ① 高级查询抽屉的条件行：草图 ③（docs/prototypes/查询区排版草图-用户故事样板.html）画的是
   「字段 ＋ 运算符 ＋ 值 ＋ 移除」同行的条件条，实测却是三行 —— 根因是控件基样式
   .n2-input/.n2-select 的 width:100% 漏进了 .n2-composite-filter__row 这条 flex 行，把两个
   select 各自撑成一整行，同一处既有的 .n2-composite-filter__row .n2-select{min-width:140px}
   因此成了死规则。本页作用域内把 select 放回 auto 宽（下限仍由那条既有规则给 140px），条件行
   恢复成一行；窄屏放不下时由该行自带的 flex-wrap 自然换行。值控件（.n2-input，180px）不受影响。 */
#usAdvDrawer .n2-composite-filter__row .n2-select,
#usAdvQueryRows .n2-composite-filter__row .n2-select {
  width: auto;
}
/* ② 扩展入口行右组：三按钮（更多条件 103 + 高级查询 90 + 方案 ▾ 78 + 2×8px 间距 = 287px）在
   ≤360px 视口下超过入口行内宽（= 视口 − 72px；320px 时只有 248px），按钮顶出查询卡右缘
   （实测 320px：右组 right=323 > 卡片 right=308，整页横向可滚 5px）。入口行基样式是 nowrap
   （桌面与移动端都不换行），这里只在放不下的那一档放开右组内部换行：右组先占满一行、组内按钮
   再折到第二行并保持右对齐。≥361px 时三按钮本来就放得下（实测 360px：287 ≤ 288），不产生
   任何折行，桌面端逐格不变（AC-05）。 */
@media (max-width: 360px) {
  #usFilterBar .n2-filter-entry-row__right {
    flex-wrap: wrap;
    justify-content: flex-end;
    flex-shrink: 1;
    min-width: 0;
    row-gap: var(--n2-space-xs);
  }
}
/* 已设条件数徽标（【更多条件查询】按钮右上角） */
.n2-filter-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  margin-left: 4px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--n2-color-primary);
  color: #fff;
  font-size: 11px;
  line-height: 1;
}
.n2-filter-badge[hidden] {
  display: none;
}
/* 抽屉容器：两个抽屉平行展开，各自独立收展，互不影响 */
.n2-filter-drawers {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-md);
}
.n2-filter-drawers[hidden] {
  display: none;
}
.n2-filter-drawer__head {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  margin-bottom: var(--n2-space-sm);
  font-size: 13px;
  font-weight: 600;
  color: var(--n2-text-secondary);
}
.n2-filter-drawer__head .n2-filter-badge {
  background: var(--n2-bg-hover);
  color: var(--n2-text-secondary);
}
/* 高级查询抽屉：条件行编辑器 + 保存为方案快捷 + 方案列表 */
.n2-adv-drawer__rows {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xs);
}
.n2-adv-drawer__foot {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  margin-top: var(--n2-space-sm);
}
.n2-adv-drawer__schemes {
  margin-top: var(--n2-space-md);
  padding-top: var(--n2-space-md);
  border-top: 1px solid var(--n2-border-split);
}
.n2-adv-drawer__schemes-title {
  margin: 0 0 var(--n2-space-xs);
  font-size: 13px;
  font-weight: 600;
  color: var(--n2-text-secondary);
}
/* 执行容器：独立于抽屉容器，按钮居中；生效条件摘要常驻其上方 */
.n2-filter-exec {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--n2-space-sm);
  margin: var(--n2-space-md) 0 var(--n2-space-lg);
}
.n2-filter-exec__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--n2-space-sm);
}
/* 生效条件摘要：常驻执行容器，点标题展开明细 */
.n2-filter-summary {
  width: 100%;
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-base);
  background: var(--n2-bg-layout);
  font-size: 13px;
}
.n2-filter-summary[hidden] {
  display: none;
}
.n2-filter-summary__head {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--n2-text-secondary);
  font-size: 13px;
  cursor: pointer;
}
.n2-filter-summary__head:hover {
  color: var(--n2-color-primary);
}
.n2-filter-summary__caret {
  transition: transform var(--n2-duration-base) var(--n2-ease);
}
.n2-filter-summary__head[aria-expanded="true"] .n2-filter-summary__caret {
  transform: rotate(180deg);
}
.n2-filter-summary__body {
  margin-top: var(--n2-space-xs);
  padding-top: var(--n2-space-xs);
  border-top: 1px dashed var(--n2-border-split);
}
.n2-filter-summary__group + .n2-filter-summary__group {
  margin-top: var(--n2-space-xs);
}
.n2-filter-summary__group-title {
  color: var(--n2-text-secondary);
  font-size: 12px;
}
.n2-filter-summary__item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin: 2px 6px 2px 0;
  padding: 1px 4px 1px 8px;
  border-radius: var(--n2-radius-sm);
  background: var(--n2-bg-container);
  color: var(--n2-text-primary);
}
.n2-filter-summary__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--n2-text-secondary);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}
.n2-filter-summary__remove:hover {
  background: var(--n2-bg-hover);
  color: var(--n2-color-danger);
}
/* 统一保存方案面板：名称 + 类型 radio + 内容预览 */
.n2-scheme-save__preview {
  margin-top: var(--n2-space-md);
  padding: var(--n2-space-sm);
  border-radius: var(--n2-radius-base);
  background: var(--n2-bg-layout);
  font-size: 13px;
  color: var(--n2-text-secondary);
}
.n2-scheme-save__types {
  display: flex;
  gap: var(--n2-space-md);
  margin-top: var(--n2-space-xs);
}
/* 管理方案抽屉：行内改名（点名字变输入框） */
.n2-scheme-manage__row {
  display: flex;
  align-items: center;
  gap: var(--n2-space-sm);
  padding: var(--n2-space-sm) 0;
  border-bottom: 1px solid var(--n2-border-split);
}
.n2-scheme-manage__star {
  flex-shrink: 0;
  width: 24px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--n2-color-warning);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
.n2-scheme-manage__star[aria-pressed="false"] {
  color: var(--n2-text-quaternary);
}
.n2-scheme-manage__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.n2-scheme-manage__name--edit {
  flex: 1;
  min-width: 0;
}
.n2-scheme-manage__actions {
  display: flex;
  gap: var(--n2-space-xs);
  flex-shrink: 0;
}
.n2-filter-range {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  /* US202610130004-1：日期区间是「日期框 + 连字符 + 日期框」的横向组合，宽度必须被列宽
     约束住 —— 两框 min-width:0 + flex:1 1 0，按 (列宽 − 间距) 均分，任何列宽下都收在列内，
     不会撑开所在列、把后面的列顶开（AC-02）。max-width:100% 兜住列本身被压窄的极端情形。 */
  min-width: 0;
  max-width: 100%;
}
.n2-filter-range .n2-input {
  min-width: 0;
  flex: 1 1 0;
}
.n2-crud-search {
  display: flex;
  align-items: flex-end;
  gap: var(--n2-space-sm);
  margin-top: var(--n2-space-md);
}
.n2-crud-search .n2-input { width: min(480px, 100%); }
.n2-crud-search__results {
  margin-top: var(--n2-space-md);
  padding: var(--n2-space-md);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
}
.n2-crud-search__group + .n2-crud-search__group { margin-top: var(--n2-space-md); }
.n2-crud-search__head { margin-bottom: var(--n2-space-xs); font-weight: 600; }
.n2-crud-search__hit {
  display: block;
  width: 100%;
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border: 0;
  border-radius: var(--n2-radius-sm);
  background: transparent;
  color: var(--n2-color-primary);
  text-align: left;
  cursor: pointer;
}
.n2-crud-search__hit:hover { background: var(--n2-bg-layout); }
.n2-crud-search__fields { display: block; color: var(--n2-text-secondary); font-size: var(--n2-font-caption); }
.n2-composite-filter {
  margin-top: var(--n2-space-md);
  padding: var(--n2-space-md);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
}
.n2-composite-filter__group {
  margin: var(--n2-space-sm) 0 0 var(--n2-space-md);
  padding: var(--n2-space-sm);
  border-left: 2px solid var(--n2-border-split);
}
.n2-composite-filter__row,
.n2-composite-filter__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--n2-space-xs);
  margin-top: var(--n2-space-xs);
}
.n2-composite-filter__row .n2-select { min-width: 140px; }
.n2-composite-filter__row .n2-input { width: 180px; }
.n2-composite-filter__actions { display: flex; gap: var(--n2-space-xs); margin-top: var(--n2-space-sm); }
.n2-crud-print-frame { position: fixed; width: 0; height: 0; border: 0; }
.n2-filter-settings {
  display: flex;
  flex-direction: column;
}
.n2-filter-settings__row {
  display: grid;
  /* US202610160002-1：固定五列轨道，名称自适应、其余四列定宽，
     保证 move/common/more/kw 四列左缘跨行对齐（文本行与非文本行一致）；
     kw 列未开启的页面（仅四列内容）自动落入前四轨，对齐不受影响。 */
  grid-template-columns: minmax(0, 1fr) 96px 76px 140px 176px;
  column-gap: var(--n2-space-md);
  align-items: center;
  padding: var(--n2-space-xs) 0;
  border-bottom: 1px solid var(--n2-border-split);
}
.n2-filter-settings__row:last-child {
  border-bottom: none;
}
.n2-filter-settings__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.n2-filter-settings__move {
  display: inline-flex;
  gap: var(--n2-space-xxs);
}
.n2-filter-settings__label {
  align-self: center;
}
.n2-filter-settings__chk {
  white-space: nowrap;
}
/* 非文本行的「—」占位：撑满 kw 轨，左缘与勾选框列对齐、内容少时不塌缩 */
.n2-filter-settings__chk.n2-caption {
  display: block;
}
/* 窄视口压缩定宽列，避免溢出（抽屉最小 320px 时由 body 滚动兜底） */
@media (max-width: 720px) {
  .n2-filter-settings__row {
    grid-template-columns: minmax(0, 1fr) 84px 56px 112px 136px;
    column-gap: var(--n2-space-xs);
  }
}
@keyframes n2-filter-down {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
}

/* 复选框 / 单选框 */
.n2-checkbox,
.n2-radio {
  display: inline-flex;
  align-items: center;
  gap: var(--n2-space-xs);
  font-size: var(--n2-font-body);
  cursor: pointer;
  user-select: none;
}
.n2-checkbox input,
.n2-radio input {
  appearance: none;
  width: 16px;
  height: 16px;
  margin: 0;
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-sm);
  background: var(--n2-bg-container);
  cursor: pointer;
  transition: all var(--n2-duration-fast) var(--n2-ease);
  flex: none;
  position: relative;
}
.n2-radio input {
  border-radius: 50%;
}
.n2-checkbox input:hover,
.n2-radio input:hover {
  border-color: var(--n2-color-primary);
}
.n2-checkbox input:checked,
.n2-radio input:checked {
  background: var(--n2-color-primary);
  border-color: var(--n2-color-primary);
}
.n2-checkbox input:checked::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 1.5px;
  width: 4px;
  height: 8px;
  border: 2px solid #fff;
  border-top: 0;
  border-left: 0;
  transform: rotate(45deg);
}
.n2-radio input:checked::after {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  background: #fff;
}
.n2-checkbox.is-indeterminate input::after {
  content: "";
  position: absolute;
  left: 3px;
  top: 6px;
  width: 8px;
  height: 2px;
  background: #fff;
  border: 0;
  transform: none;
}
.n2-checkbox.is-indeterminate input {
  background: var(--n2-color-primary);
  border-color: var(--n2-color-primary);
}

/* 开关 */
.n2-switch {
  appearance: none;
  width: 40px;
  height: 22px;
  border-radius: var(--n2-radius-round);
  background: rgba(0, 0, 0, 0.25);
  position: relative;
  cursor: pointer;
  transition: background var(--n2-duration-base) var(--n2-ease);
  flex: none;
}
.n2-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  transition: transform var(--n2-duration-base) var(--n2-ease);
}
.n2-switch:checked {
  background: var(--n2-color-primary);
}
.n2-switch:checked::after {
  transform: translateX(18px);
}

/* 表单栅格：一行最多 2 列（docx 6.3） */
.n2-form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--n2-space-md);
}
.n2-form-grid--1 {
  grid-template-columns: minmax(0, 1fr);
}
.n2-form-grid__full {
  grid-column: 1 / -1;
}
.n2-mc-group { min-width: 0; }
.n2-mc-group + .n2-mc-group { margin-top: var(--n2-space-lg); }
.n2-mc-group__title {
  margin: 0 0 var(--n2-space-md);
  padding-bottom: var(--n2-space-xs);
  border-bottom: 1px solid var(--n2-border-secondary, #f0f0f0);
  font-size: 16px;
  line-height: 24px;
  font-weight: 600;
}
.n2-mc-tabs { display: flex; gap: var(--n2-space-xs); border-bottom: 1px solid var(--n2-border); margin-bottom: var(--n2-space-md); }
.n2-mc-tabs__tab {
  border: 0;
  border-bottom: 2px solid transparent;
  padding: var(--n2-space-xs) var(--n2-space-sm);
  color: var(--n2-text-secondary);
  background: transparent;
  cursor: pointer;
}
.n2-mc-tabs__tab[aria-selected="true"] { color: var(--n2-color-primary); border-bottom-color: var(--n2-color-primary); }
.n2-mc-tabs__panel[hidden] { display: none !important; }
.n2-mc-upload, .n2-mc-relation, .n2-mc-m2m { position: relative; min-width: 0; }
.n2-mc-upload__list, .n2-mc-relation__results {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xs);
  margin-top: var(--n2-space-xs);
}
.n2-mc-upload__item, .n2-mc-relation__item {
  display: flex;
  align-items: center;
  gap: var(--n2-space-sm);
  min-width: 0;
  padding: var(--n2-space-xs);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-base);
}
.n2-mc-upload__item img { width: 40px; height: 40px; object-fit: cover; border-radius: var(--n2-radius-sm); }
.n2-mc-upload__name, .n2-mc-relation__item span { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.n2-mc-upload__remove, .n2-mc-relation__item {
  font: inherit;
  background: var(--n2-bg-container);
  color: var(--n2-text-primary);
}
.n2-mc-upload__remove { border: 0; color: var(--n2-color-error); cursor: pointer; }
.n2-mc-relation__results {
  position: absolute;
  z-index: 5;
  top: 100%;
  left: 0;
  right: 0;
  max-height: 240px;
  overflow: auto;
  padding: var(--n2-space-xs);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-base);
  background: var(--n2-bg-container);
  box-shadow: var(--n2-shadow-popover, 0 4px 12px rgba(0,0,0,.12));
}
.n2-mc-relation__item { width: 100%; text-align: left; cursor: pointer; }
.n2-mc-relation__item:hover { background: var(--n2-bg-hover, #fafafa); }
/* 多选（multi_select）的勾选框组。IR202610100020 之前这一格是原生 <select multiple>：
   展开只有一行行租户名、前面没有勾选框，单击=替换选中，多选必须按住 Ctrl —— 普通用户
   根本看不出「可以勾多个」。改成勾选框组后点一下就是切换，勾选态用主色实心勾（.n2-checkbox
   的既有样式，选中 #1677ff）。.n2-mc-multiple 这个类名保留：它从前挂在那个 select 上，
   现在挂在组容器上，取值仍走 n2DrawerVal（见那里的 [data-mc-multi] 分支）。 */
.n2-mc-multiple {
  display: flex;
  flex-wrap: wrap;
  gap: var(--n2-space-xs) var(--n2-space-md);
  min-height: var(--n2-size-control);
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-base);
  background: var(--n2-bg-container);
}
.n2-mc-multiple .n2-checkbox { max-width: 100%; }
.n2-mc-multiple .n2-checkbox span { overflow-wrap: anywhere; }
.n2-mc-multiple__empty { color: var(--n2-text-quaternary); }
.n2-mc-multiple.is-disabled { background: var(--n2-bg-disabled); cursor: not-allowed; }
.n2-mc-multiple.is-disabled .n2-checkbox { cursor: not-allowed; color: var(--n2-text-quaternary); }
.n2-field.is-invalid .n2-mc-multiple { border-color: var(--n2-color-error); }
.n2-mc-m2m__selected { display: flex; flex-wrap: wrap; gap: var(--n2-space-xs); margin-top: var(--n2-space-xs); }
.n2-mc-m2m__chip { display: inline-flex; align-items: center; gap: var(--n2-space-xs); max-width: 100%; }
.n2-mc-m2m__chip code { overflow-wrap: anywhere; }
.n2-mc-m2m__remove { border: 0; padding: 0; color: var(--n2-color-error); background: transparent; cursor: pointer; }
.n2-mc-children { min-width: 0; overflow-x: auto; }
.n2-mc-children__toolbar { display: flex; justify-content: flex-end; margin-top: var(--n2-space-xs); }
.n2-mc-children__table { width: 100%; border-collapse: collapse; }
.n2-mc-children__table th, .n2-mc-children__table td { padding: var(--n2-space-xs); border-bottom: 1px solid var(--n2-border-split); text-align: left; }
.n2-mc-children__table th { background: var(--n2-bg-table-header); white-space: nowrap; }
.n2-mc-children__table .n2-input, .n2-mc-children__table .n2-select { min-width: 120px; width: 100%; }
.n2-mc-children__empty { padding: var(--n2-space-md); color: var(--n2-text-tertiary); text-align: center; }
.n2-list-tree-toggle { display: inline-flex; width: 24px; height: 24px; align-items: center; justify-content: center; margin-right: var(--n2-space-xs); border: 1px solid var(--n2-border); border-radius: var(--n2-radius-sm); color: var(--n2-text-secondary); background: var(--n2-bg-container); cursor: pointer; }
.n2-list-tree-toggle:disabled { border-color: transparent; background: transparent; cursor: default; }
.n2-list-tree-node { display: inline-flex; align-items: center; min-width: 0; }

/* ==========================================================================
   4. 卡片
   ========================================================================== */

.n2-card {
  background: var(--n2-bg-container);
  border-radius: var(--n2-radius-md);
  box-shadow: var(--n2-shadow-card);
  padding: var(--n2-space-md);
}
.n2-card--pad-lg {
  padding: var(--n2-space-lg);
}
.n2-card--plain {
  box-shadow: none;
  border: 1px solid var(--n2-border-secondary);
}

.n2-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--n2-space-md);
  margin-bottom: var(--n2-space-md);
}
.n2-card__title {
  font-size: var(--n2-font-h3);
  line-height: var(--n2-line-h3);
  font-weight: var(--n2-font-weight-semibold);
}

/* ==========================================================================
   5. 表格
   ========================================================================== */

.n2-table-wrap {
  background: var(--n2-bg-container);
  border-radius: var(--n2-radius-md);
  box-shadow: var(--n2-shadow-card);
  overflow: hidden;
}
.n2-table-scroll {
  overflow-x: auto;
}

/* 吸顶表头浮层（US202610150001-1，只有 opts.stickyHead 的列表页会建它）。
   真表头的 th 是 sticky 的，但被 .n2-table-scroll（横向滚动容器）夹住，页面滚动时吸不住，
   所以吸顶靠这个 fixed 浮层里的表头克隆体，位置 / 宽度 / 列宽由脚本逐帧对齐。
   z-index 50：高于表体与表头（2 / 4），低于顶栏（--n2-z-topbar: 100）与下拉、弹窗（900+）。 */
.n2-float-head {
  position: fixed;
  display: none;
  z-index: 50;
  background: var(--n2-bg-table-header);
  border-radius: var(--n2-radius-md) var(--n2-radius-md) 0 0;
  box-shadow: var(--n2-shadow-card);
}
.n2-float-head.is-on {
  display: block;
}
/* 克隆体自己就是滚动容器：横向偏移按 scrollLeft 同步，左固定列在浮层里照旧生效。
   overflow 必须是 hidden（不是 clip）—— 要的正是「能带 scrollLeft 的滚动容器」。 */
.n2-float-head__clip {
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: inherit;
}
/* 列宽抓取条只在真表头提供（拖拽时真表头一定在视口里、抓得到），克隆体上的抓取条没有监听，
   画出来会让人以为能拖。 */
.n2-float-head .n2-table__resizer {
  display: none;
}

.n2-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--n2-font-body);
}

.n2-table th {
  height: var(--n2-size-table-header);
  padding: var(--n2-space-sm) var(--n2-space-md);
  text-align: left;
  font-weight: var(--n2-font-weight-semibold);
  color: var(--n2-text-primary);
  background: var(--n2-bg-table-header);
  border-bottom: 1px solid var(--n2-border-split);
  white-space: nowrap;
  position: sticky;
  top: 0;
  z-index: 2;
}

.n2-table td {
  height: var(--n2-size-table-row);
  padding: var(--n2-space-sm) var(--n2-space-md);
  color: var(--n2-text-primary);
  border-bottom: 1px solid var(--n2-border-split);
  white-space: nowrap;
}

.n2-table tbody tr {
  transition: background var(--n2-duration-base) var(--n2-ease);
}
.n2-table tbody tr:hover td {
  background: var(--n2-color-primary-bg);
}
.n2-table tbody tr.is-selected td {
  background: var(--n2-color-primary-bg);
}
.n2-table tbody tr:last-child td {
  border-bottom: none;
}

/* 列分隔：表头单元格左缘一条浅色短竖线，让相邻字段的边界看得出来。
   竖线只占单元格高度的中间一段，不顶到上下的横线。
   数据区不画竖线，避免干扰数据本身。列表表格统一加
   n2-table--col-split；文档类表格（规范页与设计系统页的说明表格）不加。
   竖线画在左缘，由本单元格自己绘制。右缘会被右邻单元格的背景盖掉。 */
.n2-table--col-split th::after {
  content: '';
  position: absolute;
  top: 25%;
  bottom: 25%;
  left: 0;
  width: 1px;
  background: var(--n2-border);
  pointer-events: none;
}
.n2-table--col-split th:first-child::after {
  content: none;
}

/* 选择列 */
.n2-table .n2-col-check {
  width: var(--n2-size-checkbox-col);
  padding-right: 0;
}
/* 序号列：紧跟选择列，用于给当前页的行编号。 */
.n2-table .n2-col-index {
  width: var(--n2-size-index-col);
  color: var(--n2-text-tertiary);
  font-variant-numeric: tabular-nums;
  text-align: center;
}
/* 操作列紧跟序号列，左对齐，12px 间距（CRUD 规范 4.6）。
   宽度由 <colgroup> 声明，页面上按按钮数量各自取值。 */
.n2-table .n2-col-actions {
  text-align: left;
}
.n2-table .n2-col-actions .n2-row-actions {
  display: inline-flex;
  gap: var(--n2-space-sm);
  justify-content: flex-start;
}
.n2-list-table .n2-mobile-actions {
  display: contents;
}
.n2-list-table .n2-mobile-actions__toggle {
  display: none;
}

/* 粘性列。左侧固定列按出现顺序累加偏移：第 1 列是选择列，第 2 列是序号列，
   第 3 列是操作列；带冻结名称列的表格第 4 列继续固定，操作列宽由
   --n2-size-actions-col 给出（在 <table> 上按页面覆盖）。
   这里的 nth-child 值只是脚本未加载时的兜底；shell.js 的 applyStickyOffsets()
   按实际渲染宽度改写为内联值，表头与单元格共用同一套值。 */
.n2-table .is-sticky-left {
  position: sticky;
  z-index: 3;
  background: var(--n2-bg-container);
}
.n2-table th.is-sticky-left {
  background: var(--n2-bg-table-header);
  z-index: 4;
}
.n2-table tbody tr:hover .is-sticky-left {
  background: var(--n2-color-primary-bg);
}
.n2-table .is-sticky-left:nth-child(1) { left: 0; }
.n2-table .is-sticky-left:nth-child(2) { left: var(--n2-size-checkbox-col); }
.n2-table .is-sticky-left:nth-child(3) {
  left: calc(var(--n2-size-checkbox-col) + var(--n2-size-index-col));
}
.n2-table .is-sticky-left:nth-child(4) {
  left: calc(var(--n2-size-checkbox-col) + var(--n2-size-index-col) + var(--n2-size-actions-col));
}
/* IR202610030003 修复（US202610030005 验收打回）：窄屏（手机）下解除全部左冻结列，
   否则选择/序号/操作三列骨架持续 sticky（操作列 150px）遮挡后续字段，横向滚动到不了列尾。 */
@media (max-width: 768px) {
  .n2-table .is-sticky-left,
  .n2-table th.is-sticky-left,
  .n2-table tbody tr:hover .is-sticky-left {
    position: static;
    z-index: auto;
  }
  .n2-table .is-sticky-left:nth-child(1),
  .n2-table .is-sticky-left:nth-child(2),
  .n2-table .is-sticky-left:nth-child(3),
  .n2-table .is-sticky-left:nth-child(4) {
    left: auto;
  }
}
.n2-table .is-sticky-right {
  position: sticky;
  right: 0;
  z-index: 3;
  background: var(--n2-bg-container);
}
.n2-table th.is-sticky-right {
  background: var(--n2-bg-table-header);
  z-index: 4;
}
.n2-table tbody tr:hover .is-sticky-right {
  background: var(--n2-color-primary-bg);
}

@media (max-width: 768px) {
  .n2-table-wrap > .n2-toolbar {
    flex-wrap: wrap;
  }
  .n2-table-wrap > .n2-toolbar > .n2-toolbar__right {
    flex: 1 1 100%;
    width: 100%;
    margin-left: 0;
    flex-wrap: wrap;
  }
  .n2-table-wrap > .n2-toolbar > .n2-toolbar__right > .n2-input {
    flex: 1 1 150px;
    width: auto;
    min-width: 0;
  }
  .n2-table-wrap .n2-table-scroll {
    max-width: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
  }
  .n2-table-wrap .n2-list-table {
    width: max-content;
    min-width: 100%;
  }
  .n2-list-table col.n2-col-check { width: 44px; }
  .n2-list-table col.n2-col-index { width: 48px; }
  .n2-list-table col.n2-col-actions { width: 52px !important; }
  .n2-list-table col.n2-mobile-primary {
    width: min(150px, max(88px, calc(100vw - 200px))) !important;
  }
  /* 移动端 max-content 布局下 fixed 不生效：长文本列（如故事标题）会被内容撑到数百 px，
     把后面的关键列（用户故事编号等）挤出首屏。给 primary 列锁 max-width + 截断，
     表格横向滚动时后续列才能进入视口。 */
  .n2-list-table th.n2-mobile-primary,
  .n2-list-table td.n2-mobile-primary {
    max-width: 150px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* 移动端「全部列显示」（n2-list--mobile-columns-open）时，长文本列（故事描述等）
     同样会被内容撑到数千 px（实测 7493px），横向滚动几十屏才能到列尾。
     给所有 secondary 列也锁上限 + 截断，滚动距离回到可接受范围。 */
  .n2-table-wrap.n2-list--mobile-columns-open .n2-list-table th.n2-mobile-secondary,
  .n2-table-wrap.n2-list--mobile-columns-open .n2-list-table td.n2-mobile-secondary {
    max-width: 260px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .n2-list-table .n2-col-check {
    padding-left: 0;
    padding-right: 0;
  }
  .n2-list-table .n2-col-check .n2-checkbox {
    width: 44px;
    height: 44px;
    justify-content: center;
  }
  .n2-list-table .n2-col-actions {
    padding-left: 4px;
    padding-right: 4px;
  }
  .n2-list-table .is-sticky-left {
    z-index: 5;
    box-shadow: 2px 0 6px rgba(0, 0, 0, 0.08);
  }
  .n2-list-table th.is-sticky-left {
    z-index: 6;
  }
  .n2-list-table .n2-col-actions:has(.n2-mobile-actions.is-open) {
    z-index: 10;
  }
  .n2-list-table th[data-sort] {
    min-height: 48px;
    padding-left: 10px;
    padding-right: 10px;
  }
  .n2-table-wrap:not(.n2-list--mobile-columns-open) .n2-list-table col.n2-mobile-secondary,
  .n2-table-wrap:not(.n2-list--mobile-columns-open) .n2-list-table th.n2-mobile-secondary,
  .n2-table-wrap:not(.n2-list--mobile-columns-open) .n2-list-table td.n2-mobile-secondary {
    display: none;
  }
  .n2-mobile-column-toggle:not([hidden]) {
    display: inline-flex;
    min-height: 44px;
  }
  .n2-list-table .n2-mobile-actions {
    position: relative;
    display: inline-flex;
    vertical-align: middle;
  }
  .n2-list-table .n2-mobile-actions__toggle {
    display: inline-flex;
    width: 44px;
    height: 44px;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--n2-border);
    border-radius: var(--n2-radius-base);
    background: var(--n2-bg-container);
    color: var(--n2-text-link);
    font-size: 22px;
    line-height: 1;
    padding: 0;
    cursor: pointer;
  }
  .n2-list-table .n2-col-actions .n2-mobile-actions__items {
    display: none;
  }
  .n2-list-table .n2-col-actions .n2-mobile-actions.is-open > .n2-mobile-actions__items {
    display: flex;
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    z-index: 20;
    min-width: 120px;
    padding: 8px 12px;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    border: 1px solid var(--n2-border);
    border-radius: var(--n2-radius-md);
    background: var(--n2-bg-elevated);
    box-shadow: var(--n2-shadow-dropdown);
  }
  .n2-table-scroll:has(.n2-mobile-actions.is-open),
  .n2-table-wrap:has(.n2-mobile-actions.is-open) {
    overflow: visible;
  }
  .n2-list-table .n2-mobile-actions__items .n2-link {
    /* US202610040015：只给 min-height 时，「编辑」这类两字文案宽度仅 28px，
       min(width,height) 仍不达 44px 无障碍下限；补 min-width 后视觉不变，多出的是热区。
       US202610060044：移动端触控规范要的是「高 >= 36px、宽 >= 46px」，宽度下限从 44 抬到 46
       ——「编辑」「删除」这类两字文案的实测宽度只有 28px，放宽的是热区、视觉不变；
       高度保持 44px（已高于 36px 下限，也是无障碍那一档）。按钮间距仍是 --n2-space-sm
       （移动端实测 12px），相邻热区不重叠。本规则只在窄视口（<=768px）生效，
       桌面端按钮维持行内文字尺寸，列宽拖拽与布局逐字不变。 */
    min-width: 46px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
  }
  .n2-toolbar__right > .n2-input,
  .n2-toolbar__right > .n2-dropdown-wrap > .n2-btn {
    min-height: 44px;
  }
  .n2-filter-bar .n2-input,
  .n2-filter-bar .n2-select,
  .n2-filter-bar__actions .n2-btn {
    min-height: 44px;
  }
}

/* 移动端操作列平铺（引擎级默认，US202610080010 / IR202610060013）：操作列按钮在窄视口
   **平铺展开**、不折叠成 ⋯（覆盖上面的移动断点规则：⋯ 隐藏、按钮容器 items 始终展开）。
   引擎的折叠判定（US202610060044 机制评估，两段合起来才生效）：
     ① 视口阈值 —— 上面的 @media (max-width: 768px) 才把 ⋯ 显示出来、并把 items 收成
        纵向下拉（宽视口本来就是 display:contents 平铺，没有折叠这回事）；
     ② 配置项 —— listPage 的引擎默认值 mobileActionsExpanded（`opts.noMobileActions !== false`）
        据此在 .n2-table-wrap 上挂 n2-list--no-mobile-actions，由下面三条把 ① 的结果整段反掉。
   所以「不折叠」是**引擎默认**（全站列表页：n2McPage 元数据页与直连 listPage 的插件页都在内）；
   页面要恢复窄屏折叠 ⋯，显式传 opts.noMobileActions === false（n2McPage 对应
   cfg.noMobileActions === false）—— 这是唯一的关闭方式；显式 true 与不传等价，
   n2McReg 存量逐页 `noMobileActions: true` 因此是冗余声明、行为不变。
   展开后的触控目标（高 >= 36px / 宽 >= 46px）由上面移动断点里的
   .n2-mobile-actions__items .n2-link 规则给足。 */
.n2-table-wrap.n2-list--no-mobile-actions .n2-mobile-actions__toggle {
  display: none !important;
}
.n2-table-wrap.n2-list--no-mobile-actions .n2-mobile-actions {
  display: contents !important;
}
.n2-table-wrap.n2-list--no-mobile-actions .n2-col-actions .n2-mobile-actions__items {
  display: inline-flex !important;
}

/* 声明了列宽的表格改用固定布局（设计规范「列宽拖拽」：表格保持 table-layout: fixed）。
   默认的 auto 布局会拿 nowrap 单元格的**内容宽度**当最小宽度 —— API 接口页的路径列
   因此被撑到 351px（声明值是 268px），九列合计超出容器、最右的状态列被裁掉，
   同时把 40/64/76 三个窄列挤小，反过来又让按声明值算的左锁偏移对不上（ISSUE-058）。
   只给显式声明了列宽的表格加这个类：没有 <colgroup> 宽度的老渲染器保持 auto。 */
.n2-table--fixed {
  table-layout: fixed;
}

/* 列宽拖拽：每个可调列在表头右缘放一个抓取区。
   只在带 <colgroup> 的简单表格上启用，由 shell.js 注入。 */
.n2-table__resizer {
  position: absolute;
  top: 0;
  right: -5px;
  width: 10px;
  height: 100%;
  z-index: 5;
  cursor: col-resize;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.n2-table__resizer::after {
  content: "";
  position: absolute;
  top: 25%;
  right: 4px;
  width: 2px;
  height: 50%;
  border-radius: 1px;
  background: transparent;
  transition: background var(--n2-duration-fast) var(--n2-ease);
}
.n2-table th:hover .n2-table__resizer::after,
.n2-table__resizer.is-active::after {
  background: var(--n2-color-primary);
}
/* 悬停或拖拽中的表头必须盖住右侧相邻表头，否则抓取区被遮挡。
   操作列本身是左冻结列（.n2-list-table th.is-sticky-left 也是 z-index:6，且规则更靠后），
   同权重下后者胜出 —— 抓取区右半被右侧冻结列压住，只剩左半 5px 可抓（US202610060002）。
   这里用 .n2-list-table 提权到 7，保证悬停/拖拽中的表头永远在最上。 */
.n2-table th.is-resizable:hover,
.n2-table th.is-resizing-col,
.n2-list-table th.is-resizable:hover,
.n2-list-table th.is-resizing-col {
  z-index: 7;
}
/* 操作列的抓取区落在本列右缘，而右缘正是下一列（也是左冻结列）的起点 —— 下一列不透明，
   会把抓取区盖掉，只剩左半约 5px 可抓（US202610060002 实测：elementFromPoint 命中下一列）。
   操作列是「冻结骨架」的最后一列，让它常驻在后续冻结列之上，抓取区才整条可点。
   只提表头（th），数据行不受影响。 */
.n2-list-table th.n2-col-actions {
  z-index: 7;
}
.n2-table.is-resizing {
  cursor: col-resize;
}
.n2-table.is-resizing td,
.n2-table.is-resizing th {
  -webkit-user-select: none;
  user-select: none;
}

/* 排序指示：可排序列的表头在字段名右侧显示一组上下箭头。
   表头里写一个空标签即可，两枚箭头由伪元素画出。未排序时两枚
   都是浅灰；当前排序列点亮对应的一枚，箭头用主色。 */
.n2-sorter {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  margin-left: var(--n2-space-xxs);
  vertical-align: middle;
  cursor: pointer;
}
.n2-sorter::before,
.n2-sorter::after {
  content: '';
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
}
.n2-sorter::before {
  border-bottom: 4px solid var(--n2-text-quaternary);
  margin-bottom: 1px;
}
.n2-sorter::after {
  border-top: 4px solid var(--n2-text-quaternary);
}
.n2-sorter.is-asc::before {
  border-bottom-color: var(--n2-color-primary);
}
.n2-sorter.is-desc::after {
  border-top-color: var(--n2-color-primary);
}

/* ==========================================================================
   6. 状态点 / 标签
   ========================================================================== */

.n2-status {
  display: inline-flex;
  align-items: center;
  gap: var(--n2-space-xs);
  font-size: var(--n2-font-body);
  color: var(--n2-text-primary);
}
.n2-status::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--n2-dot-disabled);
}
.n2-status--success::before { background: var(--n2-dot-success); }
.n2-status--disabled::before { background: var(--n2-dot-disabled); }
.n2-status--pending::before { background: var(--n2-dot-pending); }
.n2-status--error::before { background: var(--n2-dot-error); }
.n2-status--draft::before { background: var(--n2-dot-draft); }

.n2-tag {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 var(--n2-space-xs);
  font-size: var(--n2-font-caption);
  line-height: 1;
  border-radius: var(--n2-radius-sm);
  border: 1px solid transparent;
  white-space: nowrap;
}
.n2-tag--default {
  background: var(--n2-bg-hover);
  border-color: var(--n2-border);
  color: var(--n2-text-secondary);
}
.n2-tag--primary {
  background: var(--n2-color-primary-bg);
  border-color: var(--n2-color-primary-border);
  color: var(--n2-text-link);
}
.n2-tag--success {
  background: var(--n2-color-success-bg);
  border-color: var(--n2-color-success-border);
  color: var(--n2-text-success);
}
.n2-tag--warning {
  background: var(--n2-color-warning-bg);
  border-color: var(--n2-color-warning-border);
  color: var(--n2-text-warning);
}
.n2-tag--error {
  background: var(--n2-color-error-bg);
  border-color: var(--n2-color-error-border);
  color: var(--n2-text-error);
}
/* 「页面管理」里内置页面那枚标记（US-UI-048）。它与页面名称同格，靠外边距拉开距离 ——
   间距走 token、不写行内 px（audit-app.mjs 的 inline-style-px 盯着行内样式）。 */
.n2-tag--builtin {
  margin-left: var(--n2-space-xs);
}

.n2-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--n2-space-xxs);
  height: 20px;
  padding: 0 var(--n2-space-xs);
  font-size: var(--n2-font-caption);
  border-radius: var(--n2-radius-round);
  background: var(--n2-color-primary-bg);
  color: var(--n2-text-link);
}
.n2-badge--error {
  background: var(--n2-color-error-bg);
  color: var(--n2-text-error);
}
.n2-badge--success {
  background: var(--n2-color-success-bg);
  color: var(--n2-text-success);
}
.n2-badge--warning {
  background: var(--n2-color-warning-bg);
  color: var(--n2-text-warning);
}
.n2-badge--neutral {
  background: var(--n2-bg-hover);
  color: var(--n2-text-secondary);
}

/* 可勾选标签组：多选场景（能力、技术栈），单击切换 */
.n2-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--n2-space-xs);
}
.n2-chip {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 var(--n2-space-sm);
  border: 1px solid var(--n2-border-secondary);
  border-radius: var(--n2-radius-base);
  background: var(--n2-bg-container);
  font-family: inherit;
  font-size: var(--n2-font-caption);
  line-height: 1;
  color: var(--n2-text-secondary);
  cursor: pointer;
  transition: all var(--n2-duration-fast) var(--n2-ease);
}
.n2-chip:hover {
  border-color: var(--n2-color-primary);
  color: var(--n2-text-link);
}
.n2-chip.is-active {
  border-color: var(--n2-color-primary);
  background: var(--n2-color-primary-bg);
  color: var(--n2-text-link);
}

/* ==========================================================================
   7. 分页
   ========================================================================== */

.n2-pagination {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--n2-space-md);
  padding: var(--n2-space-md);
  border-top: 1px solid var(--n2-border-split);
  font-size: var(--n2-font-body);
}
.n2-pagination__total {
  color: var(--n2-text-secondary);
  margin-right: auto;
}
.n2-pagination__list {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
}
.n2-page-btn {
  min-width: var(--n2-size-control);
  height: var(--n2-size-control);
  padding: 0 var(--n2-space-xs);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-base);
  background: var(--n2-bg-container);
  color: var(--n2-text-primary);
  cursor: pointer;
  font-size: var(--n2-font-body);
  transition: all var(--n2-duration-fast) var(--n2-ease);
}
.n2-page-btn:hover {
  border-color: var(--n2-color-primary);
  color: var(--n2-text-link);
}
.n2-page-btn.is-active {
  border-color: var(--n2-color-primary);
  color: var(--n2-text-link);
}
.n2-page-btn[disabled] {
  color: var(--n2-text-quaternary);
  cursor: not-allowed;
}
.n2-page-btn[disabled]:hover {
  border-color: var(--n2-border);
  color: var(--n2-text-quaternary);
}
.n2-pagination .n2-select {
  width: auto;
  min-width: 96px;
}

/* ==========================================================================
   8. 标签页
   ========================================================================== */

.n2-tabs {
  display: flex;
  align-items: center;
  gap: var(--n2-space-lg);
  border-bottom: 1px solid var(--n2-border-split);
}
.n2-tab {
  position: relative;
  border: none;
  background: none;
  padding: var(--n2-space-sm) 0;
  font-size: var(--n2-font-body);
  color: var(--n2-text-secondary);
  cursor: pointer;
  transition: color var(--n2-duration-base) var(--n2-ease);
}
.n2-tab:hover {
  color: var(--n2-text-link);
}
.n2-tab.is-active {
  color: var(--n2-text-link);
  font-weight: var(--n2-font-weight-medium);
}
.n2-tab.is-active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--n2-color-primary);
  border-radius: 1px;
}

/* 筛选设置抽屉双页签（US202610050002）：页签头贴抽屉标题下方 */
.n2-drawer__tabs {
  padding: 0 var(--n2-space-lg);
  background: var(--n2-bg-container);
  border-bottom: 1px solid var(--n2-border-split);
}
.n2-filter-settings__panel {
  padding-top: var(--n2-space-lg);
}
/* 高级查询状态条（筛选区顶部，US202610050002 AC-2） */
.n2-adv-query-bar {
  display: flex;
  align-items: center;
  gap: var(--n2-space-sm);
  width: 100%;
  box-sizing: border-box;
  padding: var(--n2-space-sm) var(--n2-space-md);
  margin-bottom: var(--n2-space-md);
  background: rgba(22, 119, 255, 0.06);
  border: 1px solid rgba(22, 119, 255, 0.25);
  border-radius: var(--n2-radius);
  flex-wrap: wrap;
}
/* 清除/重置后 hidden 需真正隐藏：display:flex 会覆盖 UA [hidden]{display:none}，残留空框（问题单 24da8f33 / codex US050002 验收发现） */
.n2-adv-query-bar[hidden] {
  display: none;
}
.n2-adv-query-bar__tag {
  color: var(--n2-color-primary);
  font-weight: var(--n2-font-weight-medium);
}
.n2-adv-query-bar__name {
  font-weight: var(--n2-font-weight-medium);
}
.n2-adv-query-bar__count {
  color: var(--n2-text-secondary);
  margin-right: auto;
}
/* 高级查询面板 */
.n2-adv-query {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-md);
}
.n2-adv-query__list-title {
  margin: var(--n2-space-md) 0 var(--n2-space-sm);
  font-size: var(--n2-font-body);
  font-weight: var(--n2-font-weight-medium);
}
.n2-adv-scheme-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xs);
}
.n2-adv-scheme-item {
  display: flex;
  align-items: center;
  gap: var(--n2-space-md);
  padding: var(--n2-space-sm) var(--n2-space-md);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius);
}
.n2-adv-scheme-item.is-pinned {
  border-color: var(--n2-color-warning);
  background: rgba(250, 173, 20, 0.08);
}
.n2-adv-scheme-item__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.n2-adv-scheme-item__pin {
  margin-left: var(--n2-space-sm);
  padding: 1px var(--n2-space-sm);
  font-size: 12px;
  color: #ad6800;
  background: rgba(250, 173, 20, 0.15);
  border-radius: var(--n2-radius-sm);
}
.n2-adv-scheme-item__actions {
  display: flex;
  gap: var(--n2-space-sm);
  flex-shrink: 0;
}
.n2-adv-scheme-name-row {
  margin-top: var(--n2-space-sm);
}
.n2-adv-scheme-name__row {
  display: flex;
  gap: var(--n2-space-sm);
  align-items: center;
}
.n2-adv-scheme-name__row .n2-input {
  flex: 1;
}

/* ==========================================================================
   9. 弹窗 / 抽屉
   ========================================================================== */

.n2-modal-mask {
  position: fixed;
  inset: 0;
  background: var(--n2-bg-mask);
  z-index: var(--n2-z-modal);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 10vh var(--n2-space-md) var(--n2-space-md);
  overflow: auto;
  animation: n2-fade var(--n2-duration-base) var(--n2-ease);
}
@keyframes n2-fade {
  from { opacity: 0; }
}

.n2-modal {
  width: 100%;
  max-width: var(--n2-size-modal);
  background: var(--n2-bg-elevated);
  border-radius: var(--n2-radius-md);
  box-shadow: var(--n2-shadow-modal);
  animation: n2-pop var(--n2-duration-base) var(--n2-ease);
}
@keyframes n2-pop {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
}

.n2-modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--n2-space-md);
  padding: var(--n2-space-md) var(--n2-space-lg);
  border-bottom: 1px solid var(--n2-border-split);
}
.n2-modal__title,
.n2-drawer__title {
  font-size: var(--n2-font-h3);
  line-height: var(--n2-line-h3);
  font-weight: var(--n2-font-weight-semibold);
}
.n2-modal__close,
.n2-drawer__close {
  border: none;
  background: none;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--n2-text-tertiary);
  cursor: pointer;
  transition: color var(--n2-duration-fast) var(--n2-ease);
}
.n2-modal__close:hover,
.n2-drawer__close:hover {
  color: var(--n2-text-primary);
}
.n2-modal__body {
  padding: var(--n2-space-lg);
  max-height: 62vh;
  overflow: auto;
}
.n2-modal__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-sm) var(--n2-space-lg);
  border-top: 1px solid var(--n2-border-split);
}
.n2-modal--sm {
  max-width: 400px;
}
.n2-modal--lg {
  max-width: var(--n2-size-modal-lg);
}
.n2-modal--xl {
  max-width: var(--n2-size-modal-xl);
}

/* 多标签页弹窗版式：标题栏 + 通栏标签 + 内容 + 底栏。
   标签栏自带分隔线，所以标题栏不再画线，避免出现两条横线。 */
.n2-modal--tabs .n2-modal__head {
  border-bottom: none;
  padding-bottom: var(--n2-space-xs);
}
.n2-modal__tabs {
  padding: 0 var(--n2-space-lg);
}
.n2-modal--tabs .n2-modal__body {
  max-height: 64vh;
}

/* 抽屉 */
.n2-drawer-mask {
  position: fixed;
  inset: 0;
  background: var(--n2-bg-mask);
  z-index: var(--n2-z-modal);
  display: flex;
  justify-content: flex-end;
  animation: n2-fade var(--n2-duration-base) var(--n2-ease);
}
.n2-drawer {
  position: relative;
  width: var(--n2-size-drawer);
  min-width: var(--n2-size-drawer-min);
  max-width: calc(100vw - var(--n2-size-drawer-context));
  height: 100%;
  background: var(--n2-bg-elevated);
  box-shadow: var(--n2-shadow-modal);
  display: flex;
  flex-direction: column;
  /* fill-mode 必须是 both：只写 from 帧时，动画一旦被打断（换步重绘、reduced-motion 下
     duration 收成 0.001ms）就会回落到「无动画」的初始值，而初始值里没有 transform ——
     抽屉会停在 translateX(100%) 的滑出位，窄视口下整只抽屉滑出可视区、点不到。
     补上 to 帧 + both，动画结束（或被跳过）都停在 translateX(0)。 */
  animation: n2-slide-in var(--n2-duration-base) var(--n2-ease) both;
}
@keyframes n2-slide-in {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}
.n2-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--n2-space-md) var(--n2-space-lg);
  border-bottom: 1px solid var(--n2-border-split);
  flex: none;
}
.n2-drawer__body {
  padding: var(--n2-space-lg);
  overflow: auto;
  flex: 1 1 0%;
  /* 允许 flex 滚动区收缩，避免窄屏字段的固有尺寸撑出抽屉正文。 */
  min-width: 0;
  min-height: 0;
}
.n2-drawer__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-sm) var(--n2-space-lg);
  border-top: 1px solid var(--n2-border-split);
  flex: none;
}
/* 抽屉可缩放：每个抽屉的左缘都有一条抓取条，左右拖动改变宽度，
   双击恢复 Token 默认宽度。抓取条由 n2InitDrawerResize() 自动插入，页面不用写标记。
   宽度上下限由 --n2-size-drawer-min / --n2-size-drawer-context 决定，
   这里再做一次夹紧，避免样式表缺失时拖出视口。 */
.n2-drawer__resizer {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -5px;
  width: 10px;
  z-index: 1;
  cursor: col-resize;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.n2-drawer__resizer::after {
  content: "";
  position: absolute;
  top: 30%;
  left: 4px;
  width: 2px;
  height: 40%;
  border-radius: 1px;
  background: transparent;
  transition: background var(--n2-duration-fast) var(--n2-ease);
}
.n2-drawer__resizer:hover::after,
.n2-drawer__resizer.is-active::after {
  background: var(--n2-color-primary);
}
/* 拖拽中禁止选中文字，光标保持左右箭头 */
body.is-drawer-resizing {
  cursor: col-resize;
  -webkit-user-select: none;
  user-select: none;
}
/* 宽抽屉：容纳多列子表格，与宽弹窗同宽 */
.n2-drawer--lg {
  width: var(--n2-size-drawer-lg);
}
/* 多标签页抽屉版式：标签栏自带分隔线，所以标题栏不再画线，
   内容区用 flex:1 占满剩余高度。 */
.n2-drawer--tabs .n2-drawer__head {
  border-bottom: none;
  padding-bottom: var(--n2-space-xs);
}
.n2-drawer__tabs {
  padding: 0 var(--n2-space-lg);
  flex: none;
}

/* --------------------------------------------------------------------------
   US202610060045：页面区块（n21_page_block）的抽屉内样式。
   区块列表是平铺的一维行（不是树），所以行本身就是拖拽的落点槽：
   拖到某一行上 = 插到它前面，用一条 2px 主色上边线表示落点。
   配置面板就地展开在行内（不弹窗），所以行是 flex-wrap 容器。
   -------------------------------------------------------------------------- */
.n2-block-list {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xs);
}
.n2-block-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
  transition: background 0.2s, border-color 0.2s;
}
.n2-block-row:hover { background: var(--n2-bg-hover); }
.n2-block-row.is-dragging { opacity: 0.5; }
.n2-block-row.is-drop-before { border-top: 2px solid var(--n2-color-primary); }
.n2-block-row__handle {
  cursor: grab;
  color: var(--n2-text-quaternary);
  letter-spacing: -2px;
  user-select: none;
}
.n2-block-row__no {
  min-width: 20px;
  text-align: right;
  color: var(--n2-text-tertiary);
  font-variant-numeric: tabular-nums;
}
.n2-block-row__main {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  flex: 1 1 auto;
  min-width: 0;
}
.n2-block-row__title {
  font-weight: var(--n2-font-weight-medium);
  color: var(--n2-text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.n2-block-row__acts {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  flex: none;
}
.n2-block-row__confirm { color: var(--n2-color-error); }
.n2-block-cfg {
  flex: 1 1 100%;
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-md);
  margin-top: var(--n2-space-xs);
  padding: var(--n2-space-md);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-page);
}
.n2-block-cfg__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--n2-space-xs);
}
.n2-block-tpl-list {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xs);
}
.n2-block-tpl {
  display: flex;
  align-items: flex-start;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-sm);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-md);
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
}
.n2-block-tpl:hover { border-color: var(--n2-color-primary-hover); }
.n2-block-tpl.is-active {
  border-color: var(--n2-color-primary);
  background: var(--n2-color-primary-bg);
}
.n2-block-tpl__body { display: flex; flex-direction: column; gap: var(--n2-space-xxs); }
.n2-block-tpl__name { font-weight: var(--n2-font-weight-medium); color: var(--n2-text-primary); }
.n2-block-tpl__desc { color: var(--n2-text-tertiary); font-size: var(--n2-font-caption); }

/* 拖拽编排：左「区块库」、右「画布」。库里的卡片是 HTML5 draggable 的拖源，
   画布（#n2BlockList 本身）是拖入的落点 —— 落到空白处 = 追加到末尾。
   触屏没有 HTML5 拖拽，所以库里的卡片同时是按钮：点一下也追加（AC-07 移动端走这条）。
   窄视口（<=768px）两栏改上下堆叠，库横排成可换行的一行，不挤画布宽度。 */
.n2-block-work {
  display: grid;
  grid-template-columns: 172px minmax(0, 1fr);
  gap: var(--n2-space-sm);
  align-items: start;
}
.n2-block-lib,
.n2-block-canvas {
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
  min-width: 0;
}
.n2-block-lib__head,
.n2-block-canvas__head {
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border-bottom: 1px solid var(--n2-border-split);
  font-size: var(--n2-font-caption);
  line-height: var(--n2-line-caption);
  font-weight: var(--n2-font-weight-medium);
  color: var(--n2-text-secondary);
}
.n2-block-lib__items {
  padding: var(--n2-space-xs);
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xxs);
}
.n2-block-lib__item {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xxs);
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
  cursor: grab;
  font-size: var(--n2-font-caption);
  line-height: var(--n2-line-caption);
  color: var(--n2-text-primary);
  text-align: left;
}
.n2-block-lib__item:hover { border-color: var(--n2-color-primary-hover); background: var(--n2-bg-hover); }
.n2-block-lib__item.is-dragging { opacity: 0.5; }
.n2-block-canvas { padding: var(--n2-space-xs); }
.n2-block-canvas.is-drop-tail { border-color: var(--n2-color-primary); }
.n2-block-canvas__body { min-height: 132px; }
@media (max-width: 768px) {
  .n2-block-work { grid-template-columns: minmax(0, 1fr); }
  .n2-block-lib__items { flex-direction: row; flex-wrap: wrap; }
  .n2-block-lib__item { flex: 0 0 auto; }
}

/* AI 蓝图回填（AC-04）：生成后先把它摆出来给人看，再逐块微调。
   放在区块列表抽屉顶上，而不是留在 AI 抽屉里 —— 抽屉是单例，
   切到区块列表时 AI 抽屉会被顶掉，蓝图跟着一起没了。 */
.n2-block-ai-preview {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xxs);
  margin-bottom: var(--n2-space-sm);
  padding: var(--n2-space-sm);
  border: 1px solid var(--n2-color-primary);
  border-radius: var(--n2-radius-md);
  background: var(--n2-color-primary-bg);
}
.n2-block-ai-preview__hd {
  font-weight: var(--n2-font-weight-medium);
  color: var(--n2-text-primary);
}
.n2-block-ai-preview__item {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  flex-wrap: wrap;
}
.n2-block-ai-preview__fields {
  color: var(--n2-text-tertiary);
  font-size: var(--n2-font-caption);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

/* 区块配置面板（AC-05）：字段显隐/排序、内置按钮开关、工作流绑定、数据源与指标。
   视觉控件与两个 JSON 文本域是**同一份数据**：控件动一下就写回文本域（见 n2BlockCfgSync），
   保存时以文本域为准 —— 手改 JSON 与点开关不会各说各话。 */
.n2-block-cfg__sec {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xs);
  padding-top: var(--n2-space-sm);
  border-top: 1px dashed var(--n2-border-split);
}
.n2-block-cfg__sec:first-child { padding-top: 0; border-top: 0; }
.n2-block-cfg__sechd {
  font-size: var(--n2-font-caption);
  line-height: var(--n2-line-caption);
  font-weight: var(--n2-font-weight-medium);
  color: var(--n2-text-secondary);
}
.n2-block-cfg__row {
  display: flex;
  align-items: center;
  gap: var(--n2-space-sm);
  flex-wrap: wrap;
}
.n2-block-cfg__row > .n2-field { flex: 1 1 160px; min-width: 0; }
.n2-block-cfg__check {
  display: inline-flex;
  align-items: center;
  gap: var(--n2-space-xxs);
  color: var(--n2-text-secondary);
}
.n2-block-cfg__rows { display: flex; flex-direction: column; gap: var(--n2-space-xxs); }
.n2-field-item {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xxs) var(--n2-space-xs);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
}
.n2-field-item.is-hidden { opacity: 0.55; }
.n2-field-item__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--n2-text-primary);
}
.n2-field-item__acts { display: flex; align-items: center; gap: var(--n2-space-xs); flex: none; }

/* --------------------------------------------------------------------------
   页面布局设计器（US202609290002）。三栏：控件面板 / 画布 / 属性。
   画布画的是**线框**不是真渲染 —— 真渲染是页面自己那条 Amis 通路的事，
   这里要的是「结构一眼看清、落点一眼看准」。所以每个节点是一个带类型标签的方框，
   栅格的列并排画（布局唯一看得见的地方），可放子节点的容器里画一条虚线的落点槽。
   尺寸全走 token，不写行内 px。拖拽手柄复用 .n2-numrule__handle（同一枚三横线）。
   -------------------------------------------------------------------------- */
.n2-designer {
  display: grid;
  grid-template-columns: 176px minmax(0, 1fr) 232px;
  gap: var(--n2-space-sm);
  align-items: start;
}
.n2-designer__col {
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
  min-width: 0;
}
.n2-designer__colhead {
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border-bottom: 1px solid var(--n2-border-split);
  font-size: var(--n2-font-caption);
  line-height: var(--n2-line-caption);
  font-weight: var(--n2-font-weight-medium);
  color: var(--n2-text-secondary);
}
.n2-designer__body {
  padding: var(--n2-space-xs);
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xxs);
}
/* 面板里那句说明。**故意不用 .n2-caption** —— 页面 Schema 抽屉里已有一句 .n2-caption，
   而用例是按 .n2-caption 单数定位的（断言里含 Amis 那句），多一个就把它变成复数。 */
.n2-designer__hint {
  margin: 0;
  padding: var(--n2-space-xxs) var(--n2-space-xs);
  font-size: var(--n2-font-caption);
  line-height: var(--n2-line-caption);
  color: var(--n2-text-tertiary);
}
.n2-designer__grp {
  padding: var(--n2-space-xxs) var(--n2-space-xs) 0;
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
}
.n2-designer__ctl {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xxs) var(--n2-space-xs);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-sm);
  background: var(--n2-bg-container);
  font-size: var(--n2-font-caption);
  cursor: grab;
}
.n2-designer__ctl:hover {
  border-color: var(--n2-color-primary-border);
  background: var(--n2-color-primary-bg);
}
.n2-designer__ctl:active { cursor: grabbing; }
/* 面板里那枚类型小签走既有的 .n2-code（与字段表里「字段编码」同一枚），这里不再另起样式 */
/* 画布上的一个节点 */
.n2-dnode {
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-sm);
  background: var(--n2-bg-container);
}
.n2-dnode.is-selected {
  border-color: var(--n2-color-primary);
  box-shadow: 0 0 0 2px var(--n2-color-primary-bg);
}
.n2-dnode.is-dragging { opacity: 0.4; }
.n2-dnode__head {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xxs) var(--n2-space-xs);
  border-bottom: 1px solid var(--n2-border-split);
  background: var(--n2-bg-table-header);
  cursor: pointer;
}
.n2-dnode__type {
  font-family: var(--n2-font-family-code);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-secondary);
}
/* 用户自己填的标题（label / name）—— 与类型签并排，但它是人话，用正文字体 */
.n2-dnode__label {
  font-size: var(--n2-font-caption);
  color: var(--n2-text-primary);
}
.n2-dnode__acts {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
}
.n2-dnode__body,
.n2-dnode__colbody {
  padding: var(--n2-space-xxs);
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xxs);
}
/* 栅格：列并排 —— 布局在这里才看得出来 */
.n2-dnode__cols {
  display: flex;
  gap: var(--n2-space-xxs);
  padding: var(--n2-space-xxs);
}
.n2-dnode__col {
  flex: 1 1 0;
  min-width: 0;
  padding: var(--n2-space-xxs);
  border: 1px dashed var(--n2-border);
  border-radius: var(--n2-radius-sm);
}
.n2-dnode__colhead {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
}
/* 落点槽：空着是一句提示，拖过去时整块点亮（与字段行那条落点线同一种意思） */
.n2-dnode__slot {
  padding: var(--n2-space-xs);
  border: 1px dashed var(--n2-border);
  border-radius: var(--n2-radius-sm);
  text-align: center;
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
}
/* 孩子前面那条薄槽：只画一条线、不写字（写字的话一个三孩子的容器里会挂三句「放到这里」）。
   高度就是它的命中区，别调到看不出来 —— 8px 是字段行那条落点线的量级。 */
.n2-dnode__slot--thin {
  padding: 0;
  height: 8px;
  border-radius: var(--n2-radius-sm);
  flex: none;
}
.n2-dnode__slot.is-drop-target,
.n2-dnode__col.is-drop-target {
  border-color: var(--n2-color-primary);
  background: var(--n2-color-primary-bg);
  color: var(--n2-text-link);
}
.n2-designer__empty {
  padding: var(--n2-space-xl) var(--n2-space-md);
  border: 1px dashed var(--n2-border);
  border-radius: var(--n2-radius-md);
  text-align: center;
  color: var(--n2-text-tertiary);
}
.n2-designer__empty.is-drop-target {
  border-color: var(--n2-color-primary);
  background: var(--n2-color-primary-bg);
  color: var(--n2-text-link);
}
/* 属性面板：标签在上、控件在下 */
.n2-designer__prop {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xxs);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-secondary);
}
.n2-modal--xl.n2-wf-modal .n2-modal__body {
  max-height: min(78vh, 900px);
}
.n2-wf-editor {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-md);
}
.n2-wf-editor__meta {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--n2-space-sm);
  padding: var(--n2-space-md);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
}
.n2-wf-editor__tabs {
  display: flex;
  gap: var(--n2-space-xs);
  border-bottom: 1px solid var(--n2-border-split);
}
.n2-wf-editor__tab {
  min-height: 36px;
  padding: 0 var(--n2-space-md);
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--n2-text-secondary);
  cursor: pointer;
}
.n2-wf-editor__tab.is-active {
  border-bottom-color: var(--n2-color-primary);
  color: var(--n2-color-primary);
  font-weight: var(--n2-font-weight-semibold);
}
.n2-wf-editor__trigger {
  padding: var(--n2-space-md);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
}
.n2-wf-trigger-fields {
  display: flex;
  flex-wrap: wrap;
  gap: var(--n2-space-xs) var(--n2-space-md);
}
.n2-wf-trigger-field {
  display: inline-flex;
  align-items: center;
  gap: var(--n2-space-xxs);
}
.n2-wf-trigger-error {
  color: var(--n2-color-error);
}
.n2-wf-editor__steps {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-sm);
}
.n2-wf-editor__step {
  padding: var(--n2-space-md);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
}
.n2-wf-editor__stephead,
.n2-wf-editor__toolbar,
.n2-wf-editor__layers {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
}
.n2-wf-editor__stephead {
  margin-bottom: var(--n2-space-sm);
}
.n2-wf-editor__stephead .n2-input {
  max-width: 320px;
}
.n2-wf-editor__stephead strong {
  color: var(--n2-text-secondary);
  font-size: var(--n2-font-caption);
}
.n2-wf-editor__toolbar {
  flex-wrap: wrap;
}
.n2-wf-editor__connections {
  padding: var(--n2-space-md);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
}
.n2-wf-editor__layers {
  flex-wrap: wrap;
  align-items: stretch;
}
.n2-wf-editor__layer {
  flex: 1 1 220px;
  min-width: 0;
  padding: var(--n2-space-sm);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-sm);
}
.n2-wf-editor__layer-title {
  margin-bottom: var(--n2-space-xs);
  font-weight: var(--n2-font-weight-semibold);
  color: var(--n2-text-primary);
}
/* AI 任务节点（US202610090015）的行内增删区。关联记录一行 5 个控件 + 删除，
   写回字段一行 2 个控件 + 删除；都用 grid 而不是 flex，让「表名 / 外键 / 别名」三列
   在窄面板里也能对齐（画布属性面板只有 300px 宽）。 */
.n2-wf-ai-task__rel {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr) 96px 64px auto;
  gap: var(--n2-space-xs);
  align-items: center;
  margin-bottom: var(--n2-space-xs);
}
.n2-wf-ai-task__wb {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) auto;
  gap: var(--n2-space-xs);
  align-items: center;
  margin-bottom: var(--n2-space-xs);
}
.n2-wf-ai-task__rels,
.n2-wf-ai-task__wbs {
  margin-bottom: var(--n2-space-xs);
}
.n2-wf-ai-task__check {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  margin-bottom: var(--n2-space-xs);
}
/* 技能勾选组（US202610090015 AC-01）：与「绑定 AI 员工」下拉并列，窄面板里自动换行。 */
.n2-wf-ai-task__skills {
  margin-bottom: var(--n2-space-xs);
}
.n2-wf-ai-task__skills .n2-wf-ai-task__check {
  display: inline-flex;
  margin-right: var(--n2-space-md);
  margin-bottom: var(--n2-space-xxs);
}
.n2-wf-ai-task__rel .n2-input,
.n2-wf-ai-task__rel .n2-select,
.n2-wf-ai-task__wb .n2-input {
  min-width: 0;
}
@media (max-width: 900px) {
  .n2-wf-ai-task__rel,
  .n2-wf-ai-task__wb {
    grid-template-columns: minmax(0, 1fr);
  }
}
.n2-wf-canvas {
  display: grid;
  grid-template-columns: 152px minmax(0, 1fr) 300px;
  min-height: 430px;
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  overflow: hidden;
}
.n2-wf-canvas__palette,
.n2-wf-canvas__props {
  padding: var(--n2-space-sm);
  background: var(--n2-bg-container);
}
.n2-wf-canvas__palette {
  border-right: 1px solid var(--n2-border-split);
}
.n2-wf-canvas__props {
  border-left: 1px solid var(--n2-border-split);
  overflow: auto;
}
.n2-wf-canvas__workspace {
  position: relative;
  min-width: 0;
  min-height: 430px;
  padding: var(--n2-space-lg);
  overflow: auto;
  background-color: var(--n2-bg-page);
  background-image: radial-gradient(var(--n2-border) 1px, transparent 1px);
  background-size: 20px 20px;
}
.n2-wf-canvas__nodes {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: var(--n2-space-md);
  min-height: 380px;
}
.n2-wf-canvas__edges {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.n2-wf-canvas__edge {
  stroke: var(--n2-color-primary);
  stroke-width: 2;
}
.n2-wf-canvas__node {
  position: relative;
  z-index: 1;
  width: 170px;
  min-height: 72px;
  padding: var(--n2-space-sm);
  border: 1px solid var(--n2-border);
  border-radius: var(--n2-radius-md);
  background: var(--n2-bg-container);
  box-shadow: var(--n2-shadow-card);
  cursor: pointer;
}
.n2-wf-canvas__node.is-selected {
  border-color: var(--n2-color-primary);
  box-shadow: 0 0 0 2px var(--n2-color-primary-bg);
}
.n2-wf-canvas__nodehead {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  color: var(--n2-text-primary);
  font-weight: var(--n2-font-weight-semibold);
}
.n2-wf-canvas__handle {
  color: var(--n2-text-tertiary);
  cursor: grab;
}
.n2-wf-canvas__type {
  margin-top: var(--n2-space-xxs);
  color: var(--n2-text-tertiary);
  font-size: var(--n2-font-caption);
}
.n2-wf-editor__empty {
  align-self: center;
  margin: auto;
  color: var(--n2-text-tertiary);
  text-align: center;
}
@media (max-width: 900px) {
  .n2-wf-editor__meta { grid-template-columns: 1fr; }
  .n2-wf-canvas { grid-template-columns: 120px minmax(0, 1fr); }
  .n2-wf-canvas__props {
    grid-column: 1 / -1;
    border-left: 0;
    border-top: 1px solid var(--n2-border-split);
  }
}

/* ==========================================================================
   10. 反馈：消息 / 空状态 / 加载
   ========================================================================== */

.n2-message-stack {
  position: fixed;
  top: var(--n2-space-lg);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--n2-z-message);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--n2-space-xs);
  pointer-events: none;
}
.n2-message {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xs) var(--n2-space-md);
  background: var(--n2-bg-elevated);
  border-radius: var(--n2-radius-md);
  box-shadow: var(--n2-shadow-dropdown);
  font-size: var(--n2-font-body);
  animation: n2-pop var(--n2-duration-base) var(--n2-ease);
}
.n2-message__icon {
  display: flex;
  flex: none;
}
.n2-message--success .n2-message__icon { color: var(--n2-color-success); }
.n2-message--error .n2-message__icon { color: var(--n2-color-error); }
.n2-message--warning .n2-message__icon { color: var(--n2-color-warning); }
.n2-message--info .n2-message__icon { color: var(--n2-color-info); }

.n2-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--n2-space-sm);
  padding: var(--n2-space-xxl) var(--n2-space-md);
  color: var(--n2-text-tertiary);
  font-size: var(--n2-font-body);
}
.n2-empty__art {
  color: var(--n2-text-quaternary);
}

.n2-skeleton {
  background: linear-gradient(
    90deg,
    rgba(0, 0, 0, 0.06) 25%,
    rgba(0, 0, 0, 0.09) 37%,
    rgba(0, 0, 0, 0.06) 63%
  );
  background-size: 400% 100%;
  animation: n2-shimmer 1.4s ease infinite;
  border-radius: var(--n2-radius-sm);
  height: 16px;
}
@keyframes n2-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

/* ==========================================================================
   11. 下拉菜单
   ========================================================================== */

.n2-dropdown {
  min-width: 160px;
  padding: var(--n2-space-xxs);
  background: var(--n2-bg-elevated);
  border-radius: var(--n2-radius-md);
  box-shadow: var(--n2-shadow-dropdown);
  list-style: none;
  margin: 0;
}
.n2-dropdown__item {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border-radius: var(--n2-radius-base);
  font-size: var(--n2-font-body);
  color: var(--n2-text-primary);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--n2-duration-fast) var(--n2-ease);
}
.n2-dropdown__item:hover {
  background: var(--n2-bg-hover);
}
.n2-dropdown__item.is-active {
  background: var(--n2-color-primary-bg);
  color: var(--n2-text-link);
}
.n2-dropdown__divider {
  height: 1px;
  margin: var(--n2-space-xxs) 0;
  background: var(--n2-border-split);
}
/* 富菜单项（US202610080006 页面创建下拉）：图标 + 名称 + 一行说明。
   说明文字用次级色，缩在名称下面，不撑高整枚菜单项。 */
.n2-dropdown__item--rich {
  align-items: flex-start;
  gap: var(--n2-space-sm);
  padding: var(--n2-space-sm);
  min-width: 220px;
}
.n2-dropdown__item--rich .n2-dropdown__ic {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: var(--n2-radius-base);
  background: var(--n2-color-primary-bg);
  color: var(--n2-text-link);
}
.n2-dropdown__item--rich .n2-dropdown__tx {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.n2-dropdown__item--rich .n2-dropdown__name {
  font-size: var(--n2-font-body);
  color: var(--n2-text-primary);
}
.n2-dropdown__item--rich .n2-dropdown__desc {
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
  line-height: 1.45;
}
/* 页面创建引导向导（US202610080006）：步骤条 + 步骤体 + 底部三步操作。
   复用抽屉容器，向导自己的 foot 画在 body 里（多步交互不走框架的 data-ok 提交）。 */
.n2-wizard__steps {
  display: flex;
  align-items: center;
  gap: var(--n2-space-sm);
  padding: var(--n2-space-sm) var(--n2-space-md);
  margin: 0 0 var(--n2-space-md);
  border-bottom: 1px solid var(--n2-border-split);
  list-style: none;
}
.n2-wizard__step {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  font-size: var(--n2-font-body);
  color: var(--n2-text-tertiary);
  white-space: nowrap;
}
.n2-wizard__step + .n2-wizard__step::before {
  content: '';
  width: 18px;
  height: 1px;
  margin: 0 var(--n2-space-xs);
  background: var(--n2-border-split);
}
.n2-wizard__step.is-active { color: var(--n2-text-link); font-weight: 500; }
.n2-wizard__step.is-done { color: var(--n2-text-secondary); }
.n2-wizard__no {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex: none;
  border-radius: 50%;
  font-size: var(--n2-font-caption);
  background: var(--n2-bg-hover);
  color: var(--n2-text-secondary);
}
.n2-wizard__step.is-active .n2-wizard__no {
  background: var(--n2-color-primary);
  color: var(--n2-text-inverse);
}
.n2-wizard__step.is-done .n2-wizard__no {
  background: var(--n2-color-primary-bg);
  color: var(--n2-text-link);
}
.n2-wizard__body { padding: 0 var(--n2-space-md); }
.n2-wizard__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--n2-space-sm);
  padding: var(--n2-space-md);
  border-top: 1px solid var(--n2-border-split);
  margin-top: var(--n2-space-md);
}
.n2-wizard__radio {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-base);
  cursor: pointer;
  margin-right: var(--n2-space-sm);
}
.n2-wizard__radio:has(input:checked) {
  border-color: var(--n2-color-primary);
  background: var(--n2-color-primary-bg);
}
.n2-wizard__radio input { margin: 0; }
.n2-wizard__groups {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-md);
  margin-top: var(--n2-space-sm);
}
.n2-wizard__group {
  border: 1px solid var(--n2-border-split);
  border-radius: var(--n2-radius-md);
  padding: var(--n2-space-sm) var(--n2-space-md);
}
.n2-wizard__group__hd {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--n2-space-xs);
  font-size: var(--n2-font-body);
  color: var(--n2-text-primary);
}
.n2-wizard__group__hd .n2-caption { margin: 0; }
.n2-wizard__checks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--n2-space-xs) var(--n2-space-md);
}
.n2-wizard__check {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: var(--n2-font-body);
  color: var(--n2-text-secondary);
  cursor: pointer;
}
.n2-wizard__check input { margin: 0; }
.n2-wizard__summary {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-sm) var(--n2-space-md);
  background: var(--n2-bg-hover);
  border-radius: var(--n2-radius-md);
}
.n2-wizard__summary__row {
  display: flex;
  gap: var(--n2-space-sm);
  font-size: var(--n2-font-body);
}
.n2-wizard__summary__k { color: var(--n2-text-tertiary); flex: none; min-width: 96px; }
.n2-wizard__ai-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--n2-space-md);
  padding: var(--n2-space-lg) 0;
  color: var(--n2-text-secondary);
  font-size: var(--n2-font-body);
}
.n2-wizard__ai-loading__bar {
  width: 180px;
  height: 4px;
  border-radius: 2px;
  background: var(--n2-bg-hover);
  overflow: hidden;
}
.n2-wizard__ai-loading__bar::after {
  content: '';
  display: block;
  width: 40%;
  height: 100%;
  border-radius: 2px;
  background: var(--n2-color-primary);
  animation: n2WizardLoading 1.2s ease-in-out infinite;
}
@keyframes n2WizardLoading {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}
.n2-wizard__demo-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--n2-space-xs);
  margin-top: var(--n2-space-xs);
}
.n2-wizard__demo-tag {
  border: 1px dashed var(--n2-border-split);
  border-radius: 999px;
  padding: 2px var(--n2-space-sm);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-secondary);
  cursor: pointer;
  background: transparent;
}
.n2-wizard__demo-tag:hover {
  border-color: var(--n2-color-primary);
  color: var(--n2-text-link);
}

/* ==========================================================================
   12. 头像
   ========================================================================== */

.n2-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--n2-size-avatar);
  height: var(--n2-size-avatar);
  border-radius: 50%;
  background: var(--n2-color-primary);
  color: var(--n2-text-inverse);
  font-size: var(--n2-font-caption);
  font-weight: var(--n2-font-weight-medium);
  flex: none;
  user-select: none;
}
.n2-avatar--sm {
  width: 24px;
  height: 24px;
  font-size: 11px;
}
.n2-avatar--lg {
  width: var(--n2-size-ai-avatar);
  height: var(--n2-size-ai-avatar);
  font-size: 20px;
}

/* ==========================================================================
   13. AI 员工卡片（AI员工卡片式设计方案 v1.0）
   ========================================================================== */

.n2-ai-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, var(--n2-size-ai-card));
  gap: var(--n2-space-lg);
}

.n2-ai-card {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-sm);
  padding: 20px;
  width: var(--n2-size-ai-card);
  height: var(--n2-size-ai-card-h);
  background: var(--n2-bg-container);
  border: 1px solid var(--n2-border-secondary);
  border-radius: var(--n2-radius-lg);
  box-shadow: var(--n2-shadow-card-hover);
  transition: all var(--n2-duration-base) var(--n2-ease);
  cursor: pointer;
}
.n2-ai-card:hover {
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
  transform: translateY(-2px);
}
.n2-ai-card__head {
  display: flex;
  align-items: center;
  gap: var(--n2-space-sm);
}
.n2-ai-card__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--n2-size-ai-avatar);
  height: var(--n2-size-ai-avatar);
  border-radius: var(--n2-radius-md);
  background: var(--n2-color-primary-bg);
  font-size: 24px;
  flex: none;
}
.n2-ai-card__name {
  font-size: var(--n2-font-h3);
  line-height: var(--n2-line-h3);
  font-weight: var(--n2-font-weight-semibold);
}
.n2-ai-card__role {
  display: inline-flex;
  align-items: center;
  margin-top: var(--n2-space-xxs);
  padding: 0 var(--n2-space-xs);
  min-height: 20px;
  border-radius: var(--n2-radius-sm);
  background: var(--n2-bg-hover);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
}
.n2-ai-card__desc {
  font-size: 13px;
  line-height: 20px;
  color: var(--n2-text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 40px;
}
.n2-ai-card__meta {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-xxs);
  padding-top: var(--n2-space-sm);
  margin-top: auto;
  border-top: 1px dashed var(--n2-border-split);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
}
.n2-ai-card__meta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--n2-space-xs);
}
.n2-ai-card__actions {
  display: flex;
  gap: var(--n2-space-sm);
  padding-top: var(--n2-space-xs);
}
.n2-ai-card__actions .n2-btn {
  flex: 0 1 auto;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--n2-color-primary);
}
.n2-ai-card__actions .n2-btn:hover {
  background: transparent;
  color: var(--n2-color-primary-hover);
  text-decoration: underline;
}
@media (max-width: 640px) {
  .n2-ai-grid { grid-template-columns: minmax(0, 1fr); }
  .n2-ai-card { width: 100%; }
}

/* ==========================================================================
   14. 悬浮球（AI 中枢）
   ========================================================================== */

.n2-float-ball {
  position: fixed;
  right: var(--n2-space-lg);
  bottom: var(--n2-space-lg);
  width: var(--n2-size-float-ball);
  height: var(--n2-size-float-ball);
  border: none;
  border-radius: 50%;
  background: var(--n2-color-primary);
  color: #fff;
  font-size: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  box-shadow: var(--n2-shadow-float);
  z-index: var(--n2-z-float);
  transition: transform var(--n2-duration-base) var(--n2-ease);
}
.n2-float-ball:hover {
  transform: scale(1.06);
}
.n2-float-ball:active {
  cursor: grabbing;
}

.n2-float-panel {
  position: fixed;
  right: var(--n2-space-lg);
  bottom: calc(var(--n2-space-lg) + var(--n2-size-float-ball) + var(--n2-space-xs));
  /* 规范 §5.2 写的是 380px / 「高度不超过视口高度的 70%」，设计稿这一处是 400px + 定高
     560px —— 取规范值（登记 ISSUE-104）。宽度 380 是硬要求，高度仍以 560 为基准，
     只是再被 70vh 压一道；min() 里那一项保留设计稿原本的「不贴满上下边」边距。 */
  width: 380px;
  max-width: calc(100vw - var(--n2-space-lg) * 2);
  height: 560px;
  max-height: min(70vh, calc(100vh - var(--n2-space-xxl) * 2));
  background: var(--n2-bg-elevated);
  border-radius: var(--n2-radius-md);
  box-shadow: var(--n2-shadow-dropdown);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  z-index: var(--n2-z-float);
  animation: n2-pop var(--n2-duration-base) var(--n2-ease);
}
.n2-float-panel__head {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-sm) var(--n2-space-md);
  border-bottom: 1px solid var(--n2-border-split);
  flex: none;
}
.n2-float-panel__agent {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xxs) var(--n2-space-xs);
  border: none;
  background: none;
  border-radius: var(--n2-radius-base);
  font-size: var(--n2-font-body);
  font-weight: var(--n2-font-weight-medium);
  color: var(--n2-text-primary);
  cursor: pointer;
  transition: background var(--n2-duration-fast) var(--n2-ease);
}
.n2-float-panel__agent:hover {
  background: var(--n2-bg-hover);
}
.n2-float-panel__ctx {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xs) var(--n2-space-md);
  background: var(--n2-color-primary-bg);
  color: var(--n2-text-link);
  font-size: var(--n2-font-caption);
  flex: none;
}
.n2-float-panel__tools {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xs) var(--n2-space-md);
  border-bottom: 1px solid var(--n2-border-split);
  flex: none;
}
.n2-float-panel__tools .n2-btn {
  min-width: 0;
  flex: 1;
}
.n2-float-history {
  max-height: 144px;
  overflow: auto;
  border-bottom: 1px solid var(--n2-border-split);
  padding: var(--n2-space-xs) var(--n2-space-md);
  flex: none;
}
.n2-float-history__row {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  min-height: 32px;
}
.n2-float-history__pick {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  border: 0;
  border-radius: var(--n2-radius-base);
  padding: var(--n2-space-xxs) var(--n2-space-xs);
  background: transparent;
  color: var(--n2-text-primary);
  text-align: left;
  cursor: pointer;
}
.n2-float-history__pick:hover,
.n2-float-history__pick.is-active {
  background: var(--n2-bg-hover);
  color: var(--n2-text-link);
}
.n2-float-history__delete {
  border: 0;
  padding: var(--n2-space-xxs);
  background: transparent;
  color: var(--n2-text-link);
  cursor: pointer;
}
.n2-float-history__confirm {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  color: var(--n2-text-secondary);
  font-size: var(--n2-font-caption);
}
.n2-float-panel__body {
  flex: 1;
  overflow: auto;
  padding: var(--n2-space-md);
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-md);
}
.n2-float-panel__foot {
  display: flex;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-sm) var(--n2-space-md);
  border-top: 1px solid var(--n2-border-split);
  flex: none;
}
/* 规范 §5.2：「输入框支持多行，回车发送，Shift+回车换行」—— 要能换行就得是 textarea，
   不是设计稿原型里的 <input>（登记 ISSUE-104）。高度跟着内容长，上限三行左右。 */
.n2-float-panel__foot .n2-input {
  flex: 1;
  min-width: 0;
  resize: none;
  line-height: var(--n2-line-body);
  max-height: calc(var(--n2-size-control) * 3);
}
.n2-float-panel__foot .n2-btn {
  align-self: flex-end;
}

/* 头部那颗「当前 AI + 下拉」按钮的展开态（下拉是运行期挂到 body 的 ul.n2-agent-list） */
.n2-float-panel__agent[aria-expanded="true"] {
  background: var(--n2-bg-hover);
}
.n2-float-panel__agent-name {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 图标按钮：与弹窗右上角的 ✕ 同一形状，但只占一行高 */
.n2-float-panel__head .n2-modal__close {
  line-height: 1;
}

/* AI 员工下拉。挂在 body 上、用 fixed 定位（与设计稿同做法）—— 面板本身 overflow: hidden，
   下拉若留在面板里会被裁掉。层级用全站统一的 --n2-z-dropdown，高于浮层（--n2-z-float）而仍
   低于原生弹窗之上要浮的元素；它比面板晚出现，所以数值高一层就够。 */
.n2-agent-list {
  position: fixed;
  min-width: 200px;
  max-height: calc(var(--n2-size-control) * 8);
  overflow: auto;
  z-index: var(--n2-z-dropdown);
}
.n2-agent-list .n2-dropdown__item {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
}
.n2-agent-list__hint {
  margin-top: var(--n2-space-xs);
  padding: var(--n2-space-xxs) var(--n2-space-sm) 0;
  border-top: 1px solid var(--n2-border-split);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
}
/* 拖动中的球不吃 transition，否则跟手会有一帧延迟 */
.n2-float-ball.is-dragging {
  transition: none;
  cursor: grabbing;
}

/* 对话气泡 */
.n2-bubble {
  max-width: 84%;
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border-radius: var(--n2-radius-md);
  font-size: var(--n2-font-body);
  line-height: var(--n2-line-body);
  white-space: pre-wrap;
  word-break: break-word;
}
.n2-bubble--ai {
  align-self: flex-start;
  background: var(--n2-bg-hover);
  border-top-left-radius: var(--n2-radius-sm);
}
.n2-bubble--me {
  align-self: flex-end;
  background: var(--n2-color-primary);
  color: var(--n2-text-inverse);
  border-top-right-radius: var(--n2-radius-sm);
}
.n2-bubble__actions {
  display: flex;
  gap: var(--n2-space-xs);
  margin-top: var(--n2-space-xs);
}

/* ==========================================================================
   15. 分段控制器
   ========================================================================== */

.n2-segmented {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--n2-bg-disabled);
  border-radius: var(--n2-radius-base);
}
.n2-segmented__item {
  height: 28px;
  padding: 0 var(--n2-space-md);
  font-size: var(--n2-font-body);
  font-family: inherit;
  color: var(--n2-text-secondary);
  background: transparent;
  border: none;
  border-radius: var(--n2-radius-sm);
  cursor: pointer;
  transition: all var(--n2-duration-fast) var(--n2-ease);
}
.n2-segmented__item:hover {
  color: var(--n2-text-primary);
}
.n2-segmented__item.is-active {
  background: var(--n2-bg-container);
  color: var(--n2-text-primary);
  font-weight: var(--n2-font-weight-medium);
  box-shadow: var(--n2-shadow-card);
}
.n2-segmented--block {
  display: flex;
}
.n2-segmented--block .n2-segmented__item {
  flex: 1;
}

/* ==========================================================================
   16. 权限树
   ========================================================================== */

.n2-tree {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.n2-tree__group {
  padding: var(--n2-space-xs) 0 var(--n2-space-xxs);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
}
.n2-tree__node {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  min-height: 28px;
  padding: 0 var(--n2-space-xs);
  border-radius: var(--n2-radius-sm);
  font-size: var(--n2-font-body);
}
.n2-tree__node:hover {
  background: var(--n2-bg-hover);
}
.n2-tree__node--child {
  padding-left: var(--n2-space-xl);
}
.n2-tree__code {
  font-family: var(--n2-font-family-code);
  font-size: var(--n2-font-caption);
  color: var(--n2-text-tertiary);
  margin-left: auto;
}
.n2-tree__scroll {
  max-height: 320px;
  overflow-y: auto;
  padding-right: var(--n2-space-xs);
}

/* ==========================================================================
   17. 描述列表
   ========================================================================== */

.n2-desc-list {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: var(--n2-space-sm) var(--n2-space-md);
  margin: 0;
  font-size: var(--n2-font-body);
}
.n2-desc-list dt {
  color: var(--n2-text-tertiary);
}
.n2-desc-list dd {
  margin: 0;
  color: var(--n2-text-primary);
  overflow-wrap: anywhere;
}

/* ==========================================================================
   18. 布局辅助
   ========================================================================== */

.n2-row {
  display: flex;
  align-items: center;
  gap: var(--n2-space-sm);
}
.n2-row--between {
  justify-content: space-between;
}
.n2-col {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-sm);
}
.n2-stack-lg {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-lg);
}
.n2-stack-md {
  display: flex;
  flex-direction: column;
  gap: var(--n2-space-md);
}
.n2-spacer {
  flex: 1;
}
.n2-divider {
  height: 1px;
  background: var(--n2-border-split);
  border: none;
  margin: var(--n2-space-md) 0;
}

/* ==========================================================================
   19. 行内编辑与内联提示
   ========================================================================== */

/* 行内编辑：在表格行内直接编辑，避免弹窗套弹窗（UI 规范：禁止弹窗再弹窗） */
.n2-table tr.is-editing td {
  background: var(--n2-color-primary-bg);
  padding-top: var(--n2-space-xxs);
  padding-bottom: var(--n2-space-xxs);
}
.n2-table tr.is-editing .n2-input,
.n2-table tr.is-editing .n2-select {
  height: var(--n2-size-control-sm);
  padding-left: var(--n2-space-xs);
  padding-right: var(--n2-space-xs);
  width: 100%;
  min-width: 0;
}

/* 内联提示条：用于弹窗内的引用提示、二次确认与结果反馈 */
.n2-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--n2-space-xs);
  padding: var(--n2-space-xs) var(--n2-space-sm);
  border: 1px solid var(--n2-color-primary-border);
  border-radius: var(--n2-radius-base);
  background: var(--n2-color-info-bg);
  font-size: var(--n2-font-caption);
  line-height: 1.6;
  color: var(--n2-text-primary);
}
.n2-alert--success {
  border-color: var(--n2-color-success-border);
  background: var(--n2-color-success-bg);
}
.n2-alert--warning {
  border-color: var(--n2-color-warning-border);
  background: var(--n2-color-warning-bg);
}
.n2-alert--error {
  border-color: var(--n2-color-error-border);
  background: var(--n2-color-error-bg);
}
.n2-alert__icon {
  flex: none;
  display: flex;
  margin-top: 2px;
}
.n2-alert__body {
  flex: 1;
  min-width: 0;
}
.n2-alert__actions {
  display: flex;
  gap: var(--n2-space-xs);
  margin-top: var(--n2-space-xs);
}
.n2-ref-list {
  margin: var(--n2-space-xxs) 0 0;
  padding-left: var(--n2-space-md);
}
.n2-ref-list li + li {
  margin-top: 2px;
}

/* 编号规则构建器（ISSUE-229 / US-UI-032）：字段配置抽屉里「自动编号」字段的规则表。
   只加这四个小类，其余（表格、输入格、提示语、行内按钮）全部复用现成的
   .n2-table / .n2-input / .n2-field__hint / .n2-link。 */
.n2-numrule__handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--n2-text-tertiary);
  cursor: grab;
}
.n2-numrule__handle:active {
  cursor: grabbing;
}
/* 拖拽中的那一行与当前落点。给行加而不是给手柄加 —— 手柄只有 12px，
   把整行点亮用户才看得出「会落到这里」。 */
.n2-numrule__row.is-dragging {
  opacity: 0.4;
}
.n2-numrule__row.is-drop-target > td {
  box-shadow: inset 0 2px 0 var(--n2-color-primary);
}
/* 表底那条「+ 添加规则」：虚线开在整行的上边缘，与「这条线以下都是新增」同一个意思 */
.n2-numrule__add > td {
  border-top: 1px dashed var(--n2-border);
  border-bottom: none;
}
/* 规则内容这一格的「标签 值」串（读态）与「标签 + 控件」串（行内配置态） */
.n2-numrule__kv + .n2-numrule__dot {
  margin: 0 var(--n2-space-xxs);
  color: var(--n2-text-tertiary);
}
.n2-numrule__cfg {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--n2-space-xs);
}
.n2-numrule__cfg .n2-input,
.n2-numrule__cfg .n2-select {
  width: auto;
  min-width: 68px;
}

/* 验证规则页签（ISSUE-236）：行内那句「标签 值」串与「标签 + 控件」串。形状与上面编号规则
   的两条同形，但**刻意不复用**那几个类 —— 两者的版式只是碰巧一样，共用一个类名会让
   「调编号规则的间距」顺手改掉验证规则那一页。 */
.n2-vrule__kv + .n2-vrule__dot {
  margin: 0 var(--n2-space-xxs);
  color: var(--n2-text-tertiary);
}
.n2-vrule__cfg {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--n2-space-xs);
}
.n2-vrule__cfg .n2-input {
  width: auto;
  min-width: 68px;
}
/* 弹窗内容区工具条 */
.n2-modal-toolbar {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  margin-bottom: var(--n2-space-sm);
}
.n2-modal-toolbar__right {
  display: flex;
  align-items: center;
  gap: var(--n2-space-xs);
  margin-left: auto;
}

/* 弹窗 / 抽屉正文顶部的提示条与下方表格留出间距 */
.n2-modal__body > .n2-alert,
.n2-drawer__body > .n2-alert {
  margin-bottom: var(--n2-space-md);
}

.n2-mt-xs {
  margin-top: var(--n2-space-xs);
}
/* ===== n2-inline:end components ===== */
