/* ==========================================================================
   Supplovia · base.css —— 结构层（与皮肤无关）

   这里只放【布局与骨架】：栅格、间距节奏、区块的 DOM 结构样式。
   所有【视觉决策】（字体、颜色、圆角、阴影、边框风格）都在 skin-*.css 里，
   由 CSS 变量驱动。

   换皮肤 = 换一个 skin 文件，区块模板一行不改。
   ========================================================================== */

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
    margin: 0;
    font-family: var(--font-text);
    font-size: var(--fs-body);
    line-height: var(--leading-body);
    color: var(--ink);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

/* ── 滚动条 ─────────────────────────────────────────────────────────
   只走 ::-webkit-scrollbar 伪元素（Chromium / WebKit / Safari）。
   没有别的选择：圆角、轨道、内缩这些只有伪元素给得了，标准属性
   （scrollbar-width / scrollbar-color）只能调粗细和颜色；而且两者互斥 ——
   元素上只要设了非 auto 的 scrollbar-width，Chrome 就整体忽略伪元素。
   所以这里一个标准属性都不写，代价是 Firefox 用原生滚动条（它本来也只有
   粗/细两档，原生不算难看）。

   滑块用「透明边框 + background-clip: content-box」内缩：伪元素没有 margin，
   这是把 12px 槽里的滑块"瘦"成 6px 的唯一办法。 */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: var(--line-strong); border-radius: 99px;
    border: 3px solid transparent; background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-3); background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }

/* ── 排版骨架 ─────────────────────────────────────────────────────── */
h1, h2, h3, h4 {
    margin: 0;
    font-family: var(--font-display);
    font-weight: var(--weight-display);
    letter-spacing: var(--tracking-display);
    text-wrap: balance;
}
h1 { font-size: var(--fs-h1); line-height: var(--leading-display); }
h2 { font-size: var(--fs-h2); line-height: var(--leading-display); }
h3 { font-size: var(--fs-h3); line-height: 1.35; letter-spacing: -0.01em; }
p  { margin: 0; color: var(--ink-2); text-wrap: pretty; }

.eyebrow {
    display: inline-block;
    font-family: var(--font-label);
    font-size: var(--fs-eyebrow);
    font-weight: var(--weight-label);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--accent);
    margin-bottom: var(--gap-eyebrow);
}
.lede {
    font-size: var(--fs-lede);
    line-height: 1.55;
    color: var(--ink-2);
    max-width: 56ch;
}

/* ── 按钮骨架 ─────────────────────────────────────────────────────── */
.btn {
    display: inline-flex; align-items: center; gap: 9px;
    padding: var(--btn-py) var(--btn-px);
    background: var(--accent); color: var(--accent-ink);
    font-family: var(--font-text);
    font-size: var(--fs-btn); font-weight: 600; letter-spacing: -0.005em;
    text-decoration: none;
    border: var(--btn-border); border-radius: var(--radius);
    transition: transform .16s ease, box-shadow .16s ease, background .16s ease, border-color .16s ease;
}
.btn::after { content: "→"; transition: transform .16s ease; }
.btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-btn); }
.btn:hover::after { transform: translateX(3px); }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn--ghost::after { content: none; }
.btn--ghost:hover { background: transparent; border-color: var(--ink); }

/* ── 区块容器与节奏 ───────────────────────────────────────────────── */
.section { padding: var(--section-y) 0; }
.section--tight { padding: calc(var(--section-y) * .69) 0; }
.section + .section { border-top: var(--divider); }
.section__head { max-width: 62ch; margin-bottom: var(--head-gap); }
.section__head h2 { margin-bottom: 14px; }

/* ==========================================================================
   区块骨架 —— hero
   ========================================================================== */

/* hero/split_media：左文案 / 右图，两栏 */
.hero--split { padding: var(--hero-y) 0; }
.hero--split .hero__grid {
    display: grid; grid-template-columns: 1.05fr 1fr;
    gap: var(--hero-gap); align-items: center;
}
.hero--split h1 { margin-bottom: 22px; }
.hero--split .lede { margin-bottom: 34px; }
.hero__media {
    margin: 0;
    background: var(--media-bg);
    border-radius: var(--radius-lg);
    overflow: hidden;
    border: var(--media-border);
}
.hero__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: contain; padding: 7%; }

/* hero/full_bleed：全幅 + 居中，图压底部 */
.hero--full { padding: 0; background: var(--hero-full-bg); border-bottom: var(--divider); }
.hero--full .hero__inner {
    max-width: 880px; margin: 0 auto;
    padding: var(--hero-y) var(--gutter) 0; text-align: center;
}
.hero--full .lede { margin: 0 auto 34px; }
.hero--full .hero__media { max-width: 1000px; margin: var(--hero-img-gap) auto 0; }
.hero--full .hero__media img { aspect-ratio: 16 / 9; object-fit: contain; padding: 3% 8% 0; }

@media (max-width: 900px) {
    .hero--split { padding: calc(var(--hero-y) * .6) 0 calc(var(--hero-y) * .7); }
    .hero--split .hero__grid { grid-template-columns: 1fr; gap: 40px; }
    .hero--full .hero__inner { padding-top: calc(var(--hero-y) * .6); }
    .hero--full .hero__media { margin-top: calc(var(--hero-img-gap) * .6); }
}

/* ==========================================================================
   区块骨架 —— feature_grid
   ========================================================================== */

/* numbered_rows：编号行式 */
.features--numbered .features__list { border-top: var(--divider-strong); }
.features--numbered .features__item {
    display: grid; grid-template-columns: 68px 1fr 1.15fr; gap: 28px; align-items: baseline;
    padding: var(--row-py) 0; border-bottom: var(--divider-strong);
}
.features--numbered .features__no {
    font-family: var(--font-label); font-size: 13px;
    color: var(--ink-3); letter-spacing: .04em;
}
.features--numbered .features__item h3 { margin: 0; }
@media (max-width: 760px) {
    .features--numbered .features__item { grid-template-columns: 46px 1fr; gap: 8px 16px; }
    .features--numbered .features__item p { grid-column: 2; }
}

/* bordered_cells：格子墙 */
.features--cells .features__grid {
    display: grid; grid-template-columns: repeat(3, 1fr);
    border-top: var(--divider-strong); border-left: var(--divider-strong);
}
.features--cells .features__cell {
    padding: 34px 30px;
    border-right: var(--divider-strong); border-bottom: var(--divider-strong);
    background: var(--cell-bg);
    transition: background .16s ease;
}
.features--cells .features__cell:hover { background: var(--cell-bg-hover); }
.features--cells .features__tag {
    display: inline-block; font-family: var(--font-label); font-size: 11px;
    letter-spacing: .09em; text-transform: uppercase;
    color: var(--accent); margin-bottom: 22px;
}
.features--cells h3 { margin-bottom: 8px; }
@media (max-width: 820px) { .features--cells .features__grid { grid-template-columns: 1fr; } }

/* ==========================================================================
   区块骨架 —— faq
   ========================================================================== */

