/*
Theme Name: Door Studio — 挺派 TEENPAI Export Frontend
Description: Single-source design system for the TEENPAI (挺派) B2B entrance-door site. No build step, no CDN, nothing loaded from another domain — brand fonts are subset and self-hosted under fonts/.
Version: 3.1
License: GPL-2.0-or-later
*/

/* token 见 tokens.css（前台与后台共用同一份，不在这里重复定义） */

/* Block-level colour contexts. Add a variant here, never at the call site. */
.section,.page-head,.hero,.contact,.error{background:var(--bg);color:var(--fg)}
.section--alt{--bg:var(--bg-soft);background:var(--bg)}
.site-footer{--fg:var(--on-ink);--fg-muted:var(--on-ink-muted);--rule:var(--rule-dark);--accent:var(--accent-on-dark);background:var(--bg-ink);color:var(--fg)}
.scene{--fg:var(--on-ink);--fg-muted:var(--on-ink-muted);--rule:var(--rule-dark);--accent:var(--accent-on-dark);background:var(--bg-photo);color:var(--fg)}
.sheet{--fg:var(--on-ink);--fg-muted:var(--on-ink-muted);--rule:var(--rule-dark);--accent:var(--accent-on-dark)}
.cell--ink{--fg:var(--on-ink);--fg-muted:var(--on-ink-muted);--rule:var(--rule-dark);--accent:var(--accent-on-dark);background:var(--bg-ink)}

/* ==========================================================================
   2. RESET & BASE
   ========================================================================== */

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

html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  /* 常驻滚动条槽位：弹层锁滚动时布局宽度不变，避免整页元素位移 */
  scrollbar-gutter:stable;
}

body{
  margin:0;
  font-family:var(--font);
  font-size:var(--t-body);
  line-height:var(--lh-body);
  background:var(--bg-page);
  color:var(--ink);
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-wrap:break-word;
}
body.is-locked{overflow:hidden}

h1,h2,h3,h4{
  margin:0;
  font-weight:500;
  line-height:var(--lh-head);
  letter-spacing:var(--track-head);
  /* §9.8: CJK titles must not break mid-word ("入口" splitting in two) */
  word-break:keep-all;
  overflow-wrap:break-word;
}
p{margin:0}
ul{margin:0;padding:0;list-style:none}
a{color:inherit;text-decoration:none}
img{max-width:100%;height:auto;display:block}
button,input,select,textarea{font:inherit;color:inherit}
button{background:none;border:0;padding:0;cursor:pointer}
strong{font-weight:600}
i{font-style:normal}

:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:3px;
  border-radius:var(--r-sm);
}

::selection{background:var(--ink);color:#fff}

/* ==========================================================================
   3. LAYOUT PRIMITIVES
   ========================================================================== */

.shell{
  width:min(var(--max),100%);
  margin-inline:auto;
  padding-inline:var(--pad);
}
.shell--narrow{max-width:calc(var(--max-narrow) + var(--pad) * 2)}
.shell--split{
  display:grid;
  gap:var(--s-8) clamp(32px,5vw,80px);
  grid-template-columns:minmax(0,.85fr) minmax(0,1.15fr);
  align-items:start;
}

.section{padding-block:var(--section-y)}
.section > .shell{min-width:0}

/* ==========================================================================
   4. ATOMS
   ========================================================================== */

.eyebrow{
  display:inline-flex;
  align-items:center;
  gap:var(--s-2);
  font-size:var(--t-label);
  font-weight:600;
  line-height:1;
  letter-spacing:var(--track-caps);
  text-transform:uppercase;
  color:var(--fg-muted);
}
.eyebrow::before{
  content:"";
  width:20px;
  height:1px;
  background:var(--accent);
  flex:none;
}

/* Centred headline: eyebrow → title → one line → link or CTA. */
.head{
  display:grid;
  justify-items:center;
  gap:var(--s-4);
  text-align:center;
  max-width:640px;
  margin:0 auto var(--s-8);
}
.head h2{font-size:var(--t-h2);max-width:20ch}
.head p{font-size:var(--t-small);line-height:var(--lh-body);color:var(--fg-muted)}
.head .text-link,.head .button{margin-top:var(--s-2)}

.text-link{
  display:inline-flex;
  align-items:center;
  gap:var(--s-3);
  font-size:var(--t-small);
  font-weight:600;
  color:var(--fg);
  border-bottom:1px solid var(--rule);
  padding-bottom:var(--s-1);
  transition:border-color var(--dur-fast) var(--ease),gap var(--dur-fast) var(--ease);
}
.text-link b{font-size:var(--t-h3);font-weight:400;line-height:1}
.text-link:hover{border-bottom-color:currentColor;gap:var(--s-4)}

/* One button. --btn-* are the only knobs; variants just set them. */
.button{
  --btn-bg:transparent;
  --btn-fg:var(--fg);
  --btn-line:currentColor;
  display:inline-flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--s-5);
  min-height:52px;
  padding:0 var(--s-6);
  font-size:var(--t-label);
  font-weight:600;
  letter-spacing:.02em;
  background:var(--btn-bg);
  color:var(--btn-fg);
  border:1px solid var(--btn-line);
  border-radius:var(--r-md);
  transition:transform var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease);
}
.button b{font-size:var(--t-h3);font-weight:400;line-height:1}
.button:hover{transform:translateY(-2px)}
.button--solid{--btn-bg:var(--ink);--btn-fg:var(--on-ink);--btn-line:var(--ink)}
.button--ghost{--btn-bg:transparent;--btn-fg:var(--ink);--btn-line:var(--ink)}
.button--invert{--btn-bg:var(--bg-page);--btn-fg:var(--ink);--btn-line:var(--bg-page)}
.button--sm{min-height:40px;padding:0 var(--s-4);gap:var(--s-3);font-size:var(--t-label)}
.button--block{width:100%;justify-content:space-between}

.skip{
  position:absolute;
  left:var(--s-4);
  top:-64px;
  z-index:200;
  padding:var(--s-3) var(--s-5);
  background:var(--ink);
  color:#fff;
  border-radius:var(--r-md);
  transition:top var(--dur-fast) var(--ease);
}
.skip:focus{top:var(--s-4)}

.notice{
  padding:var(--s-2) var(--pad);
  background:var(--bg-ink);
  color:var(--on-ink-muted);
  font-size:var(--t-label);
  letter-spacing:.06em;
  text-align:center;
}

.empty{
  padding:var(--s-8) 0;
  text-align:center;
  color:var(--fg-muted);
  border:1px dashed var(--rule);
  border-radius:var(--r-md);
}

.flag{
  padding:var(--s-3) var(--s-4);
  border-left:2px solid var(--accent);
  background:var(--bg-soft);
  color:var(--muted);
  font-size:var(--t-small);
  margin-bottom:var(--s-6);
}

/* Honeypot: present for bots, removed from the visual and a11y trees. */
.hp{
  position:absolute;
  width:1px;height:1px;
  margin:-1px;padding:0;border:0;
  overflow:hidden;
  clip-path:inset(50%);
  white-space:nowrap;
}

