/* Project-specific UI overrides. */
/* Base backdrop color lives on <html> only. #synchronized-background is a
   position:fixed z-index:-1 canvas, which the CSS paint order draws *below*
   an in-flow body background — so an opaque body would cover the whole
   textured background layer. Keep body transparent so the canvas shows. */
html {
  background: #151515;
}
body {
  background: transparent;
}

/* Keep the reference-style canvas behind the existing VGen particle layers,
   text, and UI. The existing case pin keeps its higher z-index unchanged. */
#synchronized-background {
  z-index: 0;
  mix-blend-mode: normal;
}

/* The original PL canvas was intentionally below the old Vgl3d background.
   Keep it above the new reference stage so the existing particle field stays
   visible while the page text remains in the normal scroll layer. */
#pl-canvas {
  z-index: 1 !important;
  mix-blend-mode: screen;
}

#synchronized-cursor-line {
  z-index: 100;
}

@media (pointer: coarse), (prefers-reduced-motion: reduce) {
  #synchronized-cursor-line {
    display: none;
  }
}
.cookie-notice {
  display: none !important;
}

/* Headings use Montserrat (@font-face with the Hero block below), 2026-10-23. */
/* ---- Hero 标语 "AI makes it possible. / We make it market-ready." ----
   2026-10 第二轮:Christine 嫌立体挤出 + 衬线斜体不好读,参考 Canva 的 Garet
   (同一款几何无衬线,细 + 粗,平面字)。用开源的 Montserrat 可变字体(OFL,latin
   子集,见 fonts/Montserrat-OFL.txt):对应 Garet Book / Heavy,前段 400、后段 800,
   纯白平面字,不再加载 depth-text.js。两段各占一行(后段 block + nowrap)。 */
@font-face {
  font-family: "Montserrat";
  src: url("/fonts/Montserrat-Variable-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
.vgl-hero__title {
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif !important;
  font-weight: 400 !important;
  letter-spacing: -0.02em !important;
  line-height: 1.1 !important;
  max-width: none !important;
  font-size: min(5.2vw, 5.4rem) !important; /* 后段 800 约 13.4em 宽 → 桌面约占 70vw */
}
.vgl-hero__title .vgl-hero__title-accent {
  display: block;
  font-weight: 800;
  white-space: nowrap;
}
@media (max-width: 767px) {
  /* 手机:按 vw 缩放 + 不换行,后段两侧各留 ~20px。 */
  .vgl-hero__title {
    font-size: 6.5vw !important;
    white-space: nowrap;
  }
}

/* Font-C-compatible 400 weight for the compact utility navigation. */
@font-face {
  font-family: "Font-C";
  src: url("/fonts/PPNeueMontreal-Regular.woff2") format("woff2"),
       url("/fonts/PPNeueMontreal-Regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}


/* ---- Hero CTA: liquid-glass "Discuss a Project" button ---- */
/* Same technique as the reactbits GlassSurface component (SVG displacement
   filter -> backdrop-filter), ported to vanilla JS/CSS for this static page.
   Chrome/Edge get the refractive glass; Safari/Firefox (no SVG filters in
   backdrop-filter) fall back to a plain frosted look via .is-svg gating. */
.hero-glass-cta {
  position: relative;
  display: inline-flex;
  margin-top: clamp(1.8rem, 3vw, 2.6rem);
  pointer-events: auto;
}
.hero-glass-cta__defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}
.hero-glass-cta__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1.05rem 2.3rem;
  border-radius: 999px;
  color: #fff;
  text-decoration: none;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  border: 1px solid rgba(255, 255, 255, 0.35);
  background: rgba(255, 255, 255, 0.07);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.4),
    inset 0 0 14px rgba(255, 255, 255, 0.12),
    0 8px 30px rgba(0, 0, 0, 0.35);
  transition: transform 0.35s cubic-bezier(0.215, 0.61, 0.355, 1),
    background 0.35s ease, box-shadow 0.35s ease;
}
.hero-glass-cta.is-svg .hero-glass-cta__link {
  backdrop-filter: url(#hero-glass-filter) saturate(1.4);
}
.hero-glass-cta__link:hover {
  transform: translateY(-2px);
  background: rgba(255, 255, 255, 0.13);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.5),
    inset 0 0 18px rgba(255, 255, 255, 0.18),
    0 12px 40px rgba(128, 82, 255, 0.4);
}
.hero-glass-cta__link:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 4px;
}
.hero-glass-cta__text::after {
  content: "\2197";
  display: inline-block;
  margin-left: 0.6em;
  font-size: 1.1em;
  transition: transform 0.25s ease;
}
.hero-glass-cta__link:hover .hero-glass-cta__text::after {
  transform: translate(2px, -2px);
}

/* ---- Section titles: unify size + use the Hero (Montserrat) font ---- */
/* 各区块标题统一字号(clamp 响应式,桌面≈48px,与 Our DNA 一致)并统一用 Hero
   的 Montserrat 字体(2026-10-23 起全站标题由 ClashDisplay 改为 Montserrat,对应 Christine
   给的 Garet 参考),保持品牌一致。Hero 主标题单独设置,此处不含它。 */
.landing__title,
.introduction__title,
.manifesto__title,
.js-team-section h2,
.engagement__title,
.section.footer h2 {
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif !important;
  font-weight: 700 !important;
  font-size: clamp(30px, 3.5vw, 48px) !important;
  line-height: 1.12 !important;
  letter-spacing: normal !important;
}

/* ---- Nav: remove redundant section index; stack main nav top-right ---- */
/* The right-side section index (Our DNA / V-Power / …) duplicates the nav. */
.line-sidebar {
  display: none !important;
}

@media (min-width: 768px) {
  /* let the vertical menu extend below the header bar without clipping */
  .header,
  .header__inner {
    overflow: visible;
  }
  /* main nav becomes a simple vertical list in the top-right corner */
  .nav.js-nav {
    position: absolute !important;
    top: 1.55rem; /* 与左侧 logo 顶部对齐 */
    left: auto !important;
    right: 3.2vw;
    width: auto !important;
  }
  .nav__list.js-nav-list {
    display: flex !important;
    flex-direction: column !important;
    align-items: flex-start;
    gap: 0;
  }
  .nav__item {
    margin: 0 !important;
    padding: 0 !important;
  }
  /* Discuss a Project 主按钮:与上面的导航拉开间距、下移 */
  .nav__cta {
    margin-top: 1.7rem !important;
    padding: 0 !important;
  }
  .nav__link {
    font-family: "Font-C", "PPNeueMontreal", sans-serif;
    font-size: 15px;
    line-height: 1;
    text-transform: uppercase;
    letter-spacing: 0.01em;
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
  }
  /* keep the Discuss a Project pill anchored in the very top-right */
  .header__cta {
    position: absolute;
    top: 0;
    right: 3.2vw;
    margin: 0 !important;
  }

  /* ---- Floating menu: drop the legacy horizontal darkening bar ---- */
  /* 竖排导航下,原厂横向暗化条(.header__bg,84px + backdrop-blur)只盖住菜单
     顶部两项、割裂了竖排菜单。整体去掉,让 logo 与菜单直接悬浮在 3D 场景上。 */
  .header__bg {
    display: none !important;
  }
  /* 悬浮可读性:白色 logo(SVG)与竖排链接加柔和暗投影,亮帧下也不糊。 */
  .header__logo svg {
    filter: drop-shadow(0 1px 6px rgba(0, 0, 0, 0.55));
  }
  .nav__link {
    text-shadow: 0 1px 8px rgba(0, 0, 0, 0.45);
  }

  /* ---- CTA: 参考图二样式 —— 去掉紫色胶囊,改纯文字 + 斜向箭头 ---- */
  .nav__cta .btn {
    background: transparent !important;
    border-radius: 0 !important;
    padding: 0 !important;
    font-size: 16px !important;
    font-weight: 400 !important;
    letter-spacing: 0.02em;
    color: #fff !important;
    text-shadow: 0 1px 8px rgba(0, 0, 0, 0.45);
  }
  /* 隐藏 hover 时的圆角填充层,避免残留胶囊形状 */
  .nav__cta .btn__hover {
    display: none !important;
  }
  /* 文字后接斜向下箭头(↘),hover 时轻微位移 */
  .nav__cta .btn__text::after {
    content: "\2198";
    display: inline-block;
    margin-left: 0.5em;
    font-size: 1.1em;
    transition: transform 0.25s ease;
  }
  .nav__cta .btn:hover .btn__text::after {
    transform: translate(2px, 2px);
  }
}

/* Keep all readable UI and body copy at 14px or larger. */
html {
  font-size: clamp(14px, 0.8333333vw, 16px);
}

.t-12,
.vgl-hero__scroll,
.vgl-hero__loading,
.footer__copyright,
.contact-card__body,
.site-loader__text,
.line-sidebar__index {
  font-size: 14px !important;
}

/* Keep this long advantage heading on one line only on wide desktop screens. */
@media (min-width: 1440px) {
  .advantage-title--cinematic {
    white-space: nowrap;
  }
}

/* ---- BuzzVideo creative gallery ---- */
/* The canvas is transparent: the native particle field remains visible below it. */
/* 独立的「Key Advantages」段(内容已并入球体区右栏)—— 先隐藏 */
#vgl-last {
  display: none !important;
}

/* Engine 区标题下的编辑正文块(Built specifically… / Transforming… /
   Shattering…)已并入球体左栏 —— 先隐藏(保留上方 h2 标题) */
.buzz-engine-copy {
  display: none !important;
}

.buzz-gallery-section {
  align-items: center !important;
  justify-content: center !important;
  min-height: calc(var(--vh) * 135) !important;
  overflow: hidden !important;
  /* 只覆盖上下内距;左右沿用全站 .section 的标准安全边距 → 与其它 section 一致 */
  padding-top: clamp(5rem, 9vw, 9rem) !important;
  padding-bottom: clamp(4rem, 8vw, 8rem) !important;
}

.buzz-gallery-section__inner {
  display: flex !important;
  flex-direction: column;
  align-items: center;
  width: 100%; /* 填满 .section 内距区域 → 左右边距与其它 section 统一 */
  text-align: center;
}

/* Engine 区块加一层「底」:ASCII 模型(#ascii-hero-canvas,z-index:0 视口固定)
   与本区块文字混在一起看不清 → 在模型之上、文字之下垫一层半透明暗色 + 轻微
   背景模糊,把文字衬清楚,同时模型仍隐约可见作氛围。scrim 用 ::before 定位在
   模型上方(main 内容整体在该画布之上),内容层再 z-index 抬到 scrim 之上。 */
#vgl-lightbulb {
  position: relative;
}
#vgl-lightbulb::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(
    130% 105% at 50% 50%,
    rgba(8, 5, 20, 0.34) 0%,
    rgba(6, 4, 15, 0.62) 62%,
    rgba(3, 2, 11, 0.8) 100%
  );
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
#vgl-lightbulb .buzz-gallery-section__inner {
  position: relative;
  z-index: 1;
}

.buzz-gallery__heading {
  position: relative;
  z-index: 4;
  width: min(100%, 46rem);
  margin-bottom: clamp(1.25rem, 3vw, 2.5rem);
}

.buzz-gallery__eyebrow {
  color: rgba(255, 255, 255, 0.62);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  line-height: 1.4;
  text-transform: uppercase;
}

.buzz-gallery__eyebrow {
  margin-bottom: 0.8rem;
  color: #8052ff;
  font-weight: 600;
}

.buzz-gallery__heading h2 {
  margin-bottom: 0.7rem;
}

/* ---- Engine/BuzzVideo editorial block: this is long-form copy (a headline
   + two paragraphs + a subheading), not the short centered blurb the rest
   of .buzz-gallery-section__inner is built for. Centered text wrapped to a
   ~46rem column reads as a wall of text, so this block switches to a
   left-aligned "article" layout instead, matching how body copy reads
   elsewhere on the page (e.g. "Our DNA"). The block itself stays centered
   on the page via the flex parent's align-items:center. ---- */
#vgl-lightbulb .buzz-gallery__heading {
  width: 100%; /* 满宽靠左(与左栏左缘对齐),不再被居中留白 */
  text-align: left;
  align-self: flex-start;
}

#vgl-lightbulb .buzz-gallery__eyebrow {
  text-align: left;
}

.buzz-engine-title {
  width: 100% !important; /* 撑满标题块,让 <br> 干净排成 2 行(此前被收缩成窄列) */
  max-width: none !important;
  text-align: left;
  font-size: clamp(1.65rem, 3.2vw, 3rem) !important;
  line-height: 1.08 !important;
}

.buzz-engine-copy {
  max-width: 44rem;
  margin-top: clamp(1.5rem, 3vw, 2.5rem);
  text-align: left;
  color: rgba(255, 255, 255, 0.78);
  font-size: clamp(1rem, 1.35vw, 1.25rem);
  line-height: 1.55;
}

.buzz-engine-copy p {
  max-width: 44rem;
  margin: 0 0 1.4rem;
}

.buzz-engine-copy h3 {
  max-width: 44rem;
  margin: 2rem 0 1rem;
  color: #fff;
  font-size: clamp(1.3rem, 2.25vw, 2.2rem);
  line-height: 1.15;
}

.buzz-advantages-list {
  margin: 0;
  padding-left: 1.6rem;
  text-align: left;
}

.buzz-advantages-list li {
  margin: 0 0 1.4rem;
  padding-left: 0.45rem;
  color: rgba(255, 255, 255, 0.78);
  font-size: clamp(1rem, 1.35vw, 1.25rem);
  line-height: 1.5;
}

