/* ==========================================================
   §0. AI Lab 工作卡 (portfolio / #section-portfolio)
   ------------------------------------------------------------

   加载序: CSS 第 33 / 37 (index.html <head> 第 33 个 <link>; 后加载的覆盖先加载的)
   ★ 全站 37 个 CSS 的完整加载序 + 职责表见 index.html 头注释「📂 文件结构速查」
     (单一事实源 —— 旧版 19 个文件各抄一份清单, 且停在 25/26/28/31 四个旧版本;
      加一个 CSS 要改 19 处, 编号还是"分组号"而非真实加载位, 现已收敛)
   [Backlog #192 · 2026-10-06 · 用户纠正] 这张卡原本被我放成 LIFE COLLAGE 的第 5 张,
   用户原话:「这个ai的小说不是lifecollage的第五卡片, 而应该是portfolio部分ai lab底下
   的第二个小卡片」。已移到 .portfolio-grid--ai 之后的 .ai-work-module。

   ⚠ 三处写法都是被运行时行为逼出来的, 不是风格选择:
     ① 容器是 .portfolio-grid--ai 的**兄弟**而不是子节点 —— portfolio-grid.js:244
        的 clusterSingleGrid() 会 grid.innerHTML = '' 清空整个网格再按 watermark
        重建, 放进去就没了 (静态检查完全看不出来);
     ② class 不叫 project-card —— portfolio-grid.js:63 click 路由 / :227 水印聚类 /
        main.js:462 倾斜 / card-draw-animation.js 抽卡, 四个系统会抢走点击;
     ③ 也不靠把 N 加进 CATEGORY_ORDER 来「进网格」—— 那等于把卡交给聚类算法,
        而它并不认识 N, 结果是卡被丢弃。
   视觉值逐条对齐 .project-card (浅莫兰迪卡纸 / 12px 切角 / 硬投影 / 180px 最小高 /
   meta 三行), 与同网格里的 X1 卡读起来是一族; 底色换暖赭金以区分 X1 的浅薰衣草紫
   (book 封面本身是深蓝 + 琥珀字, 赭金与之一致)。

   ⚠ pointer-events: auto 是**必写** —— #content-wrapper 是 none 且可继承,
      详见本文件 #ai-modal 上方那段注释。
   ========================================================== */

/* 托盘: 逐值抄 .portfolio-theme-cluster (portfolio-grid.css:68-93) + ::before 标签
   (:206-219)。#193 用户要求「notes 也要像其它卡片一样有托盘衬底」,
   而 AI 卡原本**完全没托盘** (bg/border/radius 全空), 视觉上像贴错的卡片。
   min-height 420 也是抄 cluster 的 —— 让它与上方 X 簇、Notes 三块同规格。 */
.ai-work-module {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
    justify-items: center;
    align-items: center;
    width: 100%;
    max-width: 320px;          /* 与 .portfolio-grid--ai 同宽 (portfolio-entry.css:93-99) */
    margin: var(--space-9) 0 0 auto;   /* 右对齐: 只有左边 auto → 贴页面右缘 */
    box-sizing: border-box;
    padding: 22px 14px 26px;   /* 同 cluster: 上 22 给标签 / 下 26 呼吸 */
    background: rgba(252, 245, 230, 0.45);
    border: 1px solid rgba(210, 190, 155, 0.4);
    border-radius: 6px;
    min-height: 420px;
}

/* 右下角托盘标签 —— cluster 用 attr(data-cluster-label) 由 JS 填,
   这里是静态块, 直接写字面量 (同 .notes-module 的处理)。 */
.ai-work-module::before {
    content: 'AI-Gen Novels';
    position: absolute;
    bottom: 4px;
    right: 8px;
    font-family: var(--font-serif);
    font-size: var(--type-micro-sm);
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: #333333;
    font-weight: 600;
    pointer-events: none;
    z-index: 0;
}

/* [Backlog #222 · 2026-10-21] **去 ID 化**: 下面这组 `.ai-work-*` 规则原本
   挂在 `#section-portfolio .ai-work-*` 下 —— 抽卡动画的 clone 落在
   **document.body**, 一条都匹配不上, 于是背景透明 / 无内距 / 不是 flex 列 /
   封面宽度塌成 0(实测: background=rgba(0,0,0,0), padding=0px,
   .ai-work-cover display=block, offsetW=0)。
   去掉 ID 后**原卡渲染不变**(同一批声明, 只是特异性 1,1,0 → 1,0,0;
   全项目没有第二条匹配 .ai-work-* 的规则, 不存在被抢的竞争者),
   而 clone 拿到了与原卡**完全一致**的样式。
   ⚠ 只有 `.ai-work-*` 去 ID; `.ai-lab-pair` 那组**保持原样** —— 那是版面
   (托盘/栅格), 留在 portfolio 区里才对, clone 不涉及版面。 */
.ai-work-card {
    display: flex;
    flex-direction: column;
    background: #faf4e6;      /* 暖赭米 */
    --theme-color: #a8763a;
    padding: var(--space-4) var(--space-5);
    position: relative;
    overflow: hidden;
    z-index: 130;
    min-height: 180px;         /* 与 .project-card 同一基准 */
    /* 宽度对齐同子区块的 X1 卡: 实测 .project-card.clustered-card = 150px,
       而本卡原先撑满 320px 整个托盘, 一点呼吸都没有。 */
    width: 150px;
    /* [Backlog #251 · 2026-10-22] **窄屏要跟 X1 卡一起变宽**。
       portfolio-grid-responsive.css 的 @media (max-width:1279px) 把
       .project-card.clustered-card 放宽到 200px (1 列堆叠后留大片空白), 但**不**管本卡
       ⇒ ≤1279 时 X1 是 200px 而本卡还锁在 150px, 同一行里两张卡大小不一样。
       用户原话:「ai-gen novels 的卡片大小和其它卡片也不一样」。
       ⚠ 覆盖规则必须写在**本文件**: life-ai-modal.css 在加载序第 33 位,
         portfolio-grid-responsive.css 在第 9 位 —— 写在后者会被本文件的 width:150px 反覆盖。 */
    /* [Backlog #251 · 2026-10-22] **窄屏要跟 X1 卡一起变宽**。
       portfolio-grid-responsive.css 的 @media (max-width:1279px) 把
       .project-card.clustered-card 放宽到 200px (1 列堆叠后留大片空白), 但**不**管本卡
       ⇒ ≤1279 时 X1 是 200px 而本卡还锁在 150px, 同一行里两张卡大小不一样。
       用户原话:「ai-gen novels 的卡片大小和其它卡片也不一样」。
       ⚠ 覆盖规则必须写在**本文件**: life-ai-modal.css 在加载序第 33 位,
         portfolio-grid-responsive.css 在第 9 位 —— 写在后者会被本文件的 width:150px 反覆盖。 */
    justify-self: center;
    cursor: pointer;
    pointer-events: auto;      /* 父级 #content-wrapper 是 none 且可继承, 必须自己开 */
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    transform: translateZ(0);
    will-change: transform;
    transition: transform 0.3s cubic-bezier(0.25, 1, 0.5, 1), box-shadow 0.3s ease;
    box-shadow:
        3px 4px 0px rgba(0, 3, 10, 0.18),
        0 6px 14px rgba(0, 3, 10, 0.10),
        inset 0 1px 0 rgba(255, 255, 255, 0.6);
    clip-path: polygon(
        12px 0%, calc(100% - 12px) 0%, 100% 12px, 100% calc(100% - 12px),
        calc(100% - 12px) 100%, 12px 100%, 0% calc(100% - 12px), 0% 12px
    );
}

