/* ==========================================================================
   实验中心 · 详情族页面（`/labs/<id>.html`、`/labs/project/<id>.html`、
   `/labs/col/<id>[-<cid>].html`、`/lab-center/art/<id>-<aid>.html`）· 页面级样式
   落点：web/static/portal/labs.css

   分工（改样式前先认清"这段该写哪儿"）：
   · 骨架（`.ph` / `.ph__wrap` / `.ph-sec*`）与空态 / 分页 / 输入框 / 按钮的门户副本
     → `widgets/css/portal_base.css` 与 `portal/list.css`（本族页面也引 list.css）；
   · 实验项目**卡片网格** → 随件走（`widgets/css/portal_course.css` 的 `.ph-courses` / `.ph-course`）；
   · 本文件只管**中心头（banner / 名称 / 栏目导航 / 简介）、面包屑、文章条目、阅读区**。
   颜色 / 间距 / 字号一律 `--c-* / --sp-* / --fs-*` 语义 token（换肤自动生效）。
   ========================================================================== */

/* ── 中心头（刊头）：图与身份**并置成一块** ──
   三段（`__banner` 图版 / `__head` 身份 / `__about` 自述）**不再等距堆叠** —— 等距会读成
   三块无关内容。现在它们同属一个 `.ph-labhero__masthead`：
     大屏（≥1024）左图右身份（网格两列 `1.6fr / 1fr`）；
     窄屏      回归纵向（图 → 名称 / 事实 / 自述）。
   间距只有两档、**故意不等**：跨栏（图 ↔ 身份）= `--sp-5`；身份内部（名称 ↔ 事实）= `--sp-2`、
   事实 ↔ 自述 = `--sp-4`（分级才读成一块）。
   图版**不做卡片**（无圆角、无边框、无阴影）：页面下方已经全是卡片，第一屏主角不能又是卡片。
   颜色 / 字号 / 间距一律 token（换肤自动生效）。 */
/* ⚠ `.ph-labhero` 盒子本身**不给上间距**：外层 `.ph-sec` 已经给了 `--sp-6`(32px)
   （`portal_base.css`：`.ph-sec { padding: var(--sp-6, 32px) 0 0 }`）。
   两处各给一次会叠成 48px 的顶部空白（用户 2026-10-08 报"顶部的空白区域比较大"，
   实测页头下沿 → 中心名 = 48px）⇒ 中心头的上间距**只由 `.ph-sec` 一个来源负责**。
   别为了"再顶开一点"把 `padding-top` 加回来。 */
.ph-labhero__masthead {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: var(--sp-5, 24px);
    align-items: start;               /* 身份栏比图高/矮都不互相拉伸 */
}
/* 没有 banner（面板关了，或中心压根没配图）⇒ 身份栏跨满整行，并收一档行宽（通栏长文不好读） */
.ph-labhero__head:only-child {
    grid-column: 1 / -1;
    max-width: 72ch;
}

/* ── 内页（实验项目 / 栏目 / 文章）的**紧凑头**：`.ph-labhero--compact` ──
   为什么内页不能照搬门面：进了内页的读者已经知道自己在哪个中心（页头也有它的 logo 与栏目），
   再摆一张大图 banner + 一段自述，只会把正文推到第一屏之外（实测项目页被推下去 460+px）。
   所以内页只留"这是哪个中心"这件最小的信息：名称（降一档字阶）+ 事实行，排成一条，
   下接一条细线把它与正文分开 —— 结构上它退化成"内页页眉"，主角让给下面的内容。
   ⚠ 事实行 `flex: 1 1 auto`：窄屏名称与事实各自成行时，事实行不会被挤成两列。 */
.ph-labhero__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--sp-2, 8px) var(--sp-5, 24px);
    padding-bottom: var(--sp-3, 12px);
    border-bottom: 1px solid var(--c-divider);
}
.ph-labhero--compact .ph-labhero__name { font-size: var(--fs-2xl, 26px); }
.ph-labhero--compact .ph-labhero__facts { flex: 1 1 auto; }

/* ── banner = **焦点图**（一次一张，`transform` 位移切换）──
   为什么不用横向滚动（旧的 `scroll-snap` 做法）：滚动会露出邻图的边、还能被用户滑到"两张之间"
   的半张位置；焦点图要的是"一次一张、只由控件切换"（行为见 `web/static/widgets/js/lab_hero.js`）。
   视觉规格（箭头=静音圆钮 40px、指示器=32×28 热区 + 4px 短条 + `is-on` 变长）与**首页 banner**
   （`web/static/widgets/css/portal_banner.css` 的 `.ph-hero*`）**逐条对齐** —— 同一套门户语言，
   只是那边的移动方式是 scroll-snap（首页整宽横幅的既定实现，这里不动它）。 */