.buzz-advantages-list li::marker {
  color: #8052ff;
}

.buzz-advantages-list strong,
.buzz-engine-copy strong {
  color: #fff;
  font-weight: 600;
}

.buzz-gallery__intro {
  margin: 0 auto;
  max-width: 36rem;
  color: rgba(255, 255, 255, 0.72);
  font-size: clamp(0.9rem, 1.2vw, 1.1rem);
  line-height: 1.45;
}

/* ---- Three-column layout: body copy left/right wraps around the sphere's
   circular silhouette via shape-outside, matching the reference sketch. ---- */
.buzz-gallery-columns {
  /* 球体做大 + 侧栏加宽 + 缩小间距:文字内缘沿球体大弧线凹进(绕球明显),
     同时两侧撑满。球半径需明显小于侧栏宽,避免中段被挤空成断层。 */
  --ball-size: clamp(22rem, 36vw, 40rem);
  display: grid;
  grid-template-columns: 1fr var(--ball-size) 1fr;
  align-items: center;
  column-gap: clamp(0.75rem, 1.6vw, 1.75rem);
  width: 100%;
  margin: 0 auto;
}

.buzz-gallery-col {
  display: flow-root; /* contains the float without a BFC hack */
}

/* 层级:标题(白、较大、较粗)在上,正文接着绕球体往下流,连成一块。 */
.buzz-gallery-side-title {
  margin: 0 0 0.7em;
  color: #fff;
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(1.2rem, 1.7vw, 1.6rem);
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-align: inherit;
}
.buzz-gallery-side-copy {
  margin: 0;
  color: rgba(255, 255, 255, 0.72);
  font-size: clamp(0.95rem, 1.15vw, 1.1rem);
  line-height: 1.6;
  text-align: inherit;
}
/* 外侧贴边:左栏左对齐贴左缘、右栏右对齐贴右缘(内侧由 shape-outside 绕球)。 */
.buzz-gallery-col--left { text-align: left; }
.buzz-gallery-col--right { text-align: right; }

/* Full ball height, pole to pole: the circle's radius (--ball-size / 2)
   exactly matches the ball, centered at 50% of this box's own height, which
   is the same 50% point since the box IS the full height. That gives a
   smooth taper from ~zero bite at the very top/bottom to maximum bite at
   the vertical middle -- a true half-lens silhouette, not just a crushed
   band. (An earlier version shrank this box to "sample" a shorter middle
   band, but with typical paragraph lengths that only ever landed near one
   pole -- barely any curve -- or, centered on a short box, crushed every
   line to near-zero width. Full height reads correctly either way.) */
.buzz-gallery-shape {
  width: calc(var(--ball-size) / 2);
  height: var(--ball-size);
}

.buzz-gallery-col--left .buzz-gallery-shape {
  float: right;
  shape-outside: circle(calc(var(--ball-size) / 2) at 100% 50%);
}

.buzz-gallery-col--right .buzz-gallery-shape {
  float: left;
  shape-outside: circle(calc(var(--ball-size) / 2) at 0% 50%);
}

.buzz-gallery-ball-slot {
  width: var(--ball-size);
  height: var(--ball-size);
}

.buzz-gallery {
  position: relative;
  width: 100%;
  height: 100%;
  isolation: isolate;
}

.buzz-gallery__stage {
  position: relative;
  width: 100%;
  height: 100%;
  margin: 0 auto;
  overflow: visible;
}

.buzz-gallery__stage canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
  cursor: grab;
}

.buzz-gallery__stage canvas.is-dragging {
  cursor: grabbing;
}

@media (max-width: 900px) {
  .buzz-gallery-columns {
    grid-template-columns: 1fr;
    row-gap: 2rem;
    justify-items: center;
  }

  .buzz-gallery-col {
    order: 3;
    min-height: 0;
    width: min(100%, 34rem);
  }

  .buzz-gallery-shape {
    float: none;
    shape-outside: none;
    width: 0;
    height: 0;
  }

  .buzz-gallery-ball-slot {
    order: 2;
    --ball-size: min(80vw, 26rem);
    width: var(--ball-size);
    height: var(--ball-size);
  }

  .buzz-gallery-col--left {
    order: 3;
  }

  .buzz-gallery-col--right {
    order: 4;
  }
}

@media (max-width: 767px) {
  .buzz-gallery-section {
    min-height: calc(var(--vh) * 120) !important;
    padding: 5.5rem 1rem 4rem !important;
  }

  .buzz-engine-title {
    font-size: clamp(1.55rem, 7vw, 2.2rem) !important;
  }

  .buzz-engine-copy,
  .buzz-advantages-list li {
    font-size: 1rem;
  }

}

@media (prefers-reduced-motion: reduce) {
  .buzz-gallery__stage canvas {
    cursor: default;
  }
}

/* ---- Cases section: full-screen scroll-driven Vgl3d scene ---- */
/* Tall spacer inside <main>; provides scroll distance for the cases and closing text.
   Four cases (frames 119–300) = 420vh. 2026-09-30 → 2026-10 there were five
   (119–346, 500vh); the height follows the frame range to keep scroll speed. */
.vgl3d-cases {
  position: relative;
  height: 420vh;
}
/* The pin (canvas + overlays) is relocated to <body> by JS and fixed to the
   viewport, so ASScroll's transform on <main> can't drag it. */
.vgl3d-cases__pin {
  position: fixed;
  inset: 0;
  z-index: 8;
  --case-panel-shift: 1;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  isolation: isolate;
  /* The JS driver controls the reveal curve (opacity + depth push-in scale /
     blur). The lens/iris chapter-card overlays were removed on 2026-09-30. */
  transition: none;
}
.vgl3d-cases__pin.is-active {
  opacity: 1;
  visibility: visible;
}
#vgl3d-cases-mount {
  position: absolute;
  inset: 0;
  z-index: 1;
}
#vgl3d-cases-mount canvas {
  width: 100% !important;
  height: 100% !important;
  display: block;
  /* 单独开启点击(pin 为 none 以让滚动穿透);pin 隐藏时 visibility:hidden 不拦截。
     wheel 仍冒泡到 window → ASScroll 滚动不受影响,只多了对石头的点击拾取。 */
  pointer-events: auto;
  cursor: default;
}
.vgl3d-cases__pin.has-case-hit #vgl3d-cases-mount canvas {
  cursor: pointer;
}
/* 收尾 CTA 开始出现后，透明的案例 canvas 不再接管该页任何点击。 */
.vgl3d-cases__pin.is-closing #vgl3d-cases-mount canvas {
  pointer-events: none;
  cursor: default;
}
.vgl3d-cases__pin .case-info {
  position: absolute;
  top: 50%;
  transform: translate3d(0, -50%, 0);
  width: 31%;
  max-width: 29rem;
  z-index: 9;
  pointer-events: none;
  opacity: var(--case-panel-opacity, 1);
  transition: opacity 0.22s ease, transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.vgl3d-cases__pin.is-closing .case-info {
  opacity: var(--case-panel-opacity, 1);
}
.vgl3d-cases__pin.is-cta-complete .case-info {
  visibility: hidden;
}
.vgl3d-cases__pin .case-info--left {
  left: 4vw;
  transform: translate3d(calc(var(--case-panel-shift, 1) * -24px), -50%, 0);
}
.vgl3d-cases__pin .case-info--right {
  right: 4vw;
  text-align: right;
  transform: translate3d(calc(var(--case-panel-shift, 1) * 24px), -50%, 0);
}
.vgl3d-cases__pin .case-info a {
  pointer-events: auto;
}
/* 案例末端的洽谈入口:点击后把案例名带入表单留言(见 ux-fixes.js 的 data-case-title 处理) */
.vgl3d-cases__pin .case-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  margin-top: 1.1rem;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
  color: rgba(146, 106, 255, 0.95);
  text-decoration: none;
  transition: color 0.25s ease, gap 0.25s ease;
}
.vgl3d-cases__pin .case-cta:hover {
  color: #fff;
  gap: 0.75em;
}
.vgl3d-cases__pin .case-tag {
  display: inline-block;
  padding: 0.35em 0.9em;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 999px;
  font-size: 13px;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.85);
}

/* ---- Engagement: "The Workflow" collaboration models (replaces investors) ---- */
.engagement {
  position: relative;
  z-index: 5;
  max-width: 84rem;
  margin: 0 auto;
  padding: 6vw 5vw 9vw;
  color: #fff;
}
.engagement__head {
  max-width: 54rem;
  margin-bottom: 3.4rem;
}
.engagement__eyebrow {
  font-size: 14px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 500;
  margin-bottom: 1rem;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}
.engagement__title {
  margin-bottom: 1.2rem;
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.6);
}
.engagement__intro {
  font-size: clamp(16px, 1.25vw, 20px);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.74);
  max-width: 44rem;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.55);
}
.engagement__cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}
.wf-card {
  display: flex;
  flex-direction: column;
  padding: 2rem 1.9rem 2.1rem;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 18px;
  background: rgba(18, 16, 30, 0.46);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  transition: transform 0.35s ease, border-color 0.35s ease, background 0.35s ease;
}
.wf-card:hover {
  transform: translateY(-6px);
  border-color: rgba(146, 106, 255, 0.55);
  background: rgba(28, 24, 46, 0.6);
}
.wf-card__index {
  font-family: "Montserrat", "PPNeueMontreal", sans-serif;
  font-weight: 700;
  font-size: 22px;
  letter-spacing: 0.05em;
  color: rgba(146, 106, 255, 0.95);
  margin-bottom: 1rem;
}
.wf-card__title {
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-size: 19px;
  line-height: 1.32;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 1.6rem;
}
.wf-card__title .t-purple {
  font-weight: 500;
}
.wf-card__row {
  margin-bottom: 1.35rem;
}
.wf-card__label {
  font-size: 12px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
  margin-bottom: 0.5rem;
}
.wf-card__text {
  font-size: 15px;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
}
.wf-card__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.wf-card__list li {
  position: relative;
  padding-left: 1.2em;
  font-size: 14.5px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.78);
  margin-bottom: 0.55em;
}
.wf-card__list li:last-child {
  margin-bottom: 0;
}
.wf-card__list li::before {
  content: "\25E6"; /* ◦ */
  position: absolute;
  left: 0.1em;
  color: rgba(146, 106, 255, 0.95);
}
.wf-card__value {
  margin-top: auto;
  margin-bottom: 0;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.wf-card__value .wf-card__text {
  color: #fff;
}
/* 服务卡底部的洽谈入口:带入所选服务到表单留言 */
.wf-card__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  margin-top: 1.4rem;
  font-size: 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 600;
  color: rgba(146, 106, 255, 0.95);
  text-decoration: none;
  transition: color 0.25s ease, gap 0.25s ease;
}
.wf-card__cta:hover {
  color: #fff;
  gap: 0.75em;
}

@media (max-width: 900px) {
  .engagement {
    padding: 14vw 6vw;
  }
  .engagement__cards {
    grid-template-columns: 1fr;
  }
}

/* ---- Closing CTA (footer / Contact): intro line + inline link ---- */
.footer__cta-body {
  max-width: 36rem;
  margin: 0 auto 2rem;
  font-size: clamp(15px, 1.15vw, 18px);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.74);
  text-align: center;
}
.footer__cta-link {
  color: #926aff;
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color 0.25s ease;
}
.footer__cta-link:hover {
  color: #fff;
}