.ai-work-card::before {
    content: attr(data-watermark);
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-family: var(--font-serif);
    /* [Backlog #251] var(--decor-watermark)=12rem(192px) → **clamp(5rem, 8.5vw, 12rem)**,
       与 .project-card::before / .notes-module-card::before **同一个值**。
       实测 (1440 视口): AI-GEN 卡的 N1 水印 = 192px, 而 P1/X1/NT 三张都是 122.4px
       —— 代码缩写的字号在同一屏里差 1.57 倍, 这就是用户说的「N1 和 NT 字号不一样」。
       ⚠ 棘轮: 新增 1 条 clamp ⇒ 禁改值计数 30 → 31 (已同步基线并写明理由)。
         token 化反而**不能**用: --decor-watermark 还有 life-layout / seaweed-frame 两处,
         改 token 会波及别的水印 —— 跟作品卡逐值一致才是本轮要的。 */
    font-size: clamp(5rem, 8.5vw, 12rem);
    font-weight: 900;
    line-height: 0.85;
    color: var(--theme-color);
    opacity: 0.10;
    pointer-events: none;
    user-select: none;
    z-index: 1;
    letter-spacing: -0.06em;
    text-transform: uppercase;
}

.ai-work-card::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border: 1px solid rgba(0, 3, 10, 0.12);
    clip-path: inherit;
    z-index: 2;
}

.ai-work-card:hover,
.ai-work-card:focus-visible {
    transform: translateY(-5px);
    box-shadow:
        5px 7px 0px rgba(0, 3, 10, 0.22),
        0 12px 24px rgba(0, 3, 10, 0.15),
        inset 0 1px 0 rgba(255, 255, 255, 0.8);
}

.ai-work-card:hover::after {
    border-color: rgba(0, 3, 10, 0.22);
}

.ai-work-cover {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1 1 0;
    min-height: 0;
    max-height: 150px;
    margin-bottom: var(--space-2);
    overflow: hidden;
    position: relative;
    z-index: 2;
}

/* [Backlog #195 · 用户要求] logo 换成用户给的**书本**图标 (iconfont viewBox 1024²)。
   ⚠ 与 #193 那个手机线稿的根本差别: 书本是**单个闭合 path**, 必须用 fill,
     留着 `fill: none; stroke: currentColor` 会渲染成**什么都没有**(空图标)。
   高度仍受同一条约束: 卡 180 − padding 16 − meta 68 − margin 5 = 封面区 92px。 */

/* [Backlog #251] ≤1279px 时与簇内作品卡同宽 (200px) —— 理由见上面 .ai-work-card 的注释。 */
@media (max-width: 1279px) {
    .ai-work-card {
        width: 200px;
    }
}

.ai-work-cover {
    color: var(--theme-color);
}

.ai-work-cover-svg {
    width: auto;
    height: 84px;
    max-width: 100%;
    max-height: 100%;
    display: block;
    /* [Backlog #295] 图标换成**线稿 + 填色**自绘 SVG ⇒ 兜底改成 fill: none。
       ⚠ 原来这里是 `fill: currentColor` —— 那是给 #195 那个**单闭合 path** 用的(必须 fill)。
         现在 SVG 里有「只描边不填」的 path(书脊/连线), 根 svg 上的 fill 会**继承**到它们身上,
         把一条线填成一块色 ⇒ 兜底必须是 none, 而每个元素自己声明 fill。 */
    fill: none;
}

.ai-work-meta {
    position: relative;
    z-index: 5;                 /* 高于水印 z=1 */
    flex: 0 0 auto;             /* 高度不够时 meta 三行永不收缩 */
    min-height: 60px;
    text-align: left;
    overflow: visible;
}

.ai-work-tag {
    display: block;
    font-size: var(--type-micro-sm);
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: rgba(90, 70, 45, 0.85);
    position: relative;
    z-index: 3;
}

.ai-work-title {
    font-family: var(--font-display);
    font-size: var(--type-body-sm);
    font-weight: 600;
    letter-spacing: 0.03em;
    color: #333333;
    margin: 3px 0 0;
    line-height: 1.45;
    position: relative;
    z-index: 3;
}

.ai-work-date {
    display: block;
    font-size: var(--type-micro-sm);
    color: rgba(10, 31, 36, 0.5);
    margin-top: 3px;
    white-space: nowrap;
    position: relative;
    z-index: 3;
}

@media (max-width: 1024px) {
    .ai-work-module {
        max-width: 100%;
        margin-left: 0;
    }
}

/* ==========================================================
   life-ai-modal.css — 「AI 尝试」模态框
   ------------------------------------------------------------
   [Backlog #191 · 2026-10-06] 新建。视觉语言**照搬 notes.css §1**
   (米白卡纸 #f7f1e1 + 八角切角 16px + 双层硬投影), 因为它是同族里
   最新的一个「内容型」模态; 不新造配色, 省一轮视觉决策。

   铁律沿用:
     · 字号一律走 --type-* token (无裸 px, 也就不会踩 ≤8px 锁)
     · 间距一律走 --space-* token
     · .ai-close 必须 z-index:10 —— 见 notes.css:140 的事故记录:
       .ai-header 同样是 position:relative 且排在按钮**之后**, 同为定位
       元素时按 DOM 顺序绘制, 头部整个盖住按钮 → 看得见点不动。

   布局分工 (为什么滚动容器在 .ai-body 而不是 .ai-inner):
     模态入场动画由 .collage-modal-container 的 transform 负责; 若滚动
     发生在 transform 元素内部, 部分浏览器会把 fixed 子元素的定位基准
     带偏。notes 用的就是「外层不滚 + 内层 .notes-body 滚」, 这里照抄。
   ========================================================== */