.ph-labhero__banner {
    position: relative;
    overflow: hidden;                 /* 位移出去的那几张由这里裁掉，页面不会出横向滚动条 */
    background-color: var(--c-surface-2);
}
.ph-labhero__track {
    display: flex;
    transition: transform var(--dur-slow, .5s) var(--ease-out, ease);
    will-change: transform;
}
/* ⚠ 比例 3:2：并置之后图只占 1.6/(1.6+1) ≈ 62% 宽（1440 下约 690px），原来的 21:6 只剩 197px 高、
   太扁；3:2（约 460px）才撑得住"第一屏主角"。窄屏回到 16:9（见文件末尾断点）。 */
.ph-labhero__slide {
    flex: 0 0 100%;
    min-width: 100%;
    aspect-ratio: 3 / 2;
}
/* ⚠ 图片有两个落点：无链接时是 `.ph-labhero__slide` 的直接子级，有链接时在 `.ph-labhero__slide-link` 里
   （⚠ 类名**不能**叫 `.ph-labhero__link` —— 那个已经是下方"中心栏目导航"的链接了） */
.ph-labhero__slide > img,
.ph-labhero__slide-link > img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.ph-labhero__slide-link { display: block; height: 100%; }

/* 箭头：两侧静音圆钮（`inset: 0` 的托层**不接点击**，只有按钮本身接 —— 否则整幅图会被蒙住） */
.ph-labhero__arrows {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 var(--sp-4, 16px);
    pointer-events: none;
}
.ph-labhero__arrow {
    pointer-events: auto;
    width: 40px;
    height: 40px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--radius-pill, 999px);
    background-color: var(--c-surface);
    color: var(--c-text-secondary);
    box-shadow: var(--shadow-sm);
    transition: color var(--dur-fast, .14s) var(--ease-out, ease),
                background-color var(--dur-fast, .14s) var(--ease-out, ease);
}
.ph-labhero__arrow:hover { color: var(--c-primary-text); }
.ph-labhero__arrow svg { width: 16px; height: 16px; }

/* 指示器：热区给足 32×28，视觉上的短条由 `::before` 画；整条容器**不接点击** */
.ph-labhero__dots {
    position: absolute;
    left: 0;
    right: 0;
    bottom: var(--sp-3, 12px);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2, 8px);
    pointer-events: none;
}
.ph-labhero__dot {
    position: relative;
    width: 32px;
    height: 28px;
    padding: 0;
    border: 0;
    background-color: transparent;
    cursor: pointer;
    pointer-events: auto;
    transition: width var(--dur-fast, .14s) var(--ease-out, ease);
}
.ph-labhero__dot::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 4px;
    transform: translateY(-50%);
    border-radius: var(--radius-pill, 999px);
    background-color: rgba(255, 255, 255, .55);
    transition: background-color var(--dur-fast, .14s) var(--ease-out, ease);
}
.ph-labhero__dot:hover::before,
.ph-labhero__dot:focus-visible::before { background-color: rgba(255, 255, 255, .9); }
.ph-labhero__dot.is-on { width: 48px; }
.ph-labhero__dot.is-on::before { background-color: var(--c-primary); }
.ph-labhero__dot:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }

/* 身份栏（图右边那一栏）：名称 → 事实行 → 自述。
   ⚠ 这里**故意没有 logo、也没有边框**：中心 logo 与栏目导航都已经上移到门户页头
     （`.portal-head__brand` / `.portal-nav`，见 `LabCenterHeroWidget` 的类注释）。
     原来自带的那条下边框是"目录条的上夹线"，导航一走它就成了**孤儿线** ⇒ 删掉。 */
.ph-labhero__head {
    display: flex;
    flex-direction: column;
    min-width: 0;
}
.ph-labhero__id {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2, 8px);
    min-width: 0;                                    /* 长名称/长院系名要能收成省略号 */
}
.ph-labhero__name {
    font-size: var(--fs-3xl, 32px);
    font-weight: var(--fw-semibold, 600);
    line-height: 1.25;
    color: var(--c-text);
}
/* 事实行：三件"这个中心的"事实并列。
   ⚠ 分隔**不用中间点**（门户元信息那套 `A · B · C` 在这里会读成一句话），改用 24px 留白分组；
     窄屏自然折行；数字用 tabular-nums，"15 个实训"折行后也齐。 */
.ph-labhero__facts {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2, 8px) var(--sp-5, 24px);
    font-size: var(--fs-sm, 14px);
    line-height: 1.6;
    color: var(--c-text-tertiary);
    font-variant-numeric: tabular-nums;
}
/* 实训基地等级是"资质"⇒ 全刊头**唯一着色点**（列表卡片那枚徽章在这里降级为主色文字） */
.ph-labhero__fact--tag {
    color: var(--c-primary-text);
    font-weight: var(--fw-medium, 500);
}
/* 自述：身份栏的**最后一段**（紧跟事实行，不再是被推到页面下方的独立块）。
   按正文排版：行高 1.9（中文长段落靠行距透气）、二级色。
   ⚠ 身份栏本身只有 1fr 宽（≤420px）⇒ 不需要限宽；只有"没有 banner、跨满整行"时才限到 72ch
     （见上面的 `.ph-labhero__head:only-child`）。后台限 300 字，本机存量有更长的，**不截断**。 */