/* ---- Contact section: two-column layout (left copy / right form) ---- */
.section.footer {
  position: relative;
  z-index: 10;
}
/* 让 CTA 头部撑满区块宽度(原 flex 居中把它约束成窄块),否则两栏容器算宽偏窄 */
.section.footer .footer__head {
  width: 100%;
}
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1.05fr;
  gap: 4.5vw;
  align-items: start; /* 顶对齐:标题顶边与表单顶边齐平 */
  width: min(92%, 96rem);
  margin: 0 auto;
  padding: max(7vw, 7rem) 0 5vw;
  text-align: left;
}
.contact-left {
  max-width: 40rem;
}
.contact-left h2 {
  text-align: left !important;
  margin-bottom: 1.4rem;
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.7);
}
.contact-lead {
  font-size: clamp(15px, 1.15vw, 18px);
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.78);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.75);
}
.contact-form {
  background: rgba(18, 16, 30, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 22px;
  padding: 2.4rem 2.2rem;
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}
.contact-form .field {
  margin-bottom: 1.2rem;
  text-align: left;
}
.field__label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #fff;
  margin-bottom: 0.5rem;
}
.field__label .req {
  color: #ff5c8a;
}
.field__input {
  width: 100%;
  padding: 1.1rem 1.2rem;
  font-size: 15px;
  color: #fff;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 12px;
  outline: none;
  font-family: inherit;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.field__input::placeholder {
  color: rgba(255, 255, 255, 0.38);
}
.field__input:focus {
  border-color: rgba(146, 106, 255, 0.8);
  background: rgba(146, 106, 255, 0.06);
}
.field__textarea {
  resize: vertical;
  min-height: 7rem;
  line-height: 1.5;
}
/* 复用站点 .btn 组件样式(紫底胶囊 + hover 填充动画),这里只让它在表单里占满宽度 */
.contact-form__submit {
  width: 100%;
  margin-top: 0.6rem;
}
/* 提交失败时前端插入的行内提示。不弹 alert:访客填的内容全留着,改完直接重发。 */
.contact-form__error {
  display: none;
  margin-top: 0.9rem;
  padding: 0.85rem 1rem;
  font-size: 15px;
  line-height: 1.5;
  color: #fff;
  text-align: center;
  border: 1px solid rgba(255, 92, 92, 0.55);
  border-radius: 0.35rem;
  background: rgba(255, 92, 92, 0.12);
}
/* 表单下方的资料用途说明 + Privacy 链接 */
.contact-form__note {
  margin-top: 0.9rem;
  font-size: 13px;
  line-height: 1.5;
  text-align: center;
  color: rgba(255, 255, 255, 0.55);
}
.contact-form__note a {
  color: rgba(255, 255, 255, 0.8);
  text-decoration: underline;
}
.contact-form__done {
  display: none;
  padding: 1.2rem 0.2rem;
  font-size: 16px;
  line-height: 1.5;
  color: #fff;
  text-align: center;
}

/* ---- Loading handoff: keep the page usable while WebGL warms up ---- */
/* The native scene can take several seconds on a cold Vercel load. Releasing
   the blocking loader gives the real DOM and the progressively-mounted
   particle layer a chance to respond before the final frame is ready. */
.site-loader.ux-loader-released {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
  transition: opacity 0.45s ease, visibility 0s linear 0.45s !important;
}

/* ---- Mobile navigation: make the open state a true full-screen surface ---- */
@media (max-width: 767px) {
  /* A transformed header becomes the containing block for its fixed nav.
     When the header auto-hides on scroll, that pulls the open menu off-screen.
     Pin the header back to the viewport while the menu is expanded. */
  .header.js-header:has(.nav-toggle[aria-expanded="true"]) {
    top: 0 !important;
    transform: none !important;
  }

  .nav.js-nav {
    position: fixed !important;
    inset: 0 !important;
    width: 100vw !important;
    height: 100svh !important;
    min-height: 100vh;
    z-index: 1000;
  }

  .nav__bg {
    height: 100% !important;
    background:
      radial-gradient(circle at 50% 10%, rgba(128, 82, 255, 0.26), transparent 42%),
      linear-gradient(180deg, rgba(4, 4, 14, 0.98), rgba(4, 4, 14, 0.92)) !important;
  }

  .nav__bg-inner {
    height: 100% !important;
    background: transparent !important;
  }

  .nav__list.js-nav-list {
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    justify-content: center !important;
    gap: 0.7rem;
    box-sizing: border-box;
    height: 100%;
    min-height: 100svh;
    padding: 7rem 1.5rem 5rem !important;
  }

  .nav__item {
    margin: 0 !important;
    padding: 0 !important;
  }

  .nav__link {
    font-size: clamp(1rem, 4.5vw, 1.2rem) !important;
    line-height: 1.2 !important;
  }

  .nav__cta {
    margin-top: 0.2rem !important;
  }

  .nav__cta .btn {
    min-width: 9.5rem;
  }

  /* Keep the compact header CTA on one line on phones. The full wording is
     preserved for accessibility and desktop; only its flex item is prevented
     from wrapping into the two-line block seen at narrow widths. */
  .header__cta .btn,
  .header__cta .btn__text {
    white-space: nowrap;
  }
  .header__cta .btn {
    flex-shrink: 0;
  }

  /* One readable case panel on narrow screens; the desktop left/right pair
     otherwise competes with the full-bleed crystal and becomes unreadable. */
  .vgl3d-cases__pin .case-info--left {
    display: none;
  }

  .vgl3d-cases__pin .case-info--right {
    top: auto;
    right: 0;
    bottom: 4.5rem;
    width: 100%;
    max-width: none;
    padding: 0 1rem;
    box-sizing: border-box;
    text-align: center;
    transform: translate3d(0, calc(var(--case-panel-shift, 1) * 24px), 0);
    text-shadow: 0 2px 16px rgba(0, 0, 0, 0.85);
  }

  .vgl3d-cases__pin .case-info--right .case-tags {
    justify-content: center;
    column-gap: 0.85rem;
    row-gap: 0.75rem;
    margin-bottom: 0.9rem;
  }

  .vgl3d-cases__pin .case-info--right .case-headline {
    max-width: 20rem;
    margin: 0 auto;
    font-size: clamp(1.2rem, 5.4vw, 1.65rem);
    line-height: 1.12;
  }
}

/* Keep the logo legible beside the full CTA on the narrowest phones. */
@media (max-width: 360px) {
  body:has(.nav-toggle) .header__logo {
    flex: 0 0 98px;
    width: 98px;
    padding: 0;
  }
  body:has(.nav-toggle) .header__logo svg {
    width: 98px;
    height: auto;
  }
  body:has(.nav-toggle) .header__cta .btn {
    min-height: 42px;
    padding: 10px;
    font-size: 10.5px !important;
  }
  body:has(.nav-toggle) .header__cta .btn__text {
    font-size: inherit !important;
  }
}

/* Case crystal canvas — free the vertical scroll on touch.
   The pin is position:fixed (outside the native scroll flow) and the Vgl3d
   embed attaches a non-passive touch handler on the canvas (GSAP normalizer +
   pointer capture) that preventDefaults the gesture. touch-action:pan-y alone
   therefore can NOT hand the scroll back — the only reliable release is to stop
   pointer/touch events from reaching the canvas at all, so the gesture falls
   through to the overflow:auto scroll container underneath.
   Trade-off: crystal tap-to-enlarge is disabled on phones; scrolling past the
   case section (and reaching the footer/CTA) takes priority. Desktop keeps
   pointer-events:auto for click-picking + hover. */
html.is-touch #vgl3d-cases-mount canvas {
  pointer-events: none !important;
}
/* Buzz gallery keeps its own horizontal drag; only the vertical pan is the
   browser's, so touch-action (not pointer-events) is the right lever here. */
html.is-touch .buzz-gallery__stage canvas {
  touch-action: pan-y pinch-zoom !important;
}

/* ---- Small accessibility affordances ---- */
.nav-toggle:focus-visible,
.nav__link:focus-visible,
.contact-form__submit:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 4px;
}

@media (max-width: 900px) {
  .contact-grid {
    grid-template-columns: 1fr;
    gap: 2.5rem;
    padding: 10vw 6vw;
  }
  .contact-form {
    padding: 1.8rem 1.4rem;
  }
}

/* ---- Hero CTA: restored (2026-09 studio repositioning).
   "Discuss a Project" is the page's single primary conversion and must be
   visible in the hero, so the old temporary display:none is gone. ---- */
.hero-glass-cta {
  display: inline-flex !important;
  vertical-align: middle;
}

/* ---- Legacy pl-particles canvas is fully replaced by the ASCII digit layer
   (ascii-digits-hero.js global mode). Keep it permanently hidden so the old
   3D particle V never flashes through — neither during the load window (before
   the ASCII GLB models finish loading) nor from legacy scripts (hero-brain-
   morph.js / vgl3d-cases.js) that still write its opacity. The pl-particles
   engine keeps running as the shape-sequence "brain"; only its canvas is hidden. */
#pl-canvas {
  opacity: 0 !important;
  visibility: hidden !important;
}

/* ============================================================
   BuzzVideo (Engine) side copy — liquid-glass cards.
   Old layout floated the copy in 1fr columns and wrapped it around the
   central sphere with shape-outside:circle(). At the sphere's vertical
   middle the usable line width collapsed to ~2-3 words, hyphen-breaking
   words ("high-") and clipping the tail under the section's overflow:hidden.
   It also sat as bare text on the busy 3D background and could clash with
   the fixed top-right nav. Replace that with proper glass "cards" that hold
   the text at a readable measure, legible over any background. ============ */

/* Kill the circular text-wrap cutouts (source of the crushed lines). */
#vgl-lightbulb .buzz-gallery-shape {
  display: none !important;
}

/* 侧栏收窄、球体放大(见需求图):侧卡给一个显式上限并各自贴外缘,
   中间的球体区吃掉多出来的宽度。--side-max 是唯一要调的旋钮。 */
#vgl-lightbulb .buzz-gallery-columns {
  --ball-size: clamp(20rem, 38vw, 44rem);
  --side-max: clamp(15rem, 20vw, 22rem);
  column-gap: clamp(1rem, 2vw, 2rem);
  align-items: center;
}
#vgl-lightbulb .buzz-gallery-col--left,
#vgl-lightbulb .buzz-gallery-col--right {
  max-width: var(--side-max);
}
#vgl-lightbulb .buzz-gallery-col--left { justify-self: start; }
#vgl-lightbulb .buzz-gallery-col--right { justify-self: end; }

/* The two side columns become liquid-glass cards. */
#vgl-lightbulb .buzz-gallery-col--left,
#vgl-lightbulb .buzz-gallery-col--right {
  position: relative;
  align-self: center;
  text-align: left; /* both left-aligned → far easier to read than ragged-right */
  padding: clamp(1.4rem, 1.8vw, 2rem) clamp(1.4rem, 1.8vw, 2.1rem);
  border-radius: 22px;
  background: linear-gradient(
    135deg,
    rgba(255, 255, 255, 0.1) 0%,
    rgba(255, 255, 255, 0.035) 55%,
    rgba(255, 255, 255, 0.06) 100%
  );
  border: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  backdrop-filter: blur(18px) saturate(150%);
  box-shadow:
    0 18px 50px rgba(0, 0, 0, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    inset 0 0 0 1px rgba(255, 255, 255, 0.02);
  overflow: hidden;
}

/* Glossy top-edge sheen for the "liquid glass" feel. */
#vgl-lightbulb .buzz-gallery-col--left::before,
#vgl-lightbulb .buzz-gallery-col--right::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 42%;
  background: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.12),
    rgba(255, 255, 255, 0)
  );
  pointer-events: none;
}

#vgl-lightbulb .buzz-gallery-side-copy {
  color: rgba(255, 255, 255, 0.86);
  font-size: clamp(0.92rem, 1.05vw, 1.02rem);
  line-height: 1.62;
}
#vgl-lightbulb .buzz-gallery-side-title {
  font-size: clamp(1.15rem, 1.6vw, 1.5rem);
  margin-bottom: 0.65em;
}

/* 卡片改放全文(左:正文 + 小标题 + 第二段;右:5 条 Key Advantages)。
   .buzz-gallery-side-copy 从 <p> 变成了容器,内部元素的间距在这里给;
   字号比原来的单段摘要小一档,否则收窄后的卡片会顶穿区块高度。 */
#vgl-lightbulb .buzz-gallery-side-copy {
  font-size: clamp(0.82rem, 0.92vw, 0.95rem);
  line-height: 1.55;
}
#vgl-lightbulb .buzz-gallery-side-copy p {
  margin: 0 0 0.9em;
}
#vgl-lightbulb .buzz-gallery-side-copy p:last-child {
  margin-bottom: 0;
}
#vgl-lightbulb .buzz-gallery-side-subtitle {
  margin: 1.1em 0 0.55em;
  color: #fff;
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(0.95rem, 1.15vw, 1.12rem);
  line-height: 1.25;
  letter-spacing: -0.01em;
}
/* 卡片内的 Key Advantages 列表:比全宽版紧凑,项目符号内缩不占宽。 */
.buzz-advantages-list--card {
  padding-left: 1.05rem;
}
.buzz-advantages-list--card li {
  margin: 0 0 0.8em;
  padding-left: 0.15rem;
  color: inherit;
  font-size: inherit;
  line-height: inherit;
}
.buzz-advantages-list--card li:last-child {
  margin-bottom: 0;
}

/* Stacked (mobile / narrow): full-width cards, no float artifacts. */
@media (max-width: 900px) {
  #vgl-lightbulb .buzz-gallery-col--left,
  #vgl-lightbulb .buzz-gallery-col--right {
    width: min(100%, 34rem);
    text-align: left;
  }
}

/* ============================================================
   Header backdrop once scrolled past the hero (class toggled by ux-fixes.js
   from the ASScroll transform; window.scrollY is dead under ASScroll). The nav
   is a TALL vertical menu, so the legacy 84px horizontal bar only covered its
   top items (that's why .header__bg is display:none above). Instead reveal
   .header__bg as a soft, tall gradient that covers the FULL nav height and
   fades out below it — reads as an intentional backdrop, not a hard cut, and
   slides away with the auto-hide. Hero header stays transparent (floating nav
   + text-shadows, as designed). Desktop floating-nav only. ============ */
@media (min-width: 768px) {
  .header.js-header.is-scrolled .header__bg {
    display: block !important;
    top: 0;
    left: 0;
    right: 0;
    bottom: auto;
    height: 210px;
    background: linear-gradient(
      to bottom,
      rgba(6, 4, 15, 0.92) 0%,
      rgba(6, 4, 15, 0.82) 32%,
      rgba(6, 4, 15, 0.48) 66%,
      rgba(6, 4, 15, 0) 100%
    );
    -webkit-backdrop-filter: blur(14px) saturate(135%);
    backdrop-filter: blur(14px) saturate(135%);
  }
  .header.js-header.is-scrolled .header__bg-inner {
    display: none;
  }
}

/* Auto-hide the header while scrolling down (revealed on scroll-up by
   ux-fixes.js). The nav is a tall vertical menu in the top-right; sliding the
   whole header out while reading removes the overlap with section content. */
.header.js-header {
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.3s ease, background 0.3s ease, backdrop-filter 0.3s ease;
  will-change: transform;
}
.header.js-header.is-hidden {
  transform: translateY(-118%);
  opacity: 0;
  pointer-events: none;
}

/* ============================================================
   Site-loader minimal progress bar (reference: thin cream line with angled
   end-caps + a notch marker carrying the %). Driven by ux-fixes.js from the
   theme's own load progress. Sits as a full-width row just above the LOADING
   label. The % readout sits bottom-right (.vgl-loader-pct). ==== */