/* ⚠⚠ pointer-events: auto !important —— **必写, 不是可选优化**。
   根因: #content-wrapper { pointer-events: none } (portfolio-layout.css:60) 是
   **可继承**属性, 而 #ai-modal 写在 #content-wrapper 内部 (index.html L2212),
   none 沿父链一路继承到整个子树。

   症状极具迷惑性: 模态"完全能看见" —— 截图正常、排版正常、rect 也有尺寸 ——
   但 elementFromPoint 一律落到 <body>, 关闭键 / 卡片 / tab / 翻章**全都点不动**。
   静态断言(".ai-close 存在"、"CSS 规则在") 全绿, 只有真正打点才暴露。

   同款铁律已在: notes.css:70 (#notes-modal) / life-reading.css:205 (#reading-modal)。
   pointer-events 可继承 → 在遮罩根打开一次, 后代自动恢复, 不必逐个元素补。 */
#ai-modal {
    pointer-events: auto !important;
}

#ai-modal .ai-inner {
    background: #f7f1e1;
    border: 1px solid rgba(0, 80, 100, 0.4);
    box-shadow:
        2px 3px 0px rgba(10, 31, 36, 0.55),
        0 4px 12px rgba(10, 31, 36, 0.4),
        inset 0 1px 0 rgba(255, 255, 255, 0.5);
    width: min(95vw, 1240px);
    height: min(90vh, 920px);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    position: relative;
    clip-path: polygon(
        16px 0%, calc(100% - 16px) 0%,
        100% 16px, 100% calc(100% - 16px),
        calc(100% - 16px) 100%, 16px 100%,
        0% calc(100% - 16px), 0% 16px
    );
}

#ai-modal .ai-close {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 32px;
    height: 32px;
    border: 1px solid rgba(0, 80, 100, 0.35);
    background: rgba(255, 252, 240, 0.92);
    color: rgba(10, 31, 36, 0.7);
    cursor: pointer;
    font-size: 20px;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.2s ease, color 0.2s ease;
    font-family: inherit;
    z-index: 10;
}

#ai-modal .ai-close:hover {
    background: rgba(0, 80, 100, 0.85);
    color: #fff;
}

/* ---------- 头部 ---------- */
#ai-modal .ai-header {
    padding: var(--space-9) var(--space-13) var(--space-8);
    border-bottom: 1px solid rgba(0, 80, 100, 0.2);
    box-shadow: inset 0 1px 0 0 rgba(212, 165, 90, 0.4);
    position: relative;
}

#ai-modal .ai-title-en {
    display: block;
    font-family: var(--font-serif);
    font-size: var(--type-micro);
    letter-spacing: 0.28em;
    text-transform: uppercase;
    color: rgba(0, 80, 100, 0.62);
    margin-bottom: var(--space-2);
}

#ai-modal .ai-title-cn {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-section);
    color: rgba(10, 31, 36, 0.92);
    font-weight: 500;
    letter-spacing: 0.08em;
    margin: 0;
}

#ai-modal .ai-count {
    display: block;
    margin-top: var(--space-3);
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    color: rgba(10, 31, 36, 0.6);
    letter-spacing: 0.04em;
}

#ai-modal .ai-body {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-9) var(--space-13) var(--space-14);
    position: relative;
}

/* ---------- 列表视图 ---------- */

/* ---------- 筛选栏 (Backlog #269 · 2026-10-24) ----------
   范式抄 life-photography-filter.js 那套 (Backlog #85/#90/#97):
     行 = 左标签(emoji + EN) + 右 chip 列表; chip 选中态用**本模块主题色**。

   ⚠ **为什么自己写一套 .ai-chip, 不直接用 .photo-chip**:
     摄影的 chip 选中态写死 `var(--theme-color, #4a5d8f)`(靛蓝 = 摄影主题色),
     AI 模块的主题色是**赭 #a8763a**(见 §0 .ai-work-card)。
     直接套 .photo-chip 会让 AI 模态里的 chip 变成靛蓝 —— 与 AI 卡自身的赭金配色不是一族。
     ⇒ 沿用站内既有规矩「每个模块一套 chip 家族」(photo / marathon / notes 各一套),
       本模块第四套, 声明抄 photography 的, 只换主题色。

   ⚠ 主题色走 var(--theme-color) 而不是写死 —— .ai-work-card 已经定义过这个变量,
     本栏自己再定义一次是为了**独立可用**(筛选栏在模态里, 不在 portfolio 卡片里)。 */
#ai-modal .ai-filter-bar {
    --theme-color: #a8763a;          /* AI 模块主题色(赭) —— 与 .ai-work-card 同值 */
    display: flex;
    /* [Backlog #287 · 2026-10-25] 单栏 → **左右两栏**(参考 photography #90 的两栏筛选栏)。
       flex-wrap: wrap 是给「已选条件」那行用的: 它 flex-basis:100% ⇒ 换行独占第二行。 */
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--space-9);
    padding: var(--space-7) var(--space-9) var(--space-6);
    background: rgba(252, 245, 230, 0.55);
    border: 1px solid rgba(210, 190, 155, 0.4);
    border-radius: 6px;
    margin-bottom: var(--space-8);
    /* 八角切角 —— 跟 module card 同款(抄 .photo-filter-bar) */
    clip-path: polygon(
        8px 0,
        calc(100% - 8px) 0,
        100% 8px,
        100% calc(100% - 8px),
        calc(100% - 8px) 100%,
        8px 100%,
        0 calc(100% - 8px),
        0 8px
    );
}

/* [Backlog #287 · 2026-10-25] 筛选栏的两栏子容器 —— 声明逐条抄 .photo-filter-col
   (photography #90/#91 的两栏筛选栏), 只把比例按本模块的行数调:
   左栏内容属性(FORM + TOPIC, 2 行, TOPIC 有多个题材 chip 要换行) ⇒ 宽;
   右栏时间(YEAR, 1 行) ⇒ 窄。 */
#ai-modal .ai-filter-col {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    flex: 1 1 0;       /* 默认等宽, 子规则覆盖 */
    min-width: 0;      /* 允许 chip flex-wrap 时收缩 */
}
#ai-modal .ai-filter-col--content {
    flex: 1.6 1 0;     /* 内容属性栏 ~62%(与 .photo-filter-col--location 同思路) */
}
#ai-modal .ai-filter-col--time {
    flex: 1 1 0;       /* 时间栏 ~38% */
}