/* ==========================================================================
   5. HEADER / NAV / LANGUAGE / MOBILE SHEET
   ========================================================================== */

/* Solid white, not translucent: backdrop-filter would capture every fixed
   descendant into its own containing block, and the blur costs GPU on the
   low-end Android devices this market actually uses. */
.site-header{
  position:sticky;
  top:0;
  z-index:90;
  height:var(--header-h);
  background:var(--bg-page);
  border-bottom:1px solid var(--rule-light);
  color:var(--ink);
}
.site-header__inner{display:flex;align-items:center;gap:var(--s-5);height:100%}

/* 商标一行。中文站出「挺派」用系统黑体加粗，海外站出「TEENPAI」用 Krona One
   （规则在 functions.php 的 dt_brand_mark）。--font-brand 里只有 Krona 一层定制字，
   汉字取不到字形就自然落到后面的黑体，所以两种语言共用同一条栈。
   Krona 只有一个字重，latin 那档必须 font-weight:400——写粗体浏览器会合成假粗。 */
.brand{display:flex;align-items:center;flex:none;min-width:0}
.brand__mark{font-family:var(--font-brand);font-size:20px;line-height:1;white-space:nowrap;color:var(--fg)}
.brand--zh .brand__mark{font-weight:600;letter-spacing:.1em}
.brand--latin .brand__mark{font-weight:400;letter-spacing:.08em}
/* 品牌名出现在别处时也走同一个入口（各页标题上方那行、页脚版权）。 */
.eyebrow--brand,.footer-copy-brand{font-family:var(--font-brand);font-weight:400;letter-spacing:.06em}

.nav{display:flex;align-items:center;gap:clamp(20px,2.2vw,38px);margin-inline:auto}
.nav a{
  font-size:var(--t-small);
  font-weight:500;
  color:var(--muted);
  padding-block:var(--s-2);
  border-bottom:2px solid transparent;
  transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease);
}
.nav a:hover{color:var(--ink);border-bottom-color:var(--ink)}

.header-actions{display:flex;align-items:center;gap:var(--s-2);margin-left:auto;flex:none}
.lang-btn{
  display:flex;align-items:center;gap:var(--s-2);
  height:40px;padding:0 var(--s-3);
  font-size:var(--t-label);font-weight:700;letter-spacing:.08em;
  color:var(--ink);border-radius:var(--r-md);
  transition:background var(--dur-fast) var(--ease);
}
.lang-btn:hover{background:var(--bg-soft)}
.lang-btn b{font-size:var(--t-small);font-weight:400}

/* 下拉的定位上下文是触发按钮自己那一格，菜单右边缘与按钮右边缘对齐。 */
.lang{position:relative;display:flex;align-items:center}
.lang-menu{
  position:absolute;
  top:calc(100% + 6px);
  right:0;
  width:220px;
  background:var(--bg-page);
  border:1px solid var(--rule-light);
  border-radius:var(--r-md);
  box-shadow:var(--shadow-2);
  padding:var(--s-2);
  display:grid;gap:2px;
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease),visibility 0s var(--dur-fast);
}
.lang-menu.is-open{opacity:1;visibility:visible;transform:none;transition:opacity var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease),visibility 0s}
.lang-menu a{
  display:flex;justify-content:space-between;align-items:center;gap:var(--s-4);
  padding:var(--s-3);border-radius:var(--r-sm);
  font-size:var(--t-small);color:var(--ink);
}
.lang-menu a:hover{background:var(--bg-soft)}
.lang-menu a span:last-child{font-size:10px;font-weight:700;letter-spacing:.1em;color:var(--muted)}
.lang-menu a[aria-current="true"]{color:var(--accent)}
.lang-menu a[aria-current="true"] span:last-child{color:var(--accent)}

.menu-toggle{display:none;width:40px;height:40px;padding:var(--s-3);flex-direction:column;justify-content:center;gap:5px}
.menu-toggle span{display:block;height:1.5px;background:var(--ink);transition:transform var(--dur-fast) var(--ease)}

/* Off-canvas panels must never widen the document. visibility:hidden keeps
   layout, so a rightward translateX(100%) would double document.scrollWidth
   and give every page a phantom horizontal scrollbar. The sheet therefore
   parks upward (negative offsets create no scroll area) and the drawer
   clips its right-sliding panel. */
.sheet{
  position:fixed;
  inset:0;
  z-index:100;
  display:flex;flex-direction:column;
  gap:var(--s-6);
  padding:var(--s-5) var(--pad) calc(var(--s-7) + env(safe-area-inset-bottom));
  background:var(--bg-ink);
  color:var(--fg);
  transform:translateY(-100%);
  visibility:hidden;
  /* visibility must flip instantly on open and only be delayed on close.
     Transitioning it symmetrically leaves the panel computed as hidden during
     the first frame, so script cannot move focus into it. */
  transition:transform var(--dur) var(--ease),visibility 0s var(--dur);
}
.sheet.is-open{transform:none;visibility:visible;transition:transform var(--dur) var(--ease),visibility 0s}
.sheet__top{display:flex;align-items:center;justify-content:space-between}
.sheet__close{width:40px;height:40px;font-size:28px;font-weight:300;line-height:1;color:var(--on-ink)}
.sheet__nav{display:grid;gap:var(--s-1);margin-top:var(--s-6)}
.sheet__nav a{
  font-size:var(--t-h2);
  font-weight:500;
  letter-spacing:var(--track-head);
  line-height:1.25;
  color:var(--on-ink);
  padding-block:var(--s-3);
  border-bottom:1px solid var(--rule-dark);
}
.sheet__foot{margin-top:auto;display:grid;gap:var(--s-4)}
.sheet__foot p{font-size:var(--t-small);color:var(--on-ink-muted)}

/* ==========================================================================
   6. HERO
   ========================================================================== */

.hero{padding-block:var(--s-9) 0;text-align:center}
.hero__head{display:grid;justify-items:center;gap:var(--s-5);max-width:720px}
.hero__head h1{font-size:var(--t-h1);max-width:18ch}
.hero__head p{font-size:var(--t-body);color:var(--fg-muted);max-width:52ch}
.hero__actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--s-3);margin-top:var(--s-2)}

.hero__stage{
  position:relative;
  margin:var(--s-8) 0 0;
  /* 16:9 not 21:9: the concept-door fallback needs enough height to read as a door */
  aspect-ratio:16/9;
  background:var(--bg-soft);
  overflow:hidden;
}
.hero__img{width:100%;height:100%;object-fit:cover;object-position:center 60%}
/* <picture> selects WebP but must not change the existing image layout. */
.responsive-picture{display:contents}

.facts{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--s-5);
  max-width:var(--max);
  margin-inline:auto;
  padding:var(--s-7) var(--pad) var(--s-9);
  text-align:center;
}
.facts li{display:grid;gap:var(--s-2);min-width:0}
.facts strong{font-size:var(--t-h3);font-weight:600;letter-spacing:.06em}
.facts span{font-size:var(--t-label);color:var(--muted);line-height:1.5}