.vgl-loadbar {
  --p: 0%;
  --line: rgba(247, 242, 233, 0.9);
  --dim: rgba(247, 242, 233, 0.16);
  /* Sits just above the LOADING row (its parent .site-loader__info is absolute
     at the bottom with the correct horizontal insets, so left/right:0 here
     inherits that alignment). */
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + 26px);
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0;
}
.vgl-loadbar__cap {
  position: relative;
  flex: 0 0 auto;
  width: 12px;
  height: 14px;
}
.vgl-loadbar__cap::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 16px;
  height: 1px;
  background: var(--line);
  transform: translate(-50%, -50%) rotate(-56deg);
}
.vgl-loadbar__rail {
  position: relative;
  flex: 1 1 auto;
  height: 1px;
  background: var(--dim);
}
.vgl-loadbar__fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: var(--p);
  background: var(--line);
  box-shadow: 0 0 9px rgba(247, 242, 233, 0.55);
  transition: width 0.18s linear;
}
.vgl-loadbar__knob {
  position: absolute;
  top: 50%;
  left: var(--p);
  transform: translate(-50%, -50%);
  transition: left 0.18s linear;
}
/* the notch: a short tick rising from the rail at the progress point */
.vgl-loadbar__knob::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 1px;
  height: 17px;
  background: var(--line);
  transform: translateX(-50%);
  box-shadow: 0 0 8px rgba(247, 242, 233, 0.5);
}
/* 百分比回到右下角(评审 01「左下 LOADING、右下百分比保留」),与 LOADING 同一行
   底对齐;位置与字号沿用主题原 .site-loader__progress。主题那个三位数计数器跟的是
   它自己的进度,不是 ux-fixes.js 的真实阶段,所以仍隐藏,数字由这里显示。 */
.vgl-loader-pct {
  position: absolute;
  right: 0;
  bottom: 0;
  white-space: nowrap;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-weight: 300;
  font-size: 2rem;
  line-height: 1;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  color: rgba(247, 242, 233, 0.9);
}
.site-loader__progress {
  display: none !important;
}

/* Hold the loader visible until the progress bar reaches 100% (ux-fixes.js adds
   `vgl-hold` on load, removes it + adds `ux-loader-released` at 100%). Overrides
   the theme's inline opacity:0 hide that fires the moment the scene reports
   ready (~1s locally), so the 0→100 fill is actually seen before the reveal. */
.site-loader.vgl-hold {
  opacity: 1 !important;
  visibility: visible !important;
  pointer-events: auto !important;
}

/* 主题场景一就绪就给 .site-loader 加 `loaded` 并播放退场:方块 scaleDown、两行
   标题/LOADING 上移擦除、换成 COMPLETED。可闸门还在等 hero V 与案例场景(进度
   停在 ~90%),于是出现「黑屏 + 90% + COMPLETED」的空档。等待期间把它们顶回
   原位;释放后淡出的 0.45s 里方块和标题也保持可见,整块一起淡掉,不再先消失。
   (!important 声明可以压过主题写的内联 transform / animation。) */
.site-loader.loaded.vgl-hold .site-loader__spinner-svg,
.site-loader.loaded.ux-loader-released .site-loader__spinner-svg {
  animation: none !important;
  transform: scale(1) !important;
}
.site-loader.loaded.vgl-hold .site-loader__heading-inner,
.site-loader.loaded.ux-loader-released .site-loader__heading-inner,
.site-loader.loaded.vgl-hold .site-loader__loading-text {
  transform: translate(0, 0) rotate(0) !important;
}
.site-loader.loaded.vgl-hold .site-loader__ellipsis-wrapper {
  opacity: 1 !important;
  visibility: visible !important;
}
.site-loader.loaded.vgl-hold .site-loader__completed span {
  transform: translateY(110%) !important;
}

/* ---- Loader copy (2026-10 review 01) ----
   两行回答"这是什么 / 跟我有什么关系",第二行降一级亮度;下面一行小字带
   PressLogic 正式 logo。原主题 3.5rem/6.5rem 行高是给短句用的,新句子更长,
   改成按视口缩放、正常行高,两行之间不再用负 margin 叠。 */
.site-loader__heading-text {
  font-size: clamp(1.35rem, 3.1vw, 2.9rem) !important;
  line-height: 1.22 !important;
  letter-spacing: -0.025em !important;
  margin-top: 2.6rem !important;
  padding: 0 1rem;
}
.site-loader__heading-text--sub {
  margin-top: 0.15rem !important;
  color: rgba(255, 255, 255, 0.46);
}
.site-loader__byline {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.55rem;
  margin-top: clamp(1.4rem, 2.6vw, 2.1rem);
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.85rem, 1vw, 1rem);
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.62);
  opacity: 0;
  /* 0.2s 后就淡入:页面加载很快时 loader 不到 1s 就关,原来 0.9s 的延迟会让这行
     和 PressLogic logo 根本来不及出现。 */
  animation: vglLoaderByline 0.45s ease 0.2s forwards;
}
.site-loader__byline-logo {
  display: block;
  width: auto;
  height: clamp(15px, 1.3vw, 18px);
}
@keyframes vglLoaderByline {
  to { opacity: 1; }
}

/* 同一 session 再次进入:不再播文案/进度,只留黑幕等 hero 就绪。
   主题要等第一行标题的 animationend 才进入退场流程;但它注册监听时若发现
   LOADING 字的 transform 已是单位矩阵,就直接跳过等待(主题自带的捷径)。
   所以这里把 LOADING 字钉在原位,而不是缩短标题动画 —— 1ms 的动画会在
   主题注册监听前就播完,事件丢失,只能等 9s 保底。半秒还没好才淡入四个白点。 */
html.vgl-loader-seen .site-loader__heading,
html.vgl-loader-seen .site-loader__byline,
html.vgl-loader-seen .site-loader__info {
  opacity: 0 !important;
}
html.vgl-loader-seen .site-loader__loading-text {
  animation: none !important;
  transform: translate(0, 0) !important;
}
html.vgl-loader-seen .site-loader__main {
  opacity: 0;
  animation: vglLoaderByline 0.3s ease 0.5s forwards;
}

/* Session return visits never display another blocking loader. */
html.vgl-loader-seen .site-loader {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}
/* Let the theme observe readiness without waiting for the copy animation. */
.site-loader__loading-text { transform: translate(0, 0) !important; }

/* ============================================================
   顶部菜单的模糊背景 —— 轻量版(2026-09-24 体检 #6)。
   之前这里把上面那条 210px / 0.92 的厚背板整个 display:none 了,结果往回滚、
   header 重新出现时正文(Reassurance 大标题、How We Make It Work 卡片)直接
   从 logo 和竖排菜单文字底下穿过去。这里换成更浅、更矮、底边渐隐的一层:
   刚好垫住 logo + 菜单(约 170px),读起来是顶部暗角而不是一条硬边色带。
   Hero 顶部(未 is-scrolled)仍然完全透明。 ========== */
@media (min-width: 768px) {
  .header.js-header.is-scrolled .header__bg {
    display: block !important;
    height: 190px;
    background: linear-gradient(
      to bottom,
      rgba(6, 4, 15, 0.8) 0%,
      rgba(6, 4, 15, 0.62) 40%,
      rgba(6, 4, 15, 0.26) 72%,
      rgba(6, 4, 15, 0) 100%
    );
    -webkit-backdrop-filter: blur(10px) saturate(125%);
    backdrop-filter: blur(10px) saturate(125%);
    /* 让模糊本身也渐隐,避免底边出现一条清晰/模糊的分界线 */
    -webkit-mask-image: linear-gradient(to bottom, #000 58%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 58%, transparent 100%);
  }
}

/* ============================================================
   VGenLab footer — centered brand lockup + three-column directory
   ============================================================ */
.section.footer {
  overflow: visible;
  padding-bottom: 0 !important;
}

.vgen-footer {
  position: relative;
  width: min(100%, 118rem);
  margin: clamp(5rem, 8vw, 9rem) auto 0;
  padding: clamp(4.5rem, 7vw, 8rem) clamp(1.5rem, 5vw, 6rem) clamp(1.75rem, 3vw, 3rem);
  border-top: 1px solid rgba(145, 112, 255, 0.2);
  color: rgba(255, 255, 255, 0.76);
  background:
    radial-gradient(circle at 50% 12%, rgba(108, 60, 255, 0.14), transparent 24rem),
    linear-gradient(180deg, rgba(5, 4, 12, 0.18), rgba(3, 3, 8, 0.72));
  isolation: isolate;
}

.vgen-footer::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0.2;
  pointer-events: none;
  background-image:
    radial-gradient(circle at 12% 20%, #fff 0 1px, transparent 1.4px),
    radial-gradient(circle at 82% 12%, #b79cff 0 1px, transparent 1.5px),
    radial-gradient(circle at 68% 72%, #fff 0 0.8px, transparent 1.2px),
    radial-gradient(circle at 28% 82%, #8c68ff 0 0.9px, transparent 1.3px);
  background-size: 19rem 17rem, 23rem 21rem, 31rem 24rem, 27rem 29rem;
}

.vgen-footer__brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.vgen-footer__logo {
  display: block;
  width: clamp(17rem, 26vw, 30rem);
  height: auto;
  filter: drop-shadow(0 0 2.4rem rgba(116, 67, 255, 0.34));
}

.vgen-footer__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(2rem, 7vw, 8rem);
  width: min(100%, 58rem);
  margin: clamp(4rem, 6vw, 6.5rem) auto clamp(4rem, 6vw, 6rem);
}

.vgen-footer__column {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
}

.vgen-footer__label {
  margin-bottom: 1.45rem;
  color: rgba(255, 255, 255, 0.46);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.vgen-footer__link {
  appearance: none;
  width: fit-content;
  margin: 0 0 0.85rem;
  padding: 0;
  border: 0;
  color: rgba(255, 255, 255, 0.76);
  background: transparent;
  font: inherit;
  font-size: clamp(0.95rem, 1.1vw, 1.15rem);
  line-height: 1.35;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: color 0.25s ease, transform 0.25s ease;
}

.vgen-footer__link:hover,
.vgen-footer__link:focus-visible {
  color: #fff;
  transform: translateX(0.25rem);
}

.vgen-footer__bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  padding-top: 1.8rem;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.34);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.vgen-footer__bottom strong {
  color: #9c79ff;
  font-weight: 600;
  text-transform: none;
}

.vgen-footer__star {
  margin: 0 0.6rem;
  color: rgba(255, 255, 255, 0.42);
}

.vgen-footer__legal {
  display: flex;
  gap: 1.5rem;
}

.vgen-footer__legal a {
  color: inherit;
  text-decoration: none;
  transition: color 0.25s ease;
}

.vgen-footer__legal a:hover,
.vgen-footer__legal a:focus-visible {
  color: #fff;
}

@media (max-width: 767px) {
  .vgen-footer {
    padding-inline: 1.25rem;
  }

  .vgen-footer__logo {
    width: min(82vw, 22rem);
  }

  .vgen-footer__grid {
    grid-template-columns: 1fr 1fr;
    gap: 2.5rem 1.5rem;
  }

  .vgen-footer__column:last-child {
    grid-column: 1 / -1;
  }

  .vgen-footer__bottom {
    flex-direction: column;
    align-items: flex-start;
    line-height: 1.65;
  }
}

/* ============================================================
   Mobile (touch / native-scroll) polish
   ============================================================ */
@media (max-width: 767px) {
  /* --- Full-bleed frosted backdrop behind section copy ---
     The ASCII model canvas is position:fixed at z-index:0; the section text
     overlays sit at z-index:2. Lift each copy wrapper to z-index:1 so its
     ::before band paints ABOVE the model but BELOW the text. The old
     .mobile-blur gradient sat at z-index:-1 (behind the model) and was too
     light, so the model showed straight through the copy. Draw the backdrop as
     a full-bleed band — 100vw, no radius, no border — flush to both screen
     edges like the reference (matching the site's own .mobile-blur:after look:
     a vertical translucent gradient + heavy blur), NOT an inset rounded card. */
  .landing__body,
  .section.introduction .mobile-blur,
  .manifesto__body,
  .team__head {
    position: relative;
    z-index: 1;
  }
  .landing__body::before,
  .section.introduction .mobile-blur::before,
  .manifesto__body::before,
  .team__head::before {
    content: "";
    position: absolute;
    top: -2.5rem;
    bottom: -2.5rem;
    left: 50%;
    width: 100vw;
    transform: translateX(-50%);
    z-index: -1;
    background: linear-gradient(180deg, rgba(10, 8, 24, 0.58), rgba(10, 8, 24, 0.85));
    -webkit-backdrop-filter: blur(22px) saturate(1.1);
    backdrop-filter: blur(22px) saturate(1.1);
  }
  /* superseded by the ::before band above */
  .mobile-blur::after { display: none !important; }

  /* .manifesto--introduction / .section.introduction zero out the generic
     .section vertical padding (padding:0 2.5rem) and clip with overflow:hidden,
     so the ::before band's bleed above gets cut off flush with the text —
     the band reads as touching the model/next-section boundary with no
     breathing room around the copy. Give these sections real vertical
     padding back on mobile so the band has room to show before the clip. */
  .manifesto--introduction,
  .section.introduction {
    padding-top: 3rem !important;
    padding-bottom: 3rem !important;
  }

  /* .manifesto__body is a flex item (flex-grow:0, no explicit width) that
     shrinks to fit its own text, so V1/V2/V3 each end up a different width
     depending on how long their longest unwrapped line is. Stretch all three
     to the same full width so they wrap consistently and match the full-bleed
     backdrop band instead of wobbling between blocks. */
  .manifesto__body {
    width: 100%;
  }

  /* NB: the buzz section is intentionally NOT given the full-bleed band. It
     already ships its own built-in liquid-glass card
     (#vgl-lightbulb .buzz-gallery-col--left/right) which reads well; forcing
     the band over its scattered gallery images produced a hard-edged dark
     rectangle. Keep that card — only widen it to full width below. */

  /* --- Buzz section: stack the two copy blocks full-width ---
     .buzz-gallery-columns is already a single-column grid on mobile, but each
     .buzz-gallery-col was capped at max-width:210px, leaving the copy in a
     narrow left strip. Let the columns fill the width so the two blocks read as
     full-width, top-to-bottom cards. */
  .buzz-gallery-col,
  .buzz-gallery-col--left,
  .buzz-gallery-col--right,
  .buzz-gallery-side-title,
  .buzz-gallery-side-copy {
    max-width: none !important;
    width: 100% !important;
  }
}

/* =========================================================================
   Studio repositioning (2026-09) — hero: eyebrow / supporting copy /
   secondary CTA / trust line.
   .vgl-hero__overlay is pointer-events:none, so every interactive child has
   to re-enable pointer events explicitly.
   ========================================================================= */
.vgl-hero__eyebrow {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.66rem, 1.15vw, 0.82rem);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
  margin-bottom: clamp(0.9rem, 1.8vw, 1.4rem);
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.6);
}
.vgl-hero__copy {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.95rem, 1.35vw, 1.15rem);
  line-height: 1.5;
  letter-spacing: -0.01em;
  font-weight: 200;
  color: rgba(255, 255, 255, 0.82);
  max-width: 54ch;
  margin: clamp(1.1rem, 2.2vw, 1.6rem) auto 0;
  text-shadow: 0 2px 22px rgba(0, 0, 0, 0.7);
}
/* Glass CTA + text CTA share one baseline row. */
.vgl-hero__cta-secondary {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  margin-top: clamp(1.8rem, 3vw, 2.6rem);
  margin-left: clamp(1rem, 2.4vw, 2rem);
  vertical-align: middle;
  pointer-events: auto;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.85rem, 1.15vw, 1rem);
  font-weight: 600;
  letter-spacing: 0.025em;
  text-transform: uppercase;
  color: #fff;
  text-decoration: none;
  padding: 1.05rem 0.2rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.32);
  transition: border-color 0.25s ease, opacity 0.25s ease;
}
.vgl-hero__cta-secondary:hover,
.vgl-hero__cta-secondary:focus-visible {
  border-bottom-color: #fff;
}
.vgl-hero__cta-secondary span {
  transition: transform 0.25s ease;
}
.vgl-hero__cta-secondary:hover span {
  transform: translateX(3px);
}
.vgl-hero__trust {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.68rem, 1vw, 0.8rem);
  font-weight: 400;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.55);
  margin-top: clamp(1.4rem, 2.6vw, 2.1rem);
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.7);
}
@media (max-width: 767px) {
  /* Give each hero action its own row and a comfortable phone-sized tap area. */
  .vgl-hero__overlay .hero-glass-cta {
    display: flex !important;
    justify-content: center;
    width: min(100%, 20rem);
    margin: 1.6rem auto 0;
  }
  .vgl-hero__overlay .hero-glass-cta__link {
    box-sizing: border-box;
    width: 100%;
    min-height: 3.25rem;
  }
  .vgl-hero__cta-secondary {
    display: flex;
    justify-content: center;
    width: max-content;
    min-height: 3rem;
    margin: 0.65rem auto 0;
    padding: 0.5rem 0.35rem;
  }
  .vgl-hero__copy { max-width: 34ch; }
}