#ai-modal .ai-filter-row {
    display: flex;
    align-items: center;
    gap: var(--space-6);
    min-height: 32px;
    flex-wrap: wrap;
}

#ai-modal .ai-filter-label {
    font-family: var(--font-serif-tnr);
    font-size: var(--type-micro);
    font-weight: 600;
    letter-spacing: 0.18em;
    color: rgba(10, 31, 36, 0.55);
    min-width: 100px;
    text-transform: uppercase;
}

#ai-modal .ai-filter-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    flex: 1;
}

#ai-modal .ai-filter-state {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-micro-sm);
    color: rgba(10, 31, 36, 0.55);
    letter-spacing: 0.04em;
    /* [Backlog #287] 两栏布局下, 「已选条件」这行换到**第二行独占整行**(不放进某一栏) */
    flex-basis: 100%;
}

/* 无筛选时状态行为空 ⇒ 不占位(不用 inline style, 免得全站多一处 style="") */
#ai-modal .ai-filter-state:empty {
    display: none;
}

/* chip 本体 —— **声明逐条抄 .photo-chip**, 只把主题色换成 AI 的赭 #a8763a */
#ai-modal .ai-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 5px var(--space-6);
    background: var(--role-paper);
    border: 1px solid rgba(10, 31, 36, 0.18);
    border-radius: 3px;
    font-family: var(--font-serif-tnr);
    font-size: var(--type-micro);
    font-weight: 600;
    letter-spacing: 0.08em;
    color: rgba(10, 31, 36, 0.7);
    cursor: pointer;
    transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
}

#ai-modal .ai-chip:hover {
    background: rgba(168, 118, 58, 0.08);
    border-color: rgba(168, 118, 58, 0.35);
    color: rgba(10, 31, 36, 0.95);
    transform: translateY(-1px);
}

#ai-modal .ai-chip.is-active {
    background: var(--theme-color);
    color: var(--role-paper);
    border-color: var(--theme-color);
}

#ai-modal .ai-chip__label-cn {
    margin-right: var(--space-2);
    font-family: var(--font-serif-cjk);
    font-weight: 700;
    letter-spacing: 0.04em;
}

#ai-modal .ai-chip__label-en {
    opacity: 0.75;
    font-size: var(--type-micro-sm);
    letter-spacing: 0.1em;
}

#ai-modal .ai-chip__count {
    font-size: var(--type-micro-sm);
    font-weight: 500;
    opacity: 0.7;
    letter-spacing: 0.04em;
}

#ai-modal .ai-chip.is-active .ai-chip__count {
    opacity: 0.9;
}

#ai-modal .ai-intro {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body-sm);
    line-height: 1.9;
    color: rgba(10, 31, 36, 0.72);
    margin: 0 0 var(--space-9);
    padding-left: var(--space-7);
    border-left: 2px solid rgba(212, 165, 90, 0.55);
}

#ai-modal .ai-grid {
    display: grid;
    /* 目前只有 1 条, 但按「未来会变多」写: 宽屏 2 列起, 窄屏自动落 1 列。
       不用固定列数, 免得每加一条就要改断点。 */
    grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
    gap: var(--space-8);
}

/* [Backlog #202] 模态内 .ai-card 改成与站内其它作品卡同形的**八角切角**卡。

   逐条抄 `.ai-work-card`(life-ai-modal.css:63-122) 的**声明**,
   不抄任何计算值 —— 本项目已两次栽在「抄计算值」上(§223.4 的 gap / §223.10 的内距):
   渲染等价、静态检查抓不到、只有跨档或调 token 时才暴露。
   本轮拄的出处:
     box-shadow(三条)  ← 同文件 :85-88   硬投影 + 柔和投影 + 内高光
     clip-path(八边形) ← 同文件 :89-92   12px 切角
     ::after 描边      ← 同文件 :114-122 inset:0 + clip-path:inherit

   ⚠ **::after 不是装饰, 是必需的**: 元素自身的 border 会被 clip-path 切掉四个角,
     斜边上就留下一条断线; 必须再用一个 inset:0 的伪元素把八角完整描一遍。
     代价是元素本身不能再有 border, 于是 :hover 失去了 border-color 可改 ——
     故引入 `--ai-card-outline` 变量, 由 hover 改变量而不是改 border-color。 */
#ai-modal .ai-card {
    display: flex;
    gap: var(--space-8);
    align-items: flex-start;
    text-align: left;
    padding: var(--space-8);
    background: rgba(255, 252, 240, 0.75);
    /* 描边交给 ::after; 这里必须 0 —— 留 1px 会被 clip-path 切出断线 */
    border: 0;
    --ai-card-outline: rgba(0, 80, 100, 0.28);
    /* 圆角与切角同时开会互相打架(切角处会露出圆弧), 八角交给 clip-path */
    border-radius: 0;
    cursor: pointer;
    font-family: inherit;
    position: relative;
    overflow: hidden;
    box-shadow:
        3px 4px 0px rgba(0, 3, 10, 0.18),
        0 6px 14px rgba(0, 3, 10, 0.10),
        inset 0 1px 0 rgba(255, 255, 255, 0.6);
    clip-path: polygon(
        12px 0%, calc(100% - 12px) 0%, 100% 12px, 100% calc(100% - 12px),
        calc(100% - 12px) 100%, 12px 100%, 0% calc(100% - 12px), 0% 12px
    );
    transition: background 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}

/* 八角描边 —— 逐条拄 .ai-work-card::after(同文件 :114-122) */
#ai-modal .ai-card::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border: 1px solid var(--ai-card-outline);
    clip-path: inherit;
    z-index: 2;
}

#ai-modal .ai-card:hover {
    background: rgba(255, 252, 240, 1);
    --ai-card-outline: rgba(0, 80, 100, 0.6);
    transform: translateY(-2px);
}

#ai-modal .ai-card-thumb {
    width: 92px;
    height: 128px;
    object-fit: cover;
    flex: none;
    border: 1px solid rgba(10, 31, 36, 0.25);
    background: rgba(10, 31, 36, 0.06);
}

#ai-modal .ai-card-main {
    min-width: 0;
    flex: 1;
}

#ai-modal .ai-card-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-6);
    margin-bottom: var(--space-3);
}

#ai-modal .ai-card-form {
    font-family: var(--font-serif);
    font-size: var(--type-micro-sm);
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: rgba(0, 80, 100, 0.75);
}

