/* MEILI AI v5 · motion layer
   在 v4 产物之上叠加，不改任何栅格 / 文案 / 图片。
   只动 transform / opacity / filter；reduced-motion 下全部退化为静态。 */

:root {
  --v5-ease: cubic-bezier(.16, 1, .3, 1);
  --v5-fast: .45s;
}

/* ================================================================
   1. 首屏入场：按 <br> 分行，行遮罩上浮
   ================================================================ */
.v4-hero h1 .v5-line {
  display: block; overflow: hidden;
  padding-bottom: .08em; margin-bottom: -.08em;
}
.v4-hero h1 .v5-line__inner {
  display: block; transform: translateY(114%);
  animation: v5-hero-line 1.2s var(--v5-ease) both;
  will-change: transform;
}
@keyframes v5-hero-line {
  from { transform: translateY(114%); }
  to { transform: none; }
}

.v4-hero .v4-hero__aside,
.v4-hero .v4-hero__meta { animation: v5-hero-up 1s var(--v5-ease) both; }
.v4-hero .v4-hero__aside { animation-delay: .24s; }
.v4-hero .v4-hero__meta { animation-delay: .42s; }
@keyframes v5-hero-up {
  from { opacity: 0; transform: translateY(18px); }
}

/* 首屏颗粒 */
.v5-grain {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  opacity: .05; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ================================================================
   2. 通用入场 → v5.21 双向
   进场慢上浮、退场快下沉：往下滚是流，往上滚是倒带。
   三个状态互斥，由 sections.js 挂：
     v5-wait 还没滚到（藏在下面，不播动画）
     v5-in   在活动区
     v5-out  已经滚过去
   ================================================================ */
html.js-motion:not(.reveal-all) [data-reveal] {
  opacity: 0; transform: translateY(28px); filter: none;
}
html.js-motion:not(.reveal-all) [data-reveal].v5-in {
  opacity: 1; transform: none;
  transition: opacity 1s var(--v5-ease), transform 1.1s var(--v5-ease);
  transition-delay: calc((var(--i, 0) * 0.62 + var(--v5-seg, 0)) * 55ms);
}
html.js-motion:not(.reveal-all) [data-reveal].v5-out {
  opacity: 0; transform: translateY(-16px); filter: none;
  transition: opacity .34s var(--v5-ease), transform .34s var(--v5-ease);
  transition-delay: 0s;
}

/* ================================================================
   3. 区块标题：行遮罩（JS 按实际换行拆分）
   ================================================================ */
.v5-split .v5-line {
  display: block; overflow: hidden;
  padding-bottom: .08em; margin-bottom: -.08em;
}
.v5-split .v5-line__inner {
  display: block; transform: translateY(110%);
  transition: transform 1.15s var(--v5-ease);
  will-change: transform;
}
.v5-split.in .v5-line__inner { transform: none; }

/* ================================================================
   4. 按钮：磁吸 + 扫光 + 箭头旋转
   ================================================================ */
.v5-magnetic { will-change: transform; }
.v4-btn-ghost.v5-magnetic { transition: transform .45s var(--v5-ease), color .3s, border-color .3s; }
.c-btn--solid,
.v4-btn-solid { position: relative; overflow: hidden; }
.c-btn--solid::after,
.v4-btn-solid::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 18%, rgba(255, 255, 255, .32) 50%, transparent 82%);
  transform: translateX(-120%);
  transition: transform .8s var(--v5-ease);
}
.c-btn--solid:hover::after,
.v4-btn-solid:hover::after { transform: translateX(120%); }
.v4-btn-solid::after {
  background: linear-gradient(100deg, transparent 18%, rgba(0, 150, 223, .20) 50%, transparent 82%);
}
.c-btn--solid > *,
.v4-btn-solid > * { position: relative; z-index: 1; }

.v4-nav__cta i,
.v4-btn-solid span[aria-hidden] { transition: transform .5s var(--v5-ease); }
.v4-nav__cta:hover i,
.v4-btn-solid:hover span[aria-hidden] { transform: rotate(-45deg); }

/* ================================================================
   5. 卡片：聚光 + 倾斜
   ================================================================ */
.v5-tilt {
  position: relative; isolation: isolate;
  transform-style: preserve-3d;
  transition: transform .5s var(--v5-ease);
  will-change: transform;
}
.v5-spot {
  position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  pointer-events: none; opacity: 0;
  transition: opacity .4s var(--v5-ease);
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
              rgba(56, 182, 242, .16), transparent 62%);
}
.v5-tilt:hover .v5-spot { opacity: 1; }