.ph-labhero__about {
    margin-top: var(--sp-4, 16px);
    font-size: var(--fs-sm, 14px);
    line-height: 1.9;
    color: var(--c-text-secondary);
}

/* ⚠ 这里原先还有 `.ph-labhero__nav` / `__link`（中心栏目导航）与**第二份** `__about`：
   导航连同 logo 一起上移到了门户页头（那里的 `.portal-nav`，样式在 `portal/chrome.css`），
   自述已并入上面的身份栏 ⇒ 这两段规则**已删**，别再加回来（会成为无引用的死规则）。 */

/* ── 面包屑（页面级，不是内容区块） ── */
.ph-labs-crumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding-top: var(--sp-4, 16px);
    font-size: var(--fs-xs, 12px);
    color: var(--c-text-tertiary);
}
.ph-labs-crumb a { color: var(--c-text-secondary); }
.ph-labs-crumb a:hover { color: var(--c-primary-text); }

/* ── 页面内区块（项目 / 栏目 / 两栏小列表 / 教师团队）：沿用门户区块头（`.ph-sec__head`/`__body`）+ 上间距 ── */
.ph-labs-block { padding-top: var(--sp-6, 32px); }

/* ── 教师团队（中心首页区块）：头像 + 姓名 / 职称 / 简介，纵向"名片"单元 ──
   不做卡片（无边框、无阴影）：一排"人"靠头像与字阶分层就够，加框只会把版面切碎。
   列数由面板 `cols` 给（`--ph-cols`，与课程卡同一套口径），窄屏自动收列。

   ⚠ 2026-10-08 **二次优化**（用户："首页教师团队样式还需要再优化"）修两处硬伤：
     ① **简介从"一行省略"改成两行 clamp**：实测 6 人里 5 人一行只放得下 ~24 字，全被截成
        半句话（"东南大学地理信息工程学科负责人。 主要研究方向："）—— 半句比不显示更糟。
        两行 clamp + **固定两行高度**（`min-height`）⇒ 能读完一个短句，且同排各格仍然严格等高。
     ② **姓名与职称并排**（`__head`）：原来姓名 / 职称 / 简介三行竖排，在 270px 宽的格子里只占
        左半、右侧大片空白；并排后**省掉一整行高度**，块更紧凑（两行成员能一屏看全）。 */
.ph-labs-team {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    /* 行距 32 > 列距 24：同一行里的"名片"靠列距分组，而**行与行**之间要更分明 ——
       两处都 24 时第二行会被读成第一行的延续（实测贴得太近）。 */
    gap: var(--sp-6, 32px) var(--sp-5, 24px);
}
.ph-labs-team--grid { grid-template-columns: repeat(var(--ph-cols, 3), minmax(0, 1fr)); }
/* 成员单元：**纵向**（头像在上、姓名 / 职称 / 简介在下）。为什么纵向（2026-10-08 用户口径
   "教师团队的样式优化下"）：上方"中心动态"已经是横向的封面 + 文字卡，同一页两块再同构就分不出
   "人"和"文章"；纵向让这一块读成一张**团队名片**。仍然**左对齐**（保持门户一贯的左轴）。 */
.ph-labs-member {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-3, 12px);
    min-width: 0;
}
/* 头像：圆形；**姓名首字始终垫在下面**（图片挂了被 `onerror` 移除 ⇒ 首字自然露出来，
   零 JS 判断、零占位图、零误判）。
   ⚠ 底与字走 `--c-primary-soft` / `--c-primary-text`（不是灰底灰字）：实测一个中心里有头像的
     常常不到一半，全灰圆点会把整块读成"头像加载失败"；淡主色底给首字一枚**可认的标识**，
     仍是同色系（不新增色板、不违背"克制"）。有头像时图片铺满，这层底看不见。 */
