{
  "slug": "integration-hub-map",
  "title": "Integration hub map",
  "tagline": "The old page flips 180° in one go to land as a new hub page, five integration app icons pop in at once, then five rainbow light conduits connect all at once with pulses flowing continuously.",
  "description": "The old page flips 180° in one go to land as a new hub page, five integration app icons pop in at once, then five rainbow light conduits connect all at once w",
  "category": "ui-entrance",
  "tags": [
    "shotcraft",
    "remotion",
    "ui-entrance",
    "effects"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 4.72,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/integration-hub-map/poster-4238e099b6c6.jpg",
    "video": "https://gimgs.net/motion/media/integration-hub-map/video-e85f0c46016e.mp4"
  },
  "variants": [
    {
      "key": "integration-hub-map",
      "label": "integration-hub-map · Integration hub map",
      "description": "The old page flips 180° in one go (side edge flashing bright instantly) to land as a new hub page, five integration app icons pop in on the same frame, then five rainbow light conduits connect all at once, with pulses of light flowing through them continuously — \"turn a new page, and the whole ecosystem plugs in at once\".",
      "use": "",
      "video": "https://gimgs.net/motion/media/integration-hub-map/video-e85f0c46016e.mp4",
      "poster": "https://gimgs.net/motion/media/integration-hub-map/poster-4238e099b6c6.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/integration-hub-map.html",
  "json_url": "https://gimgs.net/motion/integration-hub-map.json",
  "template_url": "https://gimgs.net/motion/integration-hub-map/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/integration-hub-map.md",
  "updated_at": "2026-09-24T07:40:03.091Z",
  "prompt": "## When to use\n\nIntegration/ecosystem capability segments (one product connects to everything); a version-upgrade narrative (the old page flips into the new); a feature climax with a dark, neon-toned mood.\n\n## Intention\n\nThe flip means \"a new era\": the old document page flips a full 180° into the new hub page — it must be a complete flip, not a tilt at an angle; the flip motion is **fast with a decelerating tail**, completed in one unbroken motion with no segmented pause. The side-edge moment only \"flashes once\". The connection is **two beats**: five icons pop in on the same frame (first beat) → five conduits start drawing and connect all at once (second beat) — only \"all at once\" reads as \"the ecosystem arrives in one shot\"; staggered one-by-one connections read as negotiating individually. Once connected, bright pulses cycle continuously inside the conduits toward the hub — that \"delivery\" feeling is the life of the final state.\n\n## Duration & energy\n\n- Duration: Lead-in ~0.5s + fast flip ~1.2s + icons appearing all at once → conduits connecting all at once in two beats ~0.7s + delivery breathing ≥1.5s; whole segment 4.5–5s\n- Energy: Medium-high (the flip is the peak, the delivery segment is the afterglow)\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n翻面是\"换时代\"：旧文档页整个翻转 180° 得到新中枢页——必须是\n完整翻面不是转个角度（判例），翻的动作**快翻+尾段减速**、一口气\n完成无分段停顿（\"匀速\"判例：用户说的匀速=无停顿，不是字面\nlinear；90° 侧棱停顿版被裁）。侧棱时刻只\"闪一下\"（1–3f 脉冲，\n长光晕平台被裁）。接入是**两拍制**：五图标同帧弹现（第一拍）→\n五管同帧起画齐连（第二拍）——齐，才读作\"生态一次到位\"；错峰\n逐个连读作逐个谈判（两轮被裁）。连通后光管内亮脉冲沿图标→中枢\n方向持续循环，\"输送感\"是终态的生命。\n\n## 动效核心\n- 翻面：rotateY 0→180°，35f，Easing.out(cubic)（前 41% 时间完成\n  80% 角度，尾段长减速缓着陆），双面卡（正面旧页/背面新页）\n- 侧棱瞬闪：~90° 时刻 2f 脉冲 bloom（0→1→0.25→0，4f 内归零）\n- 两拍制接入：五图标 tIcon 同帧（翻面落定 +3f）弹现；+10f 五管\n  tPipe 同帧起画，9f 齐长全亮\n- 输送脉冲：光管 dash 亮斑沿图标→中枢方向循环（速度 ~4.6px/f，\n  各管相位错开），全连通后中枢页轻呼吸\n- 背景：暗紫底噪 + 霓虹矩形浮现衬场\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 翻面时长 | 35f（曾 70f 被裁\"再快1倍\"） | <24f 读不出双面；尾段减速比例 ~60% 时间走 20% 角度 |\n| 翻面曲线 | ease-out 单调无停顿 | 侧棱停顿/分段被裁；严格 linear 起停生硬也被裁——快翻尾缓 |\n| 侧棱闪 | 2f 脉冲、4f 归零 | 长光晕平台被裁\"闪一下就行\" |\n| 接入节奏 | 两拍制：图标同帧 → +10f 光管同帧 | 错峰逐连两轮被裁\"同时出现，然后同时连接\"；两拍间隔 <6f 读作一拍 |\n| 光管生长 | 9f 齐长 | 18f 版偏慢；各管长度不同但同帧到达（按各自 len 归一） |\n| 输送脉冲 | ~4.6px/f 循环、相位错开 | 静态连线=没有\"输送的感觉\"（判例）；同相位读作闪烁 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 时序判例链完整记录在案：错峰连入 ×2 被裁 → 管齐图标错峰被裁 →\n  两拍制过关。\"同时\"是这张卡的节奏身份\n- 与 glow-flyline-moves 分工：那张是飞线本身的光效戏；本卡是\n  翻面+两拍接入+输送的叙事结构，光管只是元素\n- 与 canvas-materialize-moves 分工：那张是画布元素实体化；本卡\n  核心是\"旧页翻成新页\"的换代语义\n- 双面卡实现：水平/垂直线 bbox=0 会使 SVG filter/渐变失效（三管\n  消失判例），管线渐变用 userSpaceOnUse 每管独立定义\n\n## 参考实现\ndemos/ui-entrance/integration-hub-map/\n（IntegrationHubMap.tsx）\n原片出处：clickup-30.mp4\n\n## Reference implementation (Remotion)\n\n- demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx\n\nThe component imports shared fixtures from `demos/_fixtures` and helpers from `assets/lib` in the repo; SKILL.md there documents the render workflow.\n",
  "flow": [
    {
      "step": "Read the recipe",
      "note": "Prompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points."
    },
    {
      "step": "Get the code",
      "tool": "git",
      "cmd": "git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install"
    },
    {
      "step": "Register + render",
      "tool": "remotion",
      "cmd": "npx remotion render <CompositionId> out.mp4   # component: demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx"
    },
    {
      "step": "Or re-implement",
      "note": "Port the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing."
    }
  ],
  "files": [
    {
      "path": "demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx"
    }
  ],
  "contract": "React/Remotion composition (TSX) from the open-source video-shotcraft library (Apache-2.0). It is not a browser-playable template: clone the repo, register the component in a Remotion project and render with `npx remotion render`. The recipe (prompt) describes timing, easing and parameters so you can also re-implement the shot in any engine.",
  "render": {
    "engine": "remotion",
    "frames": 142,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx"
    ],
    "note": "Clone https://github.com/Vincentwei1021/video-shotcraft (npm install). The component lives under demos/{category}/{name}/ and imports shared fixtures from demos/_fixtures and helpers from assets/lib. Copy it (with those imports) into a Remotion project, register a <Composition> with the template's width/height/fps/duration in Root.tsx, then `npx remotion render <CompositionId> out.mp4`. SKILL.md in the repo documents the full Claude Code / Codex workflow. The prompt below is the shot recipe (timing, easing, parameter table, pitfalls) — enough to re-implement the shot in HTML/WAAPI or another engine if you prefer."
  },
  "source": "// ===== demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx =====\n// integration-hub-map v5 —— 批次 14 单点节奏修正：\n// 用户意见（逐字）\"动作对了，但是需要翻的时候很快，最后快完成的时候变慢下来\"\n// → rotateY 0→180° 改为快翻+尾段减速：强 ease-out（cubic），前 ~40% 时间\n//   完成 ~80% 角度，尾段明显减速缓着陆；仍一次连贯完成、无分段停顿\n//   （判例：\"匀速\"=无停顿≠字面 linear，本条已被用户亲自纠正为快翻尾缓）。\n//   90° 侧棱时刻随之提前到 ~f28，白热爆发峰值同步前移对齐。其余全保留。\n// —— 以下为 v3 说明（结构沿用）：\n// ① 开头不是\"转一个角度\"，而是页面整个 rotateY 翻转 180° 翻到背面，\n//    得到一张新的页面（双面卡：正面=近景旧页，背面=翻正后的新中枢页），\n//    翻到侧棱（~90°）时白热爆发吞没画面（对应截图 3/4）。\n// ② 五图标光管连上后有\"输送感\"：亮脉冲沿管线方向（图标→中枢）持续\n//    循环流动，直到片尾不停。\n// 运动结构对照截图：S1 近景正视可读 → S2 翻转中+拉远+泛光起 →\n// S3/S4 侧棱白热爆发+图标浮现 → S5/S6 新页转正、光管连入 → S7/S8 稳定输送。\nimport React, { useId } from 'react';\nimport { AbsoluteFill, useCurrentFrame, interpolate, Easing } from 'remotion';\n\nconst mulberry32 = (a: number) => () => {\n  let t = (a += 0x6d2b79f5);\n  t = Math.imul(t ^ (t >>> 15), t | 1);\n  t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n};\nconst rand = mulberry32(20260718);\nconst NOISE: number[] = Array.from({ length: 200 }, () => rand());\n\nconst FONT = '\"Avenir Next\", \"Helvetica Neue\", Helvetica, sans-serif';\n\n// ---------- 中枢面板（Enterprise MQLs，内容对照截图 1/7） ----------\nconst LIST: { icon: string; title: string; sub: string }[] = [\n  { icon: '#4a9fd8', title: 'Q3 Enterprise Deal', sub: 'Revenue · Pipeline · Q3 Quota' },\n  { icon: '#4a9fd8', title: 'Major Enterprise Account - UK', sub: 'Revenue · MQL · International' },\n  { icon: '#34a853', title: 'Enterprise Pitch Deck', sub: 'Open in GDrive' },\n  { icon: '#a259ff', title: 'MQL Lead Form Design', sub: 'Figma File · Last Edited' },\n  { icon: '#f2c744', title: 'Enterprise Sales', sub: 'ClickUp Space' },\n  { icon: '#9a9a98', title: 'Enterprise Closed Archive', sub: 'Archived · In Enterprise Sales' },\n  { icon: '#c8c8c6', title: 'Open Enterprise Lead - Follow up', sub: 'In Progress · In Enterprise Sales · Yesterday' },\n];\n\nconst HubPanel: React.FC<{ glow: number }> = ({ glow }) => (\n  <div\n    style={{\n      width: 820,\n      height: 520,\n      background: '#fbfbfa',\n      borderRadius: 14,\n      padding: '26px 30px',\n      boxSizing: 'border-box',\n      fontFamily: FONT,\n      boxShadow: `0 0 ${40 + glow * 110}px rgba(255,255,255,${0.3 + glow * 0.55}), 0 0 ${130 + glow * 140}px rgba(215,150,255,${0.22 + glow * 0.35})`,\n      display: 'flex',\n      gap: 26,\n      overflow: 'hidden',\n    }}\n  >\n    <div style={{ flex: 2 }}>\n      <div style={{ fontSize: 27, fontWeight: 600, color: '#2f2f38' }}>Enterprise MQLs</div>\n      <div style={{ display: 'flex', gap: 16, marginTop: 12 }}>\n        {['All', 'Tasks', 'Docs', 'Whiteboards', 'Dashboards', 'Files', 'Chat', 'People'].map((t, i) => (\n          <div key={t} style={{ fontSize: 12, color: i === 0 ? '#5b55c8' : '#98989f', fontWeight: i === 0 ? 700 : 400 }}>\n            {t}\n          </div>\n        ))}\n      </div>\n      <div style={{ fontSize: 12, color: '#8b8b92', marginTop: 14 }}>Recent</div>\n      {LIST.map((it, i) => (\n        <div key={i} style={{ display: 'flex', gap: 11, alignItems: 'center', marginTop: 13.5 }}>\n          <div style={{ width: 22, height: 22, borderRadius: 6, background: it.icon, opacity: 0.85 }} />\n          <div>\n            <div style={{ fontSize: 13.5, fontWeight: 600, color: '#3c3c44' }}>{it.title}</div>\n            <div style={{ fontSize: 11, color: '#a2a2a8', marginTop: 1 }}>{it.sub}</div>\n          </div>\n        </div>\n      ))}\n    </div>\n    <div style={{ flex: 1, borderLeft: '1px solid #e7e7e5', paddingLeft: 22 }}>\n      <div\n        style={{\n          height: 30,\n          width: 168,\n          border: '1.5px solid #cacac8',\n          borderRadius: 8,\n          marginTop: 4,\n          fontSize: 12,\n          color: '#6a6a70',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n        }}\n      >\n        + Add Location Filter\n      </div>\n      <div style={{ fontSize: 10.5, color: '#adadb2', marginTop: 24, letterSpacing: 1 }}>QUICK FILTERS</div>\n      {['Assigned to Me', 'Created by Me'].map((t) => (\n        <div key={t} style={{ fontSize: 13.5, color: '#55555c', marginTop: 11 }}>{t}</div>\n      ))}\n      <div style={{ fontSize: 10.5, color: '#adadb2', marginTop: 24, letterSpacing: 1 }}>TASK FILTERS</div>\n      {['Open', 'Closed', 'Archived'].map((t) => (\n        <div key={t} style={{ fontSize: 13.5, color: '#55555c', marginTop: 11 }}>{t}</div>\n      ))}\n    </div>\n  </div>\n);\n\n// ---------- 翻转前的旧页面（正面）：另一张页面，翻面后才得到中枢页 ----------\nconst FrontPanel: React.FC<{ glow: number }> = ({ glow }) => (\n  <div\n    style={{\n      width: 820,\n      height: 520,\n      background: '#fbfbfa',\n      borderRadius: 14,\n      padding: '30px 34px',\n      boxSizing: 'border-box',\n      fontFamily: FONT,\n      boxShadow: `0 0 ${40 + glow * 110}px rgba(255,255,255,${0.3 + glow * 0.55})`,\n      overflow: 'hidden',\n    }}\n  >\n    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>\n      <div style={{ width: 26, height: 26, borderRadius: 7, background: '#4a9fd8', opacity: 0.9 }} />\n      <div style={{ fontSize: 26, fontWeight: 600, color: '#2f2f38' }}>Q3 Enterprise Deal</div>\n    </div>\n    <div style={{ display: 'flex', gap: 14, marginTop: 10 }}>\n      {['Revenue', 'Pipeline', 'Q3 Quota'].map((t) => (\n        <div\n          key={t}\n          style={{\n            fontSize: 11.5,\n            color: '#6a6a70',\n            background: '#efeff0',\n            borderRadius: 6,\n            padding: '3px 10px',\n          }}\n        >\n          {t}\n        </div>\n      ))}\n    </div>\n    <div style={{ height: 1, background: '#e8e8e6', marginTop: 18 }} />\n    {/* 文档灰条段落 */}\n    {[420, 700, 660, 540, 0, 690, 630, 380, 0, 580, 640, 460].map((w, i) =>\n      w === 0 ? (\n        <div key={i} style={{ height: 14 }} />\n      ) : (\n        <div\n          key={i}\n          style={{\n            width: w,\n            height: 13,\n            borderRadius: 6,\n            background: i % 5 === 0 ? '#d7d7db' : '#e6e6e9',\n            marginTop: 13,\n          }}\n        />\n      ),\n    )}\n  </div>\n);\n\n// ---------- 品牌图标瓷贴 ----------\nconst Tile: React.FC<{ kind: string; on: number }> = ({ kind, on }) => {\n  const glyph = (() => {\n    switch (kind) {\n      case 'figma':\n        return (\n          <svg width={46} height={46} viewBox=\"0 0 46 46\">\n            <circle cx={16} cy={9} r={7} fill=\"#f24e1e\" />\n            <circle cx={30} cy={9} r={7} fill=\"#ff7262\" />\n            <circle cx={16} cy={23} r={7} fill=\"#a259ff\" />\n            <circle cx={30} cy={23} r={7} fill=\"#1abcfe\" />\n            <circle cx={16} cy={37} r={7} fill=\"#0acf83\" />\n          </svg>\n        );\n      case 'github':\n        return <div style={{ width: 46, height: 46, borderRadius: '50%', background: '#24292f' }} />;\n      case 'salesforce':\n        return (\n          <svg width={56} height={40} viewBox=\"0 0 56 40\">\n            <ellipse cx={22} cy={22} rx={14} ry={11} fill=\"#00a1e0\" />\n            <ellipse cx={36} cy={18} rx={13} ry={10} fill=\"#00a1e0\" />\n            <ellipse cx={30} cy={26} rx={16} ry={10} fill=\"#00a1e0\" />\n          </svg>\n        );\n      case 'gdrive':\n        return (\n          <svg width={48} height={42} viewBox=\"0 0 48 42\">\n            <path d=\"M16 2 L32 2 L48 30 L40 42 L8 42 L0 30 Z\" fill=\"none\" />\n            <path d=\"M16 2 L32 2 L20 24 L4 24 Z\" fill=\"#34a853\" transform=\"translate(2,2)\" />\n            <path d=\"M32 2 L46 28 L30 28 L18 6 Z\" fill=\"#fbbc04\" transform=\"translate(0,2)\" />\n            <path d=\"M6 28 L42 28 L36 38 L12 38 Z\" fill=\"#4285f4\" />\n          </svg>\n        );\n      default: // dropbox\n        return (\n          <svg width={48} height={42} viewBox=\"0 0 48 42\">\n            <path d=\"M12 0 L24 8 L12 16 L0 8 Z\" fill=\"#0061ff\" />\n            <path d=\"M36 0 L48 8 L36 16 L24 8 Z\" fill=\"#0061ff\" />\n            <path d=\"M12 18 L24 26 L12 34 L0 26 Z\" fill=\"#0061ff\" />\n            <path d=\"M36 18 L48 26 L36 34 L24 26 Z\" fill=\"#0061ff\" />\n          </svg>\n        );\n    }\n  })();\n  return (\n    <div\n      style={{\n        width: 112,\n        height: 112,\n        borderRadius: 26,\n        background: '#fdfdfd',\n        display: 'flex',\n        justifyContent: 'center',\n        alignItems: 'center',\n        boxShadow: `0 0 ${16 + on * 46}px rgba(255,255,255,${0.2 + on * 0.55})`,\n        transform: `scale(${0.9 + on * 0.1})`,\n      }}\n    >\n      {glyph}\n    </div>\n  );\n};\n\n// ---------- 光管（彩虹渐变霓虹管） ----------\ntype Pipe = { kind: string; icon: [number, number]; path: string; len: number; tIcon: number; tPipe: number };\nconst PIPES: Pipe[] = [\n  { kind: 'figma', icon: [452, 262], path: 'M 452 322 L 452 440 Q 452 480 492 480 L 552 480', len: 300, tIcon: 52, tPipe: 62 },\n  { kind: 'github', icon: [252, 612], path: 'M 316 612 L 552 612', len: 240, tIcon: 52, tPipe: 62 },\n  { kind: 'salesforce', icon: [992, 178], path: 'M 992 240 L 992 332', len: 92, tIcon: 52, tPipe: 62 },\n  { kind: 'gdrive', icon: [1512, 272], path: 'M 1512 332 L 1512 440 Q 1512 480 1472 480 L 1372 480', len: 290, tIcon: 52, tPipe: 62 },\n  { kind: 'dropbox', icon: [1702, 618], path: 'M 1640 618 L 1372 618', len: 270, tIcon: 52, tPipe: 62 },\n];\nconst GROW = 9; // v8（批次 17）：用户意见\"翻转过来后，5个app同时出现，然后同时连接\"——两拍制：五图标 tIcon 统一 52 同帧出现，五管 tPipe 统一 62 同帧连接\n\n// ---------- 背景霓虹矩形轮廓 ----------\nconst RECTS = Array.from({ length: 9 }, (_, i) => ({\n  x: [150, 660, 1740, 250, 1150, 700, 1660, 90, 1330][i],\n  y: [255, 355, 545, 850, 935, 985, 830, 555, 760][i],\n  w: 90 + NOISE[i * 3] * 160,\n  h: 60 + NOISE[i * 3 + 1] * 70,\n  hue: [265, 285, 300, 255, 275, 210, 320, 240, 40][i],\n  ph: NOISE[i * 3 + 2] * Math.PI * 2,\n}));\n\nexport const IntegrationHubMap: React.FC = () => {\n  const frame = useCurrentFrame();\n  // 滤镜/渐变 ID 按实例生成，多实例同场不串引（useId 的 «:» 在 url() 里非法，需清洗）\n  const uid = useId().replace(/[^a-zA-Z0-9]/g, '');\n\n  // --- 相机/面板轨迹：近景正视旧页 → 整体翻转 180°（翻到背面=新页）+ 拉远落定 ---\n  const zoom = interpolate(frame, [0, 14, 58, 96], [2.05, 1.95, 1.1, 1.0], {\n    extrapolateRight: 'clamp',\n    easing: Easing.inOut(Easing.cubic),\n  });\n  // v6（批次 15）：用户意见\"翻转再快1倍\"——翻面窗口 f14–84（70 帧）\n  // 压半到 f14–49（35 帧），快翻+尾段减速曲线形状保持；\n  // 90° 侧棱相应提前到 ~f21.2（easeOut=0.5 → t≈0.206）。\n  const rotY = interpolate(frame, [14, 49], [0, 180], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.out(Easing.cubic),\n  });\n  const panX = interpolate(frame, [0, 18, 55, 96], [130, 120, 30, 0], {\n    extrapolateRight: 'clamp',\n    easing: Easing.inOut(Easing.cubic),\n  });\n  const panY = interpolate(frame, [0, 18, 55, 96], [120, 110, 30, 25], {\n    extrapolateRight: 'clamp',\n    easing: Easing.inOut(Easing.cubic),\n  });\n\n  // v6：用户意见\"中间不需要长时间的光晕，翻到中间闪一下就行\"——\n  // 长光晕平台删除，改为 90° 侧棱时刻（~f21）2 帧脉冲亮闪即回落\n  const bloom = interpolate(frame, [19, 21, 23, 27], [0, 1, 0.25, 0], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n  const noise = NOISE[Math.min(frame, NOISE.length - 1)];\n\n  // 全连通后呼吸\n  const allOn = Math.max(...PIPES.map((p) => p.tPipe)) + GROW;\n  const breathe = frame > allOn ? 0.5 + 0.5 * Math.sin((frame - allOn) * 0.16) : 0;\n  const panelGlow = bloom * 1.1 + breathe * 0.2;\n\n  // 星图元素（图标/光管/矩形）整体可见度\n  const mapIn = interpolate(frame, [34, 60], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });\n\n  return (\n    <AbsoluteFill style={{ background: '#08070c' }}>\n      {/* 暗紫底噪 */}\n      <AbsoluteFill\n        style={{\n          background:\n            'radial-gradient(ellipse at 50% 50%, rgba(90,50,140,0.25), transparent 62%), radial-gradient(ellipse at 18% 78%, rgba(140,50,120,0.12), transparent 50%)',\n        }}\n      />\n\n      {/* 背景霓虹矩形轮廓 */}\n      {RECTS.map((r, i) => {\n        const on = interpolate(frame, [36 + i * 4, 52 + i * 4], [0, 1], {\n          extrapolateLeft: 'clamp',\n          extrapolateRight: 'clamp',\n        });\n        const settle = frame > 100 ? 0.55 : 1;\n        const flick = 0.65 + 0.35 * Math.sin(frame * 0.11 + r.ph);\n        return (\n          <div\n            key={i}\n            style={{\n              position: 'absolute',\n              left: r.x,\n              top: r.y,\n              width: r.w,\n              height: r.h,\n              borderRadius: 12,\n              border: `2.5px solid hsla(${r.hue} 90% 70% / ${0.75 * on * flick * settle})`,\n              boxShadow: `0 0 18px hsla(${r.hue} 90% 65% / ${0.55 * on * flick * settle}), inset 0 0 14px hsla(${r.hue} 90% 65% / ${0.3 * on * flick * settle})`,\n            }}\n          />\n        );\n      })}\n\n      {/* 光管层 */}\n      <svg width={1920} height={1080} style={{ position: 'absolute', inset: 0, opacity: mapIn }}>\n        <defs>\n          {/* 每条管一个 userSpaceOnUse 渐变（沿管起终点），彩虹沿管线方向铺开。\n              objectBoundingBox 在纯水平/垂直线上宽或高为 0 会禁用 paint，必须用户坐标。 */}\n          {PIPES.map((p, i) => {\n            const nums = p.path.match(/-?[\\d.]+/g)!.map(Number);\n            const [x1, y1] = [nums[0], nums[1]];\n            const [x2, y2] = [nums[nums.length - 2], nums[nums.length - 1]];\n            return (\n              <linearGradient key={i} id={`rainbow-${uid}-${i}`} gradientUnits=\"userSpaceOnUse\" x1={x1} y1={y1} x2={x2} y2={y2}>\n                <stop offset=\"0%\" stopColor=\"#ffe14d\" />\n                <stop offset=\"28%\" stopColor=\"#ff8a5a\" />\n                <stop offset=\"52%\" stopColor=\"#ff5ad0\" />\n                <stop offset=\"76%\" stopColor=\"#b46bff\" />\n                <stop offset=\"100%\" stopColor=\"#5ad0ff\" />\n              </linearGradient>\n            );\n          })}\n          {/* userSpaceOnUse：纯水平/垂直直线管的 bbox 为零，百分比滤镜区域会\n              坍缩成 0 导致整条管不渲染（github/salesforce/dropbox 三管消失） */}\n          <filter id={`pipeGlow-${uid}`} filterUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width=\"1920\" height=\"1080\">\n            <feGaussianBlur stdDeviation=\"8\" result=\"b\" />\n            <feMerge>\n              <feMergeNode in=\"b\" />\n              <feMergeNode in=\"SourceGraphic\" />\n            </feMerge>\n          </filter>\n        </defs>\n        {PIPES.map((p, i) => {\n          const grow = interpolate(frame, [p.tPipe, p.tPipe + GROW], [0, 1], {\n            extrapolateLeft: 'clamp',\n            extrapolateRight: 'clamp',\n            easing: Easing.out(Easing.quad),\n          });\n          if (grow <= 0) return null;\n          const dashOn = p.len * grow;\n          const pulse = frame > allOn ? 0.78 + 0.22 * Math.sin((frame - allOn) * 0.16 + i) : 1;\n          // 输送感：亮脉冲沿管线方向（图标→中枢）持续循环流动\n          const flowOffset = -((frame - p.tPipe) * 4.6 + i * 37);\n          const flowIn = interpolate(frame, [p.tPipe + GROW, p.tPipe + GROW + 8], [0, 1], {\n            extrapolateLeft: 'clamp',\n            extrapolateRight: 'clamp',\n          });\n          return (\n            <g key={i} filter={`url(#pipeGlow-${uid})`}>\n              <path\n                d={p.path}\n                fill=\"none\"\n                stroke={`url(#rainbow-${uid}-${i})`}\n                strokeWidth={17}\n                strokeLinecap=\"round\"\n                strokeDasharray={`${dashOn} ${p.len + 60}`}\n                opacity={0.92 * pulse}\n              />\n              <path\n                d={p.path}\n                fill=\"none\"\n                stroke=\"rgba(255,255,255,0.9)\"\n                strokeWidth={5}\n                strokeLinecap=\"round\"\n                strokeDasharray={`${dashOn} ${p.len + 60}`}\n                opacity={0.85 * pulse}\n              />\n              {grow >= 1 && flowIn > 0 && (\n                <>\n                  {/* 输送流：连续多段亮脉冲沿管流动（间距 46，段长 20） */}\n                  <path\n                    d={p.path}\n                    fill=\"none\"\n                    stroke=\"#ffffff\"\n                    strokeWidth={11}\n                    strokeLinecap=\"round\"\n                    strokeDasharray=\"18 56\"\n                    strokeDashoffset={flowOffset}\n                    opacity={0.95 * flowIn}\n                  />\n                  {/* 输送流柔光晕 */}\n                  <path\n                    d={p.path}\n                    fill=\"none\"\n                    stroke=\"rgba(255,255,255,0.6)\"\n                    strokeWidth={24}\n                    strokeLinecap=\"round\"\n                    strokeDasharray=\"18 56\"\n                    strokeDashoffset={flowOffset}\n                    opacity={0.55 * flowIn}\n                  />\n                </>\n              )}\n            </g>\n          );\n        })}\n      </svg>\n\n      {/* 图标瓷贴 */}\n      {PIPES.map((p, i) => {\n        const appear = interpolate(frame, [p.tIcon, p.tIcon + 12], [0, 1], {\n          extrapolateLeft: 'clamp',\n          extrapolateRight: 'clamp',\n          easing: Easing.out(Easing.cubic),\n        });\n        const on = interpolate(frame, [p.tPipe, p.tPipe + 10], [0.15, 1], {\n          extrapolateLeft: 'clamp',\n          extrapolateRight: 'clamp',\n        });\n        if (appear <= 0) return null;\n        return (\n          <div\n            key={i}\n            style={{\n              position: 'absolute',\n              left: p.icon[0] - 56,\n              top: p.icon[1] - 56,\n              opacity: appear,\n              transform: `translateY(${(1 - appear) * 24}px)`,\n            }}\n          >\n            <Tile kind={p.kind} on={on * (frame > allOn ? 0.8 + 0.2 * breathe : 1)} />\n          </div>\n        );\n      })}\n\n      {/* 中枢面板：双面卡整体翻转 180°（正面旧页 → 背面新中枢页） */}\n      <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center', perspective: 1500 }}>\n        <div\n          style={{\n            transform: `translate(${panX}px, ${panY}px) rotateY(${rotY}deg) scale(${zoom})`,\n            position: 'relative',\n            transformStyle: 'preserve-3d',\n            width: 820,\n            height: 520,\n          }}\n        >\n          {/* 正面：翻转前的旧页面 */}\n          <div\n            style={{\n              position: 'absolute',\n              inset: 0,\n              backfaceVisibility: 'hidden',\n            }}\n          >\n            <FrontPanel glow={panelGlow} />\n          </div>\n          {/* 背面：翻正后得到的新中枢页（预转 180° 使翻完时朝向镜头且不镜像） */}\n          <div\n            style={{\n              position: 'absolute',\n              inset: 0,\n              backfaceVisibility: 'hidden',\n              transform: 'rotateY(180deg)',\n            }}\n          >\n            <HubPanel glow={panelGlow} />\n          </div>\n          {/* 面板过曝罩：爆发期盖白 */}\n          <div\n            style={{\n              position: 'absolute',\n              inset: -6,\n              borderRadius: 18,\n              background: '#ffffff',\n              opacity: Math.min(0.96, bloom * 1.05),\n              filter: 'blur(5px)',\n              pointerEvents: 'none',\n              transform: rotY > 90 ? 'rotateY(180deg) translateZ(1px)' : 'translateZ(1px)',\n              backfaceVisibility: 'hidden',\n            }}\n          />\n        </div>\n      </AbsoluteFill>\n\n      {/* 全屏白热眩光（S3/S4）：白核+品红/粉翼+青蓝斑 */}\n      {bloom > 0.02 && (\n        <AbsoluteFill style={{ pointerEvents: 'none' }}>\n          <div\n            style={{\n              position: 'absolute',\n              left: 300,\n              top: 60,\n              width: 1100,\n              height: 900,\n              background:\n                'radial-gradient(closest-side, rgba(255,255,255,0.98), rgba(255,235,255,0.75) 42%, rgba(255,120,230,0.4) 68%, transparent 88%)',\n              filter: 'blur(26px)',\n              opacity: Math.min(1, bloom * (0.94 + 0.06 * noise)),\n            }}\n          />\n          <div\n            style={{\n              position: 'absolute',\n              left: 1150,\n              top: 150,\n              width: 700,\n              height: 620,\n              background: 'radial-gradient(closest-side, rgba(255,90,208,0.85), rgba(200,70,255,0.4) 60%, transparent 85%)',\n              filter: 'blur(34px)',\n              opacity: bloom * 0.9,\n            }}\n          />\n          <div\n            style={{\n              position: 'absolute',\n              left: 40,\n              top: 480,\n              width: 620,\n              height: 520,\n              background: 'radial-gradient(closest-side, rgba(140,210,255,0.8), rgba(90,120,255,0.35) 60%, transparent 85%)',\n              filter: 'blur(30px)',\n              opacity: bloom * 0.85,\n            }}\n          />\n          {/* 横向紫白光条（S2 左侧光痕） */}\n          <div\n            style={{\n              position: 'absolute',\n              left: 30,\n              top: 700,\n              width: 420,\n              height: 46,\n              borderRadius: 23,\n              background: 'linear-gradient(90deg, rgba(255,255,255,0.95), rgba(170,90,255,0.8), transparent)',\n              filter: 'blur(14px)',\n              opacity: interpolate(frame, [20, 34, 70, 92], [0, 0.9, 0.5, 0], {\n                extrapolateLeft: 'clamp',\n                extrapolateRight: 'clamp',\n              }),\n            }}\n          />\n        </AbsoluteFill>\n      )}\n    </AbsoluteFill>\n  );\n};\n\n\n"
}