#ai-modal .ai-card-date {
    font-family: var(--font-mono);
    font-size: var(--type-micro-sm);
    color: rgba(10, 31, 36, 0.45);
    letter-spacing: 0.04em;
}

#ai-modal .ai-card-title {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body);
    font-weight: 600;
    color: rgba(10, 31, 36, 0.95);
    margin: 0 0 var(--space-2);
    line-height: 1.5;
}

#ai-modal .ai-card-sub {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    color: rgba(0, 80, 100, 0.8);
    margin: 0 0 var(--space-4);
}

#ai-modal .ai-card-sum {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    line-height: 1.85;
    color: rgba(10, 31, 36, 0.68);
    margin: 0 0 var(--space-5);
}

#ai-modal .ai-card-tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

#ai-modal .ai-card-tag {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-micro-sm);
    letter-spacing: 0.08em;
    padding: 2px var(--space-4);
    border: 1px solid rgba(0, 80, 100, 0.25);
    color: rgba(0, 80, 100, 0.8);
    border-radius: 2px;
}

#ai-modal .ai-empty {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body-sm);
    color: rgba(10, 31, 36, 0.6);
    padding: var(--space-11) 0;
    text-align: center;
}

/* ---------- 详情视图: 顶部条 ---------- */
#ai-modal .ai-detail-bar {
    position: sticky;
    top: calc(var(--space-9) * -1);
    margin: calc(var(--space-9) * -1) calc(var(--space-13) * -1) var(--space-9);
    padding: var(--space-6) var(--space-13) var(--space-6);
    background: rgba(247, 241, 225, 0.96);
    border-bottom: 1px solid rgba(0, 80, 100, 0.2);
    display: flex;
    align-items: center;
    gap: var(--space-8);
    flex-wrap: wrap;
    z-index: 5;
}

#ai-modal .ai-back {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    color: rgba(0, 80, 100, 0.85);
    background: none;
    border: 1px solid rgba(0, 80, 100, 0.3);
    border-radius: 2px;
    padding: var(--space-2) var(--space-6);
    cursor: pointer;
    flex: none;
    transition: background 0.2s ease, color 0.2s ease;
}

#ai-modal .ai-back:hover {
    background: rgba(0, 80, 100, 0.85);
    color: #fff;
}

#ai-modal .ai-detail-title {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body);
    font-weight: 600;
    color: rgba(10, 31, 36, 0.92);
    margin: 0;
    flex: 1;
    min-width: 160px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* [Backlog #195] 四个栏目改成**常驻段落**并直接下拉, 这四个按钮不再是 tab,
   只是滚动到对应段落 —— 所以没有 .is-active 状态可言(去掉)。 */
#ai-modal .ai-jumps {
    display: flex;
    gap: var(--space-3);
    flex: none;
}

#ai-modal .ai-jump {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    letter-spacing: 0.06em;
    padding: var(--space-2) var(--space-6);
    background: rgba(255, 252, 240, 0.85);
    border: 1px solid rgba(0, 80, 100, 0.3);
    color: rgba(10, 31, 36, 0.8);
    border-radius: 2px;
    cursor: pointer;
    transition: background 0.18s ease, color 0.18s ease;
}

#ai-modal .ai-jump:hover {
    background: rgba(0, 80, 100, 0.85);
    color: #fff;
}

/* 四段之间一条虚线, 便于长页里辨认「到下一段了」 */
#ai-modal .ai-sec {
    padding-top: var(--space-9);
    margin-top: var(--space-9);
    border-top: 1px dashed rgba(0, 80, 100, 0.22);
    border-radius: 2px;
    scroll-margin-top: 8px;
}

#ai-modal .ai-sec:first-child {
    padding-top: 0;
    margin-top: 0;
    border-top: none;
}

/* 跳转落点高亮: 长页里「跳过去了」和「没反应」肉眼分不出来 */
#ai-modal .ai-sec.is-flash,
#ai-modal .ai-reader-ch.is-flash {
    background: rgba(212, 165, 90, 0.14);
    transition: background 0.25s ease;
}
/* ---------- 详情: 概览 ---------- */
#ai-modal .ai-hero {
    display: flex;
    gap: var(--space-10);
    align-items: flex-start;
    margin-bottom: var(--space-9);
}

#ai-modal .ai-hero-cover {
    width: 132px;
    height: auto;
    flex: none;
    border: 1px solid rgba(10, 31, 36, 0.28);
    box-shadow: 2px 3px 0 rgba(10, 31, 36, 0.18);
    background: rgba(10, 31, 36, 0.05);
}

#ai-modal .ai-hero-info {
    min-width: 0;
}

#ai-modal .ai-hero-title {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-title-sm);
    color: rgba(10, 31, 36, 0.95);
    margin: 0 0 var(--space-2);
    line-height: 1.4;
}

#ai-modal .ai-hero-en {
    font-family: var(--font-serif);
    font-size: var(--type-micro);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: rgba(0, 80, 100, 0.55);
    margin: 0 0 var(--space-5);
}

#ai-modal .ai-hero-sub {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body-sm);
    color: rgba(0, 80, 100, 0.85);
    margin: 0 0 var(--space-4);
}

#ai-modal .ai-hero-log {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body-sm);
    line-height: 1.9;
    color: rgba(10, 31, 36, 0.82);
    margin: 0 0 var(--space-5);
    padding-left: var(--space-6);
    border-left: 2px solid rgba(212, 165, 90, 0.5);
}

#ai-modal .ai-hero-bits {
    font-family: var(--font-mono);
    font-size: var(--type-micro-sm);
    color: rgba(10, 31, 36, 0.5);
    margin: 0;
    letter-spacing: 0.04em;
}

#ai-modal .ai-stats {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin-bottom: var(--space-10);
    padding-bottom: var(--space-9);
    border-bottom: 1px dashed rgba(0, 80, 100, 0.25);
}

#ai-modal .ai-stat {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    color: rgba(10, 31, 36, 0.62);
    padding: var(--space-2) var(--space-6);
    border: 1px solid rgba(0, 80, 100, 0.22);
    border-radius: 2px;
    background: rgba(255, 252, 240, 0.6);
    white-space: nowrap;
}

#ai-modal .ai-stat b {
    font-family: var(--font-mono);
    font-weight: 500;
    color: rgba(0, 80, 100, 0.85);
    margin-right: var(--space-2);
}

#ai-modal .ai-block {
    margin-bottom: var(--space-9);
}

#ai-modal .ai-block-h {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body-sm);
    font-weight: 600;
    color: rgba(10, 31, 36, 0.88);
    margin: 0 0 var(--space-4);
    letter-spacing: 0.06em;
}

