/* ============================================================
   connect.css — 连接页布局与动效
   结构参考 ATK HUB / IPI 网页驱动：产品居中在上、超大标题在下、
   全页只有一个实心强调色按钮，其余全是留白与细线。

   主视觉是 models/hero-mouse-ml.png —— 一张银白鼠标位图，放在暖棕黑舞台里。
   它是 models/ML.png 去掉烤进画面的按键编号得到的，脚本见
   gen/work/atk-capture/inpaint-digits.cjs。三张素材里只有 ML 没把品牌 logo 烤进
   画面，偏偏它的编号紧贴着黑色侧键和滚轮，所以去编号只能用像素级遮罩，不能用
   矩形框 —— 那套踩坑过程全写在脚本注释里，改图前先读它。

   为什么必须配深底：银白机身放在近白底上会糊成一片、没有轮廓。深底加一小圈琥珀
   柔光，机身才立得住 —— 和配置页那块 .device-view 是同一套语汇。
   舞台的深色是【暖棕黑】（--stage #1a1613），和上一版的冷蓝黑不同：
   浅色页配暖底才是一个色温，配冷底会像两块拼贴。

   曾经试过内联手绘 SVG 鼠标（好处是能逐部件上色、逐部件做动画），但矢量画不出
   产品渲染的质感，效果很差，已废弃。现在改回位图，动画全靠外面套的 HTML 元素做。
   ============================================================ */

.device-gate{position:fixed;z-index:18;inset:72px 0 0;overflow:hidden}

.cinematic-stage{
  position:relative;display:flex;flex-direction:column;
  align-items:center;justify-content:center;
  height:100%;padding:24px 24px 92px;
  opacity:1;                       /* 默认可见：动效只做叠加，GSAP 挂了也不黑屏 */
}

/* ---------- 产品主视觉：银鼠位图 + 暖棕黑圆角舞台 ---------- */
.stage-media{
  position:relative;overflow:hidden;         /* 给 .stage-sheen 当裁剪框 */
  display:grid;place-items:center;
  flex:0 1 auto;min-height:0;
  padding:clamp(12px,3vh,32px) clamp(18px,4vw,48px);
  border-radius:var(--r-stage);
  background:linear-gradient(158deg,var(--stage-2) 0%,var(--stage) 62%);
  /* Double-Bezel：外圈一道发丝描边（0 0 0 1px）+ 顶部内高光（inset 0 1px 0），
     让深色面板像「嵌在托里的一块玻璃」，而不是一块平贴的色板。 */
  box-shadow:0 0 0 1px rgba(255,246,235,.06),inset 0 1px 0 rgba(255,246,235,.08),var(--sh-stage);
  margin-bottom:clamp(8px,2.4vh,28px);
}
/* 舞台内的琥珀柔光：让鼠标「落」在光里，而不是浮在黑上。
   ::before 也是子元素，按 DOM 顺序排在 .mouse-enter 之前 —— 自动在鼠标背后。
   加了一层极缓的呼吸：静止画面上有东西在慢慢变，页面才不「死」。 */
.stage-media::before{
  content:"";position:absolute;left:50%;top:50%;
  width:min(560px,58vw);height:min(560px,58vw);
  transform:translate(-50%,-50%);pointer-events:none;
  background:radial-gradient(circle,rgba(201,122,43,.22) 0%,rgba(201,122,43,.07) 42%,transparent 70%);
}
.mouse-enter{display:block;will-change:transform}
.mouse-float{position:relative;display:block}
.hero-mouse{
  display:block;height:clamp(190px,33vh,330px);width:auto;
  /* 位图是去背后的 RGBA，边缘自带一圈半透明抗锯齿像素，配深底正好柔和过渡。
     只要一点点投影把它从舞台上「抬」起来 —— 深底上给重了会显脏。
     投影带暖棕色相，不用纯黑：纯黑阴影压在暖棕底上会发灰。 */
  filter:drop-shadow(0 16px 22px rgba(30,20,12,.55));
}

/* 鼠标跟随产生的倾斜容器。和 .mouse-enter / .mouse-float 分开三层，
   各管一个 transform，互不覆盖：
     .mouse-enter  飞入        .mouse-float 常驻浮动   .mouse-tilt 光标视差
   如果都写在一个元素上，后一条动画会整个吃掉前一条的 transform。 */
.mouse-tilt{display:block;will-change:transform}

/* 扫过舞台的高光带。挂在舞台上而不是鼠标上：一是裁剪由舞台的 overflow:hidden
   负责，不用自己算边界；二是位图内部没法逐部件做动画。 */
.stage-sheen{
  position:absolute;top:-20%;left:0;width:34%;height:140%;
  pointer-events:none;transform:translateX(-120%);
  background:linear-gradient(100deg,transparent 0%,rgba(255,246,235,.06) 45%,
                             rgba(255,246,235,.11) 50%,rgba(255,246,235,.06) 55%,transparent 100%);
}