/* Hero readability: the glass V sits directly behind the copy and washes out
   the eyebrow / trust line. A soft radial scrim inside the overlay's own
   stacking context (z-index:2) keeps the type legible without dimming the
   model itself. */
.vgl-hero__overlay::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(160vw, 1500px);
  height: min(120vh, 900px);
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    ellipse at center,
    rgba(6, 4, 16, 0.78) 0%,
    rgba(6, 4, 16, 0.6) 34%,
    rgba(6, 4, 16, 0.26) 58%,
    rgba(6, 4, 16, 0) 78%
  );
}

/* =========================================================================
   Reassurance CTA + Process — new sections added by the 2026-09 studio
   repositioning. Deliberately kept OUT of .js-section / the ASScroll section
   sequence so the WebGL shape choreography upstream is untouched.
   Motion level per the brief: reassurance = typographic and quiet,
   process = low.
   ========================================================================= */
.vgl-reassure {
  position: relative;
  z-index: 2;
  padding: clamp(6rem, 14vh, 11rem) 6vw;
  display: flex;
  justify-content: center;
  text-align: center;
}
.vgl-reassure__inner { max-width: 64rem; } /* Montserrat: room for "Just bring us the business goal." on one line */
.vgl-reassure__eyebrow,
.vgl-process__eyebrow {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #a78bfa;
  margin-bottom: 1.1rem;
}
.vgl-reassure__title {
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(1.9rem, 4.6vw, 3.6rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: #fff;
  margin: 0 0 1.4rem;
}
.vgl-reassure__copy {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.98rem, 1.4vw, 1.18rem);
  font-weight: 200;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.76);
  max-width: 44rem;
  margin: 0 auto 2.2rem;
}
.vgl-reassure__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 1.05rem 2.3rem;
  border-radius: 999px;
  background: #7160fc;
  color: #fff;
  text-decoration: none;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.025em;
  text-transform: uppercase;
  transition: background 0.25s ease, transform 0.25s ease;
}
.vgl-reassure__cta:hover { background: #8674ff; transform: translateY(-1px); }
.vgl-reassure__cta span { transition: transform 0.25s ease; }
.vgl-reassure__cta:hover span { transform: translateX(3px); }

@media (max-width: 600px) {
  .vgl-reassure { padding-left: 1rem; padding-right: 1rem; }
}


/* ---- Project brief form: select controls + reassurance note (2026-09) ----
   Native selects render with the OS light background on a dark page, so the
   appearance has to be reset and the arrow drawn manually. The <option> list
   itself is painted by the OS, hence the explicit dark color-scheme. */
.field__select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  color-scheme: dark;
  cursor: pointer;
  padding-right: 2.8rem;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='rgba(255,255,255,0.55)' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1.2rem center;
}
.field__select:invalid { color: rgba(255, 255, 255, 0.38); }
.field__select option { background: #14121f; color: #fff; }
.field__select option[disabled] { color: rgba(255, 255, 255, 0.4); }
.contact-note {
  margin-top: 1rem;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.88rem;
  line-height: 1.5;
  font-weight: 200;
  color: rgba(255, 255, 255, 0.58);
  max-width: 34rem;
}
.contact-form__error {
  display: none;
  margin-top: 1rem;
  padding: 0.9rem 1.1rem;
  border-radius: 10px;
  border: 1px solid rgba(255, 120, 120, 0.4);
  background: rgba(255, 90, 90, 0.1);
  color: #ffd9d9;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.9rem;
  line-height: 1.45;
}

/* ---- Persistent mobile primary CTA (2026-09) ----
   Phone-only. Sits above the fixed hero/case canvases (which are z-index 0-2),
   and retracts when the brief form scrolls into view so it never covers the
   inputs, the submit button, or the case/video controls. */
.vgl-sticky-cta {
  display: none;
}
@media (max-width: 767px) {
  .vgl-sticky-cta {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
    background: linear-gradient(180deg, rgba(6, 4, 16, 0) 0%, rgba(6, 4, 16, 0.86) 42%);
    pointer-events: none; /* only the button itself is tappable */
    transition: transform 0.3s ease, opacity 0.3s ease;
  }
  .vgl-sticky-cta.is-hidden {
    transform: translateY(120%);
    opacity: 0;
  }
  /* .btn 提供 flex 布局、紫底 #8052ff、overflow:hidden 与 .btn__hover 蓝色涨满动效,
     与 header CTA 一致;这里只覆盖尺寸与字形。 */
  .vgl-sticky-cta__link.btn {
    display: flex;
    width: 100%;
    pointer-events: auto;
    padding: 0.95rem 1.4rem;
    border-radius: 999px;
    color: #fff;
    text-decoration: none;
    box-shadow: 0 8px 26px rgba(0, 0, 0, 0.45);
  }
  .vgl-sticky-cta__link .btn__text {
    font-family: "PPNeueMontreal", system-ui, sans-serif;
    font-size: 0.95rem;
    line-height: 1.2;
    font-weight: 600;
    letter-spacing: 0.025em;
    text-transform: uppercase;
  }
}

/* ---- Hero: PressLogic endorsement lockup (2026-09) ----
   Sits directly above the eyebrow. The SVG is solid white, so opacity — not a
   colour — is what keeps it secondary to the headline. */
.vgl-hero__pl-logo {
  display: block;
  width: clamp(112px, 12vw, 158px);
  height: auto;
  margin: 0 auto clamp(0.85rem, 1.6vw, 1.15rem);
  opacity: 0.6;
  filter: drop-shadow(0 2px 14px rgba(0, 0, 0, 0.75));
}

/* ---- Project brief form: two-column field grid (2026-09) ----
   Pairs the short fields so the form reads as four compact rows instead of one
   long column. The textarea, the phone row, the submit button and the status /
   note lines still span the full width. The honeypot is position:absolute so it
   never takes a grid cell. Collapses back to a single column on narrow screens,
   where side-by-side inputs would be too cramped to tap. */
@media (min-width: 620px) {
  .contact-form {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 1.1rem;
    align-items: start;
  }
  .contact-form .field {
    min-width: 0; /* let inputs shrink inside the grid track */
  }
  .contact-form .field--wide,
  .contact-form__submit,
  .contact-form__done,
  .contact-form__note,
  .contact-form__error {
    grid-column: 1 / -1;
  }
  /* Budget Range 暂时被注释掉(等 Sales 给区间)时,Timing 独占一行、右侧空一格。
     这时让 Phone 退回半栏,自动补到 Timing 右边;Budget 恢复后本规则自动失效,
     Phone 回到通栏,无需再改。 */
  .contact-form:not(:has(#cf-budget)) .field--wide:has(#cf-phone) {
    grid-column: auto;
  }
}

/* ---- Selected Work backdrop: left to the global WebGL pass (2026-09) ----
   An earlier revision hung a dark scrim off #vgl3d-cases-mount to calm the
   purple-noise + ASCII field under this section. It is gone on purpose: the
   ASCII layer's own intensity was halved at the source instead
   (ambient-bg.js CONFIG.ascii.intensity 0.168 -> 0.084), and the two together
   crushed the section to near-black, hiding the brand purple. Do not
   re-introduce a scrim here — tune the shader intensity instead.
   (The far-field decor thinning in vgl3d-cases.js is a separate fix and stays.) */

/* ---- Designed dropdown (2026-09) ----
   Replaces the OS-drawn option list. The native <select> stays stacked over the
   trigger at full size, invisible and click-through, so required-validation
   still anchors its bubble to the right control. */
.vgl-select {
  position: relative;
}
.vgl-select .field__select {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: none;
  /* keep it a laid-out box for reportValidity(), but never painted */
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
}
.vgl-select__trigger {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  width: 100%;
  padding: 1.1rem 1.2rem;
  font-family: inherit;
  font-size: 15px;
  text-align: left;
  color: #fff;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 12px;
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.vgl-select__trigger:hover {
  background: rgba(255, 255, 255, 0.07);
}
.vgl-select__trigger:focus-visible,
.vgl-select.is-open .vgl-select__trigger {
  outline: none;
  border-color: rgba(146, 106, 255, 0.75);
  background: rgba(255, 255, 255, 0.07);
}
.vgl-select.is-invalid .vgl-select__trigger {
  border-color: rgba(255, 92, 138, 0.8);
}
.vgl-select.is-placeholder .vgl-select__value {
  color: rgba(255, 255, 255, 0.38);
}
.vgl-select__caret {
  flex: 0 0 auto;
  display: flex;
  color: rgba(255, 255, 255, 0.55);
  transition: transform 0.22s ease, color 0.22s ease;
}
.vgl-select.is-open .vgl-select__caret {
  transform: rotate(180deg);
  color: #a78bfa;
}
.vgl-select__list {
  position: absolute;
  z-index: 30;
  top: calc(100% + 0.45rem);
  left: 0;
  right: 0;
  margin: 0;
  padding: 0.4rem;
  list-style: none;
  max-height: 16rem;
  overflow-y: auto;
  background: rgba(16, 13, 28, 0.96);
  -webkit-backdrop-filter: blur(20px) saturate(1.2);
  backdrop-filter: blur(20px) saturate(1.2);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 14px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55);
  animation: vglSelectIn 0.16s ease-out;
}
@keyframes vglSelectIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.vgl-select__option {
  padding: 0.72rem 0.85rem;
  border-radius: 9px;
  font-size: 14.5px;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.82);
  cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease;
}
.vgl-select__option.is-active {
  background: rgba(113, 96, 252, 0.22);
  color: #fff;
}
.vgl-select__option[aria-selected="true"] {
  color: #fff;
  font-weight: 600;
}
.vgl-select__option[aria-selected="true"]::after {
  content: "";
  float: right;
  width: 0.5rem;
  height: 0.5rem;
  margin-top: 0.35rem;
  border-radius: 50%;
  background: #7160fc;
}
@media (prefers-reduced-motion: reduce) {
  .vgl-select__list { animation: none; }
  .vgl-select__caret { transition: none; }
}

/* =========================================================================
   Process — horizontal scroll timeline (2026-09 redesign)

   The reference implementation is React + GSAP ScrollTrigger. This site is
   static HTML driven by ASScroll, which transforms <main>: a transform ancestor
   becomes the containing block for position:fixed, and position:sticky misbehaves
   inside it. So the pin follows the pattern vgl3d-cases.js already proves on this
   page — the pinned layer is relocated to <body> and driven by a rAF loop that
   reads the spacer's rect. That also keeps it working with the native-scroll
   path used on touch devices.

   .vgl-process        = the tall spacer that owns the scroll distance
   .vgl-process__pin   = fixed viewport-sized layer (moved to <body> by JS)
   .vgl-process__track = the horizontally translated rail
   ========================================================================= */
.vgl-process {
  position: relative;
  z-index: 2;
  height: 340vh;
}
.vgl-process__pin {
  position: fixed;
  inset: 0;
  z-index: 6;
  display: flex;
  align-items: center;
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  will-change: opacity;
}
.vgl-process__pin.is-active {
  visibility: visible;
}
.vgl-process__track {
  display: flex;
  align-items: center;
  gap: clamp(3rem, 7vw, 8rem);
  padding: 0 clamp(1.25rem, 6vw, 7rem);
  will-change: transform;
}
/* ---- Left intro block (stands in for the reference's lead image) ---- */
.vgl-process__intro {
  flex: 0 0 auto;
  width: min(34rem, 78vw);
}
.vgl-process__title {
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(1.8rem, 3.4vw, 3rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: #fff;
  margin: 0 0 1.1rem;
}
.vgl-process__lead {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.92rem, 1.2vw, 1.05rem);
  font-weight: 200;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.7);
  margin: 0 0 1.6rem;
  max-width: 34rem;
}
.vgl-process__range {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
}
/* ---- The rail ---- */
.vgl-process__rail {
  position: relative;
  flex: 0 0 auto;
  height: clamp(22rem, 34vw, 30rem);
  display: flex;
  align-items: center;
}
/* Centre axis: the connecting line grows with scroll progress. */
.vgl-process__axis {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  pointer-events: none;
}
.vgl-process__cap {
  flex: 0 0 auto;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: #7160fc;
}
.vgl-process__line {
  height: 1px;
  width: 0%;
  background: linear-gradient(90deg, #7160fc, #a78bfa);
  border-radius: 999px;
}
.vgl-process__steps {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: clamp(2.5rem, 6vw, 6.5rem);
  height: 100%;
  margin: 0;
  padding: 0 clamp(1.5rem, 4vw, 4rem);
  list-style: none;
}
.vgl-process__step {
  position: relative;
  flex: 0 0 auto;
  width: clamp(15rem, 22vw, 20rem);
  display: flex;
  flex-direction: column;
}
/* Upper steps hold their copy above the axis, lower steps below it. */
.vgl-process__step--up { justify-content: flex-start; }
.vgl-process__step--down { justify-content: flex-end; }

/* Stem = dot on the axis + stalk reaching toward the copy. */
.vgl-process__stem {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 1px;
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
}
.vgl-process__dot {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0.7rem;
  height: 0.7rem;
  margin: -0.35rem 0 0 -0.35rem;
  border-radius: 50%;
  background: #a78bfa;
  box-shadow: 0 0 0 4px rgba(113, 96, 252, 0.18);
  transform: scale(0);
  will-change: transform;
}
.vgl-process__stalk {
  position: absolute;
  left: 0;
  width: 1px;
  height: 46%;
  background: linear-gradient(180deg, rgba(167, 139, 250, 0.9), rgba(113, 96, 252, 0.15));
  transform: scaleY(0);
  will-change: transform;
}
.vgl-process__step--up .vgl-process__stalk {
  bottom: 50%;
  transform-origin: bottom center;
}
.vgl-process__step--down .vgl-process__stalk {
  top: 50%;
  transform-origin: top center;
  background: linear-gradient(0deg, rgba(167, 139, 250, 0.9), rgba(113, 96, 252, 0.15));
}
.vgl-process__body {
  padding-left: 1.6rem;
  opacity: 0;
  transform: translateY(1.4rem);
  will-change: transform, opacity;
}
.vgl-process__step--up .vgl-process__body { padding-bottom: 3.2rem; }
.vgl-process__step--down .vgl-process__body { padding-top: 3.2rem; }
.vgl-process__num {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: #a78bfa;
  margin-bottom: 0.65rem;
}
.vgl-process__step-title {
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(1.15rem, 1.8vw, 1.6rem);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: #fff;
  margin: 0 0 0.6rem;
}
.vgl-process__step-copy {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.85rem, 1.05vw, 0.95rem);
  font-weight: 200;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.66);
  margin: 0;
}
/* Phones: the pinned rail is shorter and the cards narrower, and the intro
   block must not eat the whole first screen. */