#ai-modal .ai-block-p {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body-sm);
    line-height: 2;
    color: rgba(10, 31, 36, 0.78);
    margin: 0;
    white-space: pre-line;
}

#ai-modal .ai-rules {
    margin: 0;
    padding-left: var(--space-9);
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    line-height: 2.1;
    color: rgba(10, 31, 36, 0.78);
}

#ai-modal .ai-block--quiet {
    border-top: 1px dashed rgba(0, 80, 100, 0.22);
    padding-top: var(--space-7);
}

#ai-modal .ai-disclaimer {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    line-height: 1.9;
    color: rgba(10, 31, 36, 0.52);
    margin: 0;
}

/* ---------- 详情: 提示词 ---------- */
#ai-modal .ai-prompt-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-6);
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
}

#ai-modal .ai-prompt-tag {
    font-family: var(--font-serif);
    font-size: var(--type-micro);
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: rgba(0, 80, 100, 0.7);
}

#ai-modal .ai-prompt-meta {
    font-family: var(--font-mono);
    font-size: var(--type-micro-sm);
    color: rgba(10, 31, 36, 0.45);
}

#ai-modal .ai-prompt-text {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body-sm);
    line-height: 2.15;
    color: rgba(10, 31, 36, 0.86);
    background: rgba(255, 252, 240, 0.8);
    border: 1px solid rgba(0, 80, 100, 0.25);
    border-left: 3px solid rgba(212, 165, 90, 0.65);
    padding: var(--space-9);
    margin: 0;
    white-space: pre-wrap;
    word-break: break-word;
    overflow-wrap: anywhere;
}

#ai-modal .ai-prompt-hint {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    color: rgba(10, 31, 36, 0.6);
    margin: var(--space-7) 0 0;
    line-height: 1.9;
}

#ai-modal .ai-prompt-hint code {
    font-family: var(--font-mono);
    background: rgba(0, 80, 100, 0.1);
    padding: 1px var(--space-3);
    border-radius: 2px;
    color: rgba(0, 80, 100, 0.9);
}

/* ---------- 详情: 目录 ---------- */
#ai-modal .ai-toc {
    column-width: 320px;
    column-gap: var(--space-10);
}

#ai-modal .ai-toc-vol {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    font-weight: 600;
    letter-spacing: 0.14em;
    color: rgba(0, 80, 100, 0.85);
    padding: var(--space-6) 0 var(--space-3);
    border-bottom: 1px solid rgba(0, 80, 100, 0.25);
    break-after: avoid;
    margin-bottom: var(--space-3);
}

#ai-modal .ai-toc-row {
    display: flex;
    gap: var(--space-5);
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    border-bottom: 1px solid rgba(0, 80, 100, 0.1);
    padding: var(--space-3) 0;
    cursor: pointer;
    font-family: inherit;
    break-inside: avoid;
    transition: background 0.15s ease;
}

#ai-modal .ai-toc-row:hover {
    background: rgba(0, 80, 100, 0.1);
}

#ai-modal .ai-toc-n {
    font-family: var(--font-mono);
    font-size: var(--type-micro-sm);
    color: rgba(10, 31, 36, 0.4);
    flex: none;
    width: 24px;
    text-align: right;
}

#ai-modal .ai-toc-t {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    color: rgba(10, 31, 36, 0.82);
    line-height: 1.7;
}

/* ---------- 详情: 阅读器 (全书连续, 无分页) ---------- */
/* [Backlog #195 · 用户原话]「不要分页」—— 工具条 / 上一章 / 下一章 / 章下拉
   全部删除, 24 章一次性排下去; 目录行改成滚动到对应 <article id="aiChN">。 */
#ai-modal .ai-reader-all {
    max-width: 720px;
    margin: 0 auto;
}

#ai-modal .ai-reader-ch {
    scroll-margin-top: 8px;
    padding: var(--space-6) var(--space-4);
    margin-bottom: var(--space-6);
    border-radius: 2px;
}
#ai-modal .ai-reader-vol {
    font-family: var(--font-serif);
    font-size: var(--type-micro);
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: rgba(0, 80, 100, 0.6);
    text-align: center;
    margin-bottom: var(--space-5);
}

#ai-modal .ai-reader-title {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body);
    font-weight: 600;
    color: rgba(10, 31, 36, 0.92);
    text-align: center;
    line-height: 1.7;
    margin: 0 0 var(--space-10);
    padding-bottom: var(--space-7);
    border-bottom: 1px solid rgba(0, 80, 100, 0.2);
}

#ai-modal .ai-reader-p {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body-sm);
    line-height: 2.15;
    color: rgba(10, 31, 36, 0.86);
    margin: 0 0 var(--space-6);
    text-indent: 2em;
    text-align: justify;
}

#ai-modal .ai-reader-end {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption);
    color: rgba(0, 80, 100, 0.6);
    text-align: center;
    padding: var(--space-10) 0 var(--space-8);
    border-top: 1px dashed rgba(0, 80, 100, 0.25);
    margin-top: var(--space-8);
}

#ai-modal .ai-reader-count {
    font-family: var(--font-mono);
    font-size: var(--type-micro-sm);
    color: rgba(10, 31, 36, 0.42);
    text-align: center;
    margin: var(--space-7) 0 0;
    letter-spacing: 0.04em;
}