.ph-labs-member__avatar {
    position: relative;
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 72px;                       /* 纵向排布后头像是这一格的主角（56 → 64 → 72） */
    height: 72px;
    border-radius: var(--radius-pill, 999px);
    overflow: hidden;
    background-color: var(--c-primary-soft);
}
.ph-labs-member__initial {
    font-size: var(--fs-xl, 18px);
    font-weight: var(--fw-semibold, 600);
    line-height: 1;
    color: var(--c-primary-text);
}
.ph-labs-member__avatar img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.ph-labs-member__body {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1, 4px);
    min-width: 0;
    width: 100%;
}
/* 姓名 + 职称**同排**：姓名可省略（长名字让位），职称 `flex: 0 0 auto` 不被压扁 */
.ph-labs-member__head {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2, 8px);
    min-width: 0;
    width: 100%;
}
.ph-labs-member__name {
    flex: 0 1 auto;
    font-size: var(--fs-base, 14px);
    font-weight: var(--fw-semibold, 600);
    line-height: 1.4;
    color: var(--c-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ph-labs-member__rank {
    flex: 0 0 auto;
    max-width: 50%;                    /* 职称过长时收在右半，不把姓名挤没 */
    font-size: var(--fs-xs, 12px);
    color: var(--c-text-tertiary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* 简介：**两行 clamp**（见上方 2026-10-08 二次优化说明）。
   `min-height` 锁住两行 ⇒ 简介只有一行的人也占两行高度，同排各格的下边界仍对齐。
   ⚠ clamp 模式下 `text-overflow` / `white-space: nowrap` 都不适用（省略号由 clamp 自己出）。 */
.ph-labs-member__bio {
    max-width: 100%;
    font-size: var(--fs-xs, 12px);
    line-height: 1.6;
    min-height: 3.2em;                 /* 2 行 × 1.6 */
    color: var(--c-text-tertiary);
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
}
@media (max-width: 640px) {
    /* 窄屏：一行两位、头像收小 */
    .ph-labs-team--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .ph-labs-member__avatar { width: 56px; height: 56px; }
}

/* ── 栏目页两栏：左「栏目列表」+ 右「文章列表」──
   2026-10-08 二次优化（用户口径："栏目列表背景要和文章列表有区分，现在看上去栏目列表和文章列表
   中间有巨大的空白区域"）：
     ① **左栏是一张淡底面板**（白底 `--c-surface` + 12px 圆角 + 16px 内边距）——
        页面底是 `--c-bg`(#f6f8fb)，白块放上去自然分界；右栏文章区保持"裸"在页面底上。
        ⚠ 只给**容器**一层底色（"这是一列导航"），不是把每一项做成卡片。
     ② **收窄两处**：左栏 220 → **200px**、列间距 48 → **32px**。改前左栏里文字只占 ~80px、
        再叠 48px 间距 ⇒ 中间那块空白在视觉上"巨大"（用户实测观感）。
     ③ 加**"栏目"小标题**（口径同门户 `.ph-cnav__title`：14px/600 + 一道细线），
        让这一列读成"导航卡"而不是几条飘着链接。
   `padding-top` 与右栏对齐：右栏 `.ph-labs-block` 有 32px 上留白，这里面板顶与它同线（对齐过）。
   窄屏折成一列时栏目**横排换行**到列表上方：竖排一列会把正文推到屏幕外两屏（门户口径）。 */
.ph-labs-list {
    display: grid;
    grid-template-columns: 200px minmax(0, 1fr);
    gap: var(--sp-6, 32px);
    align-items: start;
}
/* `min-width: 0`：右栏是 flex 列，不给它长标题/长 URL 会顶宽整个网格 */
.ph-labs-list__main { display: flex; flex-direction: column; min-width: 0; }

/* 单信息栏目页的**阅读区**在左栏布局里：**左对齐、占满右栏**。
   `.ph-labs-read` 单独用时是"居中 + 限宽 760"，放进两栏后若仍居中，右栏里会多出一块
   **不对称的空白**（用户对空白敏感）；右栏本身只有 900 出头，占满也仍在可读行长内。 */
.ph-labs-list__main .ph-labs-read { max-width: none; margin: 0; }

/* 左栏面板：白底 + 圆角 + 内边距（与右侧文章区在页面上**有色面区分**）。
   `margin-top` = 右栏 `.ph-labs-block` 的 32px 上留白 ⇒ 面板顶与右栏**内容**同起线
   （不然白块的顶会比右边的标题高出 32px，看着像左栏"提前开始"）。
   `--ph-cn-rule` = 层级导线的颜色：**白底**上要的对比比门户灰底略高一点（14% → 16%），
   否则导线在面板里淡到看不见（这一列的全部层级信息都压在它身上）。 */
.ph-labs-cn {
    --ph-cn-rule: color-mix(in srgb, var(--c-text) 16%, transparent);
    margin-top: var(--sp-6, 32px);
    padding: var(--sp-4, 16px);
    border-radius: var(--radius-md, 12px);
    background-color: var(--c-surface);
}
/* 小标题：与门户栏目导航的标题同一口径（14px/600 + 一道细线）。
   ⚠ 水平内边距 **12px，与栏目项的基础内边距同值** ⇒ 标题与顶级项两条左缘落在同一条竖线上。 */
.ph-labs-cn__t {
    padding: 0 var(--sp-3, 12px) var(--sp-3, 12px);
    border-bottom: 1px solid var(--c-divider);
    font-size: var(--fs-sm, 14px);
    font-weight: var(--fw-semibold, 600);
    line-height: 1.4;
    color: var(--c-text);
}
.ph-labs-cn__list {
    display: flex;
    flex-direction: column;
    margin-top: var(--sp-2, 8px);
}
/* 栏目项：**缩进 = 层级**（`--ph-lvl` 由模板给，14px/级 —— 4 级最右从 12+56=68px 起，
   200px 的栏宽里还剩下 100 多像素放文字）。
   当前项 = **主色淡底 + 主色字 + 左侧 2px 主色竖条**（底色是"我在这一列里"的层次，
   竖条是门户侧栏导航的既有语言；两者叠加不冲突）。
   ⚠ 竖条用 `box-shadow: inset`（不是 `border-left`）—— 它随 6px 圆角自然收口，
     也不会像 border 那样把文字挤动 2px。
   `position: relative` 是给子项的**导线 / 接入线**当日志位用的。 */
.ph-labs-cn__i {
    position: relative;
    display: block;
    /* 左右各 12px：文字**不贴**项（以及面板内容区）的边缘（2026-10-08 用户口径
       "左侧栏目列表中 item 文字左右有适当留白"）；hover / 当前项的圆角底块也有呼吸。
       左内边距 = `12px + 14px × lvl`（基础 12px 与标题左缘对齐，缩进只叠层级那一份）。 */
    padding: var(--sp-2, 8px) var(--sp-3, 12px) var(--sp-2, 8px) calc(var(--sp-3, 12px) + 14px * var(--ph-lvl, 0));
    border-radius: var(--radius-sm, 6px);
    font-size: var(--fs-sm, 14px);
    line-height: 1.5;
    color: var(--c-text-secondary);
    transition: color var(--dur-fast, .14s) var(--ease-out, ease),
                background-color var(--dur-fast, .14s) var(--ease-out, ease);
}
/* 子栏目：小一号字 + 更紧的行高（读成一"束"）——
   ⚠ 层级**不靠颜色分**（`--c-text-secondary` 与 `--c-text-tertiary` 实测几乎同色，主题覆写后同样接近），
     字号 + 缩进 + 下面那两笔"线"才是主力（与门户 `.ph-cnav` 的结论一致）。 */
.ph-labs-cn__i--sub {
    padding-top: 6px;
    padding-bottom: 6px;
    font-size: var(--fs-xs, 12px);
    color: var(--c-text-tertiary);
}
/* ── 层级装饰（2026-10-08 用户口径"加一些装饰，让层级看上去明显些"）──
   照搬**门户侧栏导航**（`portal_column_nav.css` 的 `.ph-cnav__item--sub`）那套"竖线语言"，
   两笔就把"它属于谁"讲清楚（比只靠缩进直觉得多）：
     · **层级导线**：贯穿该级所有兄弟（所以项之间**不能有 gap** —— 导线是每项各画一段，
       留缝会变成虚线）；
     · **接入线**：从导线指向本项文字（`├─ 文字` 的读法），落在项的垂直中心。
   ⚠ 位置由 `--ph-lvl` 算出：项文字左缘 = `12px + 14px × lvl`，导线在"文字左缘 − 7px"
     = `12px + 14px × (lvl − 1) + 7px`（与门户 `portal_column_nav.css` **同一条公式**）。 */
.ph-labs-cn__i--sub::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--sp-3, 12px) + 14px * (var(--ph-lvl, 0) - 1) + 7px);
    width: 1px;
    background-color: var(--ph-cn-rule);
}
.ph-labs-cn__i--sub::after {
    content: '';
    position: absolute;
    top: 50%;
    left: calc(var(--sp-3, 12px) + 14px * (var(--ph-lvl, 0) - 1) + 7px);
    width: 5px;
    height: 1px;
    background-color: var(--ph-cn-rule);
}
.ph-labs-cn__i:hover { color: var(--c-primary-text); background-color: var(--c-primary-softer); }
.ph-labs-cn__i.is-on {
    color: var(--c-primary-text);
    font-weight: var(--fw-medium, 500);
    background-color: var(--c-primary-soft);
    box-shadow: inset 2px 0 0 var(--c-primary);
}
.ph-labs-cn__i:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }

