/* ==========================================================================
 * topo-hero.css · Hero 拓扑动效图(仅首页加载)
 * 来源:topology-hero/hero-embed.html v13(左文案 + 右动效图)
 * 隔离说明:CSS 全部限定在 .topo-hero 作用域内,所有 id 加了 th- 前缀,
 *           不会污染主题全局样式。改文案去 front-page.php,改图形去本文件的
 *           CAPS / pods 常量(在 topo-hero.js 里)。
 * ========================================================================== */

.topo-hero {
  --bg-deep: #020510;
  --cyan: #22d3ee;
  --cyan-bright: #67e8f9;
  --cyan-deep: #06b6d4;
  --yellow: #facc15;
  --text: #f1f5f9;
  --text-dim: #94a3b8;
  --border: rgba(34, 211, 238, 0.4);
  --border-dim: rgba(34, 211, 238, 0.18);

  /* ↓↓↓ 环境光层挂在外层 .topo-hero 上(通栏),不再挂在内层 .hero。
     原因:.hero 有 max-width:1440px + margin:0 auto,伪元素铺不满视口,
     屏宽 >1440px 时左右会各露出一条页面底色窄边。
     position:relative 是伪元素的定位锚点;overflow:hidden 把光晕裁进 section。 */
  position: relative;
  overflow: hidden;
  background: var(--bg-deep); /* 兜底深底,任何屏宽下都不会露白 */

  /* ↓↓↓ 首屏全屏:占满视口高度减去顶部 sticky 导航(73px = 72px 内高 + 1px 下边框),
     这样任意分辨率下打开首页,首屏恰好是"顶栏 + 满屏 Hero",无初始滚动、无底部露白。
     svh 用于移动端:规避地址栏收起/展开造成的视口高度抖动;
     vh 作旧浏览器回退(写在前面,被 svh 覆盖) */
  min-height: calc(100vh - 73px);
  min-height: calc(100svh - 73px);
}

.topo-hero, .topo-hero * { box-sizing: border-box; margin: 0; padding: 0; }



/* ===== Hero:左文案 / 右动效图 =====
   右侧列明显比左侧宽(1.35 : 0.82):拓扑图是竖构图(1100×800),
   给窄了整图会被压到 400px 出头,终端名称和链路徽标就看不清了 */

.topo-hero .hero {
  /* 环境光已移到外层 .topo-hero::before/::after(通栏)。
     这里只负责内容栅格,并靠 z-index:1 压在环境光之上 */
  position: relative;
  z-index: 1;
  max-width: 1440px;
  margin: 0 auto;
  padding: 56px 40px 72px;
  display: grid;
  grid-template-columns: minmax(300px, 0.82fr) minmax(520px, 1.35fr);
  gap: 56px;
  align-items: center;

  /* ↓↓↓ 对接主题 style.css 的通用 .hero 规则(全屏深色通栏)。
     本 Hero 自身就是深色环境光,不需要那层背景。
     min-height 与外层 .topo-hero 取同一值(视口高 - 73px 顶栏),
     让内容在首屏内垂直居中(本规则已 align-items:center)。
     .topo-hero .hero 特异性(0,2,0)高于 .hero(0,1,0),这里直接覆盖 */
  height: auto;
  min-height: calc(100vh - 73px);
  min-height: calc(100svh - 73px);
  background: transparent;
  overflow: hidden;
}

.topo-hero .hero-copy { min-width: 0; position: relative; z-index: 1; }

/* 标题强制一行:9 个字 × 46px + 字距 ≈ 432px,左栏 @1440 是 492px,放得下。
   字号上限从 60px 压到 46px 就是为这一行服务——分行能上大字号,
   但"星地协同,全域通达"是一个完整口号,断行会把它读成两个词 */