/* ---------- 响应式 ---------- */
@media (max-width: 900px) {
    #ai-modal .ai-grid {
        grid-template-columns: 1fr;
    }
    /* [Backlog #269] 筛选栏窄屏: 标签列收窄, 栏内边距随 .ai-body 一起缩 */
    #ai-modal .ai-filter-bar {
        padding-left: var(--space-7);
        padding-right: var(--space-7);
        /* [Backlog #287] 两栏 → 单栏(与 photography #90 的 ≤1024 同思路, 断点取本模块自己的 900) */
        flex-direction: column;
        flex-wrap: nowrap;
        gap: var(--space-6);
    }
    #ai-modal .ai-filter-col {
        flex: 1 1 auto;   /* 单栏时不再按比例分宽 */
    }
    #ai-modal .ai-filter-state {
        flex-basis: auto;  /* 单栏时「已选条件」不再强制换行 */
    }
    #ai-modal .ai-filter-label {
        min-width: 84px;
        letter-spacing: 0.12em;
    }
    #ai-modal .ai-hero {
        flex-direction: column;
        gap: var(--space-8);
    }
    #ai-modal .ai-hero-cover {
        width: 108px;
    }
    #ai-modal .ai-card {
        flex-direction: column;
    }
    #ai-modal .ai-card-thumb {
        width: 100%;
        height: 180px;
    }
    #ai-modal .ai-body {
        padding-left: var(--space-8);
        padding-right: var(--space-8);
    }
    #ai-modal .ai-detail-bar {
        margin-left: calc(var(--space-8) * -1);
        margin-right: calc(var(--space-8) * -1);
        padding-left: var(--space-8);
        padding-right: var(--space-8);
        top: calc(var(--space-8) * -1);
    }
}
/* ==========================================================
   AI Lab 两卡并排 [Backlog #198 · 2026-10-11]
   ------------------------------------------------------------
   用户原话:「AI lab 这两个卡片 X1 N1, 可以参考上面建筑作品集一样,
   左右排版, 注意 margin 要对齐上面, 注意不同屏幕的适配。」

   对齐基准 = .portfolio-grid--architecture 的盒子, 实测三档视口逐值一致:
     display:grid / max-width:1080px / 居中 (1920 下 x=820, 1440 下 x=340) / gap:0
   本容器就抄这四条 —— 即 AI Lab 两列盒子与建筑子区块的内容盒子同宽同左缘。

   为什么只加一层父容器而不合并两个节点:
     portfolio-grid.js:244 的 clusterSingleGrid() 会 grid.innerHTML = 清空
     重建整个网格, .ai-work-module 塞进去会在运行时被抹掉 (#192 铁律)。
     所以两块仍平级, 只在外面套一层做两列。

   两列宽度: [Backlog #200 · 2026-10-11] 由「各 320 + gap 20 = 660 缩在右边」改为
   「两列等分铺满整条带 (540 + 540, gap 0)」。
   用户原话:「现在这 AI LAB 区域的卡片不对齐, 而且这两个卡片的浅色托盘之间
   不应该有空隙。我希望他们这两个卡片的 margin 和建筑作品集的卡片和托盘对齐
   (参考我画的红色参考线和箭头)」。

   改前三处实测 (1920 视口, page 坐标):
     建筑簇带      826..1894  (5 簇各 356, gap 0 —— portfolio-entry.css:76 原注释
                               「边框相接, 不留空隙」)
     .ai-lab-pair  820..1900  ← **盒子**左右缘其实已经对上
     两块卡本体   1234..1894  ← **左边空出 408px**, 两块中间隔 20px
   → 「盒子对齐」不等于「卡对齐」: #198 只做到了前者。

   铺满的依据是建筑网格自己的语汇 —— portfolio-entry.css:73 的
   `repeat(6, minmax(0,1fr))` 把 1080 铺满; 这里 2 列就把 1080 铺满。

   ⚠ gap 在**所有断点都保持 0**, 没有跟着 ≤1279 档建筑的 14px 走:
     建筑要那个 14px 是因为有 6 个密集小簇 (注释:「窄屏留呼吸, 防边框糊成一片」);
     AI Lab 只有 2 块大托盘, 贴边不会糊, 而用户「不留空隙」的要求是无条件的。 */
#section-portfolio .ai-lab-pair {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0;                            /* 抄建筑「边框相接, 不留空隙」 */
    align-items: stretch;              /* 两块等高, 同 cluster 的 align-self: stretch */
    width: 100%;
    max-width: 1080px;                 /* 抄 .portfolio-grid--architecture */
    /* ⚠ [Backlog #198] margin 必须抄 **`30px 0 0 auto` 右对齐**, 不是「居中」。
       两次走错路的记录:
         ① 第一版写 `margin: 0 auto` (居中) → 1920 右缘比建筑内容差 100px;
         ② 第二版照着实测看到的 `marginLeft:200px` 硬写 200px → 1920 对上了,
            但 1440/1280/768 全错 —— 因为那 200px 是 **`auto` 在 1300px 父容器里
            算出来的结果**, 不是常量 (实测 1440 下建筑自己就是 138px、1280 下 0.8px)。
       → 抄**声明**不抄**计算值**。这与「照抄视觉块要抄声明值」同源:
          实测值是某档视口下的结果, 换档就失效。
       前缀 `#section-portfolio` 抄 portfolio-entry.css 的做法: 该文件下方大量
       旧媒体查询对 .portfolio-grid 设了 !important, 无前缀会被「后写者胜」覆盖。 */
    margin: 30px 0 0 auto;
    padding: 14px var(--space-3);  /* 同 .portfolio-grid 基础规则 (portfolio-grid.css) */
}

/* 进两列后清掉各自独立的右对齐 / 宽度上限, 交给列宽决定。
   margin-top 归零 —— 竖向间距改由 .ai-lab-pair 的 row gap 统一给,
   否则两块各算各的 margin-top, 会出现 472px 的诡异 Δy。

   ⚠ [Backlog #200] **必须带 !important**: portfolio-entry.css:93-98 给
     `.portfolio-grid--ai` 写的是 `max-width:320px !important` +
     `margin:30px 0 0 auto !important`; portfolio-grid-responsive.css:121-122 的
     ≤1279 档同样是 important。**非 important 的覆盖打不过 important** ——
     这与「同特异性后写者胜」是两套规则。上一轮没意识到, 所以 320px 一直没被压掉,
     实测 X1 始终卡在 320 宽。
     特异性: 本节 (1,2,0) > portfolio-entry.css 的 (1,1,0), important 相打我方胜。 */
/* 进两列后清掉各自独立的右对齐 / 宽度上限, 交给列宽决定。
   margin-top 归零 —— 竖向间距改由 .ai-lab-pair 的 row gap 统一给,
   否则两块各算各的 margin-top, 会出现 472px 的诡异 Δy。

   ⚠ [Backlog #200] **必须带 !important**: portfolio-entry.css:93-98 给
     `.portfolio-grid--ai` 写的是 `max-width:320px !important` +
     `margin:30px 0 0 auto !important`; portfolio-grid-responsive.css:121-122 的
     ≤1279 档同样是 important。**非 important 的覆盖打不过 important** ——
     这与「同特异性后写者胜」是两套规则。上一轮没意识到, 所以 320px 一直没被压掉,
     实测 X1 始终卡在 320 宽。
     特异性: 本节 (1,2,0) > portfolio-entry.css 的 (1,1,0), important 相打我方胜。 */
#section-portfolio .ai-lab-pair > .portfolio-grid--ai,
#section-portfolio .ai-lab-pair > .ai-work-module {
    margin: 0 !important;
    max-width: none !important;
    width: auto;
    justify-content: stretch;
}