/* 窄屏：折成一列、栏目横排到列表上方（面板底色保留，内边距收紧）
   横排里"缩进"与"左侧竖条"都读不出来 ⇒ 缩进归零、子项改用**前缀小圆点**、
   当前项改**底边 2px 下划线**（门户侧栏导航在窄屏的同一套换法） */
@media (max-width: 1024px) {
    .ph-labs-list { grid-template-columns: minmax(0, 1fr); gap: var(--sp-4, 16px); }
    .ph-labs-cn { padding: var(--sp-3, 12px); }
    .ph-labs-cn__list { flex-direction: row; flex-wrap: wrap; gap: var(--sp-1, 4px); }
    .ph-labs-cn__i { padding: var(--sp-2, 8px) var(--sp-3, 12px); }
    .ph-labs-cn__i--sub { font-size: var(--fs-sm, 14px); }
    /* 横排里"上下级"读不出来 ⇒ 导线 / 接入线隐藏，子项改用**前缀小圆点**
       （⚠ 必须显式 `position: static` + `inset: auto` 把桌面那两笔的定位清掉，
         否则 `position: absolute` 会留着、圆点跑到项外） */
    .ph-labs-cn__i--sub::before {
        content: '';
        position: static;
        inset: auto;
        display: inline-block;
        width: 4px;
        height: 4px;
        margin-right: 6px;
        border-radius: 50%;
        background-color: currentColor;
        vertical-align: middle;
        opacity: .7;
    }
    .ph-labs-cn__i--sub::after { display: none; }
    .ph-labs-cn__i.is-on { border-radius: 0; box-shadow: inset 0 -2px 0 var(--c-primary); }
}

