{
  "slug": "wall-reveal-moves",
  "title": "Wall reveal: three moves",
  "tagline": "Three whole-wall batch entrances — bento cell-by-cell light-up, grid wave flip, and blueprint draw-on; all reveal in place with no displacement, complementing the fly-in, displacement-type deck-deal-flyin.",
  "description": "Three whole-wall batch entrances — bento cell-by-cell light-up, grid wave flip, and blueprint draw-on; all reveal in place with no displacement, complementing",
  "category": "ui-entrance",
  "tags": [
    "shotcraft",
    "remotion",
    "ui-entrance"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 5.06,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/wall-reveal-moves/poster-c295fdd23eae.jpg",
    "video": "https://gimgs.net/motion/media/wall-reveal-moves/video-dd3d041c884c.mp4"
  },
  "variants": [
    {
      "key": "bento-light-up",
      "label": "bento-light-up · Cell-by-cell light-up",
      "description": "A dark 3×2 bento wall sits dimmed and waiting, an amber flowing light traces each cell's outline in turn as its content brightens and lifts; once all are lit the camera pushes in slowly to settle",
      "use": "A gravitas/dark-toned brand passage; a steady-paced feature overview",
      "video": "https://gimgs.net/motion/media/wall-reveal-moves/video-dd3d041c884c.mp4",
      "poster": "https://gimgs.net/motion/media/wall-reveal-moves/poster-c295fdd23eae.jpg"
    },
    {
      "key": "grid-wave-flip",
      "label": "grid-wave-flip · Wave flip",
      "description": "A 3×3 gray-backed card wall flips 180° in place along a diagonal wavefront on rotateX, revealing its front content; the last card overshoots and bounces back",
      "use": "A brisk feature wall; a fast passage that sweeps the whole screen in one second",
      "video": "https://gimgs.net/motion/media/wall-reveal-moves/video-1e04e1a3e6a5.mp4",
      "poster": "https://gimgs.net/motion/media/wall-reveal-moves/poster-grid-wave-flip-b36c68d7c640.jpg"
    },
    {
      "key": "wireframe-draw-on",
      "label": "wireframe-draw-on · Blueprint draw-on",
      "description": "The interface is first drawn in groups as a fine SVG-line blueprint, then a glowing amber vertical line sweeps left to right, and wherever it passes the wireframe solidifies into the real interface",
      "use": "A \"from design to product / from idea to product\" narrative; the product's first full reveal",
      "video": "https://gimgs.net/motion/media/wall-reveal-moves/video-1b09574be469.mp4",
      "poster": "https://gimgs.net/motion/media/wall-reveal-moves/poster-wireframe-draw-on-615b3a2cb7e0.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/wall-reveal-moves.html",
  "json_url": "https://gimgs.net/motion/wall-reveal-moves.json",
  "template_url": "https://gimgs.net/motion/wall-reveal-moves/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/wall-reveal-moves.md",
  "updated_at": "2026-09-24T07:40:35.444Z",
  "prompt": "## When to use\n\nThe overall reveal of a feature wall/card wall/whole-page UI; passages where content is already in place and needs to \"appear\" rather than \"pour in\"\n\n## Intention\n\nUntil now, the library's only answer for \"how does a whole wall of content enter\" was deck-deal-flyin — high-energy, displacement-type, \"things pouring in.\" But not every wall should pour: a brand passage wants gravitas, a feature wall wants briskness, a \"from design to product\" passage wants to show process. These three moves share a common trait — **non-displacement batch entrance** — elements reveal in place, the layout is already correct from frame one, and what the viewer watches is \"a wavefront of state-change sweeping across a wall,\" not \"objects flying.\" Ask first when choosing: should this wall \"pour in\" or \"light up\"? The former goes to deck-deal-flyin, the latter to this card.\n\n## Duration & energy\n\n- Duration: Single style ~4.3–5s (A 150f / B 130f / C 150f @30fps, including an establishing hold and a still close)\n- Energy: Medium\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n\"一墙内容怎么出场\"库内此前只有 deck-deal-flyin 一个答案——高能量、位移型、\n\"东西涌进来\"。但不是每面墙都该涌：品牌段要庄重、功能墙要轻快、\n\"从设计到实物\"要讲工序。这三式共同点是**非位移批量入场**——元素原地显形，\n版式从第一帧就成立，观众看的是\"状态变化的波前扫过一面墙\"而不是\"物体飞行\"。\n选型先问一句：这面墙该\"涌进来\"还是\"亮起来\"？前者去 deck-deal-flyin，后者进本卡。\n\n## 三式选型\n| 式 | 做法 | 适用 |\n|----|------|------|\n| A bento-light-up 逐格点亮 | 暗场 3×2 bento 墙压暗待命，强调色流光逐格描边一圈、内容随即提亮上浮，全亮后镜头缓推收住 | 内容庄重/暗场品牌段；节奏沉稳的功能总览 |\n| B grid-wave-flip 波浪翻面 | 3×3 灰背卡墙沿对角线波前依次 rotateX 原位翻转 180°，翻出正面内容，尾张过冲回弹 | 轻快功能墙；一秒扫完全屏的快节奏段 |\n| C wireframe-draw-on 蓝图描线成形 | 界面先以 SVG 细线蓝图分组描画，再一条强调色发光竖线左→右扫过，扫过处线框实体化成真实界面 | \"从设计到实物/从想法到产品\"叙事；产品首次整体亮相 |\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| A 节拍 | hold 20f → 每格间隔 12f 依次激活（六格 ~96f 全亮）→ scale 1→1.04 缓推 25f（bezier 0.33,0,0.2,1）→ 静止收尾 | 12f 级联是\"逐格可辨\"与\"整墙拖沓\"的平衡点；格数增多可压到 8–10f，但间隔均匀本身就是这一式的庄重感来源，别学发牌加速 |\n| A 单格 | 暗态 opacity 0.18 + translateY 20px；SVG rect pathLength=100 + dashoffset 描边 8f（强调色 #e8b45e 4px + drop-shadow 辉光）；描边过半（+6f）内容 back-out(0.3,1.4,0.5,1) 弹出 8f 提亮归位；描完 +12f 流光退火到 0.4 常亮细边 | 描边与内容弹出要**接力不同步**——同帧起读作整格闪现；点亮中段辉光脉冲用 lit·(1−lit)·4 抛物线，峰值幅度按格 seed 微差（正弦哈希，禁真随机） |\n| B 波前 | hold 20f → delay=(row+col)*6f 对角线步进，每张 rotateX 0→180° 翻 14f bezier(0.35,0,0.25,1)；尾张冲到 190° 再 8f 弹回 180° | (row+col) 是对角波的灵魂，换 row 或 col 单向扫立刻平庸；只给尾张过冲——张张过冲读作整墙果冻 |\n| B 翻面结构 | 共享 perspective 1200px 容器（整墙一个消失点），每张双面卡 preserve-3d：灰背朝外 + 正面预转 rotateX(180°)，两面都 backfaceVisibility hidden | perspective 加在墙容器不是单卡上，否则九个消失点各翻各的；翻到 90° 最薄处叠一道 4px 白色高光线（不随卡旋转、贴格位随角度 45°→135° 从上缘扫到下缘，opacity=1−|angle−90|/45），阴影随 sin(angle) 抬起再收回 |\n| C 描线 | hold 20f → 分组错峰各 30f 描完：侧栏 20f 起/顶栏 30f 起/6 卡 40+i*3f 起/折线图 52f 起；SVG pathLength=1 + dashoffset 1→0，描边 #8f8f8d 2.5px round | 分组错峰是\"手在画\"的感觉来源，全部同帧起笔读作打印机；卡内占位线再按 +3f 细错峰，长短用 seed 哈希微差 |\n| C 实体化 | 88–118f 实体层 clip-path inset(0 right% 0 0) 从左向右展开 30f，bezier(0.55,0,0.25,1)；前沿跟 4px 强调色竖线（#ffc46b + 双层辉光 boxShadow），86–92f 淡入、112–120f 淡出 | 竖线位置与 clip 前沿必须同一个 scan 值驱动，异步读作穿帮；描线段与扫描段之间留 ~6f 喘息（82→88f），线框画完立刻扫读作赶工 |\n| 静止收尾 | 三式动作全部落定后真静止 ≥15f（A 在缓推结束后、B 在尾张回弹后、C 在扫描线淡出后起算） | R1 呼吸判例：静止期一切属性锁死，包括辉光/阴影/高光残留 |\n\n## 已知坑\n- **demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验**\n- A/C 的强调色描边/扫描线属\"单点高质量光效\"范畴（Q4 三约束内合规），但一支片\n  整墙点亮 ≤1 次（P4 手法去重）——A 和 C 同片共用即算两次整墙光效，选一\n- B 式翻面后的正面实战要接真实截图：backface 侧预留高清纹理（Q2），翻转中\n  正面会以斜角被看，低倍截图先按审美准则 Q2 的高分辨率栅格化技法处理再上\n- C 式线框几何必须与实体层布局逐像素对位（demo 里按 FakeDashboard A 手排），\n  换素材要重排线框——对不齐则扫描线扫过瞬间元素跳位，一眼穿帮\n- 三式都是\"原位显形\"，别混入位移入场（如 B 翻面时再加 translateY 飞入）——\n  位移需求去 deck-deal-flyin，杂交会两头不像\n- A 式暗态 opacity 0.18 依赖暗底（#2a2a28）成立，亮底下压暗读作褪色故障\n\n## 参考实现\ndemos/ui-entrance/wall-reveal-moves/\n（BentoLightUp.tsx / GridWaveFlip.tsx / WireframeDrawOn.tsx）\n\n实现状态：三式均有参考实现与 Gallery 动态样片。\n\n## Reference implementation (Remotion)\n\n- demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx\n- demos/ui-entrance/wall-reveal-moves/GridWaveFlip.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/wall-reveal-moves/GridWaveFlip.tsx\n- demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.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/wall-reveal-moves/BentoLightUp.tsx, demos/ui-entrance/wall-reveal-moves/GridWaveFlip.tsx, demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.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/wall-reveal-moves/BentoLightUp.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx"
    },
    {
      "path": "demos/ui-entrance/wall-reveal-moves/GridWaveFlip.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/wall-reveal-moves/GridWaveFlip.tsx"
    },
    {
      "path": "demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.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": 152,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx",
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/wall-reveal-moves/GridWaveFlip.tsx",
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.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/wall-reveal-moves/BentoLightUp.tsx =====\nimport React from 'react';\nimport { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';\nimport { Card, TitleBlock } from '../../_fixtures/Fixtures';\n\n// bento-light-up：暗场里 3×2 bento 墙压暗待命，随节拍逐格点亮——\n// 边框流光先描一圈（琥珀），格内内容随后提亮上浮弹出；六格全亮后整体微推收住。\n// 节拍：0–20 建立(hold) → 每格间隔 12f 依次激活(描边 8f + 内容弹出 8f)\n//       → ~96f 全亮 → 96–121 scale 1→1.04 缓推 → 121–150 静止收尾。\n\nconst BG = '#2a2a28';\nconst AMBER = '#e8b45e';\nconst FIRST = 20; // 首格激活帧\nconst GAP = 12; // 格间节拍\nconst CELL_W = 480;\nconst CELL_H = 330;\nconst GUT = 44;\nconst LEFT = (1920 - (CELL_W * 3 + GUT * 2)) / 2;\nconst TOP = (1080 - (CELL_H * 2 + GUT)) / 2 + 30;\n\nconst Cell: React.FC<{ i: number; frame: number }> = ({ i, frame }) => {\n  const start = FIRST + i * GAP;\n  const col = i % 3;\n  const row = Math.floor(i / 3);\n  const x = LEFT + col * (CELL_W + GUT);\n  const y = TOP + row * (CELL_H + GUT);\n\n  // ① 边框流光：pathLength=100 的 dashoffset 描边，8f 走完一圈\n  const draw = interpolate(frame, [start, start + 8], [0, 1], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.out(Easing.cubic),\n  });\n  // 描完后流光退火：琥珀亮边 → 弱化成常亮细边\n  const strokeFade = interpolate(frame, [start + 12, start + 26], [1, 0.4], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.out(Easing.quad),\n  });\n\n  // ② 内容提亮 + 上浮弹出：描边过半后接力，8f 弹出（back-out 带一点过冲）\n  const lit = interpolate(frame, [start + 6, start + 14], [0, 1], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.out(Easing.cubic),\n  });\n  const rise = interpolate(frame, [start + 6, start + 14], [0, 1], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.bezier(0.3, 1.4, 0.5, 1),\n  });\n  const opacity = 0.18 + 0.82 * lit;\n  const ty = 20 * (1 - rise);\n  // seed 正弦哈希做每格微差（点亮瞬间的辉光强度略有随机感）\n  const jitter = Math.abs(Math.sin(i * 127.3) * 43758.5453 % 1);\n  const glow = lit * (1 - lit) * 4 * (14 + jitter * 6); // 点亮中段最亮的辉光脉冲\n\n  return (\n    <div style={{ position: 'absolute', left: x, top: y, width: CELL_W, height: CELL_H }}>\n      {/* 暗态卡 + 点亮后的内容（同一张卡，靠 opacity/translateY 提亮浮出） */}\n      <div\n        style={{\n          opacity,\n          transform: `translateY(${ty}px)`,\n          boxShadow: lit > 0.5 ? `0 0 ${glow}px rgba(232,180,94,${0.35 * lit * (1 - lit) * 4})` : 'none',\n          borderRadius: 14,\n        }}\n      >\n        <Card w={CELL_W} h={CELL_H} seed={i + 1} />\n      </div>\n      {/* 边框流光：SVG rect 描边一圈 */}\n      {draw > 0 && (\n        <svg\n          width={CELL_W}\n          height={CELL_H}\n          viewBox={`0 0 ${CELL_W} ${CELL_H}`}\n          style={{ position: 'absolute', left: 0, top: ty, overflow: 'visible' }}\n        >\n          <rect\n            x={2}\n            y={2}\n            width={CELL_W - 4}\n            height={CELL_H - 4}\n            rx={14}\n            fill=\"none\"\n            stroke={AMBER}\n            strokeWidth={4}\n            pathLength={100}\n            strokeDasharray={100}\n            strokeDashoffset={100 * (1 - draw)}\n            opacity={strokeFade}\n            style={{ filter: `drop-shadow(0 0 ${6 + jitter * 4}px ${AMBER})` }}\n          />\n        </svg>\n      )}\n    </div>\n  );\n};\n\nexport const BentoLightUp: React.FC = () => {\n  const frame = useCurrentFrame();\n\n  // ③ 六格全亮(~96f)后整体缓推 scale 1→1.04，25f 收住，之后真静止\n  const push = interpolate(frame, [96, 121], [1, 1.04], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.bezier(0.33, 0, 0.2, 1),\n  });\n\n  // 标题随首格点亮微微提亮，交代场景\n  const titleLit = interpolate(frame, [FIRST, FIRST + 20], [0.25, 0.75], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.out(Easing.quad),\n  });\n\n  return (\n    <AbsoluteFill style={{ background: BG, overflow: 'hidden' }}>\n      <div\n        style={{\n          position: 'absolute',\n          inset: 0,\n          transform: `scale(${push})`,\n          transformOrigin: '960px 540px',\n        }}\n      >\n        <div style={{ position: 'absolute', left: LEFT, top: TOP - 110, opacity: titleLit, filter: 'invert(1)' }}>\n          <TitleBlock text=\"Features\" size={64} />\n        </div>\n        {Array.from({ length: 6 }).map((_, i) => (\n          <Cell key={i} i={i} frame={frame} />\n        ))}\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n\n// ===== demos/ui-entrance/wall-reveal-moves/GridWaveFlip.tsx =====\nimport React from 'react';\nimport { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';\nimport { Card, G } from '../../_fixtures/Fixtures';\n\n// grid-wave-flip〔入场退场〕：3×3 灰背卡片墙沿对角线波前依次 rotateX 翻转 180°，\n// 灰背翻成正面内容卡；波浪约一秒扫完全屏，最后一张落定带轻微过冲。\n// 结构：hold 20f → delay=(row+col)*6f、每张 14f bezier(0.35,0,0.25,1) → 尾张过冲回弹 → 静止收尾。\n\nconst COLS = 3;\nconst ROWS = 3;\nconst CELL_W = 520;\nconst CELL_H = 280;\nconst GAP = 36;\nconst HOLD = 20; // 开头建立\nconst STAGGER = 6; // 对角线波前步进\nconst FLIP = 14; // 单张翻转时长\n\nconst flipEase = Easing.bezier(0.35, 0, 0.25, 1);\n\n// 单张卡的翻转角度：普通卡 0→180；最后一张（波前最末）过冲到 ~190 再回落 180\nconst angleAt = (frame: number, row: number, col: number): number => {\n  const delay = HOLD + (row + col) * STAGGER;\n  const isLast = row === ROWS - 1 && col === COLS - 1;\n  if (!isLast) {\n    return interpolate(frame, [delay, delay + FLIP], [0, 180], {\n      extrapolateLeft: 'clamp',\n      extrapolateRight: 'clamp',\n      easing: flipEase,\n    });\n  }\n  // 过冲：14f 冲到 190°，再 8f 弹回 180°\n  const main = interpolate(frame, [delay, delay + FLIP], [0, 190], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: flipEase,\n  });\n  const settle = interpolate(frame, [delay + FLIP, delay + FLIP + 8], [0, -10], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.out(Easing.cubic),\n  });\n  return main + settle;\n};\n\nexport const GridWaveFlip: React.FC = () => {\n  const frame = useCurrentFrame();\n  const wallW = COLS * CELL_W + (COLS - 1) * GAP;\n  const wallH = ROWS * CELL_H + (ROWS - 1) * GAP;\n\n  return (\n    <AbsoluteFill\n      style={{\n        background: G.bg,\n        justifyContent: 'center',\n        alignItems: 'center',\n      }}\n    >\n      {/* 卡片墙：共享 perspective 1200px 容器，整墙一个消失点 */}\n      <div\n        style={{\n          width: wallW,\n          height: wallH,\n          perspective: 1200,\n          perspectiveOrigin: '50% 50%',\n          display: 'grid',\n          gridTemplateColumns: `repeat(${COLS}, ${CELL_W}px)`,\n          gridTemplateRows: `repeat(${ROWS}, ${CELL_H}px)`,\n          gap: GAP,\n        }}\n      >\n        {Array.from({ length: ROWS * COLS }).map((_, i) => {\n          const row = Math.floor(i / COLS);\n          const col = i % COLS;\n          const angle = angleAt(frame, row, col);\n          // 高光线：翻到 90°（最薄处）时最亮，位置随角度从上缘扫向下缘\n          const glow = Math.max(0, 1 - Math.abs(angle - 90) / 45);\n          const glowTop = interpolate(angle, [45, 135], [8, 92], {\n            extrapolateLeft: 'clamp',\n            extrapolateRight: 'clamp',\n          });\n          // 翻转中抬起阴影，落定后收回\n          const lift = Math.sin(Math.min(Math.max(angle, 0), 180) * (Math.PI / 180));\n          return (\n            <div key={i} style={{ width: CELL_W, height: CELL_H, position: 'relative' }}>\n              <div\n                style={{\n                  position: 'absolute',\n                  inset: 0,\n                  transformStyle: 'preserve-3d',\n                  transform: `rotateX(${angle}deg)`,\n                  boxShadow: `0 ${4 + lift * 22}px ${10 + lift * 40}px rgba(0,0,0,${0.08 + lift * 0.16})`,\n                  borderRadius: 14,\n                }}\n              >\n                {/* 灰背面（初始朝外） */}\n                <div\n                  style={{\n                    position: 'absolute',\n                    inset: 0,\n                    backfaceVisibility: 'hidden',\n                    WebkitBackfaceVisibility: 'hidden',\n                    background: '#c7c7c5',\n                    border: `2px solid ${G.bar}`,\n                    borderRadius: 14,\n                    boxSizing: 'border-box',\n                    display: 'flex',\n                    justifyContent: 'center',\n                    alignItems: 'center',\n                  }}\n                >\n                  {/* 背面只留一个哑光圆点标记，强调\"灰背\" */}\n                  <div\n                    style={{\n                      width: 44,\n                      height: 44,\n                      borderRadius: 22,\n                      background: G.mid,\n                      opacity: 0.55,\n                    }}\n                  />\n                </div>\n                {/* 正面内容卡（预转 180°，翻过来正好朝外） */}\n                <div\n                  style={{\n                    position: 'absolute',\n                    inset: 0,\n                    backfaceVisibility: 'hidden',\n                    WebkitBackfaceVisibility: 'hidden',\n                    transform: 'rotateX(180deg)',\n                    borderRadius: 14,\n                  }}\n                >\n                  <Card w={CELL_W} h={CELL_H} seed={i + 1} style={{ width: '100%', height: '100%' }} />\n                </div>\n              </div>\n              {/* 最薄处高光线：不随卡旋转，贴在格位上随角度纵向移动 */}\n              {glow > 0.01 && (\n                <div\n                  style={{\n                    position: 'absolute',\n                    left: '4%',\n                    width: '92%',\n                    top: `${glowTop}%`,\n                    height: 4,\n                    borderRadius: 2,\n                    background:\n                      'linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.95) 50%, rgba(255,255,255,0) 100%)',\n                    boxShadow: '0 0 14px rgba(255,255,255,0.8)',\n                    opacity: glow,\n                    pointerEvents: 'none',\n                  }}\n                />\n              )}\n            </div>\n          );\n        })}\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n\n// ===== demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx =====\nimport React from 'react';\nimport { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';\nimport { FakeDashboard, G } from '../../_fixtures/Fixtures';\n\n// wireframe-draw-on〔入场退场〕：界面先以蓝图细线描画成形（stroke-dashoffset\n// 分组错峰），随后一条发光竖线从左向右扫过，扫过之处线框实体化为真实灰阶界面。\n// 节拍：0–20 hold 空底 → 20–82 分组描线 → 88–118 光线扫描实体化 → 118–150 hold。\n\n// FakeDashboard variant A 的几何（1920×1080）：\n// 侧栏 220 宽；顶栏 x220–1920 h72；内容区 padding36 gap28，3×2 卡片\n// 卡 w524 h454，x = 256/808/1360，y = 108/590。\nconst CARD_W = 524;\nconst CARD_H = 454;\nconst CARD_X = [256, 808, 1360];\nconst CARD_Y = [108, 590];\n\nconst seedFrac = (i: number) => {\n  const v = Math.sin(i * 127.3) * 43758.5453;\n  return v - Math.floor(v);\n};\n\nexport const WireframeDrawOn: React.FC = () => {\n  const frame = useCurrentFrame();\n\n  // 描线进度：每组 30f 画完，start 错峰\n  const draw = (start: number) =>\n    interpolate(frame, [start, start + 30], [0, 1], {\n      extrapolateLeft: 'clamp',\n      extrapolateRight: 'clamp',\n      easing: Easing.bezier(0.4, 0, 0.3, 1),\n    });\n\n  const tSide = draw(20); // 侧栏\n  const tTop = draw(30); // 顶栏\n  const tCards = Array.from({ length: 6 }, (_, i) => draw(40 + i * 3)); // 卡片错峰\n  const tChart = draw(52); // 折线图\n\n  // 实体化扫描：88–118 从左向右\n  const scan = interpolate(frame, [88, 118], [0, 1], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n    easing: Easing.bezier(0.55, 0, 0.25, 1),\n  });\n  const scanX = scan * 1920;\n  const lineOpacity = interpolate(frame, [86, 92, 112, 120], [0, 1, 1, 0], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n\n  // 描线小工具：pathLength=1 + dashoffset 从 1 → 0\n  const stroke = (t: number): React.SVGAttributes<SVGElement> => ({\n    fill: 'none',\n    stroke: G.mid,\n    strokeWidth: 2.5,\n    pathLength: 1,\n    strokeDasharray: 1,\n    strokeDashoffset: 1 - t,\n    strokeLinecap: 'round',\n    opacity: t > 0 ? 1 : 0,\n  });\n\n  // 折线图数据（seed 正弦哈希，落在卡 (1,1) 内部）\n  const chartCardX = CARD_X[1];\n  const chartCardY = CARD_Y[1];\n  const chartPts = Array.from({ length: 8 }, (_, i) => {\n    const px = chartCardX + 50 + (i * (CARD_W - 100)) / 7;\n    const py = chartCardY + 130 + seedFrac(i + 3) * 230;\n    return `${px},${py}`;\n  }).join(' ');\n\n  return (\n    <AbsoluteFill style={{ background: G.bg, overflow: 'hidden' }}>\n      {/* —— 线框层（蓝图描线） —— */}\n      <svg\n        width={1920}\n        height={1080}\n        viewBox=\"0 0 1920 1080\"\n        style={{ position: 'absolute', inset: 0 }}\n      >\n        {/* 侧栏 */}\n        <rect x={4} y={4} width={216} height={1072} rx={2} {...stroke(tSide)} />\n        <rect x={26} y={30} width={40} height={40} rx={10} {...stroke(draw(24))} />\n        {Array.from({ length: 7 }).map((_, i) => {\n          const w = (216 - 44) * (0.6 + ((i * 29) % 35) / 100);\n          return (\n            <line\n              key={`s${i}`}\n              x1={26}\n              y1={94 + i * 30}\n              x2={26 + w}\n              y2={94 + i * 30}\n              {...stroke(draw(26 + i * 2))}\n            />\n          );\n        })}\n\n        {/* 顶栏 */}\n        <line x1={220} y1={72} x2={1916} y2={72} {...stroke(tTop)} />\n        <rect x={252} y={27} width={180} height={18} rx={9} {...stroke(draw(34))} />\n        <rect x={1476} y={18} width={320} height={36} rx={18} {...stroke(draw(36))} />\n        <circle cx={1834} cy={36} r={18} {...stroke(draw(38))} />\n\n        {/* 3×2 卡片：轮廓 + 内部占位线 */}\n        {tCards.map((t, i) => {\n          const cx = CARD_X[i % 3];\n          const cy = CARD_Y[Math.floor(i / 3)];\n          const titleW = (CARD_W - 36) * (0.45 + (((i + 1) * 37) % 40) / 100);\n          return (\n            <g key={`c${i}`}>\n              <rect x={cx} y={cy} width={CARD_W} height={CARD_H} rx={14} {...stroke(t)} />\n              <rect x={cx + 18} y={cy + 18} width={titleW} height={16} rx={8} {...stroke(draw(46 + i * 3))} />\n              <line x1={cx + 18} y1={cy + 62} x2={cx + 18 + (CARD_W - 36) * 0.82} y2={cy + 62} {...stroke(draw(49 + i * 3))} />\n              <line x1={cx + 18} y1={cy + 88} x2={cx + 18 + (CARD_W - 36) * 0.62} y2={cy + 88} {...stroke(draw(51 + i * 3))} />\n              <circle cx={cx + 31} cy={cy + CARD_H - 31} r={13} {...stroke(draw(53 + i * 3))} />\n            </g>\n          );\n        })}\n\n        {/* 折线图（画在中列下排卡内） */}\n        <polyline points={chartPts} {...stroke(tChart)} strokeWidth={3} />\n      </svg>\n\n      {/* —— 实体层：clip-path inset 从左向右展开 —— */}\n      <div\n        style={{\n          position: 'absolute',\n          inset: 0,\n          clipPath: `inset(0 ${(1 - scan) * 100}% 0 0)`,\n        }}\n      >\n        <FakeDashboard variant=\"A\" />\n      </div>\n\n      {/* —— 4px 发光扫描竖线（琥珀微光） —— */}\n      <div\n        style={{\n          position: 'absolute',\n          left: scanX - 2,\n          top: 0,\n          width: 4,\n          height: 1080,\n          background: '#ffc46b',\n          opacity: lineOpacity,\n          boxShadow:\n            '0 0 18px 6px rgba(232, 163, 61, 0.65), 0 0 60px 18px rgba(232, 163, 61, 0.28)',\n        }}\n      />\n    </AbsoluteFill>\n  );\n};\n\n\n"
}