/* Shared image frame used by the factory band. */
.stage{
  position:relative;
  margin:0 0 var(--s-8);
  aspect-ratio:16/9;
  background:var(--bg-page);
  overflow:hidden;
  border-radius:var(--r-md);
}
.stage__img{width:100%;height:100%;object-fit:cover}

/* §11 honesty caption — one definition, reused by every concept visual. */
.stage-note,
.visual__note{
  position:absolute;
  left:var(--s-4);
  bottom:var(--s-4);
  z-index:5;
  padding:var(--s-2) var(--s-3);
  background:rgba(255,255,255,.9);
  color:var(--muted);
  font-size:10px;
  font-weight:600;
  letter-spacing:.1em;
  text-transform:uppercase;
  line-height:1.4;
  border-radius:var(--r-sm);
  max-width:min(280px,70%);
}

/* ==========================================================================
   7. CARDS — one component for home rail, product grid and collection grid
   ========================================================================== */

/* auto-fit, not a fixed column count: the rail must look intentional whether
   the shop has two, three or four products published. */
.rail{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  gap:var(--gap);
}
.grid{
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--gap);
}

.card{
  display:grid;
  grid-template-rows:auto 1fr;
  min-width:0;
  background:var(--bg-page);
  border:1px solid var(--rule);
  border-radius:var(--r-md);
  overflow:hidden;
  transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.card:hover{transform:translateY(-4px);box-shadow:var(--shadow-2);border-color:transparent}
.card[hidden]{display:none}

.card__media{
  position:relative;
  display:block;
  aspect-ratio:3/4;
  background:var(--bg-soft);
  overflow:hidden;
}
.visual__img{width:100%;height:100%;object-fit:cover}

.card__body{
  display:grid;
  grid-template-rows:auto auto 1fr;
  gap:var(--s-1);
  padding:var(--s-5);
  min-width:0;
}
.card__type{font-size:10px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--fg-muted)}
.card__title{font-size:var(--t-h3);letter-spacing:var(--track-head)}
.card__more{align-self:end;justify-self:end;font-size:var(--t-h2);font-weight:300;color:var(--fg-muted);line-height:1}

.card__specs{
  align-self:end;
  display:grid;gap:var(--s-1);
  margin-top:var(--s-3);
  padding-top:var(--s-3);
  border-top:1px solid var(--rule);
  font-size:var(--t-label);
}
.card__specs > span{display:flex;justify-content:space-between;gap:var(--s-3)}
.card__specs i{color:var(--fg-muted)}
.card__specs b{font-weight:600;text-align:right;min-width:0}

/* ==========================================================================
   8. CELLS — numbered text block in a bordered grid.
   Used by: purchase routes, product value props, service promises.
   ========================================================================== */

.cells{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  border-top:1px solid var(--rule);
  border-left:1px solid var(--rule);
}
.cell{
  display:grid;
  grid-template-rows:auto auto 1fr auto;
  gap:var(--s-3);
  min-width:0;
  padding:var(--s-7) var(--s-6);
  border-right:1px solid var(--rule);
  border-bottom:1px solid var(--rule);
  color:var(--fg);
}
.cell__num{font-size:var(--t-label);font-weight:600;letter-spacing:.14em;color:var(--accent)}
.cell__title{font-size:var(--t-h3)}
.cell__desc{font-size:var(--t-small);line-height:var(--lh-body);color:var(--fg-muted)}
.cell .text-link{align-self:end;margin-top:var(--s-3);border-bottom-color:var(--rule)}

/* ==========================================================================
   9. SCENE — full-bleed photographic band with centred copy
   ========================================================================== */

.scene{
  position:relative;
  display:grid;
  align-items:center;
  min-height:600px;
  padding-block:var(--s-10);
  overflow:hidden;
}
.scene__media{position:absolute;inset:0;display:block}
.scene__img{width:100%;height:100%;object-fit:cover}
.scene__veil{
  position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(11,12,14,.62) 0%,rgba(11,12,14,.46) 45%,rgba(11,12,14,.74) 100%);
}
.scene__inner{
  position:relative;z-index:2;
  display:grid;justify-items:center;gap:var(--s-5);
  text-align:center;
  max-width:720px;
  margin-inline:auto;
}
.scene__inner h2{font-size:var(--t-h2);max-width:20ch;color:var(--fg)}
.scene__inner p{font-size:var(--t-body);color:var(--fg-muted);max-width:52ch}

.chips{
  display:flex;
  flex-wrap:wrap;
  justify-content:center;
  gap:var(--s-2);
  margin-top:var(--s-1);
}
.chips li{
  padding:var(--s-2) var(--s-4);
  border:1px solid var(--rule);
  border-radius:999px;
  font-size:var(--t-label);
  font-weight:500;
  letter-spacing:.04em;
  color:var(--fg-muted);
}

/* ==========================================================================
   10. FLOW — four manufacturing steps
   ========================================================================== */

.flow{
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  border-top:1px solid var(--rule);
}
.flow__step{
  display:grid;
  grid-template-rows:auto auto 1fr;
  gap:var(--s-2);
  min-width:0;
  padding:var(--s-6) var(--s-6) 0 0;
  border-right:1px solid var(--rule);
}
.flow__step:last-child{border-right:0;padding-right:0}
.flow__num{font-size:var(--t-label);font-weight:600;letter-spacing:.14em;color:var(--accent)}
.flow__step h3{font-size:var(--t-body)}
.flow__step p{font-size:var(--t-small);line-height:var(--lh-body);color:var(--fg-muted)}

/* ==========================================================================
   11. PAGE HEAD — one component for listing, collection and static pages
   ========================================================================== */

.page-head{padding-block:var(--s-9) var(--s-8);text-align:center;border-bottom:1px solid var(--rule)}
.page-head > div{display:grid;justify-items:center;gap:var(--s-5);max-width:760px;margin-inline:auto}
.page-head h1{font-size:var(--t-h1);max-width:20ch}
.page-head p{font-size:var(--t-small);color:var(--fg-muted);max-width:60ch}

.filters{
  display:flex;
  flex-wrap:wrap;
  gap:var(--s-2);
  margin-bottom:var(--s-7);
}
/* 系列筛选是链接（服务端按系列出列表），不是 JS 按钮：
   禁用脚本的访客与爬虫看到的是同一份内容，当前系列的高亮也不会说谎。 */
.filters a{
  display:inline-flex;
  align-items:center;
  min-height:40px;
  padding:0 var(--s-4);
  border:1px solid var(--rule);
  border-radius:999px;
  font-size:var(--t-label);
  font-weight:600;
  color:var(--muted);
  transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.filters a:hover{color:var(--ink);border-color:var(--ink)}
.filters a.is-active{background:var(--ink);border-color:var(--ink);color:var(--on-ink)}

/* 分页：只有主查询真有多页时才渲染出来，平时不占位。 */
.pager{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:var(--s-4);
  margin-top:var(--s-7);
  font-size:var(--t-label);
}
.pager__link{
  display:inline-flex;
  align-items:center;
  min-height:40px;
  padding:0 var(--s-4);
  border:1px solid var(--rule);
  border-radius:999px;
  font-weight:600;
  color:var(--fg-muted);
  transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease);
}
.pager__link:hover{color:var(--fg);border-color:var(--fg)}
.pager__now{color:var(--fg-muted);font-variant-numeric:tabular-nums}