/* ── 栏目页的文章列表：**档案式条目**（日期锚点 ｜ 标题 + 摘要 + 元信息 ｜ 封面）──
   版式语言与**门户栏目页**（`portal/list.css` 的 `.ph-news__main .ph-art*`）逐条对齐：
   同一套"日期锚点 + 三列骨架 + 一道通栏细线"，只是类名自持（labs 页没有 `.ph-news__main`
   那层上下文，所以不直接复用那边的规则）。
   为什么不用"一行一条"（改前的做法）：同一条列表里**有封面**与**没封面**的文章混排时，
   一行一条把两者拉成同一行高、封面根本没处放；三列骨架让两种条目共用同一张"档案卡"，
   去掉封面只是少一列 ⇒ 文字自然向右铺满，不用为"缺图"另做一套样式。
   ⚠ 线的颜色**不能**用 `--c-divider`（#eff3f9）：门户页底是 #f6f6f6，它比底色还亮 ⇒ 看不见
     （口径与 `portal/list.css` 的新闻条目线一致）。用 `--c-text` 兑 12% 透明，跟随主题。 */
.ph-labs-arts { display: flex; flex-direction: column; }
.ph-labs-art {
    display: grid;
    grid-template-columns: 56px minmax(0, 1fr) 240px;
    gap: var(--sp-5, 24px);
    align-items: start;
    padding: var(--sp-5, 24px) 0;
}
/* ⚠ 没封面的条目用**同权重**的独立选择器补两列：`.ph-labs-art`(0,1,0) 会压过一行里后写的规则，
   少了这条，没封面的条目仍按三列排、右侧留一块空位（门户那边踩过同一个坑）。 */
.ph-labs-art--text { grid-template-columns: 56px minmax(0, 1fr); }
.ph-labs-art:first-child { padding-top: 0; }
.ph-labs-art + .ph-labs-art { border-top: 1px solid color-mix(in srgb, var(--c-text) 12%, transparent); }

/* 日期锚点：月-日（大字、等宽数字）+ 年份（小字）⇒"什么时候发的"一眼可扫 */
.ph-labs-art__date { display: flex; flex-direction: column; gap: 2px; }
.ph-labs-art__md {
    font-size: var(--fs-lg, 16px);
    font-weight: var(--fw-semibold, 600);
    line-height: 1.2;
    color: var(--c-text);
    font-variant-numeric: tabular-nums;
}
.ph-labs-art__y {
    font-size: var(--fs-xs, 12px);
    line-height: 1.4;
    color: var(--c-text-tertiary);
    font-variant-numeric: tabular-nums;
}
.ph-labs-art__body { display: flex; flex-direction: column; min-width: 0; }
.ph-labs-art__t {
    font-size: var(--fs-base, 14px);
    line-height: 1.6;
    color: var(--c-text);
    transition: color var(--dur-fast, .14s) var(--ease-out, ease);
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
}
/* 没封面的条目：标题升一档（纯文字条目里标题就是主角，也让"没图"看着是有意为之）、摘要多一行 */
.ph-labs-art--text .ph-labs-art__t { font-size: var(--fs-lg, 16px); }
.ph-labs-art__t:hover { color: var(--c-primary-text); }
.ph-labs-art__s {
    margin-top: var(--sp-1, 4px);
    font-size: var(--fs-sm, 14px);
    line-height: 1.6;
    color: var(--c-text-tertiary);
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
}
.ph-labs-art--text .ph-labs-art__s { -webkit-line-clamp: 3; line-clamp: 3; }
.ph-labs-art__m {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2, 8px) var(--sp-4, 16px);
    margin-top: var(--sp-2, 8px);
    font-size: var(--fs-xs, 12px);
    color: var(--c-text-tertiary);
    font-variant-numeric: tabular-nums;
}
.ph-labs-art__v { display: inline-flex; align-items: center; gap: 4px; }
/* 封面：16:9 小图 + 底衬；hover 轻微放大（与门户 `.ph-art__cover` 同一手法） */
.ph-labs-art__cover {
    display: block;
    aspect-ratio: 16 / 9;
    border-radius: var(--radius-sm, 6px);
    overflow: hidden;
    background-color: var(--c-surface-2);
}
.ph-labs-art__cover > img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--dur-slow, .5s) var(--ease-out, ease);
}
.ph-labs-art:hover .ph-labs-art__cover > img { transform: scale(1.03); }

/* ── ~~详情页的「通知公告 / 新闻资讯」：两栏~~ **已下线**（2026-10-09 用户口径"首页的'首页栏目'
   相关模块不要了"）：`.ph-labs-cols` / `.ph-labs-mini*` 整段删除（它们只服务那两个区块，
   全仓已无引用）。同职能现由「中心动态」（`.ph-arts` 两列文章卡）承担。 */