/* 首屏就绪前先把要入场的元素藏起来。body 上的 booting 由 index.html 末尾
   一段【内联】脚本摘掉 —— 内联、不依赖任何网络资源，所以不会出现
   「外链 JS 没加载出来 → 永远白屏」。禁用 JS 时由 <noscript> 里的样式复原。 */
body.booting .mouse-enter,
body.booting .gate-copy>*{opacity:0}

/* ============================================================
   入场编舞
   三层各管一件事，免得 transform 互相覆盖：
     .stage-media   ← 整块舞台先「落定」
     .mouse-enter   ← 鼠标飞入
     .mouse-float   ← 常驻浮动
     .hero-mouse    ← 只管尺寸与投影，不参与动画

   两个坑，都写在这一行的选择器里：
   1. 动画必须挂在 body:not(.booting) 下。直接写在 .mouse-enter 上的话，
      动画在 <head> 里 CSS 一生效就开始跑，而那时页面还没绘制 ——
      等用户看得见，1.25 秒早就播完了，表现就是「压根没有入场动画」。
      挂到 :not(.booting) 上，摘掉 booting 与首次绘制落在同一帧，才真看得见。
   2. 用 CSS 而不是 GSAP：GSAP 的 fromTo 要等 timeline 开始才落「起始态」，
      在那之前元素静止可见 → 会看到「先显示 → 跳回左边 → 再飞入」的闪动。
      CSS 动画的 fill-mode:both 从首帧就是起始态，且不依赖第三方脚本。
   reduced-motion 下整段不生效，元素直接停在终态（可见），不会白屏。 */
@media (prefers-reduced-motion:no-preference){
  /* 舞台先落定，给后面的鼠标一个「落点」 */
  body:not(.booting) .stage-media{animation:stageIn .8s var(--ease-out) both}

  /* 缓动必须是【起步缓】的曲线。这里踩过一次：先前写成
     cubic-bezier(.34,1.16,.64,1)，想用末段回弹做「机械件落位」的手感，
     但那条曲线的控制点 y 起手就是 1.16 —— 前段极陡，实测 1.35s 的动画在
     0.44s 就走完了，看起来不是「飞入」是「闪一下」，正是要避免的效果。
     正确写法是让第一个控制点的 y 贴近 0（起步慢），第二个控制点略大于 1
     （末段轻微过冲再收回）：.36,0,.74,1.12。
     过冲幅度也要小 —— 1.12 已经是「有重量」和「弹过头」的分界。 */
  body:not(.booting) .mouse-enter{animation:mouseEnter 1.35s cubic-bezier(.36,0,.74,1.12) both .16s}
  body:not(.booting) .mouse-float{animation:mouseFloat 7s ease-in-out infinite 1.4s}
  body:not(.booting) .stage-media::before{animation:glowBreath 6.5s var(--ease-io) infinite}
  body:not(.booting) .stage-sheen{animation:stageSweep 6.4s var(--ease-io) infinite 1.2s}

  /* 文案：h1 交给逐字动画（.ch，见下），其余按序浮出 */
  body:not(.booting) .gate-copy>*:not(h1){animation:copyEnter .56s var(--ease-out) both}
  body:not(.booting) .gate-copy>*:nth-child(1){animation-delay:.44s}
  body:not(.booting) .gate-copy>*:nth-child(3){animation-delay:.78s}
  body:not(.booting) .gate-copy>*:nth-child(4){animation-delay:.86s}
  body:not(.booting) .gate-copy>*:nth-child(5){animation-delay:.94s}

  /* 逐字上浮。span.ch 由 motion.js 在 DOM 就绪时拆出来 ——
     它必须早于这段动画生效，而 motion.js 是外链脚本、排在 index.html 末尾
     那段内联 rAF 之前，所以顺序是对的（拆字在前，摘 booting 在后）。
     JS 没跑（禁用/加载失败）时 .ch 不存在，h1 直接以终态显示，不会白屏。 */
  .gate-copy h1 .ch{display:inline-block;opacity:0;transform:translateY(.44em) rotate(1.6deg)}
  body:not(.booting) .gate-copy h1 .ch{animation:charIn .72s var(--ease-out) both}
}
@keyframes stageIn{
  from{opacity:0;transform:scale(.972) translateY(12px)}
  to{transform:none;opacity:1}
}
/* 起点距离是算出来的，不是拍的：舞台 overflow:hidden，鼠标飞出面板的部分会被
   切掉，静止帧看就像渲染错误。横向余量 = 舞台半宽 − 鼠标半宽。
   桌面档舞台 284px、鼠标 188px → 余量 ±48px；再乘入场时的 scale(.9)
   （此时鼠标只有 169px 宽）→ 约 ±57px。取 44px 留出旋转带来的外扩。
   早先写的 -190px 远超这个数，实测 0.6s 那一帧鼠标被拦腰切断。 */