/* ==========================================================================
   12. PRODUCT / ITEM DETAIL
   ========================================================================== */

/* 首屏必须跟全站一样待在居中的壳里。以前它是通栏出血的：左边那张图直接顶到视口左边缘，
   而下面每个区块都收在 --max 的居中场子里——两套中轴线，怎么看都是"重心偏了"。
   现在它和 .shell 共用同一组宽度与左右留白，图与文字作为一个整体居中。 */
.detail{
  display:grid;
  grid-template-columns:minmax(0,.84fr) minmax(0,1.16fr);
  align-items:stretch;
  width:min(var(--max),100%);
  margin-inline:auto;
  padding-inline:var(--pad);
  background:var(--bg-page);
  color:var(--ink);
}
/* 主图就是产品本身，用竖版——和列表卡片同一口径，老板传一张照片两处都能用。
   4:5 而不是 3:4：3:4 会把首屏撑到 780px 高，右边那点文字根本压不住。 */
.detail__media{
  position:relative;
  display:block;
  margin:0;
  aspect-ratio:4/5;
  overflow:hidden;
}
.detail__side{
  display:grid;
  align-content:center;
  gap:var(--s-4);
  min-width:0;
  padding:var(--s-9) clamp(24px,4vw,64px);
  background:var(--bg-page);
}
.back{justify-self:start;font-size:var(--t-label);font-weight:600;letter-spacing:.06em;color:var(--muted);border-bottom:1px solid var(--rule-light);padding-bottom:var(--s-1)}
.back:hover{color:var(--ink);border-bottom-color:var(--ink)}
.detail__side h1{font-size:clamp(28px,3vw,38px);max-width:16ch}
.detail__side p{font-size:var(--t-body);color:var(--muted);max-width:42ch}
.detail__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-5);margin-top:var(--s-2)}
/* 首屏那四项关键参数：右栏必须有分量才压得住左边那张竖图，否则标题飘在正中、
   上下各空一大块（老板圈的"视觉中心太偏"）。两列小表，取值口径与下面的完整参数表一致。 */
.detail__key{
  margin:var(--s-2) 0 0;
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--s-4) var(--s-5);
  border-top:1px solid var(--rule);
  padding-top:var(--s-5);
}
.detail__key > div{display:grid;align-content:start;gap:var(--s-1);min-width:0}
.detail__key dt{font-size:var(--t-label);letter-spacing:.06em;color:var(--fg-muted)}
.detail__key dd{margin:0;font-size:var(--t-small);font-weight:600;overflow-wrap:anywhere}

.specs__head{display:grid;gap:var(--s-4);align-self:start;position:sticky;top:calc(var(--header-h) + var(--s-5))}
.specs__head h2{font-size:var(--t-h2)}
.specs__head p{font-size:var(--t-small);color:var(--fg-muted)}

.specs{margin:0;display:grid;border-top:1px solid var(--rule)}
.specs__row{
  display:grid;
  grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);
  gap:var(--s-4);
  padding:var(--s-4) 0;
  border-bottom:1px solid var(--rule);
  font-size:var(--t-small);
}
.specs__row dt{color:var(--fg-muted)}
.specs__row dd{margin:0;font-weight:600;min-width:0}
.specs__row .is-tbd{font-weight:400;color:var(--fg-muted)}
/* 「按方案确认」不是数值，不能和真值一样重。首屏以前漏了这条，
   占位话术和真实数值一样黑粗，看着像已确认。 */
.detail__key .is-tbd{font-weight:400;color:var(--fg-muted)}

/* ---- 细节图：三列画廊网格 ----
   说明统一压在图下面，不再左右交替、不再把字悬在图旁的半空里。
   图放进同一比例（3:4）的框，object-fit:contain 完整呈现——细节图 1 常是整机照，
   门就在画面边缘，cover 一裁就把门切掉；留出的空底用 --bg-soft 当衬底。
   没有说明的图只是少一行字，格子照常对齐（.shot--bare 不再需要特殊版式）。 */
.shots{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-5)}
.shot{margin:0}
.shot__media{display:block;aspect-ratio:3/4;border-radius:var(--r-md);overflow:hidden;background:var(--bg-soft)}
.shot__media img{width:100%;height:100%;object-fit:contain;display:block}
.shot__text{margin-top:var(--s-3)}
.shot__text p{margin:0;font-size:var(--t-small);line-height:var(--lh-body);color:var(--fg-muted)}
@media (max-width:860px){.shots{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:620px){.shots{grid-template-columns:minmax(0,1fr)}}

/* ---- 参数分组 ---- */
.specs__group + .specs__group{margin-top:var(--s-7)}
.specs__group-t{
  margin:0 0 var(--s-2);font-size:var(--t-label);font-weight:600;
  letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--fg-muted);
}

/* ==========================================================================
   12b. DOOR DIAGRAMS — 门型立面与开向平面（纯 CSS，站内零外部图形资源）
   ========================================================================== */

/* 立面：门扇留缝+把手，固定扇（侧光/上亮）填玻璃色。
   看图的人要先分清"哪几片会开"——那是报价时最容易说错的地方。 */
.dv{
  display:block;position:relative;box-sizing:border-box;
  aspect-ratio:3/4;padding:8% 11% 11%;
  background:var(--bg-soft);border-radius:var(--r-sm);color:var(--ink);
}
.dv__wall{display:flex;flex-direction:column;height:100%;border-left:3px solid currentColor;border-right:3px solid currentColor}
.dv__transom{flex:0 0 17%;margin-bottom:3px}
.dv__row{display:flex;flex:1;gap:3px}
.dv__part{position:relative;min-width:0;border:1.5px solid currentColor;background:var(--bg-page)}
.dv__part--glass{background:var(--glass)}
.dv__handle{position:absolute;top:43%;width:4px;height:14%;min-width:4px;background:currentColor;border-radius:2px}
/* 合页在洞口外侧、把手在中间门缝边——两片都这样才关得上，单开门也不会画反。
   哪一片的合页在右由模板给 .dv__part--rh 标注，不用 :nth-child(... of S) 那种支持不齐的写法。 */
.dv__hinge{position:absolute;top:14%;bottom:14%;left:-5px;width:5px;border:1.5px solid currentColor;border-left:0;border-radius:0 3px 3px 0}
.dv__handle{right:12%}
.dv__part--rh .dv__hinge{left:auto;right:-5px;border-left:1.5px solid currentColor;border-right:0;border-radius:3px 0 0 3px}
.dv__part--rh .dv__handle{right:auto;left:12%}
.dv__floor{position:absolute;left:6%;right:6%;bottom:8%;height:2px;background:currentColor;opacity:.28}
.dv__tag{
  position:absolute;left:50%;bottom:5%;transform:translateX(-50%);
  font-size:9px;line-height:1;letter-spacing:.04em;color:var(--muted);
  background:var(--bg-page);border:1px solid var(--rule-light);border-radius:999px;padding:2px 5px;white-space:nowrap;
}
.dv__part--glass .dv__tag{background:var(--glass);border-color:var(--glass-line)}