/* ── 文章阅读区：一列居中（版式语言对齐**门户文章详情页** `portal/article.css` 的 `.ph-post*`） ──
   对齐的三项硬规格：阅读栏 **760px**（改前 820）、正文 **16px / 1.9**（改前 14）、
   标题 **26px / 1.3**（改前 22）—— 内容页唯一该计较的就是"读起来舒不舒服"。 */
.ph-labs-read {
    max-width: 760px;
    margin: 0 auto;
    padding-top: var(--sp-5, 24px);
    padding-bottom: var(--sp-6, 32px);
}
.ph-labs-read__t {
    font-size: var(--fs-2xl, 26px);
    font-weight: var(--fw-semibold, 600);
    line-height: var(--lh-tight, 1.3);
    color: var(--c-text);
    overflow-wrap: break-word;      /* 长标题（含英文 / URL）不撑破阅读栏 */
}
/* 元信息：日期 ｜ 作者 ｜ 浏览 —— 靠间距分隔（不用中点），数字等宽避免抖动（同 `.ph-post__meta`） */
.ph-labs-read__m {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2, 8px) var(--sp-4, 16px);
    margin-top: var(--sp-3, 12px);
    padding-bottom: var(--sp-4, 16px);
    border-bottom: 1px solid color-mix(in srgb, var(--c-text) 12%, transparent);
    font-size: var(--fs-xs, 12px);
    color: var(--c-text-tertiary);
    font-variant-numeric: tabular-nums;
}
/* 头图（**有封面才渲染**）：通栏、12px 圆角、带占位底衬（同 `.ph-post__cover`） */
.ph-labs-read__cover { margin: var(--sp-5, 24px) 0 0; }
.ph-labs-read__cover > img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-md, 12px);
    background: url(/static/images/course_cover.jpg) center / cover no-repeat;
}
/* ── 正文 ──
   ⚠ 后台是**富文本**，元素远不止 `p`：改前只给了 `p` / `img` 两条规则 ⇒ `h2` / 列表 / 引用 /
     表格全是浏览器默认样式（小标题和正文一样大、缩进乱、表格没边线）。这里按门户详情页
     把常用元素补齐 —— 这是"参考门户"里最影响阅读的一项。 */
.ph-labs-read__body {
    margin-top: var(--sp-5, 24px);
    font-size: var(--fs-lg, 16px);
    line-height: 1.9;
    color: var(--c-text);
    overflow-wrap: break-word;
}
.ph-labs-read__body > :first-child { margin-top: 0; }
.ph-labs-read__body > :last-child { margin-bottom: 0; }
.ph-labs-read__body p { margin: 0 0 1.1em; }
.ph-labs-read__body img {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 1.5em auto;
    border-radius: var(--radius-md, 12px);
    background: url(/static/images/course_cover.jpg) center / cover no-repeat;
    /* 图取不回来时**高度会塌成一条色带**（看着像分隔线而不是"图的位置"）⇒ 给个下限 */
    min-height: 160px;
    /* alt 透明：图片没到位的那一瞬间只露底衬，不叠一行文件名（屏幕阅读器仍读 alt） */
    color: transparent;
}
.ph-labs-read__body figure { margin: 1.5em 0; }
.ph-labs-read__body figcaption {
    margin-top: var(--sp-2, 8px);
    font-size: var(--fs-xs, 12px);
    line-height: 1.6;
    color: var(--c-text-tertiary);
    text-align: center;
}
/* 正文小标题：靠字号 + 上方留白分层（不编号、不加上标 —— 那要内容真有顺序才用） */
.ph-labs-read__body h2 {
    margin: 1.7em 0 .7em;
    font-size: var(--fs-xl, 22px);
    font-weight: var(--fw-semibold, 600);
    line-height: var(--lh-tight, 1.3);
    color: var(--c-text);
}
.ph-labs-read__body h3,
.ph-labs-read__body h4 {
    margin: 1.5em 0 .6em;
    font-size: var(--fs-lg, 16px);
    font-weight: var(--fw-semibold, 600);
    line-height: var(--lh-base, 1.6);
    color: var(--c-text);
}
/* 中文正文的加粗只到 600：700 在汉字上会糊成一团（门户原话） */
.ph-labs-read__body strong,
.ph-labs-read__body b { font-weight: var(--fw-semibold, 600); }
.ph-labs-read__body a {
    color: var(--c-primary-text);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}