.topo-hero .hero-title {
  font-size: clamp(26px, 3vw, 46px);
  font-weight: 800;
  line-height: 1.3;
  letter-spacing: 2px;
  white-space: nowrap;
  margin-bottom: 28px;
  /* 整行一条四段渐变:近白 → 冰蓝 → 青 → 深蓝。
     一行铺开刚好把四段色都露出来,比"第二行单独上色"更连贯,
     视线会顺着字的走向从亮滑到暗,正好指向右边的图 */
  background: linear-gradient(96deg, #f2fdff 0%, #a5f3fc 24%, #22d3ee 58%, #0ea5e9 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  /* drop-shadow 而不是 text-shadow:background-clip:text 把填充变透明后,
     text-shadow 画在那层透明填充之上,等于给空壳描边,什么都看不见 */
  filter: drop-shadow(0 0 22px rgba(34, 211, 238, .26));
}

.topo-hero .hero-sub {
  font-size: clamp(14px, 1.02vw, 16.5px);
  line-height: 2.05;
  letter-spacing: .4px;
  color: var(--text-dim);
  max-width: 34em;
}

.topo-hero .hero-sub .lead { color: #cbd5e1; font-weight: 600; }

.topo-hero .hero-visual { min-width: 0; position: relative; z-index: 1; }


/* ===== Hero 环境光 =====
   原来不协调的根因是"两个光源 + 一条硬边":
   ① 页面自己有个径向渐变,中心在视口中央(正好压在文案上)
   ② 图里又有一层径向底板,中心在图中央 → 中间一团蓝、四周(尤其左侧)发黑
   ③ 图带圆角,于是在页面底色上又多出一圈可见的板边
   解法是只留一个光源、并让图"溶"进这块光里:
   - 隐掉图自带的底板(.svg-bg),光由这一层统一给
   - 光源偏右(图那一侧),视线从文案自然滑向图形
   - 网格纹理铺满整块 Hero 而不是只在图的矩形里,再用 mask 从中心淡出,
     这样纹理没有边界,不会再出现"贴了一块"的观感 */

/* 挂 .topo-hero(通栏)而不是 .hero(1440px 定宽),
   否则屏宽 >1440px 时左右各留一条页面底色窄边。
   光斑百分比已按通栏重算:原来 70%/62% 是相对 1440px 内容列的,
   换算到 1600~1920 视口约等于 66%/60%,这样光源仍落在右侧拓扑图上,
   视线从文案自然滑向图形。 */

.topo-hero::before, .topo-hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.topo-hero::before {
  background:
    /* 青色辉光:只叠一层很淡的,压过就变成"中间发蓝的一块" */
    radial-gradient(ellipse 44% 56% at 66% 46%, rgba(34, 211, 238, .085) 0%,
                    rgba(34, 211, 238, .028) 46%, transparent 72%),
    /* 底色:从图那一侧往左平缓压暗,左栏仍有 #070f20 左右的底,
       不是纯黑——纯黑会让文案区像被挖掉一块 */
    radial-gradient(ellipse 96% 118% at 60% 40%, #0b1a30 0%, #061024 46%,
                    #030814 78%, #020610 100%);
}

.topo-hero::after {
  background-image:
    linear-gradient(rgba(34, 211, 238, .045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(34, 211, 238, .045) 1px, transparent 1px);
  background-size: 52px 52px;
  -webkit-mask-image: radial-gradient(ellipse 74% 84% at 60% 44%, #000 0%, #000 30%, transparent 76%);
          mask-image: radial-gradient(ellipse 74% 84% at 60% 44%, #000 0%, #000 30%, transparent 76%);
}



/* 页面里隐掉图自带的底板:环境光已经给足,再叠一层就是"板中板"。
   注意这条规则写在 MOTION 区块之外,不会被 _export-svg.js 内联,
   所以导出的 topology.svg 仍然自带深底,丢到任何背景上都能看 */

.topo-hero .topo-svg .svg-bg { display: none; }

.topo-hero .topo-svg {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 14px;
}


/* ===== MOTION-BEGIN ===== */

/* 本段会被 _export-svg.js 原样内联进 topology.svg,两个标记注释别删。
   新增动效一律用 CSS 而非 SMIL 动画节点:SMIL 是每个节点一份 DOM,
   CSS 只加 class,且能被下方的 prefers-reduced-motion 一条规则整体降级 */


/* ---- 第二层能力环 · 6 块梯形扇片 ---- */

.topo-hero .cap-btn { cursor: pointer; }

/* 扇片状态全部交给 class 管理。不要再混用 setAttribute('opacity', ...):
   CSS 优先级高于表现属性,两边都写会以 CSS 为准,attr 那半边等于白写 */

.topo-hero .cap-hl { opacity: 0; }

.topo-hero .cap-bg { stroke-width: 1.3; stroke: url(#th-capRing); }

.topo-hero .cap-text { fill: #67e8f9; }

/* 过渡时长必须短于 C1 的停留时长(0.2s):比停留还慢的话,
   下一片已经开始亮、这一片还没淡到位,6 片糊成一条拖影,看不出"逐个按下"。
   .13s 与下面的下压 transform 同步,抬起和点亮是一个动作 */

.topo-hero .cap-hl, .topo-hero .cap-bg, .topo-hero .cap-text, .topo-hero .cap-side, .topo-hero .cap-lift {
  transition: opacity .13s ease, stroke-width .13s ease, fill .13s ease,
              transform .13s cubic-bezier(.3,.7,.4,1);
}


/* C2 按键下压:hover 预压 1px 做"可以点"的暗示(无 JS 的导出件里也有效)。
   顶面整体下沉,侧壁则被裁掉顶部相应宽度 —— 机械按键下压时底面是不动的,
   所以侧壁只能"变矮"不能"整体下移"。注意这里用 clip-path 而不是 scaleY:
   侧壁要减少的是固定 2.5px(均匀),缩放是按比例的,两者不等价 */

.topo-hero .cap-btn:hover .cap-hl { opacity: .45; }

.topo-hero .cap-btn:hover .cap-lift { transform: translateY(1px); }

.topo-hero .cap-btn:hover .cap-side { clip-path: var(--clip-h); }


/* 激活态(.on)写在 hover 之后 —— 两者特异性相同,靠顺序让 .on 压过 hover */

.topo-hero .cap-btn.on .cap-hl { opacity: 1; }

.topo-hero .cap-btn.on .cap-bg { stroke-width: 2.2; stroke: #a5f3fc; }

.topo-hero .cap-btn.on .cap-text { fill: #ffffff; }

.topo-hero .cap-btn.on .cap-lift { transform: translateY(2.5px); }

.topo-hero .cap-btn.on .cap-side { clip-path: var(--clip); }


/* C4 激活扩散:点击时从核心圈向外推三道波纹。
   --ro 由 JS 写成绝对坐标(550px 390px),因为波纹层不在扇片的局部坐标系里 */

@keyframes capRipple {
  0%   { opacity: .5; transform: scale(.95); }
  100% { opacity: 0;  transform: scale(1.35); }
}

.topo-hero .cap-ripple {
  animation: capRipple .9s ease-out forwards;
  fill: none; stroke: #67e8f9; stroke-width: 1.4;
}


/* C3 能量环:沿能力环外缘巡回的一段亮弧(持续)。
   pathLength="100" 把周长归一化,dasharray 就能直接按百分比写,
   以后改半径不用重算周长;dashoffset 正好走 100 一圈,首尾接缝无跳变。
   opacity 基准取 0 —— 否则等待期(--ed 之前)会先露出一段静止不动的亮弧 */

@keyframes capEnergy {
  0%   { opacity: .85; stroke-dashoffset: 100; }
  100% { opacity: .85; stroke-dashoffset: 0; }
}

.topo-hero .cap-energy { opacity: 0; animation: capEnergy 4s linear infinite; animation-delay: var(--ed, 0s); }


/* T2 悬浮起伏:平台 ±3px。幅度别再加,6px 看着就晃了 */

@keyframes podFloat { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }

@keyframes podShadow { 0%,100% { transform: scale(1); opacity: .5; } 50% { transform: scale(.84); opacity: .28; } }

.topo-hero .pod-lift { animation: podFloat  4.6s ease-in-out infinite; animation-delay: var(--fd, 0s); }

.topo-hero .pod-shadow {
  animation: podShadow 4.6s ease-in-out infinite; animation-delay: var(--fd, 0s);
  transform-box: fill-box; transform-origin: center;
}


/* T3 高光扫过:裁剪在顶面内的一道斜亮带,11 个终端同相位扫一遍就停。
   一次性(默认 iteration-count=1)+ fill:both:等待期停在 0% 帧(画外、透明),
   跑完停在 100% 帧(画外、透明)—— 不会残留一条亮带糊在台面上 */

@keyframes podSheen {
  0%   { transform: translateX(-150px); opacity: 0; }
  8%   { opacity: .32; }
  88%  { opacity: .32; }
  100% { transform: translateX(150px); opacity: 0; }
}

.topo-hero .pod-sheen { animation: podSheen 1s linear both; animation-delay: var(--sd, 0s); }


/* T6 到达闪烁:终端边框"数据抵达"式快闪两下,之后转 2.4s 一次的心跳并一直保持。
   11 个终端同相位(--td 全部相同)——错开就变成跑马灯,读不出"同时到达" */

@keyframes podFlash {
  0%, 32%, 100% { opacity: 0;   stroke-width: 1.2; }
  5%            { opacity: .9;  stroke-width: 2.8; }
  11%           { opacity: .2;  stroke-width: 1.4; }
  17%           { opacity: .72; stroke-width: 2.3; }
}

.topo-hero .pod-flash { animation: podFlash 2.4s ease-out infinite; animation-delay: var(--td, 0s); }


/* T5 弹射入场:从"核心 → 终端"方向的 55% 处弹到终点,只播一次。
   fill:both 让它停在 100% 帧(= 单位变换),不会残留位移 */

@keyframes podIn {
  0%   { opacity: 0; transform: translate(var(--sx,0px), var(--sy,0px)) scale(.3); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: translate(0,0) scale(1); }
}

.topo-hero .pod-in {
  animation: podIn .9s cubic-bezier(.22,1.25,.36,1) both;
  animation-delay: var(--id, 0s);
  transform-box: fill-box; transform-origin: center;
}

/* 链路跟着淡入,否则终端还在弹射、链路已经画死了 */

@keyframes linkIn { from { opacity: 0; } to { opacity: 1; } }

.topo-hero .link-in { animation: linkIn .7s ease-out both; animation-delay: var(--ld, 0s); }

/* ===== MOTION-END ===== */


/* 1080px 以下并成单列:两列各分到 400px 出头时,
   拓扑图会被压到看不清终端名称,不如上下排开 */

@media (max-width: 1080px) {
  /* 小屏单列时,强制满屏反而会让背景铺满、内容顶在上方留空,
     故整段 Hero 回退为内容自适应高度 */
  .topo-hero { min-height: auto; }

  .topo-hero .hero {
    grid-template-columns: 1fr;
    gap: 34px;
    padding: 40px 26px 52px;
    height: auto;
    min-height: auto;
  }

  .topo-hero .hero-sub { max-width: none; }

}

@media (max-width: 768px) {
  .topo-hero .hero { padding: 28px 16px 40px; gap: 26px; }

  .topo-hero .hero-title { letter-spacing: 2px; margin-bottom: 20px; }

  .topo-hero .hero-sub { line-height: 1.95; }

}

@media (prefers-reduced-motion: reduce) {
  .topo-hero, .topo-hero * { animation: none !important; transition: none !important; }

}