@media (max-width: 767px) {
  .vgl-process { height: 420vh; }
  .vgl-process__track { gap: 2.5rem; padding: 0 1.25rem; }
  .vgl-process__intro { width: 82vw; }
  .vgl-process__rail { height: 26rem; }
  .vgl-process__step { width: 64vw; }
  .vgl-process__body { padding-right: 0.9rem; }
  .vgl-process__steps { gap: 1.5rem; padding: 0 0.5rem; }
  .vgl-process__body { padding-left: 1.2rem; }
}
@media (prefers-reduced-motion: reduce) {
  /* No pin, no horizontal travel: fall back to a plain readable stack. */
  .vgl-process { height: auto; }
  .vgl-process__pin {
    position: static;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    overflow: visible;
    padding: 4rem 0;
  }
  .vgl-process__track { flex-direction: column; align-items: flex-start; transform: none !important; }
  .vgl-process__rail { height: auto; }
  .vgl-process__axis { display: none; }
  .vgl-process__steps { flex-direction: column; height: auto; padding: 0; }
  .vgl-process__step { width: 100%; }
  .vgl-process__body { opacity: 1 !important; transform: none !important; padding: 0 0 0 1.2rem; }
  .vgl-process__dot { transform: scale(1) !important; }
  .vgl-process__stalk { display: none; }
}

/* Process timeline: frosted panel instead of a scrim (2026-09).
   The earlier full-bleed dark gradient crushed the section the same way the
   case-section scrim did. This replaces it with the same frosted plate the
   brief form uses (rgba(18,16,30,.55) + blur(16px) + hairline border), run as a
   full-width band rather than a rounded card: it keeps the step copy legible
   while the brand purple still reads through the blur.
   It sits at z-index:-1 inside the pin, so it stays put while the rail
   translates over it and inherits the pin's scroll-driven opacity. */
.vgl-process__pin::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  /* Matches .vgl-process__rail's height plus breathing room above/below. */
  height: calc(clamp(22rem, 34vw, 30rem) + 7rem);
  transform: translateY(-50%);
  z-index: -1;
  pointer-events: none;
  background: rgba(18, 16, 30, 0.55);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}
@media (max-width: 767px) {
  .vgl-process__pin::before { height: calc(26rem + 4rem); }
}

.vgl-process__step-copy { color: rgba(255, 255, 255, 0.76); }
.vgl-process__lead { color: rgba(255, 255, 255, 0.78); }

/* ---- Selected Work: per-case results block ----
   Since the 2026-10 review this holds the results (stats + source); see
   "Case panel, 2026-10 review" at the end of this file. */
.case-fact {
  margin: 1rem 0 1.15rem;
  padding: 0;
  display: grid;
  gap: 0.42rem;
}
.case-fact dt {
  margin: 0;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #a78bfa;
}
.case-fact dd {
  margin: 0;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.84rem;
  font-weight: 200;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.78);
}
@media (max-width: 900px) {
  .case-fact { gap: 0.6rem; }
}

/* ---- Post-work conversion band (2026-09, brief Section 04) ----
   The brief asks for a real CTA band between Selected Work and Services. What
   existed was only the decorative "SEE A DIRECTION WORTH EXPLORING?" text drawn
   onto the 3D canvas (aria-hidden, no button). This is the actionable one.
   Frosted plate like the form/process, because the ASCII field is visible again
   right after the case section. */
.vgl-postwork {
  position: relative;
  z-index: 2;
  /* 左右不留 padding —— 磨砂带要通到屏幕两边 */
  padding: clamp(3.5rem, 8vh, 6rem) 0;
}
/* 满屏宽的通栏磨砂带(不是居中卡片):跟屏同宽、无圆角、只保留上下发丝边,
   与 Process 那条带子同一套做法。内容自身再限宽居中,超宽屏上文字不会拉长。 */
.vgl-postwork__inner {
  width: 100%;
  text-align: center;
  padding: clamp(2.2rem, 4.5vw, 3.4rem) 6vw;
  border-radius: 0;
  background: rgba(18, 16, 30, 0.55);
  border: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}
.vgl-postwork__title {
  /* 左右 auto:通栏后标题要在整屏内居中;限宽避免超宽屏上拉成一长条 */
  max-width: 30ch;
  margin: 0 auto 1rem;
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(1.5rem, 3.2vw, 2.4rem);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: #fff;
}
.vgl-postwork__copy {
  margin: 0 auto 1.8rem;
  max-width: 44rem;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.92rem, 1.3vw, 1.08rem);
  font-weight: 200;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.76);
}
.vgl-postwork__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 1.05rem 2.3rem;
  border-radius: 999px;
  background: #7160fc;
  color: #fff;
  text-decoration: none;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.025em;
  text-transform: uppercase;
  transition: background 0.25s ease, transform 0.25s ease;
}
.vgl-postwork__cta:hover { background: #8674ff; transform: translateY(-1px); }
.vgl-postwork__cta span { transition: transform 0.25s ease; }
.vgl-postwork__cta:hover span { transform: translateX(3px); }
@media (max-width: 600px) {
  /* 仍然通栏,只把内容的左右留白收窄 */
  .vgl-postwork__inner { padding-left: 1.25rem; padding-right: 1.25rem; }
}


/* ============================================================
   2026-09-24 体检修复集中块(#1 #8 #9 #11 #14)。
   都是在原规则之后追加的覆盖,要回退某一项直接删对应小节即可。
   ============================================================ */

/* ---- #14 导航 About:theme.js 只给 .js-nav-link 逐字拆成 .nav__link__chars
   (inline-block + padding-top:7px,行盒 22px);About 是普通跳页链接没被拆,只有
   15px,和 Contact 挤在一起。HTML 里给 About 整词包了一层同款 .nav__link__chars
   (只用样式类,不挂 js-nav-link-chars 钩子)→ 各断点与其它项同高。 ---- */

/* ---- #9 案例标题只出现一次:右栏只留标签,不再重复标题 ---- */
.vgl3d-cases__pin .case-info--right .case-headline {
  display: none;
}
.vgl3d-cases__pin .case-info--right .case-tags {
  margin-bottom: 0;
}

/* ---- #8 桌面:左右信息栏不能压到中间的视频石头 ----
   石头的屏幕尺寸主要跟视口高度走(相机是竖向 FOV),实测 1024×768 时约 295px 宽、
   居中。所以栏宽 = 半屏 - 左边距(4vw) - 石头半宽(≈21vh) - 间隙(2rem),
   再夹在 13rem~29rem、且不超过原来的 31%。宽屏下跟原来一样,窄桌面/平板自动收窄。 */
@media (min-width: 768px) {
  .vgl3d-cases__pin .case-info {
    width: min(31%, clamp(13rem, calc(46vw - 21vh - 2rem), 29rem));
  }
}

/* ---- #1 手机:案例信息从"只有标签+标题"改回完整信息 ----
   原来 ≤767px 把左栏整个 display:none,手机上看不到说明、Objective/Role/
   Deliverables 三行事实和"Discuss a Project Like This"。现在:左栏移到屏幕底部
   作为唯一信息面板(标题 + 两行说明 + 三行事实 + CTA),右栏(标签)隐藏。
   底板沿用站内手机端的定稿样式:满宽通栏、无圆角、磨砂渐变,上沿渐隐。 */
@media (max-width: 767px) {
  .vgl3d-cases__pin .case-info--right {
    display: none;
  }
  .vgl3d-cases__pin .case-info--left {
    display: block;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-width: none;
    box-sizing: border-box;
    padding: 1.9rem 1.25rem calc(1.1rem + env(safe-area-inset-bottom, 0px));
    text-align: left;
    transform: translate3d(0, calc(var(--case-panel-shift, 1) * 24px), 0);
  }
  .vgl3d-cases__pin .case-info--left::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(180deg, rgba(10, 8, 24, 0) 0%, rgba(10, 8, 24, 0.72) 26%, rgba(10, 8, 24, 0.9) 100%);
    -webkit-backdrop-filter: blur(18px) saturate(1.1);
    backdrop-filter: blur(18px) saturate(1.1);
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 1.6rem);
    mask-image: linear-gradient(to bottom, transparent 0, #000 1.6rem);
    pointer-events: none;
  }
  .vgl3d-cases__pin .case-info--left .case-title {
    font-size: clamp(1.1rem, 5vw, 1.35rem);
    margin-bottom: 0.45rem;
  }
  .vgl3d-cases__pin .case-info--left .case-body {
    font-size: 0.9rem;
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .vgl3d-cases__pin .case-fact {
    margin: 0.7rem 0 0.2rem;
    gap: 0.28rem;
  }
  .vgl3d-cases__pin .case-cta {
    margin-top: 0.7rem;
    padding: 0.35rem 0; /* 触控目标高度 */
  }
}

/* ---- #11 小字可读性 ----
   主要问题不是颜色而是字重:这些小字都是 font-weight:200(极细)+ 0.55~0.78 透明度,
   压在深紫背景和 3D 物体上几乎糊掉。统一提到 Regular(400,已 preload)+ ≥0.8 不透明,
   并给压在 3D 上的几处加一层柔和暗投影。 */
.vgl-hero__trust {
  font-size: clamp(0.78rem, 1vw, 0.9rem);
  color: rgba(255, 255, 255, 0.8);
}
.vgl3d-cases__pin .case-body {
  font-weight: 400;
  color: rgba(255, 255, 255, 0.86);
}
.case-fact dt {
  font-size: 0.7rem;
  color: #b9a2ff;
}
.case-fact dd {
  font-size: 0.88rem;
  font-weight: 400;
  color: rgba(255, 255, 255, 0.88);
}
.vgl3d-cases__pin .case-info {
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.7);
}
.vgl-process__step-copy {
  font-size: clamp(0.9rem, 1.05vw, 1rem);
  font-weight: 400;
  color: rgba(255, 255, 255, 0.82);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);
}
.vgl-process__lead,
.vgl-postwork__copy,
.vgl-reassure__copy {
  font-weight: 400;
  color: rgba(255, 255, 255, 0.84);
}
.contact-form__note {
  color: rgba(255, 255, 255, 0.72);
}