/* columns：问题左 / 答案右 */
.faq--columns .faq__list { border-top: var(--divider-strong); }
.faq--columns .faq__item {
    display: grid; grid-template-columns: 1fr 1.3fr; gap: 40px;
    padding: var(--row-py) 0; border-bottom: var(--divider-strong);
}
.faq--columns .faq__item h3 { font-size: var(--fs-h3); }
@media (max-width: 760px) { .faq--columns .faq__item { grid-template-columns: 1fr; gap: 8px; } }

/* table：带表头的问答表 */
.faq--table .faq__table { width: 100%; border-collapse: collapse; }
.faq--table th {
    text-align: left; font-family: var(--font-label); font-size: 11px;
    letter-spacing: .1em; text-transform: uppercase;
    color: var(--ink-3); font-weight: 500;
    padding: 0 24px 14px 0; border-bottom: var(--divider-strong);
}
.faq--table td {
    vertical-align: top; padding: 26px 24px 26px 0; border-bottom: var(--divider);
}
.faq--table td:first-child { width: 38%; font-weight: 600; color: var(--ink); }
.faq--table td:last-child { color: var(--ink-2); padding-right: 0; }
@media (max-width: 760px) {
    .faq--table thead { display: none; }
    .faq--table tr { display: block; padding: 22px 0; border-bottom: var(--divider); }
    .faq--table td { display: block; width: auto; padding: 0 0 8px; border: 0; }
}

/* ==========================================================================
   区块骨架 —— cta_band
   ========================================================================== */

/* centered：细带居中 */
.cta--centered .cta__inner { max-width: 620px; margin: 0 auto; text-align: center; }
.cta--centered h2 { margin-bottom: 14px; }
.cta--centered p  { margin-bottom: 32px; }

/* split：左文案 / 右按钮，顶部一道强调线 */
.cta--split { position: relative; }
.cta--split::before {
    /* 站点配了品牌辅色就用辅色（--accent-2 由品牌色覆盖块注入），没配退回主色 */
    content: ""; position: absolute; inset: 0 0 auto; height: var(--cta-top-line);
    background: var(--accent-2, var(--accent));
}
.cta--split .cta__grid {
    display: grid; grid-template-columns: 1.4fr auto; gap: 40px; align-items: center;
}
.cta--split h2 { font-size: var(--fs-h2); margin-bottom: 10px; }
.cta--split .cta__actions { display: flex; gap: 12px; flex-wrap: wrap; }
@media (max-width: 760px) { .cta--split .cta__grid { grid-template-columns: 1fr; gap: 24px; } }

/* ==========================================================================
   面包屑（02-页面与URL规范.md §5）
   ========================================================================== */
/* 层级由控制器按页面类型算好，这里只管画。

   分隔符用 ::before 画（content 是空的，靠两条边框转 45° 拼出尖角），
   而不是在 DOM 里塞 "/" 或 "›" 字符：那些字符会被读屏软件念出来
   （"斜杠"），而它纯粹是装饰。

   字号压到 12px、用 ink-3：面包屑是【定位用】的，不该和正文抢注意力。
   标题同级的东西（区块 h2）已经很大了，这里再大就变成第二个标题。 */
.crumbs { padding: 22px 0 0; }
.crumbs__list {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0 10px;
    margin: 0; padding: 0; list-style: none;
    font-size: 12px; color: var(--ink-3);
}
/* min-width: 0 是为了让长产品名能被压缩 —— flex 项默认 min-width: auto，
   压不下去，只会把整行撑破。 */
.crumbs__item { display: flex; align-items: center; gap: 10px; min-width: 0; }
.crumbs__item + .crumbs__item::before {
    content: ""; flex: none; width: 5px; height: 5px;
    border-top: var(--divider-strong); border-right: var(--divider-strong);
    transform: rotate(45deg);
}
/* 溢出走省略号而不是折行：名称折两行会把面包屑撑成一块，
   而它的作用是"一眼看到自己在哪"，不是给人读完。 */