.ph-labs-read__body a:hover { text-decoration-thickness: 2px; }
.ph-labs-read__body blockquote {
    margin: 1.5em 0;
    padding: var(--sp-3, 12px) var(--sp-4, 16px);
    border-left: 2px solid var(--c-primary-border);
    border-radius: 0 var(--radius-sm, 6px) var(--radius-sm, 6px) 0;
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    color: var(--c-text-secondary);
}
.ph-labs-read__body ul,
.ph-labs-read__body ol { margin: 1.2em 0; padding-left: 1.6em; }
.ph-labs-read__body li { margin: .4em 0; }
.ph-labs-read__body table {
    width: 100%;
    margin: 1.5em 0;
    border-collapse: collapse;
    font-size: var(--fs-sm, 14px);
}
.ph-labs-read__body th,
.ph-labs-read__body td {
    padding: var(--sp-2, 8px) var(--sp-3, 12px);
    border: 1px solid color-mix(in srgb, var(--c-text) 12%, transparent);
    text-align: left;
}
.ph-labs-read__body th {
    background: color-mix(in srgb, var(--c-text) 4%, transparent);
    font-weight: var(--fw-semibold, 600);
}
.ph-labs-read__body hr {
    margin: 2em 0;
    border: 0;
    border-top: 1px solid color-mix(in srgb, var(--c-text) 12%, transparent);
}
/* ── 文章导航（上一篇 / 下一篇）：有哪篇显示哪篇，缺的一侧不占位（同门户 `.ph-post__nav`） ── */
.ph-labs-read__nav {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-4, 16px);
    margin-top: var(--sp-6, 32px);
    padding-top: var(--sp-4, 16px);
    border-top: 1px solid color-mix(in srgb, var(--c-text) 12%, transparent);
}
.ph-labs-read__link {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1, 4px);
    min-width: 0;
    padding: var(--sp-3, 12px);
    border-radius: var(--radius-sm, 6px);
    transition: background-color var(--dur-fast, .14s) var(--ease-out, ease);
}
.ph-labs-read__link:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }
/* 方向靠对齐表达：上一篇贴左、下一篇贴右（`grid-column` 让它落在右半栏） */
.ph-labs-read__link--next { grid-column: 2; text-align: right; }
.ph-labs-read__dir { font-size: var(--fs-xs, 12px); line-height: 1.6; color: var(--c-text-tertiary); }
.ph-labs-read__name {
    font-size: var(--fs-sm, 14px);
    line-height: 1.6;
    color: var(--c-text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: color var(--dur-fast, .14s) var(--ease-out, ease);
}
.ph-labs-read__link:hover { background-color: var(--c-primary-softer); }
.ph-labs-read__link:hover .ph-labs-read__name { color: var(--c-primary-text); }
.ph-labs-read__back { margin-top: var(--sp-5, 24px); }

@media (max-width: 1024px) {
    /* 平板：日期列与封面列各收一档（同门户栏目页的 1024 断点） */
    .ph-labs-art { grid-template-columns: 48px minmax(0, 1fr) 160px; gap: var(--sp-3, 12px); }
    .ph-labs-art--text { grid-template-columns: 48px minmax(0, 1fr); }
}

/* 窄屏（含平板竖屏）：并置改回**纵向**（图在上、身份在下）；图回 16:9（3:2 在窄屏太矮）。
   这时自述是通栏长文 ⇒ 限到 72ch 更好读。 */
@media (max-width: 1023px) {
    .ph-labhero__masthead { grid-template-columns: minmax(0, 1fr); }
    .ph-labhero__slide { aspect-ratio: 16 / 9; }
    .ph-labhero__about { max-width: 72ch; }
}

@media (max-width: 640px) {
    /* 箭头在窄屏收起（与首页 banner 同口径：手机上"自动播放 + 指示器"够用） */
    .ph-labhero__arrow { display: none; }
    .ph-labhero__name { font-size: var(--fs-xl, 22px); }
    /* 窄屏：文章条目**一列**（日期折回标题上方一行、封面落到文字下面）—— 与门户栏目页
       同一口径（`portal/list.css` 的 640 断点）。DOM 顺序 = 日期 → 正文 → 封面 ⇒ 折成一列自然成立。 */
    .ph-labs-art { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2, 8px); padding: var(--sp-4, 16px) 0; }
    .ph-labs-art--text { grid-template-columns: minmax(0, 1fr); }
    .ph-labs-art__date { flex-direction: row; align-items: baseline; gap: var(--sp-2, 8px); }
    .ph-labs-read__t { font-size: var(--fs-xl, 22px); }
    .ph-labs-read__body { line-height: 1.85; }
    /* 上下篇竖排，都改成左对齐（右侧对齐在窄屏读起来别扭） */
    .ph-labs-read__nav { grid-template-columns: minmax(0, 1fr); }
    .ph-labs-read__link--next { grid-column: 1; text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
    /* 焦点图的位移过渡也要停（`labs-hero.js` 在同样偏好下不自动播放）。
       ⚠ 原先这里还有 `.ph-labhero__link`（中心栏目导航的链接）—— 导航已上移到门户页头
         （样式在 `portal/chrome.css`），随 `labs.css` 里那段规则一并删掉。 */
    .ph-labhero__track,
    .ph-labhero__dot,
    .ph-labs-art__t { transition: none !important; }
}