/* 门型选择器：单选按钮 + :checked 兄弟选择器切换，不依赖 JavaScript。
   没有 JS 的访客（目标市场上并不少见）照样能看全部门型。 */
.cfg{display:grid;grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);gap:var(--s-6) var(--s-8);align-items:start;margin-top:var(--s-7)}
.cfg__in{position:absolute;width:1px;height:1px;opacity:0}
.cfg__stage{position:relative;background:var(--bg-page);border:1px solid var(--rule-light);border-radius:var(--r-md);padding:var(--s-5);display:grid;gap:var(--s-4)}
.cfg__view{display:none;grid-template-columns:minmax(0,200px) minmax(0,1fr);gap:var(--s-5);align-items:center}
.cfg__cap{display:grid;gap:4px}
.cfg__cap b{font-size:var(--t-h3);font-weight:600;letter-spacing:var(--track-head)}
.cfg__cap em{font-style:normal;font-size:var(--t-small);color:var(--ink);font-variant-numeric:tabular-nums}
.cfg__cap i{font-style:normal;font-size:var(--t-label);color:var(--fg-muted)}
.cfg__pick{display:flex;flex-wrap:wrap;gap:var(--s-2);align-content:start}
.cfg__chip{
  display:grid;grid-template-columns:34px auto;align-items:center;gap:8px;
  padding:8px 12px 8px 8px;border:1px solid var(--rule);border-radius:var(--r-md);
  cursor:pointer;font-size:var(--t-small);
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.cfg__chip .dv{padding:5% 6%;border-color:currentColor;width:40px;background:var(--bg-soft)}
.cfg__chip .dv__tag{display:none}
.cfg__chip:hover{border-color:var(--ink)}
.cfg__hint{grid-column:1/-1;margin:0;font-size:var(--t-label);color:var(--fg-muted)}

/* 选中的门型：舞台上换图 + 胶囊描边。按 id 成对写死，比 nth-of-type 的索引耦合清楚。 */
#dt-single:checked ~ .cfg__stage .cfg__view--single,
#dt-family:checked ~ .cfg__stage .cfg__view--family,
#dt-pair:checked ~ .cfg__stage .cfg__view--pair,
#dt-sidelight:checked ~ .cfg__stage .cfg__view--sidelight,
#dt-pairwide:checked ~ .cfg__stage .cfg__view--pairwide,
#dt-transom:checked ~ .cfg__stage .cfg__view--transom,
#dt-full:checked ~ .cfg__stage .cfg__view--full{display:grid}
#dt-single:checked ~ .cfg__pick [for="dt-single"],
#dt-family:checked ~ .cfg__pick [for="dt-family"],
#dt-pair:checked ~ .cfg__pick [for="dt-pair"],
#dt-sidelight:checked ~ .cfg__pick [for="dt-sidelight"],
#dt-pairwide:checked ~ .cfg__pick [for="dt-pairwide"],
#dt-transom:checked ~ .cfg__pick [for="dt-transom"],
#dt-full:checked ~ .cfg__pick [for="dt-full"]{border-color:var(--ink);background:var(--bg-page)}
.cfg__in:focus-visible ~ .cfg__pick .cfg__chip:first-child{outline:0}
.cfg__chip:has(.cfg__in:focus-visible),.cfg__in:focus-visible ~ .cfg__pick .cfg__chip:first-child{box-shadow:0 0 0 3px rgba(138,106,69,.25)}

.hand{margin-top:var(--s-9);padding-top:var(--s-7);border-top:1px solid var(--rule)}
.hand__head{max-width:62ch}
.hand__head h3{margin:0 0 var(--s-2);font-size:var(--t-h3);letter-spacing:var(--track-head)}
.hand__head p{margin:0;font-size:var(--t-small);color:var(--fg-muted);line-height:var(--lh-body)}
.hand__list{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:var(--s-5);margin:var(--s-6) 0 0;padding:0;list-style:none}
.hand__list li{display:grid;grid-template-columns:152px minmax(0,1fr);grid-template-rows:auto auto;gap:2px var(--s-4);align-items:center}
.hand__list b{font-size:var(--t-small);font-weight:600}
.hand__d{grid-column:2;font-size:var(--t-label);color:var(--fg-muted);line-height:1.55}
.hand__tip{margin:var(--s-6) 0 0;font-size:var(--t-label);color:var(--fg-muted);max-width:66ch}

/* 开向平面。没有"室内 / 室外 / 你站这里"三个参照，左内开和右外开看上去
   就是同一根斜线——所以参照必须画在图里，不能只写在旁边。
   弧线用整圆裁四分之一：圆心落在合页上、半径=门扇长，叶尖正好压在弧上。 */
.hv{
  grid-row:1/3;position:relative;display:block;box-sizing:border-box;
  aspect-ratio:1/1;color:var(--ink);
  /* 上下两半直接分色：不读字也知道哪边是屋里。这是这四个图能不能看懂的关键。 */
  background:linear-gradient(to bottom,var(--bg-soft) 0 50%,var(--bg-page) 50% 100%);
  border:1px solid var(--rule-light);border-radius:var(--r-sm);overflow:hidden;
}
/* 标签垫一块底色：门扇和弧线会扫过这两行字，没有底就会叠成一团糊。 */
.hv__zone{position:absolute;left:8px;padding:2px 4px;border-radius:2px;background:var(--bg-page);
  font-size:10px;line-height:1;letter-spacing:.04em;color:var(--muted)}
.hv__zone--in{top:8px;background:var(--bg-soft)}
.hv__zone--out{bottom:8px;color:var(--ink);font-weight:600}
.hv__geo{position:absolute;inset:0}
.hv__wall{position:absolute;top:calc(50% - 3px);height:6px;background:currentColor;border-radius:1px}
.hv__wall--l{left:0;width:24%}
.hv__wall--r{right:0;width:24%}
.hv__leaf{position:absolute;left:24%;top:calc(50% - 2px);width:52%;height:4px;background:currentColor;border-radius:2px;transform-origin:0 50%;transform:rotate(-42deg)}
.hv__hinge{position:absolute;left:calc(24% - 5px);top:calc(50% - 5px);width:10px;height:10px;border-radius:50%;background:currentColor}
/* 整圆裁四分之一：圆心落在合页、半径=门扇长，叶尖正好压在弧上。
   inset() 的顺序是 上 右 下 左 —— 写反就会裁到左上象限，弧线看着像画歪了。
   盒子左上角 = 合页(24%,50%) 减半径(52%) = (-28%,-2%)，边长 104%。 */
.hv__arc{position:absolute;left:-28%;top:-2%;width:104%;height:104%;border:2px dashed currentColor;border-radius:50%;opacity:.6;clip-path:inset(0 0 50% 50%)}
.hv--lo .hv__leaf,.hv--ro .hv__leaf{transform:rotate(42deg)}
.hv--lo .hv__arc,.hv--ro .hv__arc{clip-path:inset(50% 0 0 50%)}
/* 左右开只镜像几何层：整块镜像会把"室内/室外"两行字翻成反字 */
.hv--ri .hv__geo,.hv--ro .hv__geo{transform:scaleX(-1)}

/* ==========================================================================
   13. PROSE — block-editor output on static pages and items
   ========================================================================== */

.prose{
  max-width:var(--max-narrow);
  margin-inline:auto;
  font-size:var(--t-body);
  line-height:1.8;
  color:var(--ink);
}
.prose > * + *{margin-top:var(--s-5)}
.prose h2{font-size:clamp(24px,2.2vw,32px);margin-top:var(--s-8);line-height:1.3}
.prose h3{font-size:var(--t-h3);margin-top:var(--s-7)}
.prose h4{font-size:var(--t-body);margin-top:var(--s-6)}
.prose li + li{margin-top:var(--s-2)}
.prose a{color:var(--ink);border-bottom:1px solid var(--rule-light)}
.prose a:hover{border-bottom-color:var(--ink)}
.prose ul,.prose ol{padding-left:var(--s-5)}
.prose ul{list-style:disc}
.prose ol{list-style:decimal}
.prose blockquote{
  margin:var(--s-6) 0;
  padding-left:var(--s-5);
  border-left:2px solid var(--accent);
  color:var(--muted);
}
.prose figure{margin:var(--s-7) 0}
.prose figcaption{margin-top:var(--s-2);font-size:var(--t-label);color:var(--muted)}
.prose img{border-radius:var(--r-md)}
.prose hr{margin:var(--s-8) 0;border:0;border-top:1px solid var(--rule-light)}
.prose table{width:100%;border-collapse:collapse;font-size:var(--t-small)}
.prose th,.prose td{padding:var(--s-3);text-align:left;border-bottom:1px solid var(--rule-light)}
.prose th{font-weight:600;font-size:var(--t-label);letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.prose .alignwide{margin-inline:calc(var(--pad) * -1)}

/* ==========================================================================
   14. CONTACT
   ========================================================================== */

.contact__info{display:grid;gap:var(--s-4);align-content:start}
.contact__info h2{font-size:var(--t-h2)}
.contact__desc{font-size:var(--t-small);color:var(--muted)}
.contact__form{min-width:0}

.contact-lines{display:grid;gap:var(--s-2);font-size:var(--t-small);color:var(--fg-muted)}
.contact-lines a{border-bottom:1px solid var(--rule)}
.contact-lines a:hover{color:var(--fg);border-bottom-color:currentColor}

/* ==========================================================================
   15. ERROR
   ========================================================================== */

.error{padding-block:clamp(96px,16vh,180px);text-align:center}
.error__inner{display:grid;justify-items:center;gap:var(--s-5)}
.error h1{font-size:var(--t-h1)}
.error p{font-size:var(--t-small);color:var(--fg-muted);max-width:48ch}
.error__actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--s-6);margin-top:var(--s-3)}