/* ⚠ [Backlog #200 · 修正] padding 归零**只对 X1 那一侧**, 且必须区分两个层次:
     `.portfolio-grid--ai` 是**网格盒**, `.portfolio-theme-cluster` 才是**托盘**;
     而 `.ai-work-module` **本身就是托盘**(它直接画 background/border/border-radius)。

   第一版把两个子节点并成一个选择器一起 `padding: 0 !important`, 结果把
   `.ai-work-module` 这个托盘自己的内距也抹了 —— 两侧托盘内距变成
   X1 = 22px 14px 26px / AI-Gen = 0, 高度差虽然被「治好」了, 视觉却更不齐。
   是全量回归里的 `_b195f_test` H3「托盘圆角+内距一致」报的
   (实测 6px/0px, 期望 6px/22px 14px 26px)。

   28px 高度差的**唯一**来源本来就只有网格盒的 padding:
     `.portfolio-grid` 基础规则给了 `padding: 14px 6px`
     → X1 那个格子变成「420px 的簇 + 上下各 14px = 448px」
     而 `.ai-work-module` 是 border-box + min-height 420, 自身就是 420px。
   所以只压 `.portfolio-grid--ai` 一侧即可, 托盘内距两边都保持
   `22px 14px 26px`(portfolio-grid.css:78 与 life-ai-modal.css:39 写的同值)。
   内缩(托盘相对 pair 的 6px 缩进)仍由 `.ai-lab-pair` 的 `padding: 14px var(--space-3)` 给 ——
   实测 X1 簇 826 = pair 820 + 6, 与建筑簇 826 = 建筑网格 820 + 6 完全一致。

   → 与「抄机制抄到一半」「照抄要抄声明」同源: 同一个属性在**不同层级**上
     含义不同, 并成一个选择器就会把两个层次的声明一起改掉。 */
#section-portfolio .ai-lab-pair > .portfolio-grid--ai {
    padding: 0 !important;
}

/* [Backlog #200] max-width 阶梯逐档抄建筑网格。
   此前 .ai-lab-pair 的 1080px 是死值, 不随视口收缩, 于是 pair 盒子比建筑网格宽:
     1100 视口 pair 926 / 建筑 760 → 左缘差 166px
     1024 视口 pair 910 / 建筑 760 → 左缘差 150px
      900 视口 pair 808 / 建筑 760 → 左缘差  48px
   建筑两档的出处: portfolio-grid-responsive.css:100-105 / :133-137。 */
@media (max-width: 1279px) {
    #section-portfolio .ai-lab-pair {
        max-width: 760px;              /* 抄 ≤1279 档建筑 */
    }
    /* 两块托盘等高: 建筑簇在此档降为 400px (portfolio-grid-responsive.css:115-117),
       而 .ai-work-module 仍是 420px —— 不跟的话两块又差 20px。 */
    #section-portfolio .ai-lab-pair > .ai-work-module {
        min-height: 400px;
        /* 托盘内距也跟: 建筑簇在 ≤1279 降到 var(--space-8) 14px var(--space-9)
           (portfolio-grid-responsive.css:88-91), 但那条规则的选择器**只覆盖 cluster**,
           .ai-work-module 拿不到, 于是一直停在基础规则的 22px 14px 26px ——
           与 X1 托盘差 2px。既存不一致, 本轮补齐。
           ⚠ 抄的是**声明** var(--space-8) 14px var(--space-9), 不是它的计算值
              20px 14px 24px —— 与本节末尾那次 gap 的教训同源。 */
        padding: var(--space-8) 14px var(--space-9);
    }
}
/* ≤767: 建筑簇的 min-height 归 0 (portfolio-grid-responsive.css:149-151), 同跟。 */
@media (max-width: 767px) {
    #section-portfolio .ai-lab-pair > .ai-work-module {
        min-height: 0;
    }
}

/* ≤899: 与建筑同档折单列 (建筑在此档是 1 列 + max-width:100% + gap:var(--space-6))。
   ⚠ [Backlog #200] **托盘不再收成 320px 居中, 改成铺满整列** ——
      实测 ≤899 建筑簇是**满列宽**(899→795 / 768→683 / 600→528 / 390→331),
      而 AI Lab 托盘只有 320 居中, 窄屏下「托盘和建筑对齐」直接不成立:
        建筑托盘 795  vs AI Lab 托盘 320  (差 475px)
      满列宽才与建筑同宽; 卡仍各自在托盘内居中(托盘 `padding:14px var(--space-3)`),
      这与建筑簇「满宽托盘 + 卡片居中」的形态一致(目视依据 .trash/_b200_shots/band-899.png)。
   ⚠ 旧写法 `max-width:320px !important` 还**反向覆盖了**
      portfolio-grid-responsive.css:152-154 给 `.portfolio-grid--ai` 写的
      `max-width:100% !important` —— 把一个本来就正确的满宽规则按回了 320。
      这就是「important 打赢 important 时看特异性」的又一次实例: 我这条
      (1,2,0) 比它 (1,1,0) 特异性更高, 所以赢的是错的那个。
   ⚠ margin 一并去掉 auto: 满宽时 auto 无意义; 留着的话一旦 max-width 被更高优先级
      规则改小, 就会退化成居中窄块, 变成另一种不对齐。 */
@media (max-width: 899px) {
    #section-portfolio .ai-lab-pair {
        max-width: 100%;
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-6);
    }
    #section-portfolio .ai-lab-pair > .portfolio-grid--ai,
    #section-portfolio .ai-lab-pair > .ai-work-module {
        margin: 0 !important;
        max-width: none !important;
        width: auto;
    }
}

/* [Backlog #200] 把 §#200 ≤767 块的 `gap: 10px` 换成 `gap: var(--space-5)`。
   ⚠ 这是一次**抄了计算值**的现场纠正, 值得单独留档:
       portfolio-grid-responsive.css:147 在 ≤767 档写的是 `gap: var(--space-5) !important`,
       而 `--space-5 = 10px`。我上一版照着**实测值**写了裸 `10px` —— 渲染完全等价,
       静态检查也抓不到, 但它把「间距阶梯由 token 管理」这条约定悄悄破坏了:
       将来有人调 `--space-5` 时, AI Lab 这条不会跟着动。
       与「照抄要抄声明不抄计算值」(#198 的 200px 教训) 同源, 只是载体是 gap 不是 margin。 */
@media (max-width: 767px) {
    #section-portfolio .ai-lab-pair {
        /* 抄 portfolio-grid-responsive.css:147 的**声明**, 不是 --space-5 的计算值 10px */
        gap: var(--space-5);
    }
}
