/* 首页展示卡（Our Partners 上方那组，2026-10-10 加入）
   数据：后台「首页展示」→ content/showcase.json（title/intro/items，中英双语）。
   交互：默认每张等宽、只显示图片标题；鼠标悬停或键盘聚焦（卡片是链接时可 Tab）时
   **当前卡变宽、其余三张变窄**，同时当前卡展开一段简介。

   ⚠️ 这个组件踩过的坑（2026-10-10 五次返工，改动前务必读完）：
   症状始终是"鼠标在两卡之间的缝附近时，卡片宽度频闪/来回跳"。根因只有一个：
   **卡片之间若用父容器 `gap` 留缝，那条缝不属于任何卡片** —— 光标落在缝里就"谁都没 hover"，
   布局回弹成等宽；回弹后缝的位置变了、光标又落到某张卡上 → 再放大 → 再回弹，如此循环。
   期间试过三种补丁（`:hover` 直接绑卡片 / JS 静态均分命中区 / 按当前或目标矩形用 JS 判），
   都是在给这个缺陷打补丁，各自又引入新毛病（提前切换、过渡中间态误判）。
   **定稿（三条缺一不可）**：
   1) **命中区不留缝**：缝由卡片自己的 padding 提供（`.showcase-card` 左右各 --sc-gap/2），
      父容器不用 `gap` → 整行每一像素都属于某张卡，不存在"无主"状态 → 没有回弹循环；
   2) **单一激活状态**：`activeIndex` 只由指针事件驱动（卡片 `pointerenter` + 容器 `pointermove`），
      **不用卡片的 `:hover`/`mouseenter`** —— 卡片一变宽，边界就会从光标下扫过，那类事件会被动画
      而不是用户的移动触发，必然频闪（见 app.js 的 bindShowcase）；
   3) **滞回锁**：每次切换后锁 420ms（≈ 这里的过渡时长），锁定期内不立即切换，等动画停稳后
      按指针位置再判一次 → 动画带来的假事件既不生效也不丢，真实移动最多晚一个过渡时长。
   容器尺寸与间距在悬停时**完全不变**（不改 gap/margin/border/定位），只有 flex-grow 参与过渡。

.showcase-section .showcase-head{max-width:1000px;margin-bottom:40px}
.showcase-section .showcase-head .section-title{margin:0 0 16px}
.showcase-section .showcase-head p{margin:0;font-size:17px;line-height:1.5;color:#3c3c3c;max-width:920px}
/* --sc-hover-w = 悬停/聚焦时那张卡**看得见的宽度**（上限）。**这是唯一的宽度调节旋钮**：
   2026-10-10 首版用纯 flex-grow 比例（2.7），1400 视口下量到 551px，反馈"有点太大"，
   改为「grow 3 + max-width 封顶」——桌面端稳定在 460px、不再随视口无限变宽；
   窄屏下 grow 3 算出来的宽度小于 460 时自然更窄，不会硬撑。改这一个数即可调。 */
.showcase-cards{display:flex;height:min(62vh,540px);min-height:400px;--sc-hover-w:460px;--sc-gap:12px;--sc-anim:440ms;--sc-ease:cubic-bezier(.4,0,.2,1);--sc-n:4}
/* 外层=命中槽：左右各半个 --sc-gap 的透明内边距 → 视觉上 12px 的缝，但命中区连成一片（关键，见文件头）
   —— 宽度怎么做动画（2026-10-10 第三版，别再改回 max-width / flex-grow 封顶）——
   需求：悬停时该卡变宽、其余变窄，**不允许过冲（先变大又缩小）**，进出观感一致。
   ❌ 写法 A：`flex-grow:1 → 3` + `max-width:none → 472px` 封顶。grow 升到 3 时**未封顶**宽度是 600px，
      而 max-width 要整段动画才收紧 → 实测轨迹 `288→499→560→522→460`，**峰值 560 再回落**，
      就是用户反馈的"鼠标放上去宽度先增大又缩小"。
   ❌ 写法 B：`flex-basis:0 → 472px` 与 `flex-grow:1 → 0` 同时过渡。过冲没了，但两条属性互相拉扯，
      起点仍有 4px 小回缩（实测 `288→284→291→…→460`）。
   ✅ 写法 C（现在）：**宽度全部用 `flex-basis` 表达，`flex-grow/flex-shrink` 恒为 0**，
      让每张卡的 basis 各自插值、且**任意时刻总和恒等于容器宽**（因此不产生剩余空间、不触发 shrink、
      两张卡之间没有互相拉扯）：
        - 静止：每张 `calc(100% / var(--sc-n))`（四张各 25%）
        - 激活：那张 `calc(--sc-hover-w + --sc-gap)`（472px 槽宽 = 460px 可见宽）
        - 其余：`calc((100% - 472px) / (n-1))`，用 `:has()` 由"组内存在激活卡"触发
      三者相加 = 472px + (100% - 472px) = 100% ✓ 恒定。
      `--sc-n`（卡片数）由 app.js 渲染时内联写到 `.showcase-cards` 上（卡片数来自数据，最多 8 张）。
   - 进入与移开共用同一条 `transition`（只过渡 flex-basis，同属性/时长/曲线 → 天然对称）；
     曲线 ease-in-out（强 ease-out 会"一放上去就弹开"）；
   - 时长/曲线单点控制：`--sc-anim`（440ms，属用户给的 300–450ms 区间）/ `--sc-ease`。 */
.showcase-card{position:relative;flex:0 0 calc(100% / var(--sc-n, 4));min-width:0;display:block;color:#fff;padding:0 calc(var(--sc-gap) / 2);
  transition:flex-basis var(--sc-anim) var(--sc-ease)}