/* ==========================================================================
   16. FOOTER
   ========================================================================== */

.site-footer{padding-block:var(--s-10) var(--s-7);border-top:1px solid var(--rule)}
.footer-cta{
  display:grid;justify-items:center;gap:var(--s-5);
  text-align:center;
  padding-bottom:var(--s-9);
  margin-bottom:var(--s-8);
  border-bottom:1px solid var(--rule);
}
.footer-cta h2{font-size:var(--t-h2);max-width:22ch}

.footer-grid{
  display:grid;
  grid-template-columns:minmax(0,1.6fr) repeat(3,minmax(0,1fr));
  gap:var(--s-7) var(--s-6);
  padding-bottom:var(--s-8);
}
.footer-brand{display:grid;gap:var(--s-4);align-content:start;min-width:0}
.footer-brand p{font-size:var(--t-small);color:var(--fg-muted);max-width:34ch}

.footer-col{display:grid;gap:var(--s-3);align-content:start;min-width:0}
.footer-col h3{font-size:10px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--on-ink-muted)}
.footer-col a{font-size:var(--t-small);color:var(--on-ink);padding-block:2px}
.footer-col a:hover{color:#fff;text-decoration:underline;text-underline-offset:3px}
.footer-col .text-link{margin-top:var(--s-3)}

.footer-bottom{
  display:flex;flex-wrap:wrap;justify-content:space-between;gap:var(--s-3);
  padding-top:var(--s-5);
  border-top:1px solid var(--rule);
  font-size:var(--t-label);
  color:var(--on-ink-muted);
}

.mobile-bar{display:none}

/* ==========================================================================
   17. INQUIRY DRAWER + FORM
   ========================================================================== */

/* 询价抽屉。整块删掉重做过一次。
   旧版叠了三层动画：面板从右边滑进来、遮罩淡入、内容再错峰浮现。
   每一层都能自己出毛病——内容相对面板横向漂移、收尾时合成层销毁文字重新栅格化、
   540px 配长尾减速曲线看着"停不下来"。而真正的抖动根因根本不在动画里：
   打开瞬间 overlay() 把焦点移进抽屉，浏览器顺手把页面滚了几十像素
   （body 被 is-locked 锁住后依然能被程序滚动），背景在抽屉后面一路滑 = 抖。

   重做后的规则：**几何一律不动，只让遮罩这一层纯色淡入。**
   面板瞬间到位，表单立刻可填；背景压暗用 --dur-fast 过渡，柔一下就行。
   没有位移，就没有可以错位的一层；文字不参与动画，也就不存在收尾重新栅格化。 */
/* 入场动画只有一层：面板从右边滑进来 + 遮罩同时淡入，同一个时长同一条曲线。
   规格来自业界通行做法，不是拍脑袋：NN/g 建议显著的界面变化用 200–300ms、
   大位移上限 ~400ms；Material 的 dialog 是 235ms + cubic-bezier(.4,0,.2,1)，
   入场配 decelerate。这里用 --dur(.3s) + --ease-enter。
   刻意仍然只有这两样在动：内容不做二次动画（会相对面板漂移、收尾还会重新栅格化），
   也不写 will-change。关闭时 visibility 延后一拍，让滑出的那一下能看见。 */
.drawer{position:fixed;inset:0;z-index:140;pointer-events:none;visibility:hidden;transition:visibility 0s var(--dur)}
.drawer.is-open{pointer-events:auto;visibility:visible;transition:none}
.drawer__scrim{
  position:absolute;inset:0;
  background:var(--scrim);
  opacity:0;
  transition:opacity var(--dur) var(--ease-enter);
}
.drawer.is-open .drawer__scrim{opacity:1}
.drawer__panel{
  position:absolute;top:0;right:0;
  width:min(540px,100%);
  height:100%;
  overflow-y:auto;
  overscroll-behavior:contain;
  padding:var(--s-7) clamp(20px,4vw,48px) var(--s-9);
  background:var(--bg-page);
  color:var(--ink);
  box-shadow:var(--shadow-2);
  transform:translateX(100%);
  transition:transform var(--dur) var(--ease-enter);
}
.drawer.is-open .drawer__panel{transform:none}

.drawer__head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-4)}
.drawer__head h2{font-size:clamp(24px,2.4vw,32px);margin-top:var(--s-3)}
.drawer__close{width:40px;height:40px;flex:none;font-size:28px;font-weight:300;line-height:1;color:var(--muted);border:1px solid var(--rule-light);border-radius:var(--r-md)}
.drawer__close:hover{color:var(--ink);border-color:var(--ink)}
.drawer__intro{font-size:var(--t-small);color:var(--muted);margin-block:var(--s-4) var(--s-6)}