/* ================================================================
   6. 导航：滚动收缩成悬浮胶囊
   ================================================================ */
@media (min-width: 1024px) {
  .v4-nav__inner {
    max-width: 1440px; margin-inline: auto;
    border: 1px solid transparent; border-radius: var(--r-pill);
    /* v5.30：max-width 仍参与过渡，但 is-stuck 的判定加了滞回（见 v4.js）。
       真正放大 CLS 的不是这段过渡本身，而是 is-stuck 在 y=24 附近来回翻转 ——
       每翻一次，胶囊宽度就在 1440↔1240 之间拽 200px，导航里的字标与 CTA
       跟着横向位移（实测整页滚动 41 条 layout-shift、CLS 0.049）。
       滞回之后每次浏览只翻一次，位移总量固定下来。 */
    transition: max-width .7s var(--v5-ease), transform .7s var(--v5-ease),
                background .7s var(--v5-ease), border-color .7s var(--v5-ease),
                box-shadow .7s var(--v5-ease);
  }
  .v4-nav.is-stuck {
    background: transparent;
    border-bottom-color: transparent;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .v4-nav.is-stuck .v4-nav__inner {
    max-width: 1240px;
    transform: translateY(6px);
    background: rgba(5, 7, 12, .94);
    border-color: var(--line-1);
    box-shadow: 0 20px 60px -24px rgba(0, 0, 0, .85);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* ================================================================
   7. CTA：指针跟随的径向光 + 颗粒
   ================================================================ */
.h-cta { position: relative; isolation: isolate; }
.h-cta::before {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0; transition: opacity .6s var(--v5-ease);
  background: radial-gradient(620px circle at var(--gx, 72%) var(--gy, 28%),
              rgba(2, 139, 215, .20), transparent 60%);
}
.h-cta:hover::before { opacity: 1; }
.h-cta > .l-container { position: relative; z-index: 1; }

/* ================================================================
   8. 跑马灯悬停暂停
   ================================================================ */
@media (hover: hover) {
  .c-marquee:hover .c-marquee__track { animation-play-state: paused; }
}

/* ================================================================
   9. 尊重站点自己的"减少动效"开关（html 去掉 js-motion）
   ================================================================ */
html:not(.js-motion) .v5-line__inner,
html:not(.js-motion) .v5-magnetic,
html:not(.js-motion) .v5-tilt { transform: none !important; }
html:not(.js-motion) .v4-hero h1 .v5-line__inner { animation: none !important; }
html:not(.js-motion) .v5-tilt:hover .v5-spot { opacity: 0; }
html:not(.js-motion) .v4-hero__copy { transform: none !important; opacity: 1 !important; }
html:not(.js-motion) .v4-hero__aside,
html:not(.js-motion) .v4-hero__meta { animation: none !important; }
html:not(.js-motion) .h-cta::before { display: none; }

/* ================================================================
   10. reduced motion / 触屏兜底
   ================================================================ */
@media (prefers-reduced-motion: reduce) {
  .v5-line__inner { transform: none !important; animation: none !important; }
  .v4-hero .v4-hero__aside,
  .v4-hero .v4-hero__meta { animation: none !important; opacity: 1 !important; transform: none !important; }
  [data-reveal] { opacity: 1 !important; transform: none !important; filter: none !important; }
  .v5-tilt, .v5-magnetic { transform: none !important; }
  .v4-nav__inner { transition: none !important; transform: none !important; }
}

/* ================================================================
   11. AIGC 区块头重排：chip 跟标题、note 全宽脚注、右侧只留导语
   ================================================================ */
.c-head--aigc { align-items: start; }
.c-head--aigc > div:first-child { display: grid; gap: var(--sp-3); align-content: start; min-width: 0; }
.c-head--aigc .c-head__title { margin-top: 0; max-width: none; }
.c-head--aigc .c-head__aside { align-content: start; justify-self: start; max-width: none; }
.c-head--aigc .c-head__lead { max-width: none; }
.c-head--aigc .c-chips { margin-top: var(--sp-1); }
.c-head--aigc .c-note {
  grid-column: 1 / -1; margin-top: var(--sp-5); padding-top: var(--sp-3);
  border-top: 1px solid var(--line-1); max-width: none;
}
@media (min-width: 1024px) {
  .c-head--aigc { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: var(--sp-5) var(--sp-7); }
  .c-head--aigc .c-head__aside { grid-column: 2; }
}
@media (min-width: 768px) and (max-width: 1023px) {
  .c-head--aigc { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .c-head--aigc .c-head__aside { grid-column: 2; }
}
@media (max-width: 767px) {
  .c-head--aigc .c-head__aside { grid-column: auto; max-width: none; }
  .c-head--aigc .c-note { grid-column: auto; }
}

/* ================================================================
   12. 中文段落：只在标点处换行，避免「每一 / 步」被拆开
   ================================================================ */
.v5-flow {
  word-break: keep-all;
  overflow-wrap: break-word;
  line-break: strict;
  text-wrap: pretty;
}

/* v5.30：极窄视口（≤380px）放开断行。
   keep-all 让中文段落只能在标点处断，于是「一段没有标点的长句」的 min-content
   能到 323px（15px 字号约 21 字）；320px 视口下容器只有 280px，标题组被撑出
   23px、末字直接被切掉，而页面又不产生横向滚动（WCAG 1.4.10 Reflow 失败）。
   这里只在极窄视口放开：390px 及以上保持「只在标点处断行」的排版意图不变。 */
@media (max-width: 380px) {
  .v5-flow { word-break: normal; overflow-wrap: anywhere; }
}

/* ================================================================
   13. 行业卡片图标
   ================================================================ */
.h-ind__name { display: inline-flex; align-items: center; gap: 10px; }
.h-ind__icon { width: 20px; height: 20px; flex: none; color: var(--t-accent); opacity: .9; }
.h-ind__card:hover .h-ind__icon { opacity: 1; filter: drop-shadow(0 0 8px rgba(2, 139, 215, .4)); }

/* ================================================================
   13b. v5.38：12 张行业卡片统一
   ---------------------------------------------------------------
   先取证：标题字号**本来就是统一的**（--fs-h4 = clamp(...) → 恒 20px/600，
   像素级量了 12 张卡「AI +」的墨迹高度，全是 53px，没有一张不同）。

   真正让这一块看起来"大小不一"的，是另外三件事：

   ① 标题有 1 行也有 2 行（AI + Tourism and Culture / AI + Manufacturing /
      AI + Food and Beverage / AI + Industrial Internet 是 2 行），
      标题区高度不同 → 下面那行描述的起始位置在 12 张卡之间错位。
      → 标题区固定占 2 行，描述的起始位置就对齐了；
      → text-wrap: balance 让 2 行时的断点更均衡
        （"AI + Tourism and / Culture" → "AI + Tourism / and Culture"）。

   ② .h-ind__name 原来是 inline-flex + align-items: center。
      标题占 2 行时，图标会被垂直居中到**两行之间**，跟第一行的字脱开。
      → 改 block 级 flex + align-items: flex-start，图标跟首行对齐。

   ③ 卡片高度有 126 / 146 / 152 / 172 四种，同一行的卡也不等高 ——
      因为卡在 <li> 里面，<li> 被 stretch 了但卡没有 height:100%。
      → .h-ind__grid > li 改成 grid，卡撑满行高 → 同行等高。
      标题区固定 2 行之后，三行的高度也自然一致了（每行都有一张 3 行描述的卡）。
   ================================================================ */
.h-ind__grid > li { display: grid; }

/* home.css 里 .h-ind__card 用的是 align-items: baseline ——
   那会让 grid 第 1 行的高度随标题行数浮动（标题 1 行时行高 26px，
   标题 2 行时因为 name 成了 flex 容器、baseline 落到 margin box 底边，
   序号被推到行底，行高多出 10px），描述起始位置就跟着错开。
   改成 start 对齐 + 给序号一个显式的上边距，12 张卡的行高就完全一致了。
   同时把三个子项的 grid 位置写死 —— .h-ind__tags 带着 grid-column: 2 / -1
   参与 auto-placement，摆放结果会随行高变化，落到哪一行不确定。 */
.h-ind__card {
  align-items: start;
  /* 原来第三列是空的（0px 宽），却白占了一个 12px 的 column-gap ——
     把这份宽度让给标题列，长标题才放得下。 */
  grid-template-columns: auto minmax(0, 1fr);
  /* 第一行锁死成「标题区 2 行」的高度：跨列的 tags 会让 Chrome 在 auto
     行高下给第一行的分配随描述行数浮动（实测 01 给 62.39px、02 给 52px），
     描述起始位置就跟着错开 10px。minmax 的 auto 上限同样会浮动
     （实测 57.19px），所以直接用固定值 —— .h-ind__name 的 min-height
     也是这 2 行，标题真排到 2 行时正好填满，不会溢出。 */
  grid-template-rows: calc(2 * 1.3 * 18px) auto;
}
.h-ind__num  { grid-column: 1; grid-row: 1; margin-top: 6px; }  /* 11px 序号对齐 18px 标题首行 */
.h-ind__tags { grid-column: 2; grid-row: 2; }
.h-ind__name {
  grid-column: 2; grid-row: 1;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-height: calc(2 * 1.3em);   /* 标题区固定 2 行：1 行标题的卡也占满，描述起始位置对齐 */
  text-wrap: balance;
  /* 20px → 18px：去掉空列后标题列宽出 38px，最长的
     「AI + Tourism and Culture」在 18px 下是 209px，正好塞进 218px 的可用宽度 ——
     12 个标题因此全部单行，而字号仍然 12 张一模一样。 */
  font: 600 18px / 1.3 var(--font-display);
}
.h-ind__icon { width: 18px; height: 18px; flex: none; margin-top: .1em; color: var(--t-accent); opacity: .9; }  /* 图标与首行文字视觉居中 */


/* ================================================================
   13c. v5.39：标题缩到单行 + 行业区 768–1023px 改 2 列
   ---------------------------------------------------------------
   v5.38 把标题区锁成 2 行，是因为当时 12 个标题里有 4 个是 2 行
   （AI + Tourism and Culture / AI + Manufacturing /
     AI + Food and Beverage / AI + Industrial Internet）——
   行数不齐 → 描述起始位置在 12 张卡之间错开 → 看着"大小不一"。

   v5.39 用户拍板「缩文案」，改 3 条英文标题：
     AI + Tourism and Culture → AI + Tourism
     AI + Food and Beverage   → AI + Food
     AI + Industrial Internet  → AI + Industry
   中文 12 个标题本来就全是单行（AI+教育 / AI+零售 / AI+文旅 / AI+地产 /
   AI+传媒 / AI+制造 / AI+物流 / AI+电商 / AI+汽车 / AI+建筑 / AI+餐饮 /
   AI+工业互联网），**中文零改动**。

   实测（容器最大宽 1280px，4 列时标题文字可用宽封顶 192px）：
   最长那条「AI + Industrial Internet」是 191px —— 只剩 1px 余量，
   1280–1379px 视口还会直接折行。缩成「AI + Industry」（约 110px）之后，
   最长的一条变成「AI + Manufacturing」163.3px，余量够了。

   标题区随之从 2 行收成 1 行：row 1 = 1.3 × 18px = 23.4px。
   ⚠ 行高不能写 minmax(23.4px, auto)：卡内 grid 的 align-content 默认 stretch，
     卡被同行最高的那张撑高后，剩余高度会**平分给所有 auto 轨道** →
     标题行高度随描述行数浮动 → 描述起始位置又错开（实测 54.4px / 64.8px 两档）。
     这正是 v5.38 用 calc(2 * 1.3 * 18px) 写死行高要躲的坑。
     这里用 minmax(23.4px, max-content)：max-content 不是 auto 轨道，
     不参与 stretch 分配；万一标题真排到 2 行，行高自己长一档
     （不会跟描述叠在一起），且同行共享、依然对齐。

   768–1023px：3 列时标题可用宽只有 132px，12 个行业名物理上放不下 →
   用户拍板这一段改 2 列（字号不变，标签全部单行）。

   560–599px：2 列窄卡（可用宽 155–164px），「AI + Manufacturing」163.3px
   差一点点放不下 → 这一小段锁 2 行标题区。 */
.h-ind__card {
  grid-template-rows: minmax(calc(1.3 * 18px), max-content) auto;
}
.h-ind__name {
  min-height: 1.3em;   /* 1 行标题区：1.3 × 18px = 23.4px */
}

/* 768–1023px：行业区改 2 列 —— 3 列时标题可用宽只有 132px，放不下 18px 的行业名 */
@media (min-width: 768px) and (max-width: 1023px) {
  .h-ind__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 560–599px：2 列窄卡，「AI + Manufacturing」163.3px 差一点点放不下 → 锁 2 行标题区 */
@media (min-width: 560px) and (max-width: 599px) {
  .h-ind__card {
    grid-template-rows: minmax(calc(2 * 1.3 * 18px), max-content) auto;
  }
  .h-ind__name {
    min-height: calc(2 * 1.3em);
  }
}


/* ================================================================
   14. 触控热区：手机上点得准，视觉一点不变

   实测（360px）：60 处可交互元素的高度小于 44px —— 伙伴图例只有 17px 高、
   导航字标 14px、推荐词 24px、分段控件 30px、页脚链接 17px。

   直接改标签尺寸会动到视觉（筛选条会变高、分段控件会变胖、页脚会挪位），
   所以这里用伪元素把「可点区域」撑开：绝对定位不参与布局，
   标签本身的大小、间距、配色、圆角全都不变。

   v5.31 复查发现第一版有个致命细节：分段控件、推荐词胶囊、平台胶囊
   为了文字省略号都带 overflow:hidden，而伪元素是元素的子节点 ——
   撑出去的部分被自己的 overflow 裁掉了，热区根本没变大
   （实测：60 个元素里 0 个达到 44px）。

   改法：overflow:hidden → overflow:clip + overflow-clip-margin。
   clip 一样能裁文字（省略号照旧），clip-margin 把裁剪边界向外放行，
   伪元素就能伸到盒子外。margin 只放行纵向（30px 0），横向照旧在
   content box 处截断 —— 省略号的位置和长度一个字都不变。

   撑开方向按实际排版定，避免抢走相邻控件的点击：
     · 居中撑开 —— 上下都是非交互内容
     · 往下撑   —— 上方紧挨着输入框（推荐词胶囊）
     · 往下填满 —— 页脚链接列（间距 18px，填满不与上一条咬）
   ================================================================ */
@media (max-width: 1023px) {
  .v4-nav__brand,
  .v4-btn-ghost,
  .c-write__tab,
  .c-write__lang,
  .c-views__tab,
  .c-lamp__plat,
  .c-tab,
  .c-brand,
  .pw-filter button,
  .pw-legend button,
  .c-footer__list a,
  .v4-contact__email a,
  .c-btn,
  .c-lamp__chip,
  .c-demo__label,
  .c-demo__check { position: relative; }

  /* 自带 overflow:hidden 的控件：放行裁剪边界，热区才伸得出去。
     html body 提高一级权重，压过 aigc-assets.css / components.css 里的原规则。
     注意：Chrome 的 overflow-clip-margin 只支持单值（简写 30px 0 会被整条丢弃），
     所以四边都放行 30px —— 横向不会有东西溢出：伪元素宽度就等于控件宽度，
     而这几个控件都带 text-overflow:ellipsis，文字本身不会画出边界。 */
  html body .c-write__tab,
  html body .c-views__tab,
  html body .c-lamp__plat,
  html body .c-lamp__chip {
    overflow: clip;
    overflow-clip-margin: 30px;
  }

  /* 居中撑开 44px */
  .v4-nav__brand::after,
  .v4-btn-ghost::after,
  .c-write__tab::after,
  .c-write__lang::after,
  .c-views__tab::after,
  .c-lamp__plat::after,
  .c-tab::after,
  .c-brand::after,
  .pw-filter button::after,
  .pw-legend button::after,
  .v4-contact__email a::after,
  .c-demo__check::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
    z-index: 1;
  }

  /* .c-btn 的 ::after 被 .c-btn--solid 的高光占着，整族改用 ::before */
  .c-btn::before {
    content: '';
    position: absolute;
    left: 0; right: 0;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
    z-index: 1;
  }

  /* 推荐词胶囊：往下撑（上方 6px 处就是输入框，往上撑会抢输入框的点击；
     下方到提交按钮之间有 64px 空隙，往下撑是安全的） */
  .c-lamp__chip::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    top: 0;
    height: 44px;
    z-index: 1;
  }

  /* 输入框的说明文字：往上撑 19px，把 label 自己的 25px 一起包进去 = 44px。
     input 是替换元素不支持伪元素，但它有 label.htmlFor 关联，
     点 label 等于点输入框 —— 热区由 label 代理。
     （label 与输入框之间只隔 1px，不往下伸，免得盖住输入框上沿） */
  .c-demo__label::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    bottom: 0;
    height: 44px;
    z-index: 1;
  }

  /* 色板 radio：input 是 opacity:0 的隐形覆盖层，直接把它自己撑高
     （26 + 9 + 9 = 44px）。隐形元素怎么撑都不动视觉。 */
  html body .c-demo__swatch input { inset: -9px 0; }

  /* 勾选框：label 的热区铺在它上面，把勾选框抬回热区之上，
     免得点到框上沿时变成「点 label」而不是「点框」。 */
  html body .c-demo__check input { position: relative; z-index: 2; }

  /* 页脚链接：列内行距只有 15px（li 行高 26px，链接行内盒只有 17px），
     居中撑开会和上一条互相咬。改成往下填满行距：
     自身 17px + 13px = 30px，两条之间留 2px 不重叠。
     想上 44px 就得改行距，那会动到页脚排版，不做。 */
  html body .c-footer__list a::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    top: 0;
    height: 30px;
    transform: none;
    z-index: 1;
  }
}