/* 内层=看得见的卡片：圆角、裁切、底色都在这里，图片与文案都在它里面 */
/* 卡片直角（2026-10-10 用户要求「卡片也去掉圆角」，与 service 页卡片的处理保持一致）：
   原来是 16px 圆角，现改为 0；overflow:hidden 保留（图片 hover 放大要靠它裁切）。 */
.showcase-card .sc-inner{position:relative;display:block;width:100%;height:100%;overflow:hidden;border-radius:0;background:#262b29}
.showcase-card .sc-inner img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center}
.showcase-card .sc-inner::after{content:'';position:absolute;inset:0;background:linear-gradient(180deg,rgb(0 0 0/0%) 45%,rgb(0 0 0/62%) 100%)}
.showcase-card .sc-body{display:block;position:absolute;left:22px;right:22px;bottom:25px;z-index:2}
/* 简介的出现时机（2026-10-10 用户反馈「文字出现太早，会看到换行效果」）：
   宽度动画跑完（--sc-anim）之后才开始淡入 —— 否则卡片变宽的过程中文字一直在重新折行。
   做法：**延迟写在激活态上**（transition-delay 取的是"要迁入的那条规则"里的值）→
   进入时等 --sc-anim 再淡入；离开时用基础规则的 0s 立即淡出（文字先消失、卡片再收缩），
   两边都不会在宽度变化的过程中出现折行抖动。 */
.showcase-card .sc-brief{display:block;margin:0 0 20px;font-size:14px;line-height:1.45;color:#f4f4f4;max-width:460px;
  opacity:0;transform:translateY(8px);
  transition:opacity var(--sc-anim) var(--sc-ease) 0s,transform var(--sc-anim) var(--sc-ease) 0s}
/* 标题在左、播放按钮**贴右**（2026-10-10 按设计稿修正）：设计稿里播放按钮并不跟在标题后面，
   而是压在卡片右下角、与卡片右内边距对齐（即与文案区同一条右边界）。
   space-between 在"没有播放按钮"时只有一个子元素 → 标题仍靠左，不受影响。 */
.showcase-card .sc-foot{display:flex;align-items:center;gap:12px;justify-content:space-between}
.showcase-card .sc-title{font:23px DMSerif,Georgia,serif;line-height:1.12}
/* 播放按钮（2026-10-10 起用客户给的 Frame.svg：图标自带 #C4EB8B 圆底 + 深色三角），
   所以这里不再画圆底/三角，只给尺寸与投影；投影用 drop-shadow 才会跟着圆形轮廓走（box-shadow 会露出方角）。 */
/* 播放按钮（2026-10-10 用户反馈「鼠标放上去以后再出现、尺寸小一点」）：
   默认 opacity:0（**占位保留**，不引起布局跳动），卡片激活（悬停/键盘聚焦）时才淡入；尺寸 44 → 32px。 */
.showcase-card .sc-play{flex:none;width:32px;height:32px;filter:drop-shadow(0 2px 6px rgb(0 0 0 / 22%));
  opacity:0;transition:opacity .2s ease}
.showcase-card.is-active .sc-play,.showcase-card:focus-within .sc-play{opacity:1}
.showcase-card .sc-play .play-ico{width:100%;height:100%;display:block}
/* 悬停/聚焦：`flex:0 0 <目标槽宽>` —— 宽度就等于 basis；其余卡见下面 :has() 规则。
   键盘走 :focus-within —— 焦点不随布局移动，安全。 */
.showcase-card.is-active,.showcase-card:focus-within{flex:0 0 calc(var(--sc-hover-w) + var(--sc-gap))}
/* 组内存在激活卡（或键盘聚焦）时，其余各卡收缩为"剩余宽度均分" —— 与激活卡 basis 相加恒为 100%。
   用 :has() 表达"兄弟里有激活卡"这件事（项目里 core-services.css 已在用 :has()）。 */
.showcase-cards:has(.showcase-card.is-active) .showcase-card:not(.is-active),
.showcase-cards:has(.showcase-card:focus-within) .showcase-card:not(:focus-within){flex-basis:calc((100% - var(--sc-hover-w) - var(--sc-gap)) / (var(--sc-n, 4) - 1))}
.showcase-card.is-active .sc-brief,.showcase-card:focus-within .sc-brief{opacity:1;transform:none;
  transition-delay:var(--sc-anim),var(--sc-anim)}
.showcase-card:focus-visible{outline:none}
.showcase-card:focus-visible .sc-inner{outline:2px solid #8dff6a;outline-offset:3px}
@media(max-width:800px){
  .showcase-section{padding:70px 0}
  .showcase-section .showcase-head{margin-bottom:26px}
  /* 纵向堆叠：缝隙同样用 padding 提供（上下的 --sc-gap）；注意纵向 flex 里 flex-basis 指的是**高度**，
     所以必须把上面那套横向宽度规则（含 :has() 那条）整体复位，否则 hover 会去改高度 */
  .showcase-cards{flex-direction:column;height:auto;min-height:0}
  .showcase-card,.showcase-card.is-active,.showcase-card:focus-within{flex:0 0 300px;padding:calc(var(--sc-gap) / 2) 0}
  .showcase-cards:has(.showcase-card.is-active) .showcase-card:not(.is-active),
  .showcase-cards:has(.showcase-card:focus-within) .showcase-card:not(:focus-within){flex-basis:300px}
  .showcase-card .sc-brief{opacity:1;transform:none}
  .showcase-card .sc-title{font-size:20px}
  .showcase-card .sc-play{width:30px;height:30px;opacity:1}
}
@media(prefers-reduced-motion:reduce){
  .showcase-card,.showcase-card .sc-brief{transition:none}
  .showcase-card.is-active .sc-brief,.showcase-card:focus-within .sc-brief{transition-delay:0s}
}