.inquiry-form{display:grid;gap:var(--s-4)}
.inquiry-form label{display:grid;gap:var(--s-2);min-width:0}
.inquiry-form label > span{font-size:var(--t-label);font-weight:600;letter-spacing:.06em;color:var(--muted)}
.inquiry-form input,
.inquiry-form select,
.inquiry-form textarea{
  width:100%;
  min-width:0;
  padding:var(--s-3) var(--s-4);
  background:var(--bg-page);
  border:1px solid var(--rule-light);
  border-radius:var(--r-md);
  font-size:var(--t-body);
  transition:border-color var(--dur-fast) var(--ease);
}
.inquiry-form input:hover,.inquiry-form textarea:hover{border-color:var(--muted)}
.inquiry-form input:focus,.inquiry-form textarea:focus{border-color:var(--ink)}
.inquiry-form textarea{resize:vertical;min-height:112px;line-height:1.6}
.inquiry-form ::placeholder{color:var(--muted);opacity:1}
.form-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-4)}
.check-row{grid-template-columns:auto minmax(0,1fr);align-items:start}
.check-row input{width:18px;height:18px;margin-top:2px;accent-color:var(--ink)}
.check-row span{font-size:var(--t-label);line-height:1.6;color:var(--muted)}
.check-row a{border-bottom:1px solid var(--rule-light)}
.inquiry-form .button{margin-top:var(--s-2)}
.inquiry-form small{font-size:var(--t-label);line-height:1.6;color:var(--muted)}

.form-status{
  padding:var(--s-4);
  border-radius:var(--r-md);
  font-size:var(--t-small);
  margin-bottom:var(--s-4);
}
.form-status--ok{background:var(--ok-bg);border:1px solid var(--ok-line);color:var(--ok-fg)}
.form-status--bad{background:var(--bad-bg);border:1px solid var(--bad-line);color:var(--bad-fg)}
/* 提交失败后除了顶上那段汇总，错在哪一格里也要就地写出来：
   客户视线停在刚填的那一格，不会先回到页面上方找原因。 */
.form-status p{margin:0}
.form-status__list{margin:var(--s-2) 0 0;padding-left:var(--s-5);display:grid;gap:var(--s-1)}
.inquiry-form .is-invalid{border-color:var(--bad-line);background:var(--bad-bg)}
.inquiry-form .is-invalid:focus{border-color:var(--bad-dot)}
.field-err{font-style:normal;font-size:var(--t-label);line-height:1.5;color:var(--bad-fg)}
/* 同意那一格是「复选框 + 文字」两列，错误提示要落在文字下面，不是复选框下面。 */
.check-row .field-err{grid-column:2}

/* ==========================================================================
   18. CONCEPT DOOR VISUAL — pure CSS product stand-in.
   Rendered whenever a product has no uploaded photo. Studio-lit on purpose:
   a light wall with a dark slab reads as a product shot and keeps every
   caption legible, which the previous dark-on-dark version did not.
   ========================================================================== */