.crumbs__link, .crumbs__now {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.crumbs__link { color: var(--ink-3); text-decoration: none; }
.crumbs__link:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.crumbs__now { color: var(--ink); }

/* ==========================================================================
   页头 / 页脚 / 演示工具条
   ========================================================================== */
.site-head {
    display: flex; align-items: center; justify-content: space-between; padding: 26px 0;
    border-bottom: var(--head-divider);
}
.site-head__brand {
    font-family: var(--font-display); font-size: 18px; font-weight: 700;
    letter-spacing: -0.02em; text-decoration: none; color: var(--ink);
}
.site-head__brand:hover { color: var(--ink); opacity: .78; }
/* 配了 logo 时显示图：高度跟文字行高对齐，宽度自适应；暗色皮肤上也看得清 */
.site-head__logo { height: 30px; width: auto; display: block; }
.site-head__nav { display: flex; gap: 26px; font-size: 14px; color: var(--ink-2); }
.site-head__nav a { text-decoration: none; }
.site-head__nav a:hover { color: var(--ink); }
/* 尚未实现的导航项：不可点，视觉上和已实现的区分开 */
.nav-placeholder { color: var(--ink-3); cursor: default; }
@media (max-width: 640px) { .site-head__nav { display: none; } }

/* 页头右侧那一组：导航 + 语言切换。包这一层是为了窄屏 ——
   640px 以下只藏 .site-head__nav，切换器得留着（见上面那条媒体查询）。 */
.site-head__end { display: flex; align-items: center; gap: 22px; }
@media (max-width: 640px) { .site-head__end { gap: 14px; } }

/* ---- 语言切换器 ----
   原生 <details>，一行 JS 都不写。收起态就是默认态，无脚本时也是对的 ——
   和皮肤切换、悬浮操作栏一样，能不用脚本就不用。

   13 个语言摊在页头会撑爆，所以必须收进弹出层。 */
.lang-switch {
    /* 面板要 absolute 定位到它身上，得先有定位上下文 */
    position: relative;
}
.lang-switch__toggle {
    display: flex; align-items: center; gap: 7px;
    padding: 6px 10px;
    font-size: 13px; color: var(--ink-2);
    /* 边框用 --divider-strong（= 1px solid --line-strong），不是 --btn-border：
       三个皮肤里的 --btn-border 都是 1px solid transparent
       （实心按钮不需要边框），拿它当控件描边会得到一圈看不见的边。 */
    border: var(--divider-strong); border-radius: var(--radius);
    cursor: pointer; user-select: none; list-style: none;
}
/* 收掉 summary 自带的三角标。三个都写，因为各家走的选择器不同：
   Chrome/Safari 认 ::-webkit-details-marker，Firefox 认 ::marker，
   list-style: none（在上面）是给不支持 ::marker 的老 Firefox 兜底。 */
.lang-switch__toggle::marker { content: ""; }
.lang-switch__toggle::-webkit-details-marker { display: none; }
.lang-switch__toggle:hover { color: var(--ink); border-color: var(--ink); }
.lang-switch[open] > .lang-switch__toggle { color: var(--ink); }
.lang-switch__globe { width: 15px; height: 15px; flex: none; }

/* 展开面板。贴按钮右下角 —— 它挂在页头最右端，左对齐会顶出屏幕。
   max-height 60vh：13 项约 13×34 = 442px，小屏笔记本（800 高）加页头就到边了，
   超了走滚动而不是把页头顶下去。
   z-index 45：压过正文和悬浮操作栏（40），但不压演示工具条（50）——
   那是个 sticky 的调试框，本来就该在最上面。 */
.lang-switch__menu {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 45;
    min-width: 172px; max-height: 60vh; overflow: auto;
    margin: 0; padding: 6px; list-style: none;
    background: var(--surface);
    border: var(--divider-strong); border-radius: var(--radius);
}
.lang-switch__item {
    display: block; padding: 7px 10px;
    font-size: 14px; line-height: 1.25; color: var(--ink-2);
    border-radius: var(--radius); white-space: nowrap;
}
/* hover 只给 <a>。置灰项和当前项也是 .lang-switch__item，
   拿类选择器写 hover 会让它们一划过就冒出"能点"的背景。 */
a.lang-switch__item { text-decoration: none; }
a.lang-switch__item:hover { background: var(--cell-bg-hover); color: var(--ink); }

/* 当前语言：加粗 + 满色，但【不给背景】—— 背景是 hover 的语言，
   两边都上色的话鼠标一划过就分不清「我在哪」和「我指着哪」。
   前面那个点只是让"当前"在满色文字里也一眼可见。 */
.lang-switch__item.is-current { color: var(--ink); font-weight: 600; cursor: default; }
.lang-switch__item.is-current::before { content: "· "; color: var(--accent); }

/* 这门语言站上还没有已发布的商品。不可点，但【不是坏链接】——
   访问过去不会 404，控制器会回退成默认语言的内容（还是中文，只标 noindex）。
   灰度和 .nav-placeholder 用同一个 --ink-3，页头里"不可点"只有一种长相。 */
.lang-switch__item.is-off { color: var(--ink-3); cursor: default; }

.site-foot {
    /* 底部那 200px 是给悬浮操作栏让位的。操作栏是 fixed 的，滚到页底就压在
       右下角；页脚是 space-between，最后一项（版权行）正好靠右 —— 不让开的话
       在窄屏上它是【永远】被盖住，滚到底也看不见。
       数值 = 操作栏高度（详情页三个按钮约 152px）+ 它的 bottom 偏移（32px）+ 余量。
       列表页只有两个按钮，会多留一点白 —— 但按最坏情况留，
       总好过某些页面的版权行被压在按钮底下。 */
    padding: 44px 0 200px; border-top: var(--divider-strong);
    display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
    font-size: 13px; color: var(--ink-3);
}

/* ---- 悬浮操作栏（返回上一页 / 产品列表 / 返回顶部）----
   钉在右下角，跟着页面滚。

   默认【常显】，靠 JS 在滚回顶部附近时加 .site-rail--off 收起来。
   反过来（默认藏、JS 显）会让无脚本的用户彻底够不着这些入口 ——
   而它们本来就只是锚点和链接，不依赖任何脚本。所以默认态必须是可用态，
   脚本只负责"什么时候不该看见"。（唯一的例外是 Back，见 site-rail.js。） */
.site-rail {
    position: fixed; right: var(--gutter); bottom: 32px; z-index: 40;
    display: flex; flex-direction: column; gap: 7px;
    /* 过渡不写在这里，见下面那条。 */
}
/* 过渡只在这条挂上之后才生效，而它由 site-rail.js 在 window load 之后挂。
   原因：页面在顶部加载时操作栏要【瞬间】收起。如果基础规则里就有
   transition，脚本一上来加 .site-rail--off 会变成"先显示 200ms 再淡出"，
   右下角闪一下。没有 transition 时它是直接从没画过的状态开始收起的。 */
.site-rail--animated { transition: opacity .2s ease, transform .2s ease, visibility .2s; }
.site-rail--off {
    opacity: 0; visibility: hidden; pointer-events: none;
    transform: translateY(8px);
}
.site-rail__btn {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; min-width: 56px; padding: 8px 7px;
    font-family: var(--font-label); font-size: 10px; letter-spacing: .06em;
    line-height: 1.3; text-transform: uppercase; text-decoration: none;
    white-space: nowrap; color: var(--ink-2);
    background: var(--surface, var(--bg));
    border: var(--divider-strong); border-radius: var(--radius);
    box-shadow: var(--shadow-btn);
    transition: color .16s ease, border-color .16s ease, transform .16s ease;
}
/* <button>（Back）和 <a> 的默认样式不一样：字体、line-height、cursor 都要拉平，
   不然相同规则下那一格会比上面两格胖一圈、点上去也没有手型。 */
button.site-rail__btn { cursor: pointer; appearance: none; }
/* .site-rail__btn 的 display:flex 是作者样式，会盖掉浏览器给 [hidden] 的
   display:none。不补这一条，模板里 Back 上的 hidden 根本藏不住 ——
   那正是"无脚本时多一个点不动的按钮"这个要避免的情况。 */
.site-rail__btn[hidden] { display: none; }
.site-rail__btn:hover { color: var(--ink); border-color: var(--ink); transform: translateY(-1px); }
.site-rail__btn svg { width: 15px; height: 15px; display: block; }
/* 回顶按钮兼做【滚动进度】：底色被 site-rail.js 维护的 --rail-progress
   从下往上填。用「百分比色标」而不是伪元素高度动画 —— 百分比色标是最老牌
   的一档，三套皮肤共用一条规则，也不用碰 stroke-dashoffset 那种各家实现
   不一致的地方。脚本没跑 = 0%，退化成普通按钮。
   写在 background-image 长属性上而不是 background 简写上：简写会把
   上面那条 background-color 一起重置掉。 */
.site-rail__btn--top {
    background-image: linear-gradient(
        to top,
        color-mix(in srgb, var(--ink) 12%, transparent) var(--rail-progress, 0%),
        transparent var(--rail-progress, 0%)
    );
}
/* 触屏上没有 hover，点完还留着位移很别扭 */
@media (hover: none) { .site-rail__btn:hover { transform: none; } }
@media (max-width: 700px) { .site-rail { right: 14px; bottom: 16px; } }
@media print { .site-rail { display: none; } }

.demo-bar {
    background: #18181B; color: #D4D4D8;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12.5px; padding: 9px 0;
    position: sticky; top: 0; z-index: 50;
}
.demo-bar a { color: #7DD3FC; text-decoration: none; }
.demo-bar a:hover { text-decoration: underline; }
.demo-bar b { color: #FCD34D; }
.demo-bar .sep { opacity: .35; margin: 0 6px; }

/* ==========================================================================
   区块骨架 —— page_header（页面标题）
   ========================================================================== */
.page-header { padding: calc(var(--section-y) * .62) 0 calc(var(--section-y) * .38); }
.page-header h1 { margin-bottom: 14px; }
.page-header__inner { max-width: 680px; margin: 0 auto; text-align: center; }

/* ==========================================================================
   区块骨架 —— category_nav（分类导航，变体 chips_row）
   ========================================================================== */
/* 列表页的出场顺序是 page_header → category_nav → product_grid。
   这个块【不带 .section】：.section 自带 112px 留白和区块间分隔线，
   但分类条和产品网格是同一件事（"这是哪个分类" + "分类里有啥"），
   中间横一道线、隔一屏空白，反而把它们切成两块。 */
.catnav-block { padding: calc(var(--section-y) * .18) 0; }
/* 紧跟分类条的那个区块（现在是产品网格）顶留白收一档。网格自己是 .section，
   112px 是给"两个主题区块之间"定的音量；它下面是分类条，不需要那么多。 */
.catnav-block + .section { padding-top: calc(var(--section-y) * .34); }

.catnav { display: flex; flex-wrap: wrap; gap: 8px; }
.catnav__chip {
    display: inline-flex; align-items: center;
    padding: 7px 14px; font-size: 13px; line-height: 1.4;
    white-space: nowrap; text-decoration: none;
    color: var(--ink-2); background: var(--surface, var(--bg));
    border: var(--divider-strong); border-radius: var(--radius);
    transition: color .14s ease, border-color .14s ease;
}
.catnav__chip:hover { color: var(--ink); border-color: var(--ink); }
/* 当前分类用实底反白，不用"加深一点边框"那种：列表页上"我在哪"必须一眼看到，
   而且下面网格里的产品也全是这个分类的，两者要能对上。 */
.catnav__chip--on {
    color: var(--accent-ink); background: var(--accent);
    border-color: var(--accent);
}
/* 必须显式写这条：.catnav__chip:hover 是 (0,2,0)，而 .catnav__chip--on 只有
   (0,1,0) —— 悬停时上面那条会赢，把文字改成 --ink，在深色实底上糊成一片。 */
.catnav__chip--on:hover { color: var(--accent-ink); border-color: var(--accent); }

/* ==========================================================================
   区块骨架 —— product_grid（产品网格）
   ========================================================================== */
.pgrid { display: grid; gap: 32px 24px; }
.pgrid--3 { grid-template-columns: repeat(3, 1fr); }
.pgrid--4 { grid-template-columns: repeat(4, 1fr); gap: 24px; }
@media (max-width: 1000px) { .pgrid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 820px)  { .pgrid--3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .pgrid--3, .pgrid--4 { grid-template-columns: 1fr; } }

/* 卡片（grid_3）：图在上、文在下 */
.pcard__link { display: block; text-decoration: none; color: inherit; }
.pcard__media {
    margin: 0 0 14px; aspect-ratio: 4 / 3;
    background: var(--media-bg); border: var(--media-border); border-radius: var(--radius-lg);
    display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.pcard__media img { width: 100%; height: 100%; object-fit: contain; padding: 8%;
                    transition: transform .25s ease; }
.pcard__link:hover .pcard__media img { transform: scale(1.04); }
.pcard__ph { color: var(--ink-3); font-size: 12px; }
.pcard__name { font-size: 16px; margin-bottom: 6px; line-height: 1.4;
               display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.pcard__model { font-size: 12px; color: var(--ink-3); margin-bottom: 8px;
                font-family: var(--font-label); letter-spacing: .02em; }
.pcard__price { font-size: 15px; font-weight: 640; color: var(--ink); }
.pcard__quote { font-weight: 500; color: var(--ink-2); font-size: 14px; }
.pcard__moq { font-size: 12px; color: var(--ink-3); margin-top: 4px; }

/* 紧凑行式（grid_4）：图左、文右 */
.pgrid-block--compact .prow {
    display: grid; grid-template-columns: 84px 1fr; gap: 14px;
    text-decoration: none; color: inherit; align-items: center;
    padding: 12px; border: var(--divider-strong); border-radius: var(--radius-lg);
    background: var(--cell-bg, transparent); transition: background .16s ease;
}
.pgrid-block--compact .prow:hover { background: var(--cell-bg-hover); }
.prow__media { margin: 0; width: 84px; height: 84px; background: var(--media-bg);
               border-radius: var(--radius); display: flex; align-items: center;
               justify-content: center; overflow: hidden; }
.prow__media img { width: 100%; height: 100%; object-fit: contain; padding: 6%; }
.prow__name { font-size: 14px; line-height: 1.4; margin-bottom: 4px;
              display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.prow__meta { font-size: 11px; color: var(--ink-3); font-family: var(--font-label); }
.prow__price { font-size: 13px; font-weight: 640; margin-top: 6px; }

/* 分页 */
.pager { display: flex; align-items: center; justify-content: center; gap: 20px;
         margin-top: 56px; padding-top: 28px; border-top: var(--divider-strong); }
.pager__btn { font-size: 14px; font-weight: 600; text-decoration: none; color: var(--ink);
              padding: 9px 18px; border: var(--divider-strong); border-radius: var(--radius);
              transition: border-color .16s ease, background .16s ease; }
.pager__btn:hover { border-color: var(--ink); background: var(--cell-bg-hover); }
.pager__info { font-size: 13px; color: var(--ink-3); font-family: var(--font-label); }

.empty { text-align: center; color: var(--ink-3); padding: 60px 0; }

/* ==========================================================================
   产品详情页
   ========================================================================== */

/* ---- product_hero 通用 ---- */
.pdp-hero { padding: calc(var(--section-y) * .7) 0 calc(var(--section-y) * .5); }

/* .pdp-grid 是「画廊 + 购买框」的共同祖先 ——
   主图/缩略图/色块的联动全靠它下面的 :has() 规则，所以这个类名不能动。 */
.pdp-grid {
    display: grid; grid-template-columns: 1.1fr 1fr;
    gap: var(--hero-gap); align-items: start;
}
@media (max-width: 900px) {
    .pdp-grid { grid-template-columns: 1fr; gap: 36px; }
}

/* gallery_top 变体：画廊通栏在上，购买框分两栏在下 */
.pdp-hero--top .pdp-grid { grid-template-columns: 1fr; gap: 40px; }
.pdp-hero--top .pdp-gallery { max-width: 940px; margin: 0 auto; width: 100%; }
.pdp-hero--top .pdp-buy--split {
    display: grid; grid-template-columns: 1fr auto; gap: 48px; align-items: end;
}
.pdp-hero--top .pdp-buy__foot { text-align: right; }
@media (max-width: 760px) {
    .pdp-hero--top .pdp-buy--split { grid-template-columns: 1fr; gap: 24px; }
    .pdp-hero--top .pdp-buy__foot { text-align: left; }
}

/* ---- 画廊（主图切换本身是纯 CSS）----
   结构：主图区每个候选图 = .pdp-shot（内含隐藏 radio + img），
        缩略图 = <label for="pdpShotN">。
   input:checked 决定显示哪张 —— 键盘可用、不改 URL、爬虫看得到全部图。

   这一层的【增强部分】（放大镜 / 左右按钮 / 收起滚动条）都挂在
   .pdp-gallery--js、.pdp-gallery--multi 两个类下面，由 /site/pdp-gallery.js 挂上。
   脚本没跑起来 = 完全回到下面这些纯 CSS 规则，功能不缺，只是没有镜头和按钮。 */
.pdp-gallery { margin: 0; }
.pdp-gallery__main {
    position: relative;
    background: var(--media-bg); border: var(--media-border); border-radius: var(--radius-lg);
    /* 源图是正方形（1688 主图基本 750/800 见方），容器用 1:1 才不浪费空间 ——
       4:3 的容器配 contain 会在左右各空出一条，主图白小一圈。
       max-height 兜住单栏（≤900px）的情况，否则 1100px 宽的面包机会撑成一屏高。 */
    aspect-ratio: 1 / 1; max-height: 660px; overflow: hidden;
}
.pdp-shot { display: none; width: 100%; height: 100%; }
.pdp-shot:has(input:checked) { display: block; }
.pdp-shot input {
    position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.pdp-shot img {
    /* 不留内边距：主图直接铺满容器（源图是方图、容器也是方的，正好严丝合缝）。
       contain 只在比例对不上时才让 --media-bg 露出来当底，
       所以这个 padding 去掉之后，正常产品图四周不会再有一圈浅灰。 */
    width: 100%; height: 100%; object-fit: contain; padding: 0;
    /* 主图也不能被拖走：拖出去就是一个幽灵图，放大镜也跟着断 */
    -webkit-user-drag: none; user-select: none;
}
.pdp-gallery__ph {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: var(--ink-3); font-size: 13px;
}

/* ---- 缩略图带：左右各一个切换按钮，中间是可横滚的条 ----
   .pdp-thumbs 是按钮和条的共同祖先，条本身（.pdp-strip）依旧是 label 的父节点 ——
   base.css 里那些 .pdp-strip label:nth-child(N) 的联动规则靠的就是这个，别往中间插层。 */
.pdp-thumbs {
    display: flex; align-items: center; gap: 10px; margin-top: 12px;
}
.pdp-strip {
    flex: 1 1 auto; min-width: 0;      /* min-width:0 是 flex 子项能横向滚动的必要条件 */
    display: flex; gap: 8px;
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    padding-bottom: 4px;
    position: relative;                 /* 脚本量 label.offsetLeft 用 */
    scroll-behavior: smooth;
    /* 这里【没有】scrollbar-width: thin —— 见顶部「滚动条」那段的互斥说明。
       设了它，Chrome 就会连下面三条 ::-webkit-scrollbar 一起忽略。 */
}
.pdp-strip::-webkit-scrollbar { height: 6px; }
/* border 归零：全局滑块用「3px 透明边框」内缩，套在 6px 的槽上会缩成 0 宽。 */
.pdp-strip::-webkit-scrollbar-thumb {
    background: var(--line-strong); border: 0; border-radius: 99px;
}
.pdp-strip::-webkit-scrollbar-track { background: transparent; }
/* safe：缩略图多到装不下时退回左对齐 —— 纯 center 会让左边那几张滚不到（scrollLeft 不能为负） */
.pdp-strip--center { justify-content: safe center; }
.pdp-strip label { display: block; cursor: pointer; flex: 0 0 auto; }
.pdp-strip img {
    /* 同主图：不留内边距，缩略图铺满自己的小框 */
    width: 72px; height: 72px; object-fit: contain; padding: 0;
    background: var(--media-bg); border: var(--media-border);
    border-radius: var(--radius); opacity: .72;
    transition: opacity .16s ease, border-color .16s ease;
    /* 缩略图不能被拖走 —— 浏览器默认会把 img 当成可拖拽元素，
       一拖就是一张幽灵图跟着光标，放大镜和点击切换全乱 */
    -webkit-user-drag: none; user-select: none;
}
.pdp-strip label:hover img { opacity: 1; }

/* ---- 左右切换按钮 ----
   默认 display:none，由脚本挂上 .pdp-gallery--multi 后才出现。
   这样脚本没跑起来时页面上不会留两个点了没反应的死按钮。 */
.pdp-nav {
    display: none;
    flex: 0 0 auto;
    width: 38px; height: 38px; padding: 0;
    align-items: center; justify-content: center;
    font: inherit; font-size: 20px; line-height: 1;
    color: var(--ink);
    background: var(--surface, var(--bg)); border: var(--divider-strong);
    border-radius: var(--radius); cursor: pointer;
    transition: border-color .16s ease, background .16s ease, opacity .16s ease;
}
.pdp-gallery--multi .pdp-nav { display: flex; }
.pdp-nav:hover:not(:disabled) { border-color: var(--ink); background: var(--cell-bg-hover, var(--bg)); }
.pdp-nav:disabled { opacity: .3; cursor: default; }

/* ---- 脚本增强层（.pdp-gallery--js 由 /site/pdp-gallery.js 挂上）----
   增强规则都挂在这个类下面：脚本没跑起来就是纯 CSS 的原始行为 ——
   滚动条还在（无 JS 时它是唯一能碰到后面几张缩略图的途径），按钮不出现。 */
.pdp-gallery--js .pdp-strip { scrollbar-width: none; -ms-overflow-style: none; }
.pdp-gallery--js .pdp-strip::-webkit-scrollbar { display: none; }

/* ---- 放大镜：跟随光标的镜头 ----
   pointer-events: none 是必须的 —— 镜头跟着光标走，能被自己"接住"就会疯狂闪。 */
.pdp-lens {
    display: none;
    position: absolute; z-index: 2; pointer-events: none;
    background-repeat: no-repeat; background-color: var(--media-bg);
    border: var(--divider-strong); border-radius: var(--radius);
}
.pdp-lens.is-on { display: block; }
.pdp-gallery__main.is-zoomable { cursor: zoom-in; }

/* ---- 联动高亮（无 JS）----
   选中态由 .pdp-shot 里的 radio 承载，缩略图和色块的 <label for> 都指向它。
   它们分散在 .pdp-grid 的两个子树上，只能用 :has() 从共同祖先反查 ——
   每个序号一行，这里支持到 12 张（1688 主图一般 ≤10）。
   超出的第 13 张起仍能切换主图，只是缩略图不高亮。 */
.pdp-grid:has(.pdp-shot:nth-child(1) input:checked)  :is(.pdp-strip label:nth-child(1),  .pdp-opt[data-shot="0"])  img,
.pdp-grid:has(.pdp-shot:nth-child(2) input:checked)  :is(.pdp-strip label:nth-child(2),  .pdp-opt[data-shot="1"])  img,
.pdp-grid:has(.pdp-shot:nth-child(3) input:checked)  :is(.pdp-strip label:nth-child(3),  .pdp-opt[data-shot="2"])  img,
.pdp-grid:has(.pdp-shot:nth-child(4) input:checked)  :is(.pdp-strip label:nth-child(4),  .pdp-opt[data-shot="3"])  img,
.pdp-grid:has(.pdp-shot:nth-child(5) input:checked)  :is(.pdp-strip label:nth-child(5),  .pdp-opt[data-shot="4"])  img,
.pdp-grid:has(.pdp-shot:nth-child(6) input:checked)  :is(.pdp-strip label:nth-child(6),  .pdp-opt[data-shot="5"])  img,
.pdp-grid:has(.pdp-shot:nth-child(7) input:checked)  :is(.pdp-strip label:nth-child(7),  .pdp-opt[data-shot="6"])  img,
.pdp-grid:has(.pdp-shot:nth-child(8) input:checked)  :is(.pdp-strip label:nth-child(8),  .pdp-opt[data-shot="7"])  img,
.pdp-grid:has(.pdp-shot:nth-child(9) input:checked)  :is(.pdp-strip label:nth-child(9),  .pdp-opt[data-shot="8"])  img,
.pdp-grid:has(.pdp-shot:nth-child(10) input:checked) :is(.pdp-strip label:nth-child(10), .pdp-opt[data-shot="9"])  img,
.pdp-grid:has(.pdp-shot:nth-child(11) input:checked) :is(.pdp-strip label:nth-child(11), .pdp-opt[data-shot="10"]) img,
.pdp-grid:has(.pdp-shot:nth-child(12) input:checked) :is(.pdp-strip label:nth-child(12), .pdp-opt[data-shot="11"]) img {
    opacity: 1; border-color: var(--accent);
}

/* 尺码：自己的 radio，选中态是普通的兄弟选择器 */
.pdp-opt--pick input:checked ~ span {
    border-color: var(--accent); color: var(--ink); font-weight: 640;
}

/* ---- 购买框（标题 / 价格 / 规格 / MOQ / CTA 同在一个右栏）---- */
.pdp-buy { padding-top: 4px; }
/* 产品名单独放开行距/字距 —— 不能用全局 h1 那套。
   h1 默认继承的是 --leading-display(1.05~1.08) + --tracking-display(-0.022~-0.032em)，
   那是给【单行、短、拉丁文】的大标题调的（首页 hero 那种），三个皮肤都这么定。
   而这里的标题是【中文长句、要折 2~3 行】：1.06 的行距配 42px 的中文字，
   行与行几乎贴上；负字距再把每个字往里挤一道。折行文本需要的是行能分清。 */
.pdp-buy h1 {
    font-size: var(--fs-h2); margin-bottom: 10px;
    line-height: 1.35; letter-spacing: normal;
}
.pdp-buy__model {
    font-family: var(--font-label); font-size: 12px; letter-spacing: .04em;
    color: var(--ink-3); margin-bottom: 14px;
}
.pdp-buy__head { margin-bottom: 4px; }
.pdp-buy__foot { margin-top: 4px; }

.pdp-price { font-size: 26px; font-weight: 680; color: var(--ink); letter-spacing: -.02em; }
/* 这一块的上下留白收得比别处紧：它是购买框里最高、也最"松"的一段，
   主图和右栏要对齐高度时，动这里代价最小（不影响字号和层级） */
.pdp-facts {
    list-style: none; margin: 18px 0 22px; padding: 16px 0;
    border-top: var(--divider-strong); border-bottom: var(--divider-strong);
}
.pdp-facts li {
    display: flex; justify-content: space-between; gap: 20px;
    padding: 7px 0; font-size: 14px;
}
.pdp-facts li span { color: var(--ink-3); }
.pdp-facts li b { color: var(--ink); font-weight: 600; }
.pdp-facts__inline { font-size: 14px; margin: 10px 0 18px; color: var(--ink-3); }
.pdp-facts__inline b { color: var(--ink); }

/* ---- product_specs / table_striped ---- */
.specs-table { width: 100%; border-collapse: collapse; max-width: 860px; }
.specs-table tr { border-bottom: var(--divider); }
.specs-table tr:nth-child(odd) { background: var(--cell-bg-hover, transparent); }
.specs-table th {
    text-align: left; font-weight: 600; color: var(--ink); font-size: 14px;
    padding: 12px 20px 12px 0; width: 34%; vertical-align: top;
}
.specs-table td { padding: 12px 0; color: var(--ink-2); font-size: 14px; }
@media (max-width: 640px) {
    .specs-table th { width: 42%; font-size: 13px; }
    .specs-table td { font-size: 13px; }
}

/* ---- 详情页下半页的节奏 ----
   --section-y 是 92-112px，为首页那种「一屏一主题」的营销区块调的音量。
   属性 → SKU → 详情图 是连续阅读的三块信息，套同一个音量会散成三座孤岛 ——
   每个区块都像刚开场，读的人要不停重新起步。这里单独收紧。 */
.pdp-specs, .pdp-sku { padding: calc(var(--section-y) * .58) 0; }
.pdp-gallery-block { padding: calc(var(--section-y) * .7) 0; }

/* 首屏没有 .section（它自带整幅画廊），所以「.section + .section」那条分隔线
   在首屏和下一块之间不生效，中间会少一道线。这里补上，线宽与其它一致
   （通栏，不是 .wrap 内的短横线）。 */
.pdp-hero + .section { border-top: var(--divider); }

/* 属性 / SKU 的标题是给这块内容起名的，不是拿来占版面的。--fs-h2（编辑风
   clamp 到 42px）+ --head-gap(56px) 是给首页那种整屏区块的音量，压在一张
   39 行的参数表头上，标题比内容还抢眼。按比例各收一档 —— 写 calc 而不是
   写死 px，三个皮肤跟着自己的 token 一起缩放。 */
.pdp-specs .section__head, .pdp-sku .section__head {
    margin-bottom: calc(var(--head-gap) * .5);
}
.pdp-specs .section__head h2, .pdp-sku .section__head h2 {
    font-size: calc(var(--fs-h2) * .7);
}

/* ---- product_specs / table_dense ----
   属性表实际长这样：一堆很短的键值对（演示数据 22-39 项，名称 2-12 字、
   值多数 1-3 字）。一行一对摊在 860px 宽的表里，值后面整片空白 —— 那才是
   "别扭"的来源。这里让它流成【两列】。

   为什么是两列而不是"能塞几列塞几列"（原先 auto-fit/minmax(260px) 桌面落 3 列）：
   一列太浪费（值后面半屏空白），三列每列不到 400px，扣掉 dt 的 88px 之后
   长值只剩 250px 左右，「5.0白蓝、5.0黑蓝、3.0桔色…」那种顿号串还是折三行。
   两列在 1124px 内容宽下每列约 540px，长值能整行放下。

   minmax(0, 1fr) 而不是 1fr：1fr 等于 minmax(auto, 1fr)，auto 最小值会被
   内容顶住压不下去，长串能把格子撑破。写 0 才是真的可以压。

   另外两处在跟别扭较劲：
   1. dt 给固定宽度。flex:0 1 auto 会让每对的名称各占各的宽，整片 dd 的起始
      位置参差不齐，扫读时眼睛每次都要重新找落点。定宽之后 dd 全部左对齐。
   2. 值里的列举（"5.0白蓝、5.0黑蓝、3.0桔色…"）拆成 chip。29 字的顿号串挤在
      一格里的结果是折成三行 —— 既是视觉噪声，又数不出到底几个选项。 */
.specs-dense {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 40px;
    margin: 0;
}
.specs-dense__pair {
    /* 每对独立布局。这是不用真 table 的核心原因：table 里所有名称列
       共用一个宽度，最长的那个（演示数据里 12 字）会把其余列全挤窄。 */
    display: flex; gap: 14px; align-items: baseline;
    padding: 9px 10px 9px 0;
    border-bottom: var(--divider);
    transition: background-color .12s ease;
}
.specs-dense__pair:hover { background: var(--cell-bg-hover); }
.specs-dense dt {
    /* 定宽，不用 flex:0 1 auto —— 见文件头注释第 1 条。
       12 字的「上市年份季节（上市时间）」在自己这 88px 里折两行，只影响它自己。 */
    flex: 0 0 88px;
    color: var(--ink-3); font-size: 13px; line-height: 1.55;
}
.specs-dense dd {
    flex: 1 1 0; min-width: 0; margin: 0;
    color: var(--ink); font-size: 14px; font-weight: 500; line-height: 1.55;
    overflow-wrap: anywhere;    /* 没有空格的串也要能断行 */
}
/* 列举值渲染成的一组 chip（模板按顿号拆，>=3 段才拆） */
.specs-dense__chips { display: flex; flex-wrap: wrap; gap: 5px; }
.specs-dense__chip {
    display: inline-block; padding: 0 8px; font-size: 12px; font-weight: 400;
    line-height: 1.75; color: var(--ink-2);
    border: var(--divider); border-radius: var(--radius);
    background: var(--cell-bg-hover);
}
/* 两列要到 820px 才成立：再窄每列不到 340px，扣掉 dt 就剩 240px，
   长值开始折行 —— 那正是换两列要解决的问题，不如直接掉成一列。 */
@media (max-width: 820px) {
    .specs-dense { grid-template-columns: 1fr; column-gap: 0; }
}
@media (max-width: 620px) {
    .specs-dense dt { flex: 0 0 76px; }
}

/* ---- product_specs / table_grouped ---- */
.specs-grid {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
    margin: 0; background: var(--line-strong);
    border: var(--divider-strong); border-radius: var(--radius-lg); overflow: hidden;
}
.specs-grid__item { background: var(--cell-bg, var(--bg)); padding: 16px 20px; }
.specs-grid dt {
    font-family: var(--font-label); font-size: 11px; letter-spacing: .06em;
    text-transform: uppercase; color: var(--ink-3); margin-bottom: 6px;
}
.specs-grid dd { margin: 0; font-size: 15px; color: var(--ink); font-weight: 600; }
@media (max-width: 900px) { .specs-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .specs-grid { grid-template-columns: 1fr; } }

/* ---- product_gallery ----
   stacked 的宽度收到 800px：源图实测就是 800x800 方图和 790x? 的切片，
   再宽只是放大（750 → 1124 是 1.5 倍，肉眼能看出糊）。800px 是原尺寸。

   不留缝、不要边框：供应商的详情长图是按切片出的，一张图切成七八张上传，
   留缝加边框等于把一张图拆成一摞卡片。方图连着放也成立，所以统一这样。 */
.detail-stack { display: block; max-width: 800px; margin: 0 auto; }
.detail-stack img {
    display: block; width: 100%; height: auto; background: var(--media-bg);
}
.detail-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
.detail-grid figure { margin: 0; }
.detail-grid img {
    width: 100%; background: var(--media-bg);
    border: var(--media-border); border-radius: var(--radius-lg);
}
@media (max-width: 700px) { .detail-grid { grid-template-columns: 1fr; } }

/* ==========================================================================
   区块骨架 —— product_sku（规格与 SKU）
   ========================================================================== */

/* 规格维度行（颜色 / 尺码）—— 现在住在购买框里 */
.pdp-dim {
    display: flex; gap: 14px; align-items: baseline;
    margin-bottom: 13px; flex-wrap: wrap;
}
.pdp-dim__name {
    font-family: var(--font-label); font-size: 12px; letter-spacing: .07em;
    text-transform: uppercase; color: var(--ink-3); min-width: 64px;
}
.pdp-dim__opts { display: flex; gap: 10px; flex-wrap: wrap; }

/* 单个可选项。两种形态：
   .pdp-opt            → <label for="pdpShotN">  颜色，点了切主图
   .pdp-opt--pick      → <label><input radio>    尺码，只做选中态 */
.pdp-opt { display: block; cursor: pointer; position: relative; }
.pdp-opt input {
    position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.pdp-opt img {
    width: 46px; height: 46px; object-fit: contain; padding: 3%;
    background: var(--media-bg); border: var(--media-border);
    border-radius: var(--radius); opacity: .8;
    transition: border-color .16s ease, opacity .16s ease;
}
.pdp-opt span {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; padding: 8px 12px; font-size: 13px;
    border: var(--divider-strong); border-radius: var(--radius); color: var(--ink-2);
    transition: border-color .16s ease, color .16s ease;
}
.pdp-opt:hover img, .pdp-opt:hover span { opacity: 1; border-color: var(--ink-3); }

/* price_table：可滚动的 SKU 明细
   演示数据是一个产品 5-100 个 SKU（均值 30），全展开会把后面三块内容顶到
   三屏以外，所以留内滚动 + 吸顶表头。 */
.sku-scroll {
    max-height: 420px; overflow: auto;
    border: var(--divider-strong); border-radius: var(--radius-lg);
    /* 表头是 sticky 的，滚到边界时那一行的下边框会被容器圆角切掉一点，
       留 1px 内边距让它有地方待。 */
    overscroll-behavior: contain;
}
/* 内层滚动区：槽比整页那条收窄一档 —— 12px 的滑块装在卡片里太占地方 */
.sku-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.sku-scroll::-webkit-scrollbar-thumb { border-width: 2px; }
.sku-table { width: 100%; border-collapse: collapse; }
.sku-table th {
    position: sticky; top: 0; z-index: 1;
    background: var(--surface, var(--bg));
    text-align: left; font-family: var(--font-label); font-size: 11px;
    letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3);
    padding: 12px 18px; border-bottom: var(--divider-strong);
}
.sku-table td {
    padding: 11px 18px; font-size: 14px;
    border-bottom: var(--divider); color: var(--ink-2);
}
.sku-table tbody tr:last-child td { border-bottom: 0; }
/* 30 行以上的数字表，斑马纹比行线更好跟行；hover 再深一档。
   斑马用 token，hover 用 color-mix 现算 —— 深浅两个皮肤下都成立，
   不用为「比 --cell-bg-hover 再深一点」专门加一个变量。
   （不用 box-shadow 做 hover：table-row 上的阴影各浏览器表现不一。） */
.sku-table tbody tr:nth-child(even) { background: var(--cell-bg-hover); }
.sku-table tbody tr:hover { background: color-mix(in srgb, var(--ink) 7%, transparent); }
/* 数字列右对齐 + 等宽数字，个位才竖着对得齐 */
.sku-table__num { text-align: right; font-variant-numeric: tabular-nums; }
.sku-table__price { font-weight: 640; color: var(--ink); }
.sku-table__stock { color: var(--ink-3); }
.sku-table__stock--out { color: var(--danger, #DC2626); }

/* ==========================================================================
   404
   ==========================================================================
   刻意只给「版式」，不给颜色和按钮外观 —— 那些走皮肤 token 和 .btn，
   换一版皮肤这里不用动。

   用 --section-y 而不是拍一个固定 padding：404 是完整页面（有页头页脚），
   留白要跟其它页面一个量级，否则一进来看起来就是个「出错了」的残页。 */
.not-found {
    padding: var(--section-y) 0 calc(var(--section-y) * 1.4);
    max-width: 46ch;
}
.not-found__title {
    margin: 0 0 18px;
    font-family: var(--font-display);
    font-size: clamp(30px, 4.4vw, 46px);
    line-height: var(--leading-display);
    font-weight: var(--weight-display);
    letter-spacing: var(--tracking-display);
    color: var(--ink);
}
.not-found__body {
    margin: 0 0 32px;
    font-size: var(--fs-lede);
    line-height: var(--leading-body);
    color: var(--ink-2);
}

/* ── 询盘表单 ───────────────────────────────────────────────────────
   结构层只做「排列 + 可读性」，颜色/边框/圆角都取皮肤变量。
   不依赖 JS：蜜罐、必填、原生 email 校验都由浏览器完成。 */
.inquiry__inner { max-width: 760px; }
.inquiry__form { display: grid; gap: 14px; }
.inquiry__row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.inquiry__field {
    display: grid; gap: 6px;
    font-size: var(--fs-eyebrow); letter-spacing: var(--tracking-label);
    text-transform: uppercase; color: var(--ink-3);
}
.inquiry__field input,
.inquiry__field textarea {
    width: 100%; font: inherit; letter-spacing: 0; text-transform: none;
    color: var(--ink); background: var(--surface);
    border: var(--divider); border-radius: var(--radius);
    padding: 10px 12px;
}
.inquiry__field input:focus,
.inquiry__field textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.inquiry__form .btn { justify-self: start; margin-top: 4px; }
/* 蜜罐：正常用户看不到；不要用 display:none（部分机器人会跳过隐藏字段） */
.inquiry__trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.inquiry__sent,
.inquiry__error { padding: 12px 14px; margin-bottom: 16px; border-radius: var(--radius); }
.inquiry__sent { background: var(--surface); border: var(--divider); border-left: 3px solid var(--accent); }
.inquiry__error { border: 1px solid #C2410C; color: #C2410C; }
@media (max-width: 640px) { .inquiry__row { grid-template-columns: 1fr; } }

/* ==========================================================================
   新闻 / 文章（1.4）
   —— 列表卡片、分类侧栏、长文排版、相关文章。
   皮肤只改 CSS 变量，所以这些结构样式放 base，三套皮肤自动继承。
   ========================================================================== */
.news-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 260px;
    gap: 40px;
    align-items: start;
}
.news-head { margin-bottom: 20px; }
.news-head h1 { margin: 6px 0 0; }
.news-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 24px; }
.news-chip {
    display: inline-flex; padding: 6px 12px; border: var(--divider);
    border-radius: 999px; font-size: 13px; color: var(--ink-2); text-decoration: none;
}
.news-chip:hover { border-color: var(--ink); color: var(--ink); }
.news-chip.is-active {
    background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}

.news-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
.news-grid--related { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.acard {
    border: var(--divider); border-radius: var(--radius);
    overflow: hidden; background: var(--surface);
}
.acard__link { display: block; height: 100%; text-decoration: none; color: inherit; }
.acard__media {
    margin: 0; aspect-ratio: 16 / 10; overflow: hidden; background: var(--cell-bg-hover);
}
.acard__media img {
    width: 100%; height: 100%; object-fit: cover; display: block;
    transition: transform .25s ease;
}
.acard__link:hover .acard__media img { transform: scale(1.03); }
.acard__body { padding: 14px 16px 18px; }
.acard__meta {
    display: flex; flex-wrap: wrap; gap: 10px; font-size: 12px; color: var(--ink-3);
    text-transform: uppercase; letter-spacing: .05em;
}
.acard__title { margin: 8px 0 6px; font-size: 19px; line-height: 1.35; color: var(--ink); }
.acard__summary { margin: 0 0 10px; font-size: 14px; line-height: 1.6; color: var(--ink-2); }
.acard__more { font-size: 13px; font-weight: 600; color: var(--accent); }
.acard__link:hover .acard__title { text-decoration: underline; text-underline-offset: 3px; }

.news-side { border-top: var(--divider); padding-top: 18px; }
.news-side h3 {
    margin: 0 0 10px; font-size: 12px; text-transform: uppercase;
    letter-spacing: .08em; color: var(--ink-3);
}
.news-side__list { list-style: none; margin: 0; padding: 0; }
.news-side__list li {
    display: flex; justify-content: space-between; gap: 10px; padding: 8px 0;
    border-bottom: var(--divider); font-size: 14px;
}
.news-side__list a { color: var(--ink-2); text-decoration: none; }
.news-side__list a:hover { color: var(--ink); }
.news-side__count { color: var(--ink-3); font-size: 12px; }

.article-head__inner { max-width: 860px; }
.article-head__title { margin: 8px 0 12px; font-size: clamp(28px, 4vw, 44px); line-height: 1.15; }
.article-head__meta { color: var(--ink-3); font-size: 14px; margin: 0 0 24px; }
.article-head__cover { margin: 0; border-radius: var(--radius); overflow: hidden; }
.article-head__cover img { width: 100%; display: block; }

.article-body__inner { max-width: 860px; }
.article-prose { font-size: 17px; line-height: 1.8; color: var(--ink-2); }
.article-prose p { margin: 0 0 1.1em; }
.article-prose h2,
.article-prose h3,
.article-prose h4 { margin: 1.6em 0 .6em; line-height: 1.3; color: var(--ink); }
.article-prose ul,
.article-prose ol { margin: 0 0 1.1em 1.3em; padding: 0; }
.article-prose li { margin: .35em 0; }
.article-prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.article-prose img {
    max-width: 100%; height: auto; display: block; margin: 1.2em 0;
    border-radius: var(--radius);
}
.article-prose blockquote {
    margin: 1.4em 0; padding: .6em 1.2em;
    border-left: 3px solid var(--accent); color: var(--ink-2);
}
.article-prose pre {
    overflow: auto; padding: 12px; border-radius: var(--radius); background: var(--cell-bg-hover);
}
.article-prose table { width: 100%; border-collapse: collapse; margin: 1.2em 0; font-size: 15px; }
.article-prose th,
.article-prose td { border: var(--divider); padding: 8px 10px; text-align: left; }

.article-cta {
    margin: 32px 0 8px; padding: 20px; border-radius: var(--radius);
    background: var(--cell-bg-hover); display: flex; justify-content: center;
}
.article-back {
    display: inline-flex; align-items: center; gap: 6px;
    margin-bottom: 18px; font-size: 14px; font-weight: 600;
    color: var(--ink-2); text-decoration: none;
}
.article-back:hover {
    color: var(--ink); text-decoration: underline; text-underline-offset: 3px;
}
/* 文末三个操作：返回列表 / 返回顶部 / 返回首页。普通链接，不依赖脚本。 */
.article-actions {
    display: flex; flex-wrap: wrap; gap: 10px;
    margin-top: 36px; padding-top: 22px; border-top: var(--divider);
}
.article-actions__btn {
    display: inline-flex; align-items: center; justify-content: center;
    padding: 9px 16px; font-size: 14px; font-weight: 600;
    color: var(--ink-2); text-decoration: none;
    border: var(--divider-strong); border-radius: var(--radius);
    background: var(--surface, var(--bg));
    transition: color .16s ease, border-color .16s ease, transform .16s ease;
}
.article-actions__btn:hover {
    color: var(--ink); border-color: var(--ink); transform: translateY(-1px);
}
@media (hover: none) { .article-actions__btn:hover { transform: none; } }
.article-related { border-top: var(--divider); }
.article-related__title { margin: 0 0 20px; font-size: 22px; }

@media (max-width: 900px) {
    .news-layout { grid-template-columns: 1fr; gap: 28px; }
    .news-grid, .news-grid--related { grid-template-columns: 1fr; }
}