/* ---- #18 FAQ(Process 与表单之间)----
   文档一 §10:表单附近要"最安静、最清楚"——所以只做原生 <details> 手风琴,
   无滚动动画;底板与 postwork/process 同款磨砂通栏。3D V 经过这里时由
   ascii-digits-hero.js 的 foregroundSections 压到 UI 后面。 */
.vgl-faq {
  position: relative;
  z-index: 2;
  padding: clamp(3.5rem, 9vh, 6.5rem) 0;
}
.vgl-faq::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: rgba(18, 16, 30, 0.55);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}
.vgl-faq__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
  gap: clamp(2rem, 5vw, 5rem);
  max-width: 76rem;
  margin: 0 auto;
  padding: 0 5vw;
  color: #fff;
}
.vgl-faq__eyebrow {
  font-size: 14px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 500;
  color: #a78bfa;
  margin-bottom: 1rem;
}
.vgl-faq__title {
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(1.9rem, 3.2vw, 2.8rem);
  font-weight: 700;
  line-height: 1.08;
  margin: 0 0 1.1rem;
}
.vgl-faq__lead {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.95rem, 1.2vw, 1.05rem);
  font-weight: 400;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.78);
  margin: 0;
  max-width: 24rem;
}
.vgl-faq__list {
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.vgl-faq__item {
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
}
.vgl-faq__q {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  padding: 1.25rem 0;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(1rem, 1.3vw, 1.15rem);
  font-weight: 500;
  line-height: 1.35;
  color: #fff;
}
.vgl-faq__q::-webkit-details-marker {
  display: none;
}
.vgl-faq__q:hover {
  color: #cdbcff;
}
.vgl-faq__q:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 4px;
}
/* "+" → "–" */
.vgl-faq__icon {
  position: relative;
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
}
.vgl-faq__icon::before,
.vgl-faq__icon::after {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 100%;
  height: 1.5px;
  background: #a78bfa;
  transform: translateY(-50%);
  transition: transform 0.25s ease;
}
.vgl-faq__icon::after {
  transform: translateY(-50%) rotate(90deg);
}
.vgl-faq__item[open] .vgl-faq__icon::after {
  transform: translateY(-50%) rotate(0deg);
}
.vgl-faq__a p {
  margin: 0;
  padding: 0 2.5rem 1.35rem 0;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(0.92rem, 1.1vw, 1rem);
  font-weight: 400;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.82);
  max-width: 44rem;
}
@media (max-width: 767px) {
  .vgl-faq__inner {
    grid-template-columns: 1fr;
    gap: 1.6rem;
    padding: 0 1.25rem;
  }
  .vgl-faq__a p {
    padding-right: 0;
  }
}

/* ---- PressLogic 背书(2026-09-24,参照香港版 trust bridge)----
   ① 首屏徽章:把原来 60% 透明的小 logo 升级成 "POWERED BY │ PressLogic" 胶囊,
      5 秒内看得到集团关系(文档一 §1 / 文档三 P0)。
   ② #presslogic 背书区:作品 + 转化带之后、服务之前。左文右卡;右卡的位置
      预留给以后的 V→PL 粒子变形(V 现在经过时退到本区磨砂底板后面)。 */
.vgl-hero__pl-badge {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  width: fit-content;
  margin: 0 auto clamp(0.9rem, 1.6vw, 1.25rem);
  padding: 0.5rem 0.9rem 0.5rem 0.85rem;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  background: rgba(10, 8, 20, 0.45);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 8px 30px rgba(0, 0, 0, 0.35);
}
.vgl-hero__pl-badge-label {
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.66);
  white-space: nowrap;
}
.vgl-hero__pl-badge .vgl-hero__pl-logo {
  width: clamp(88px, 7.5vw, 112px);
  margin: 0;
  opacity: 1;
  filter: none;
}

.vgl-pl-bridge {
  position: relative;
  z-index: 2;
  padding: clamp(5rem, 11vh, 8.5rem) 0;
  color: #fff;
}
.vgl-pl-bridge::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(40% 60% at 78% 50%, rgba(113, 96, 252, 0.2), transparent 70%),
    linear-gradient(90deg, rgba(8, 6, 16, 0.9) 0%, rgba(8, 6, 16, 0.74) 55%, rgba(8, 6, 16, 0.6) 100%);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}
.vgl-pl-bridge__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 0.7fr);
  gap: clamp(2rem, 5vw, 5.5rem);
  align-items: center;
  max-width: 84rem;
  margin: 0 auto;
  padding: 0 5vw;
}
.vgl-pl-bridge__lockup {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: clamp(1.6rem, 3.5vw, 2.6rem);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
}
.vgl-pl-bridge__lockup i {
  font-style: normal;
  color: rgba(255, 255, 255, 0.35);
}
.vgl-pl-bridge__lockup strong {
  font-weight: 600;
  color: #cdbcff;
}
.vgl-pl-bridge__eyebrow {
  margin: 0 0 1rem;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #a78bfa;
}
.vgl-pl-bridge__title {
  max-width: 21ch;
  margin: 0;
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(2.3rem, 3.6vw, 3.9rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.02em;
}
.vgl-pl-bridge__title span {
  color: #a78bfa;
}
.vgl-pl-bridge__body {
  max-width: 38rem;
  margin: clamp(1.3rem, 2.4vw, 1.8rem) 0 0;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(1rem, 1.25vw, 1.15rem);
  font-weight: 400;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.82);
}
.vgl-pl-bridge__points {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.9rem, 2vw, 1.6rem);
  margin: clamp(1.8rem, 3.5vw, 2.6rem) 0 0;
  padding: clamp(1.2rem, 2vw, 1.5rem) 0 0;
  list-style: none;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.vgl-pl-bridge__points li {
  display: flex;
  gap: 0.7rem;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.86rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.74);
}
.vgl-pl-bridge__points b {
  flex: 0 0 auto;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: #a78bfa;
  padding-top: 0.2rem;
}
.vgl-pl-bridge__points strong {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 0.95rem;
  font-weight: 600;
  color: #fff;
}
.vgl-pl-bridge__link {
  display: inline-flex;
  gap: 0.6rem;
  margin-top: clamp(1.8rem, 3vw, 2.4rem);
  padding-bottom: 0.4rem;
  border-bottom: 1px solid rgba(167, 139, 250, 0.6);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  text-decoration: none;
  transition: color 0.25s ease, gap 0.25s ease;
}
.vgl-pl-bridge__link:hover {
  color: #cdbcff;
  gap: 0.9rem;
}
.vgl-pl-bridge__card {
  position: relative;
  display: flex;
  flex-direction: column; /* 上下结构:两个 logo 各自可以占满卡片宽度的 ~78% */
  align-items: center;
  justify-content: center;
  gap: clamp(1rem, 1.8vw, 1.5rem);
  aspect-ratio: 1 / 0.92;
  padding: 2rem;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 26px;
  background:
    radial-gradient(70% 70% at 50% 45%, rgba(113, 96, 252, 0.28), rgba(17, 16, 26, 0.35) 72%),
    rgba(12, 10, 22, 0.55);
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.1);
  overflow: hidden;
}
.vgl-pl-bridge__card::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.04) 0 1px, transparent 1px 44px),
    repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.04) 0 1px, transparent 1px 44px);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000, transparent 72%);
  mask-image: radial-gradient(circle at 50% 50%, #000, transparent 72%);
}
.vgl-pl-bridge__vgl,
.vgl-pl-bridge__pl {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  width: min(78%, 20rem);
  height: auto;
}
.vgl-pl-bridge__x {
  position: relative;
  z-index: 1;
  color: #a78bfa;
  font-size: clamp(1.4rem, 2.3vw, 2rem);
  font-weight: 200;
  line-height: 1;
}
.vgl-pl-bridge__tag {
  position: absolute;
  left: 1.4rem;
  bottom: 1.2rem;
  z-index: 1;
  font-size: 0.64rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
}
@media (max-width: 900px) {
  .vgl-pl-bridge__inner {
    grid-template-columns: 1fr;
  }
  .vgl-pl-bridge__visual {
    order: -1;
  }
  /* 卡片堆到上方后,紧跟的文字版 "VGenLab × PressLogic" 就重复了 */
  .vgl-pl-bridge__lockup {
    display: none;
  }
  .vgl-pl-bridge__card {
    aspect-ratio: auto;
    min-height: 15rem;
  }
}
@media (max-width: 767px) {
  .vgl-pl-bridge {
    padding: 3.5rem 0;
  }
  .vgl-pl-bridge__inner {
    padding: 0 1.25rem;
    gap: 1.8rem;
  }
  .vgl-pl-bridge__title {
    font-size: clamp(2.2rem, 10vw, 3rem);
  }
  .vgl-pl-bridge__points {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
  .vgl-pl-bridge__card {
    min-height: 14.5rem;
    padding: 1.8rem 1rem 3.4rem; /* 给左下角 "PressLogic / Foundation" 标签留出与 logo 的间距 */
  }
  .vgl-pl-bridge__vgl,
  .vgl-pl-bridge__pl {
    width: min(72%, 15rem);
  }
  .vgl-hero__pl-badge {
    padding: 0.42rem 0.75rem;
    gap: 0.55rem;
  }
  .vgl-hero__pl-badge .vgl-hero__pl-logo {
    width: 84px;
  }
}

/* ---- 服务卡 / 合作流程步骤图标(2026-09-24)----
   一套 24×24、1.5px 线性描边的图标(index.html 顶部 <symbol> 雪碧图),垫一块与
   全站一致的紫色毛玻璃小方块。只用 currentColor,悬停时整体提亮。 */
.vgl-icon-tile {
  flex: 0 0 auto;
  display: inline-grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 15px;
  color: #cbb9ff;
  background: linear-gradient(145deg, rgba(146, 106, 255, 0.26), rgba(146, 106, 255, 0.06));
  border: 1px solid rgba(167, 139, 250, 0.36);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 10px 30px rgba(80, 50, 200, 0.2);
  transition: color 0.3s ease, border-color 0.3s ease, background 0.3s ease, transform 0.3s ease;
}
.vgl-icon-tile svg {
  width: 26px;
  height: 26px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.vgl-icon-tile--sm {
  width: 38px;
  height: 38px;
  border-radius: 11px;
}
.vgl-icon-tile--sm svg {
  width: 20px;
  height: 20px;
}

/* 服务卡:图标在左、编号在右 */
.wf-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.4rem;
}
.wf-card__head .wf-card__index {
  margin-bottom: 0;
}
.wf-card:hover .vgl-icon-tile {
  color: #fff;
  border-color: rgba(190, 170, 255, 0.6);
  background: linear-gradient(145deg, rgba(146, 106, 255, 0.42), rgba(146, 106, 255, 0.12));
  transform: translateY(-2px);
}

/* 流程步骤:编号行 = 小图标 + 编号 */
.vgl-process__num {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

@media (max-width: 767px) {
  .vgl-icon-tile {
    width: 46px;
    height: 46px;
    border-radius: 13px;
  }
  .vgl-icon-tile svg {
    width: 23px;
    height: 23px;
  }
  .vgl-icon-tile--sm {
    width: 34px;
    height: 34px;
  }
  .vgl-icon-tile--sm svg {
    width: 18px;
    height: 18px;
  }
}

/* ---- 2026-09-30 案例信息左右平衡 + 拖转晶片 ----
   左栏:标题 + 说明 + CTA;右栏:标签 + 目标 / 职责 / 交付(vgl3d-cases.js 按断点把
   .case-fact 搬到右栏,手机仍留在左栏底部面板)。右栏整体贴右边距、内部左对齐,
   读起来是一栏规整的项目档案,和左栏的叙述分量对等。
   右栏比左栏窄:晶片停在中央时右缘最远到约 50vw + 26vh + 7vw(实测 1440×900 为
   1018px,案例五最偏右),栏宽 40vw − 26vh 让左缘退到它外面;窄栏里事实改成
   「标签在上、内容在下」。 */
@media (min-width: 768px) {
  /* 左栏沿用 #8 的「半屏 − 边距 − 石头半宽 − 间隙」公式,只把石头半宽从方块的
     21vh 改成晶片的 26vh(晶片 replacementScale 0.5,实测居中时宽约 52vh)。 */
  .vgl3d-cases__pin .case-info--left {
    width: min(31%, clamp(13rem, calc(46vw - 26vh - 2rem), 29rem));
  }
  .vgl3d-cases__pin .case-info--right {
    text-align: left;
    width: clamp(13rem, calc(40vw - 26vh), 22rem);
  }
  .vgl3d-cases__pin .case-info--right .case-tags {
    justify-content: flex-start;
  }
  .vgl3d-cases__pin .case-info--right .case-fact {
    margin: 1.35rem 0 0;
    padding-top: 1.2rem;
    border-top: 1px solid rgba(255, 255, 255, 0.14);
    gap: 0.9rem;
  }
}
/* 画面中央那块晶片可以按住拖转(见 vgl3d-cases.js 的 CHIP_DRAG) */
.vgl3d-cases__pin.can-spin-chip #vgl3d-cases-mount canvas {
  cursor: grab;
}
.vgl3d-cases__pin.is-chip-dragging #vgl3d-cases-mount canvas {
  cursor: grabbing;
}
.vgl3d-cases__pin.is-chip-dragging {
  -webkit-user-select: none;
  user-select: none;
}