.door-art{
  position:absolute;inset:0;
  display:block;
  overflow:hidden;
  background:linear-gradient(170deg,#fbfbfc 0%,#eceef1 58%,#e0e2e6 100%);
}
.door-art__wall{
  position:absolute;inset:0;
  background:radial-gradient(ellipse 60% 45% at 50% 22%,rgba(255,255,255,.95),transparent 70%);
}
.door-art__floor{
  position:absolute;left:0;right:0;bottom:0;height:22%;
  background:linear-gradient(180deg,#dcdfe4,#cfd3d9);
  border-top:1px solid rgba(11,12,14,.07);
}
.door-art__portal{
  position:absolute;
  width:26%;height:74%;
  left:50%;bottom:18%;
  transform:translateX(-50%);
  filter:drop-shadow(0 20px 34px rgba(11,12,14,.22));
}
.door-art__frame{position:absolute;inset:-4% -5%;background:#1c1d20;border-radius:1px}
.door-art__slab{
  position:absolute;inset:0;
  background:linear-gradient(100deg,#3a3b3f 0%,#232427 52%,#191a1d 100%);
  box-shadow:inset 1px 0 rgba(255,255,255,.09),inset -1px 0 rgba(0,0,0,.55);
}
.door-art__line{position:absolute;background:rgba(255,255,255,.13)}
.door-art__line--v{width:1px;height:74%;right:20%;top:13%}
.door-art__line--h{height:1px;width:76%;left:12%;bottom:22%}
.door-art__handle{
  position:absolute;right:11%;top:34%;
  width:6px;height:30%;
  border-radius:4px;
  background:linear-gradient(90deg,#8a6a45,#d8bd97 50%,#6d5233);
  box-shadow:0 2px 8px rgba(0,0,0,.45);
}
.door-art__lock{
  position:absolute;right:8%;top:44%;
  width:16px;height:42px;
  border-radius:5px;
  background:linear-gradient(140deg,#0d0e10,#2b2c2f 55%,#08090a);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.07);
}
.door-art__shadow{
  position:absolute;
  width:34%;height:5%;
  left:50%;bottom:14%;
  transform:translateX(-50%);
  background:rgba(11,12,14,.2);
  filter:blur(18px);
}
.door-art__side,.door-art__top{display:none}

/* wide "system" opening: side panel + header, used for hero / scene / detail.
   It must read as an entrance inside 16:9 and 21:9 containers, so the portal
   is taller and wider than the single-door variant. */
.door-art--system .door-art__portal{width:44%;height:84%;bottom:12%}
.door-art--system .door-art__side{
  display:block;position:absolute;left:0;top:0;bottom:0;width:26%;
  background:linear-gradient(90deg,#2b2c2f,#3a3b3f);
  border-right:1px solid rgba(0,0,0,.6);
}
.door-art--system .door-art__top{
  display:block;position:absolute;left:0;right:0;top:0;height:12%;
  background:linear-gradient(#34353a,#26272a);
  border-bottom:1px solid rgba(0,0,0,.6);
}
.door-art--system .door-art__slab,
.door-art--system .door-art__side{top:13%}
.door-art--system .door-art__shadow{width:50%;bottom:9%}

/* slab tones — set by the _concept ACF field, or cycled by index in yd_map() */
.door-art--bronze .door-art__slab,
.door-art--bronze .door-art__side{background:linear-gradient(100deg,#5b4a39,#7d6248 45%,#372e28)}
.door-art--sand .door-art__slab,
.door-art--sand .door-art__side{background:linear-gradient(100deg,#c4b8a4,#9a8d7c 45%,#6f665c)}
.door-art--olive .door-art__slab,
.door-art--olive .door-art__side{background:linear-gradient(100deg,#4a4c42,#2f322b 60%,#1d1f1a)}
.door-art--silver .door-art__slab,
.door-art--silver .door-art__side{background:linear-gradient(100deg,#9a9c9e,#6c6f72 50%,#3e4043)}
.door-art--umber .door-art__slab,
.door-art--umber .door-art__side{background:linear-gradient(100deg,#554337,#3a302a 58%,#211d1a)}

/* ==========================================================================
   19. MOTION
   Opt-in: .reveal is added by script, so a JS failure leaves content visible.
   ========================================================================== */

.js .reveal{opacity:0;transition:opacity var(--dur-slow) var(--ease) var(--rd,0ms)}
.js .reveal.in-view{opacity:1}

/* Stagger is expressed in CSS against the grid children, so the script only
   has to add one class and never compute an index. */
.rail .reveal:nth-child(2),.grid .reveal:nth-child(2),.cells .reveal:nth-child(2),
.flow .reveal:nth-child(2){--rd:80ms}
.rail .reveal:nth-child(3),.grid .reveal:nth-child(3),.cells .reveal:nth-child(3),
.flow .reveal:nth-child(3){--rd:160ms}
.cells .reveal:nth-child(4),.flow .reveal:nth-child(4){--rd:240ms}

/* ==========================================================================
   20. RESPONSIVE
   Breakpoints are literals: CSS custom properties are not usable in
   @media queries, so these three values are documented rather than tokenised.
   ========================================================================== */

@media (max-width:1080px){
  /* 手机上图文上下堆叠，图恢复通栏出血——窄屏没有"两条中轴线"的问题，满宽反而更透气 */
  .detail{grid-template-columns:1fr;max-width:none;padding-inline:0}
  .detail__media{aspect-ratio:16/10}
  .detail__side{padding:var(--s-8) var(--pad)}
  .shell--split{grid-template-columns:minmax(0,1fr)}
  .specs__head{position:static}
  .footer-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .flow{grid-template-columns:repeat(2,minmax(0,1fr))}
  .flow__step:nth-child(2){border-right:0;padding-right:0}
  .flow__step{border-bottom:1px solid var(--rule);padding-bottom:var(--s-5)}
  .flow__step:last-child{border-bottom:0}
  /* 门型选择器：两列时左栏在 1080 下只剩 ~330px，扣掉卡片内边距还要放 200px 立面图，
     说明文字被挤到一列一个字（老板截图里的"错乱"）。窄屏一律上下堆叠，
     并把舞台封顶——不封顶的话 1000px 上会出现一张左半边有字、右半边全空的大卡。
     宽度必须写死 min(100%,…)：光有 max-width + auto 外边距，网格项会缩到内容宽，
     514 下整张卡只剩 240px，图也跟着变小。 */
  .cfg{grid-template-columns:minmax(0,1fr);gap:var(--s-6)}
  .cfg__stage{width:min(100%,var(--max-narrow));margin-inline:auto}
  .cfg__cap{max-width:44ch}
}

@media (max-width:860px){
  :root{--header-h:56px;--section-y:clamp(64px,11vw,88px)}
  .nav{display:none}
  .menu-toggle{display:flex}
  .header-cta{display:none}
  .hero{padding-block:var(--s-8) 0}
  .hero__stage,.stage{aspect-ratio:4/3}
  .facts{gap:var(--s-4);padding-block:var(--s-6) var(--s-8)}
  .cells{grid-template-columns:minmax(0,1fr)}
  .cell{padding:var(--s-6) var(--s-5)}
  .scene{min-height:520px;padding-block:var(--s-9)}
  .drawer__panel{width:100%;padding-top:var(--s-6)}
}

@media (max-width:620px){
  :root{--pad:20px}
  .rail{
    display:flex;
    gap:var(--s-3);
    margin-inline:calc(var(--pad) * -1);
    padding-inline:var(--pad) var(--s-2);
    overflow-x:auto;
    scroll-snap-type:x mandatory;
    scrollbar-width:none;
  }
  .rail::-webkit-scrollbar{display:none}
  .rail .card{min-width:74%;scroll-snap-align:start}
  .grid{grid-template-columns:minmax(0,1fr)}
  .card__media{aspect-ratio:4/5}
  .facts{grid-template-columns:minmax(0,1fr);text-align:left;gap:var(--s-3)}
  .facts li{grid-template-columns:56px minmax(0,1fr);align-items:baseline;gap:var(--s-3);padding-block:var(--s-3);border-top:1px solid var(--rule-light)}
  .hero__actions .button,.error__actions .button{flex:1 1 auto}
  .flow{grid-template-columns:minmax(0,1fr)}
  .flow__step{border-right:0;padding-right:0}
  .specs__row{grid-template-columns:minmax(0,1fr);gap:var(--s-1)}
  /* 手机上舞台已是通栏，但 200px 定宽图 + 说明并排时说明只剩 ~85px，还是会竖排。
     按比例分栏：图和说明一起缩，谁也不被挤成一列一个字，卡片右半也不会空一块。 */
  .cfg__view{grid-template-columns:minmax(0,42%) minmax(0,1fr);gap:var(--s-4)}
  .detail__actions{gap:var(--s-4)}
  .form-row{grid-template-columns:minmax(0,1fr)}
  .footer-grid{grid-template-columns:minmax(0,1fr);gap:var(--s-6)}
  .footer-bottom{flex-direction:column}
  .site-footer{padding-bottom:calc(var(--s-8) + 56px + env(safe-area-inset-bottom))}
  .mobile-bar{
    display:grid;
    grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);
    position:fixed;left:0;right:0;bottom:0;z-index:80;
    padding-bottom:env(safe-area-inset-bottom);
    background:#fff;
    border-top:1px solid var(--rule-light);
    box-shadow:0 -8px 28px rgba(11,12,14,.09);
  }
  .mobile-bar a,.mobile-bar button{min-height:54px;font-size:var(--t-label);font-weight:600;display:grid;place-items:center}
  .mobile-bar a{color:var(--ink)}
  .mobile-bar button{background:var(--ink);color:#fff}
}

/* 420 是本轮新加的一档：329px 窗口实测按比例分栏后说明只剩 114px，
   "单开 + 侧光 + 上亮"要断三行。卡片本身已经这么窄，图在上、说明通栏在下才是能读的长度。 */
@media (max-width:420px){
  .cfg__view{grid-template-columns:minmax(0,1fr);gap:var(--s-3)}
  .cfg__view .dv{width:min(150px,52%)}
}

@media (prefers-reduced-motion:reduce){
  /* Zero the duration tokens — no per-rule !important overrides needed,
     because every transition already reads from them. */
  :root{--dur-fast:0s;--dur:0s;--dur-slow:0s}
  html{scroll-behavior:auto}
  .js .reveal{opacity:1}
  .button:hover,.card:hover{transform:none}
  .text-link:hover{gap:var(--s-3)}
}

@media print{
  .site-header,.mobile-bar,.drawer,.notice,.hero__actions{display:none}
}
