{
  "slug": "runway-ground-skim",
  "title": "Runway ground skim entrance",
  "tagline": "From a low ground-skim angle, UI cards rain down fast and land with zero bounce, then the whole page stands up and squares to camera.",
  "description": "From a low ground-skim angle, UI cards rain down fast and land with zero bounce, then the whole page stands up and squares to camera.",
  "category": "ui-entrance",
  "tags": [
    "shotcraft",
    "remotion",
    "ui-entrance",
    "camera"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 3.99,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/runway-ground-skim/poster-6b4849500ae3.jpg",
    "video": "https://gimgs.net/motion/media/runway-ground-skim/video-1f54bc580b53.mp4"
  },
  "variants": [
    {
      "key": "runway-ground-skim",
      "label": "runway-ground-skim",
      "description": "From a low ground-skim camera angle, a group of UI cards rain down fast from the air (starting points slightly staggered, massively overlapping in parallel as they fall, stopping dead on landing with zero bounce), then once they've all landed the whole page stands up and squares to camera to close.",
      "use": "",
      "video": "https://gimgs.net/motion/media/runway-ground-skim/video-1f54bc580b53.mp4",
      "poster": "https://gimgs.net/motion/media/runway-ground-skim/poster-6b4849500ae3.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/runway-ground-skim.html",
  "json_url": "https://gimgs.net/motion/runway-ground-skim.json",
  "template_url": "https://gimgs.net/motion/runway-ground-skim/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/runway-ground-skim.md",
  "updated_at": "2026-09-24T07:40:19.001Z",
  "prompt": "## When to use\n\nEntrance for a dashboard/card-flow interface (content \"falls from the sky to complete itself\"); a squaring-up close after a low-angle showcase segment; a heavy \"land-all-at-once\" version of the ClickUp-style hover-and-drop language\n\n## Intention\n\nShoots the interface like a runway: in a low-angle perspective, UI cards hover at different heights, then rain down fast in reading order (row-first, left→right) — the falling feeling is crisp decisiveness. Three critical rules: **massively overlapping in parallel** (starting points offset by only 1.5f, each fall taking 9f ⇒ there are always 5–6 cards falling at once in the air, \"almost together but with a ripple feel\"; a version with two serial waiting rounds was rejected), **zero bounce** (they stop dead on landing; any bounce was rejected), and **fast** (a 9f fall; a 15f version was rejected as \"needs to be a bit faster\"). Once they've all landed, the page stands up from lying flat (rotateX 66°→0°) and the camera pulls back to center, taking the viewer from \"watching the show\" back to \"looking at the interface.\"\n\n## Duration & energy\n\n- Duration: Hover display ~0.4s + drop ~1.2s + stand up & square ~1.8s; full shot 4s\n- Energy: High (the falling feeling is the spectacle, standing up and squaring is the resolution)\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n把界面拍成跑道：低角度透视里 UI 卡片悬空在不同高度，随后按界面\n位置顺序（行优先左→右）一阵急雨式贴落——掉落感=干脆利落。\n三个命门：**重叠并行**（起点只错 1.5f、下落 9f ⇒ 空中恒有 5–6 张\n同时在落，\"几乎一起但有涟漪感\"；串行等待两轮被裁）、**零回弹**\n（着地即停，弹起被裁）、**快**（下落 9f，15f 版被裁\"快一点\"）。\n落齐后页面从躺倒立起（rotateX 66°→0°）、镜头拉远居中，观众从\n\"看戏\"回到\"看界面\"。\n\n## 动效核心\n- 机位：低角度强透视（rotateX ~66° 起），页面平躺如跑道\n- 悬空：卡片初始悬浮 560–880px（高度错落），空中卡追光提亮\n  ~1.35x（判例：被环境压黑带吞掉=悬空不可感）\n- 贴落：起点 = 6 + i×1.5f + jitter(≤1.2f)（保序），下落 9f 重力\n  加速（距离∝t²），着地即停零回弹\n- 立起：全员落定后 rotateX 66°→0° + 镜头拉远归中（easeRise），\n  终态正视整页\n- 压黑带控制在 ~32%，给空中卡留可见度\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 起点差 | 1.5f/张（jitter ≤1.2f 不乱序） | 3f 版被裁\"不需要一个落下了再启动第二个\"；0 差读作开关（那是 orbit 的同帧语法） |\n| 下落时长 | 9f 重力加速 | 15f 被裁慢；<6f 读不出下落方向 |\n| 回弹 | 零（着地即停） | 压弹 4.5% 版被裁\"去掉弹起\"——掉落感=干脆利落 |\n| 悬浮高度 | 560–880px 错落 | 同高读作阅兵；>1000px 落地时间拉长 |\n| 空中提亮 | ~1.35x 追光 | 不提亮悬空不可感（返工判例） |\n| 立起段 | rotateX 66→0 + 拉远，~56f | 只落不立起=没有收束；立起太快读作弹板 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 贴落节奏语义（三卡判例族）：本卡=急雨式重叠错峰；\n  graze-face-tour=巡礼错峰；neon-frame-orbit-drop=同帧齐落。\n  按镜头语义选，错用即被裁\n- 与 deck-deal-flyin 分工：那张是卡片飞入列队（水平方向运动）；\n  本卡是垂直贴落+页面立起的两段式，掉落感是本体\n- 立起段原片无对应参考（用户口述新增），终态版式对齐 clickup11\n  正视图\n\n## 参考实现\ndemos/ui-entrance/runway-ground-skim/\n（RunwayGroundSkim.tsx）\n原片出处：clickup-30.mp4 ≈46–50s\n\n## Reference implementation (Remotion)\n\n- demos/ui-entrance/runway-ground-skim/RunwayGroundSkim.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/runway-ground-skim/RunwayGroundSkim.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/runway-ground-skim/RunwayGroundSkim.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/runway-ground-skim/RunwayGroundSkim.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/runway-ground-skim/RunwayGroundSkim.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": 120,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/runway-ground-skim/RunwayGroundSkim.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/runway-ground-skim/RunwayGroundSkim.tsx =====\n// runway-ground-skim v5 —— 源片 clickup-30.mp4 约 46–50s（clickup10 截图 5 张）：\n// 用户 v5 意见（逐字）：\"去掉落地后弹起的效果，然后整个下落的过程快一点\"\n// 落实：①删除落地压弹——着地即停，零回弹零压缩（判例：掉落感=干脆利落）；\n// ②下落整体提速——单卡下落 15→9 帧，全员落定 f45→f33，立起段随之前移；\n// ③保留项不动：错峰 3 帧起点、界面位置顺序（行优先左→右）、重力加速\n// （距离∝t²）、贴落完成后页面立起转正收尾。\nimport React from 'react';\nimport { AbsoluteFill, interpolate, useCurrentFrame, Easing } from 'remotion';\n\nconst FONT = 'Helvetica, Arial, sans-serif';\nconst INK = '#3c3c42';\nconst MID = '#8d8d94';\nconst FAINT = '#d2d2d5';\n\nconst easeRise = Easing.bezier(0.42, 0, 0.16, 1);\n\n/* mulberry32 带种子（起跳节奏 ≤1.5 帧微差 < 3 帧错峰，顺序不乱） */\nconst mulberry32 = (seed: number) => () => {\n  seed |= 0; seed = (seed + 0x6d2b79f5) | 0;\n  let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);\n  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n};\n\nconst CARD_W = 760;\nconst MiniCardFace: React.FC<{ title: string; sub: string }> = ({ title, sub }) => (\n  <div style={{\n    width: CARD_W, border: `4px solid ${FAINT}`, borderRadius: 22, padding: '30px 40px',\n    background: '#fcfcfb', display: 'flex', flexDirection: 'column', gap: 14, boxSizing: 'border-box',\n  }}>\n    <div style={{ display: 'flex', alignItems: 'center', gap: 24 }}>\n      <div style={{ width: 40, height: 40, border: '6px solid #85858b', borderRadius: 8, flexShrink: 0 }} />\n      <div style={{ fontFamily: FONT, fontSize: 46, color: INK, fontWeight: 650, whiteSpace: 'nowrap' }}>{title}</div>\n    </div>\n    <div style={{ fontFamily: FONT, fontSize: 36, color: MID, paddingLeft: 64, whiteSpace: 'nowrap' }}>{sub}</div>\n  </div>\n);\n\n/* 界面位置顺序 = 数组顺序：第一行左→右，再第二行左→右 */\nconst CARDS: { title: string; sub: string; col: number; row: number }[] = [\n  { title: 'Creative Refresh', sub: 'New logo exploration', col: 0, row: 0 },\n  { title: 'New Bugs Per Week', sub: 'Bug tracker Dashboard', col: 1, row: 0 },\n  { title: 'Tiger Team Roadmap', sub: 'Roadmap Outline', col: 2, row: 0 },\n  { title: 'Design System', sub: 'Design Handbook Inspo', col: 3, row: 0 },\n  { title: 'Development Sprint Dashboard', sub: 'Dev Team Sprints', col: 0, row: 1 },\n  { title: 'CSS Bug Tracker', sub: 'Query Reports', col: 1, row: 1 },\n  { title: 'Platform', sub: 'System Health Monitor', col: 2, row: 1 },\n];\n\n/* Recent 网格槽位（面板内容坐标） */\nconst GRID_X = 1180, GRID_Y = 760, COL_GAP = 850, ROW_GAP = 250;\nconst slotPos = (col: number, row: number) => ({ x: GRID_X + col * COL_GAP, y: GRID_Y + row * ROW_GAP });\n\n/* 平躺地面：Home 仪表盘（卡片槽位留空，由悬浮卡片落入） */\nconst Ground: React.FC = () => (\n  <div style={{ width: 4600, height: 2600, background: '#f6f6f5', borderRadius: 60, position: 'relative', overflow: 'hidden' }}>\n    <div style={{ display: 'flex', height: '100%' }}>\n      {/* 左侧栏 */}\n      <div style={{ width: 860, borderRight: `4px solid ${FAINT}`, padding: '70px 60px 0', background: '#f1f1f0' }}>\n        <div style={{ display: 'flex', alignItems: 'center', gap: 26 }}>\n          <div style={{ width: 64, height: 64, borderRadius: 18, background: 'linear-gradient(135deg,#adadb3,#6b6b72)' }} />\n          <div style={{ fontFamily: FONT, fontSize: 56, fontWeight: 800, color: INK }}>ClickUp</div>\n        </div>\n        <div style={{ height: 46 }} />\n        {['Home', 'Inbox', 'Company', 'People & Teams', 'Goals', 'Docs', 'More'].map((t, i) => (\n          <div key={t} style={{\n            display: 'flex', alignItems: 'center', gap: 30, height: 108, paddingLeft: 32,\n            background: i === 0 ? '#e6e6f0' : 'transparent', borderRadius: 20,\n          }}>\n            <div style={{ width: 36, height: 36, border: '6px solid #90909a', borderRadius: 9 }} />\n            <div style={{ fontFamily: FONT, fontSize: 46, color: INK, fontWeight: i === 0 ? 650 : 400 }}>{t}</div>\n          </div>\n        ))}\n        <div style={{ height: 60 }} />\n        <div style={{ fontFamily: FONT, fontSize: 38, letterSpacing: 5, color: MID, fontWeight: 600, paddingLeft: 32 }}>SPACES</div>\n        <div style={{ height: 16 }} />\n        {['EPD', 'Product roadmap', 'Design', 'Designer handbook', '3.0', 'Design system'].map((t) => (\n          <div key={t} style={{ display: 'flex', alignItems: 'center', gap: 30, height: 96, paddingLeft: 32 }}>\n            <div style={{ width: 44, height: 44, borderRadius: 12, background: '#d6d6da' }} />\n            <div style={{ fontFamily: FONT, fontSize: 42, color: INK }}>{t}</div>\n          </div>\n        ))}\n      </div>\n      {/* 主区 */}\n      <div style={{ flex: 1, padding: '70px 100px 0', position: 'relative' }}>\n        {/* 顶部 tab 条 */}\n        <div style={{ display: 'flex', gap: 110, fontFamily: FONT, fontSize: 42, color: MID, marginBottom: 60 }}>\n          <div>Product analytics</div><div style={{ fontWeight: 700, color: INK }}>ClickUp 3.0</div>\n          <div>Widget brainstorm</div><div>Design system</div><div>Design</div>\n        </div>\n        <div style={{ fontFamily: FONT, fontSize: 110, fontWeight: 750, color: INK }}>Home</div>\n        <div style={{ height: 40 }} />\n        <div style={{\n          display: 'flex', alignItems: 'center', gap: 30, border: `4px solid ${FAINT}`,\n          borderRadius: 24, padding: '28px 42px', background: '#fff', width: 1400,\n        }}>\n          <div style={{ width: 38, height: 38, borderRadius: 19, border: '6px solid #9a9aa0' }} />\n          <div style={{ fontFamily: FONT, fontSize: 42, color: MID }}>Search by app, filetype, or keyword</div>\n        </div>\n        <div style={{ height: 66 }} />\n        <div style={{ display: 'flex', gap: 70, fontFamily: FONT, fontSize: 46 }}>\n          <div style={{ color: INK, fontWeight: 700 }}>Recent</div>\n          <div style={{ color: MID }}>Favorites</div>\n        </div>\n        {/* ↑ Recent 网格槽位区域留空（卡片从空中落入 GRID_X/GRID_Y 起的两行四列） */}\n        <div style={{ height: 560 }} />\n        <div style={{ display: 'flex', gap: 70, fontFamily: FONT, fontSize: 44 }}>\n          <div style={{ color: INK, fontWeight: 700 }}>Todo</div>\n          <div style={{ color: MID }}>Comments</div>\n          <div style={{ color: MID }}>Done</div>\n          <div style={{ color: MID }}>Delegated</div>\n        </div>\n        <div style={{ height: 36 }} />\n        <div style={{\n          display: 'inline-block', padding: '16px 40px', background: '#e4e4e3', borderRadius: 14,\n          fontFamily: FONT, fontSize: 36, letterSpacing: 4, color: '#6f6f75', fontWeight: 600,\n        }}>TODAY</div>\n        <div style={{ height: 40 }} />\n        {['New Bugs Per Week', 'Designer handbook', 'Mobile screens', 'Product roadmap'].map((t) => (\n          <div key={t} style={{\n            display: 'flex', alignItems: 'center', gap: 34, height: 118,\n            borderBottom: '3px solid #e5e5e3', width: 2600,\n          }}>\n            <div style={{ width: 32, height: 32, borderRadius: 9, background: '#c04a6e' }} />\n            <div style={{ fontFamily: FONT, fontSize: 46, color: INK, fontWeight: 550 }}>{t}</div>\n            <div style={{ marginLeft: 'auto', width: 180, height: 16, background: '#e3e3e8', borderRadius: 8 }} />\n          </div>\n        ))}\n      </div>\n    </div>\n  </div>\n);\n\nexport const RunwayGroundSkim: React.FC = () => {\n  const frame = useCurrentFrame();\n  const rand = mulberry32(20260718);\n  const jit = CARDS.map(() => rand() * 1.2); // ≤1.2 帧微差 < 3 帧错峰，顺序绝不乱\n\n  /* ---- 节奏（掉落提速+着地即停版，118 帧）----\n   * f0–6    开场：全员悬空在黑色空域\n   * f6–33   掉落：第 i 张起跳 f = 6 + i*3（错峰只差 3 帧），下落 9 帧\n   *         ⇒ 9 >> 3，空中同时 3–4 张在落（重叠并行，非串行等待）；\n   *         重力加速（距离∝t²），着地即停——零回弹零压缩\n   * f38–94  页面立起 + 视角转正（rotateX 66→0，镜头拉远居中）\n   * f94–118 终态正视整页 hold */\n  // v6（批次 15）：用户意见\"下落的时间差多调小一些，不需要一个落下了\n  // 再启动第二个\"——起点差 3→1.5 帧（9 帧下落窗口重叠度 6 倍，\n  // 任意时刻空中 5–6 张同落，几乎齐落带涟漪感）\n  const START0 = 6, GAP = 1.5, FALLF = 9;\n\n  const lifts = CARDS.map((c, i) => {\n    const t = frame - (START0 + i * GAP + jit[i]);\n    const H = 560 + (i % 3) * 160; // 初始悬浮高度错落\n    if (t <= 0) return H;\n    const p = t / FALLF;\n    if (p < 1) return H * (1 - p * p); // 重力加速：下落距离 ∝ t²\n    return 0; // 着地即停，无弹起\n  });\n\n  /* 立起段进度 */\n  const riseP = interpolate(frame, [38, 94], [0, 1], { easing: easeRise, extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });\n\n  /* 镜头：落卡段轻微推进（72→66°），立起段转正（→0°）并拉远居中 */\n  const landP = interpolate(frame, [0, 34], [0, 1], { easing: Easing.bezier(0.3, 0.1, 0.6, 0.9), extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });\n  const rx = interpolate(landP, [0, 1], [72, 66]) - 66 * riseP;\n  const z = interpolate(landP, [0, 1], [-620, -320]) + riseP * (-1620 - -320);\n  const bright = interpolate(frame, [0, 32, 86], [0.32, 0.8, 1.0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });\n\n  /* 锚点/透视原点随立起归中：终态整页入画、中心≈画面中心 */\n  const anchorTop = 58 - riseP * 6;           // % ：58 → 52（终态整页不裁底）\n  const perspY = 30 + riseP * 20;             // % ：30 → 50\n\n  const cam = (children: React.ReactNode, extra?: React.CSSProperties) => (\n    <AbsoluteFill style={{ perspective: 1050, perspectiveOrigin: `50% ${perspY}%`, ...extra }}>\n      <div style={{\n        position: 'absolute', left: '50%', top: `${anchorTop}%`, width: 0, height: 0,\n        transformStyle: 'preserve-3d',\n        transform: `translateZ(${z}px) rotateX(${rx}deg)`,\n      }}>\n        {children}\n      </div>\n    </AbsoluteFill>\n  );\n\n  const scene = (\n    <div style={{ position: 'absolute', transformStyle: 'preserve-3d', transform: 'translate(-2300px, -1500px)' }}>\n      {/* 地面 */}\n      <div style={{ filter: `brightness(${bright})` }}>\n        <Ground />\n      </div>\n      {/* 地面上的软影（z≈0，卡片同形，随悬浮高度变化大小/偏移/浓度） */}\n      {CARDS.map((c, i) => {\n        const h = lifts[i];\n        if (h < 2) return null;\n        const s = slotPos(c.col, c.row);\n        return (\n          <div key={'sh' + i} style={{\n            position: 'absolute', left: s.x + 20, top: s.y + 14, width: CARD_W - 40, height: 150,\n            transform: `translateZ(1px) translate(${h * 0.08}px, ${h * 0.12}px) scale(${1 + h * 0.0004})`,\n            background: 'rgba(10,8,16,0.9)', borderRadius: 24,\n            filter: `blur(${10 + h * 0.03}px)`,\n            opacity: Math.max(0.12, 0.38 - h * 0.0003),\n          }} />\n        );\n      })}\n      {/* 悬空卡片：与地面同向平躺，translateZ 抬高，落回槽位。\n          空中时被\"追光\"打亮（比暗地面亮），落地融入地面亮度 */}\n      {CARDS.map((c, i) => {\n        const h = lifts[i];\n        const s = slotPos(c.col, c.row);\n        const airLit = h > 2 ? Math.max(1.35, bright) : bright;\n        return (\n          <div key={'card' + i} style={{\n            position: 'absolute', left: s.x, top: s.y,\n            transform: `translateZ(${h}px)`,\n            filter: `brightness(${airLit})`,\n            boxShadow: h > 2 ? `0 0 ${30 + h * 0.05}px rgba(240,235,255,${Math.min(0.3, h * 0.0004)})` : 'none',\n          }}>\n            <MiniCardFace title={c.title} sub={c.sub} />\n          </div>\n        );\n      })}\n    </div>\n  );\n\n  /* 立起段各氛围层收敛：黑空域压顶/地平线光/近景糊都随转正淡出 */\n  const airOp = 1 - riseP;\n\n  return (\n    <AbsoluteFill style={{ background: '#07060a' }}>\n      {/* 地平线微光（转正后消失） */}\n      <AbsoluteFill style={{\n        background: `radial-gradient(ellipse 60% 14% at 50% 40%, rgba(190,170,255,${(0.16 + landP * 0.1) * airOp}), transparent 75%)`,\n      }} />\n      {cam(scene)}\n      {/* 近景轻糊（转正后消失） */}\n      {airOp > 0.02 && (\n        <AbsoluteFill style={{\n          filter: 'blur(10px) brightness(0.88)', opacity: airOp,\n          WebkitMaskImage: 'linear-gradient(180deg, transparent 60%, rgba(0,0,0,0.7) 84%, black 98%)',\n          maskImage: 'linear-gradient(180deg, transparent 60%, rgba(0,0,0,0.7) 84%, black 98%)',\n        }}>\n          {cam(scene)}\n        </AbsoluteFill>\n      )}\n      {/* 上半空域压黑（随立起淡出） */}\n      <AbsoluteFill style={{\n        background: 'linear-gradient(180deg, rgba(4,3,8,0.9) 0%, rgba(4,3,8,0.35) 16%, transparent 32%)',\n        opacity: airOp, pointerEvents: 'none',\n      }} />\n      {/* 暗角（转正后减弱不消失） */}\n      <AbsoluteFill style={{\n        background: 'radial-gradient(ellipse 95% 90% at 50% 55%, transparent 50%, rgba(3,2,7,0.55) 85%, rgba(2,1,5,0.88) 100%)',\n        opacity: 1 - riseP * 0.55, pointerEvents: 'none',\n      }} />\n    </AbsoluteFill>\n  );\n};\n\n\n"
}