/* =========================================================================
   Case panel, 2026-10 review (copy: vgl3d-cases.js CASE_COPY).
   左栏:品牌 / 标题 / 一句说明 / CTA;右栏:标签 / What we did / Results。
   手机:右栏隐藏,Results 由 placeCaseFacts() 搬回左栏底部面板;"做了什么"不出,
   晶片里的视频片段本身就在讲这件事。
   待补(.is-pending):线上不出现(数字不能估算);html.vgl-draft(本机/局域网
   或 ?draft)显示成 TBC 虚线框。整组数字都待补时 .case-fact 自带 is-pending。
   基础样式里 .case-fact dt/dd 是旧「目标/职责/交付」的小号紫色字,这里用
   .vgl3d-cases__pin 前缀覆盖。
   ========================================================================= */
.vgl3d-cases__pin .case-brand {
  margin: 0 0 0.75rem;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.4;
  color: #b9a2ff;
}
.vgl3d-cases__pin .case-label {
  margin: 0 0 0.6rem;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.vgl3d-cases__pin .case-proof {
  margin-top: 1.35rem;
  padding-top: 1.2rem;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.vgl3d-cases__pin .case-proof:empty { display: none; }
.vgl3d-cases__pin .case-proof__list {
  display: grid;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: case-proof;
}
.vgl3d-cases__pin .case-proof__list li {
  counter-increment: case-proof;
  display: grid;
  grid-template-columns: 1.6rem minmax(0, 1fr);
  column-gap: 0.35rem;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.82rem;
  line-height: 1.35;
}
.vgl3d-cases__pin .case-proof__list li::before {
  content: counter(case-proof, decimal-leading-zero);
  padding-top: 0.12rem;
  font-size: 0.66rem;
  font-weight: 600;
  color: rgba(167, 139, 250, 0.7);
  transition: color 0.25s ease;
}
/* 每一项就是缩略图条里的一段:可点(跳到那段并锁定),当前项和缩略图同步高亮 */
.vgl3d-cases__pin .case-proof__item {
  display: grid;
  gap: 0.1rem;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  color: inherit;
  text-shadow: inherit;
}
.vgl3d-cases__pin button.case-proof__item { cursor: pointer; }
.vgl3d-cases__pin .case-proof__list b { font-weight: 600; color: rgba(255, 255, 255, 0.82); transition: color 0.25s ease; }
.vgl3d-cases__pin .case-proof__list span { font-weight: 400; color: rgba(255, 255, 255, 0.6); transition: color 0.25s ease; }
.vgl3d-cases__pin .case-proof__item:hover b,
.vgl3d-cases__pin .case-proof__item.is-active b { color: #fff; }
.vgl3d-cases__pin .case-proof__item.is-active span { color: rgba(255, 255, 255, 0.85); }
.vgl3d-cases__pin .case-proof__list li:has(.is-active)::before { color: #c4b5fd; }
.vgl3d-cases__pin .case-proof__item:focus-visible { outline: 2px solid #a78bfa; outline-offset: 3px; border-radius: 4px; }
/* 这一步做了、但画面待补:文字照常,稍弱一点;草稿里标出来 */
.vgl3d-cases__pin .case-proof__list li.is-pending { opacity: 0.7; }
html.vgl-draft .vgl3d-cases__pin .case-proof__list li.is-pending b::after {
  content: " · 待補素材";
  font-weight: 400;
  font-size: 0.66rem;
  color: rgba(190, 180, 255, 0.9);
}
.vgl3d-cases__pin .case-fact { display: block; }
.vgl3d-cases__pin .case-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.85rem 1rem;
  margin: 0;
}
.vgl3d-cases__pin .case-stat {
  display: flex;
  flex-direction: column-reverse;
  justify-content: flex-end;
  gap: 0.25rem;
  margin: 0;
}
.vgl3d-cases__pin .case-stat dd {
  margin: 0;
  font-family: "Montserrat", "PPNeueMontreal", system-ui, sans-serif;
  font-size: clamp(1.25rem, 1.8vw, 1.7rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.01em;
  color: #fff;
}
.vgl3d-cases__pin .case-stat dt {
  margin: 0;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.3;
  text-transform: none;
  color: rgba(255, 255, 255, 0.72);
}
.vgl3d-cases__pin .case-source,
.vgl3d-cases__pin .case-note {
  margin: 0.75rem 0 0;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  font-size: 0.64rem;
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.5);
}
.vgl3d-cases__pin .case-note { margin-top: 0.35rem; }
.vgl3d-cases__pin .case-fact.is-pending,
.vgl3d-cases__pin .case-stat.is-pending,
.vgl3d-cases__pin .case-source.is-pending { display: none; }
html.vgl-draft .vgl3d-cases__pin .case-fact.is-pending,
html.vgl-draft .vgl3d-cases__pin .case-source.is-pending { display: block; }
html.vgl-draft .vgl3d-cases__pin .case-stat.is-pending { display: flex; opacity: 0.5; }
html.vgl-draft .vgl3d-cases__pin .case-stat.is-pending dd,
html.vgl-draft .vgl3d-cases__pin .case-source.is-pending {
  outline: 1px dashed rgba(169, 155, 255, 0.55);
  outline-offset: 3px;
}
@media (max-width: 767px) {
  .vgl3d-cases__pin .case-brand { margin-bottom: 0.4rem; font-size: 0.66rem; }
  .vgl3d-cases__pin .case-fact .case-label { display: none; }
  .vgl3d-cases__pin .case-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.5rem; }
  .vgl3d-cases__pin .case-stat dd { font-size: 1rem; }
  .vgl3d-cases__pin .case-stat dt { font-size: 0.58rem; }
  .vgl3d-cases__pin .case-source,
  .vgl3d-cases__pin .case-note { margin-top: 0.45rem; font-size: 0.56rem; }
}

/* ---- Proof strip (2026-10) — under the chip on desktop, top of the phone
   panel (vgl3d-cases.js placeCaseFacts 搬节点)。一个缩略图 = 晶片视频里的一段;
   不点时跟着视频走(当前项高亮 + 底部进度线),点击 = 跳过去并锁定(进度线满格),
   See all = 图库总览。线上不足 2 个已到位画面就不出;草稿里待补项是虚线占位。
   2026-10-16 放大(72px → 100–148px,"一眼看得见"):最宽 = 左边距到右栏左缘
   (92vw − 右栏宽 − 间隙),放不下时缩略图等比收窄;右栏垂到这一行时
   vgl3d-cases.js 的 fitStrip() 用 --case-strip-dx 把整条往左挪开,不压右栏。 ---- */
.vgl3d-cases__pin .case-strip {
  --case-thumb-w: clamp(100px, 8.5vw, 148px);
  position: absolute;
  left: 50%;
  bottom: clamp(1.25rem, 4.5vh, 2.75rem);
  z-index: 9;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: max-content;
  max-width: calc(92vw - clamp(13rem, calc(40vw - 26vh), 22rem) - 24px); /* 24px = fitStrip 的 gap */
  opacity: var(--case-panel-opacity, 1);
  transform: translate3d(calc(-50% + var(--case-strip-dx, 0px)), calc(var(--case-panel-shift, 1) * 24px), 0);
  transition: opacity 0.22s ease, transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
  pointer-events: none;
  font-family: "PPNeueMontreal", system-ui, sans-serif;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.7);
}
.vgl3d-cases__pin.is-cta-complete .case-strip { visibility: hidden; }
.vgl3d-cases__pin .case-strip:empty,
.vgl3d-cases__pin .case-strip.is-sparse { display: none; }
html.vgl-draft .vgl3d-cases__pin .case-strip.is-sparse { display: flex; }
.vgl3d-cases__pin .case-strip__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  min-width: 0;
  /* 条宽只由缩略图那一行决定:说明文字随视频换段时变长变短,不该让整条跟着伸缩、重新居中 */
  contain: inline-size;
}
.vgl3d-cases__pin .case-strip__caption {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
  margin: 0;
  font-size: 0.84rem;
  line-height: 1.3;
  white-space: nowrap;
}
.vgl3d-cases__pin .case-strip__caption em {
  font-style: normal;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: #a78bfa;
}
.vgl3d-cases__pin .case-strip__caption b { font-weight: 600; color: #fff; }
.vgl3d-cases__pin .case-strip__caption span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: rgba(255, 255, 255, 0.66);
}
.vgl3d-cases__pin .case-strip__all {
  flex: none;
  padding: 0.2rem 0;
  border: 0;
  background: none;
  font: 600 0.7rem/1 "PPNeueMontreal", system-ui, sans-serif;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #b9a2ff;
  cursor: pointer;
  transition: color 0.2s ease;
}
.vgl3d-cases__pin .case-strip__all:hover { color: #fff; }
.vgl3d-cases__pin .case-strip__row { display: flex; gap: 0.625rem; }
.vgl3d-cases__pin .case-strip__thumb {
  position: relative;
  flex: 0 1 var(--case-thumb-w);
  width: var(--case-thumb-w);
  min-width: 56px;
  height: auto;
  aspect-ratio: 4 / 3;
  padding: 0;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 12px;
  background: #0d0b19;
  opacity: 0.7;
  cursor: pointer;
  transition: opacity 0.25s ease, border-color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}
.vgl3d-cases__pin .case-strip__thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.vgl3d-cases__pin .case-strip__thumb:hover { opacity: 0.95; }
.vgl3d-cases__pin .case-strip__thumb.is-active {
  opacity: 1;
  border-color: #a78bfa;
  transform: translateY(-3px);
  box-shadow: 0 8px 22px rgba(113, 96, 252, 0.35);
}
.vgl3d-cases__pin .case-strip__thumb::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: #a78bfa;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.2s linear;
}
.vgl3d-cases__pin .case-strip__thumb.is-active::after { transform: scaleX(var(--p, 0)); }
.vgl3d-cases__pin .case-strip.is-pinned .case-strip__thumb.is-active::after { transform: scaleX(1); }
.vgl3d-cases__pin .case-strip__thumb:focus-visible,
.vgl3d-cases__pin .case-strip__all:focus-visible { outline: 2px solid #a78bfa; outline-offset: 3px; }
.vgl3d-cases__pin .case-strip__thumb.is-pending { display: none; }
html.vgl-draft .vgl3d-cases__pin .case-strip__thumb.is-pending {
  display: flex;
  align-items: center;
  justify-content: center;
  border-style: dashed;
  border-color: rgba(169, 155, 255, 0.6);
  opacity: 0.85;
  cursor: help;
}
.vgl3d-cases__pin .case-strip__thumb.is-pending i {
  padding: 0.2rem;
  font-style: normal;
  font-size: 0.62rem;
  line-height: 1.2;
  text-align: center;
  color: rgba(190, 180, 255, 0.92);
}
/* 鼠标设备上可直接点;触屏时 pin 保持 pointer-events:none(纵向手势要穿透给原生
   滚动),点按由 vgl3d-cases.js 的 tap 识别按坐标转发。 */
html:not(.is-touch) .vgl3d-cases__pin .case-strip button,
html:not(.is-touch) .vgl3d-cases__pin button.case-proof__item { pointer-events: auto; }
@media (max-width: 767px) {
  .vgl3d-cases__pin .case-info--left .case-strip {
    position: static;
    width: auto;
    max-width: none;
    margin: 0 0 0.9rem;
    opacity: 1;
    transform: none;
  }
  .vgl3d-cases__pin .case-strip__caption { font-size: 0.74rem; }
  .vgl3d-cases__pin .case-strip__row { gap: 0.4rem; }
  /* 手机:按面板宽度均分(最大 92px),原来固定 56px,6 张以上会被屏幕右缘切掉 */
  .vgl3d-cases__pin .case-strip__thumb { flex: 1 1 0; width: auto; max-width: 92px; min-width: 0; border-radius: 8px; }
}

/* ---- 2026-10-23 全站标题统一 Montserrat ----
   Montserrat 比原来的 ClashDisplay 宽,长标题容易剩一个词单独成行("goal." / "us.")。
   所有区块标题按行平衡断行。 */
.js-team-section h2,
.introduction__title,
.manifesto__title,
.section.footer h2,
.vgl-postwork__title,
.vgl-pl-bridge__title,
.vgl-reassure__title,
.vgl-process__title,
.vgl-faq__title,
.wf-card__title,
.cs-ch__title {
  text-wrap: balance;
}