@keyframes mouseEnter{
  from{transform:translate(-44px,26px) rotate(-11deg) scale(.9);opacity:0}
  55%{opacity:1}
  to{transform:none;opacity:1}
}
@keyframes charIn{
  from{opacity:0;transform:translateY(.44em) rotate(1.6deg)}
  to{opacity:1;transform:none}
}
@keyframes glowBreath{
  0%,100%{opacity:.82;transform:translate(-50%,-50%) scale(1)}
  50%    {opacity:1;transform:translate(-50%,-50%) scale(1.055)}
}
@keyframes copyEnter{
  from{transform:translateY(16px);opacity:0}
  to{transform:none;opacity:1}
}
@keyframes mouseFloat{
  0%,100%{transform:translate3d(0,0,0)}
  50%{transform:translate3d(0,-9px,0)}
}
/* 高光带扫过舞台：扫完停一会儿再重来，避免一直动显得吵。
   光带自身宽 34%，要从完全在左侧外扫到完全在右侧外，位移约 (100+34)/34 ≈ 394%
   个自身宽度 —— 也就是这里给 420% 的来源。 */
@keyframes stageSweep{
  0%{transform:translateX(-120%)}
  38%,100%{transform:translateX(420%)}
}

/* ---------- 文案：居中堆叠 ---------- */
.gate-copy{position:relative;z-index:6;text-align:center;max-width:560px}
.gate-copy .eyebrow{display:inline-block;margin-bottom:2px}
.gate-copy h1{
  font-size:clamp(40px,5.4vw,74px);line-height:.98;letter-spacing:-.045em;
  margin:12px 0 14px;font-weight:700;color:var(--text);
  /* 逐字拆成 inline-block 后，浏览器会在任意两字之间断行。
     balance 让两行长度接近，避免「连接设 / 备」这种难看的孤字。 */
  text-wrap:balance;
}
.gate-copy h1 em{font-style:normal;color:var(--accent-display)}
.gate-copy p{color:var(--muted);font-size:14px;line-height:1.8;margin:0 auto;max-width:420px;text-wrap:pretty}

.gate-connect{
  margin:26px auto 0;width:min(300px,100%);padding:14px 18px 14px 22px;
  display:flex;align-items:center;justify-content:space-between;
  font:700 14px var(--font);letter-spacing:.01em;
  /* 位移用自定义曲线，不用 ease-in-out */
  transition:transform .3s var(--ease-out),box-shadow .3s var(--ease-out),background .3s var(--ease-out);
}
/* 悬停整体微抬，按下回落 —— 模拟真实的按压 */
.gate-connect:hover{transform:translateY(-1px)}
.gate-connect:active{transform:scale(.975) translateY(0)}
/* b 的圆底座与悬停位移定义在 theme.css（Button-in-Button） */

.gate-copy>small{
  display:block;margin-top:14px;color:var(--muted);
  font:500 11px var(--mono);letter-spacing:.08em;
}

/* ---------- 光标进入舞台时的「吸气」----------
   光标移到舞台上，整块面板极轻微放大。幅度只有 1.2%，静止看是察觉不到的，
   但会让「可交互区域」这件事被身体感觉到。纯 CSS :hover，不需要 JS。 */
@media (prefers-reduced-motion:no-preference) and (hover:hover){
  .stage-media{transition:transform .6s var(--ease-out),box-shadow .6s var(--ease-out)}
  .stage-media:hover{transform:scale(1.012);box-shadow:0 0 0 1px rgba(255,246,235,.10),inset 0 1px 0 rgba(255,246,235,.10),var(--sh-stage)}
}

/* ---------- 底部步骤指示 ---------- */
.gate-index{
  position:absolute;left:6vw;right:6vw;bottom:30px;z-index:6;
  display:flex;align-items:center;gap:12px;
  font:600 10px var(--mono);letter-spacing:.14em;color:var(--muted);
}
.gate-index b{color:var(--muted);font-weight:600}
.gate-index i{flex:1;height:1px;background:var(--line);min-width:20px}

/* ---------- 窄屏 ---------- */
@media (max-width:900px){
  .cinematic-stage{padding:16px 22px 84px;justify-content:flex-end}
  .stage-media{margin-bottom:auto;margin-top:6vh}
  .hero-mouse{height:clamp(150px,26vh,230px)}
  .stage-media{padding:10px 16px}
  .stage-media::before{width:86vw;height:86vw}
  .gate-copy h1{font-size:clamp(34px,10vw,46px)}
  .gate-index{display:none}
}

/* 矮屏（笔记本横屏）压缩产品图，保证 CTA 始终在首屏内 */
@media (max-height:720px) and (min-width:901px){
  .hero-mouse{height:clamp(130px,22vh,190px)}
  .gate-copy h1{font-size:clamp(34px,4.2vw,56px)}
}

/* 连接页上顶栏的「连接设备」和主 CTA 重复 → 藏掉。
   判据用 #deviceGate 自身是否可见，不能用 body.device-connected：
   掉线回工作室时 body 也是 device-disconnected，那时顶栏按钮是必需的。 */
body:has(#deviceGate:not([hidden])) .connection{display:none}
