{
  "slug": "spotlight-hero-card",
  "title": "Spotlight hero card reveal",
  "tagline": "A spotlight sweeps the page and locks onto one card; it pushes in at 45°, the card lifts and hovers, a light beam circles its outline twice, then it settles back in place.",
  "description": "A spotlight sweeps the page and locks onto one card; it pushes in at 45°, the card lifts and hovers, a light beam circles its outline twice, then it settles b",
  "category": "intro",
  "tags": [
    "shotcraft",
    "remotion",
    "intro",
    "opening",
    "effects",
    "camera"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 5.33,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/spotlight-hero-card/poster-6d80a4111eb8.jpg",
    "video": "https://gimgs.net/motion/media/spotlight-hero-card/video-79aabe034a24.mp4"
  },
  "variants": [
    {
      "key": "spotlight-hero-card",
      "label": "spotlight-hero-card",
      "description": "A spotlight sweeps the page and locks onto one card; it pushes in at 45°, the card lifts and hovers, a light beam circles its outline twice, then it settles back in place",
      "use": "",
      "video": "https://gimgs.net/motion/media/spotlight-hero-card/video-79aabe034a24.mp4",
      "poster": "https://gimgs.net/motion/media/spotlight-hero-card/poster-6d80a4111eb8.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/spotlight-hero-card.html",
  "json_url": "https://gimgs.net/motion/spotlight-hero-card.json",
  "template_url": "https://gimgs.net/motion/spotlight-hero-card/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/spotlight-hero-card.md",
  "updated_at": "2026-09-24T07:40:24.751Z",
  "prompt": "## When to use\n\n\"Single hero\" style product openings; establishing one core object (a card/entry/module) as the whole film's protagonist\n\n## Intention\n\nThe opening says only one thing: this one card is the atomic unit of the product. The spotlight does the eye-guiding for the viewer; the lift and hover give it volume and weight; the outline light beam is a metaphor for \"scanning/inspecting\"; settling back in place says it belongs to this page.\n\n## Duration & energy\n\n- Duration: ~4.6s (82–220f)\n- Energy: Medium (the most textured shot, slow and steady pacing)\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n开场只讲一件事：这一张卡就是产品的原子单位。聚光灯替观众做视线引导，弹起悬浮给它体积和重量，轮廓光束是\"扫描/检视\"的隐喻，最后贴回原位表示它属于这个页面。\n\n## 动效核心\n- 游走聚光灯经几个中间站后锁定卡心，光池收拢 + 锁定瞬间脉冲，外部 vignette 压暗\n- 相机从全页正视推进到斜侧特写（左侧机位主导），焦点略偏让卡落在画面偏右\n- 卡片 rise（带过冲）→ 悬停 sin bob → reseat（落地微压）\n- SVG rounded-rect 轮廓光束跑两圈：第一圈快而亮、第二圈慢而弱\n- 卡起飞后原位铺页面底色补丁 + 强调色（模板片为琥珀）呼吸描边，落地瞬间增亮消失\n- 可选加配 3D 悬空注记（hover-3d-annotation 手法）：悬停期在卡左侧浮现大衬线两行注记（字号 37、宽 230px），translateZ 92 + sin 3px bob（周期 44f，与卡 40f 相近但不同步——\"共感\"而非镜像），关键词背后 12f 长出马克笔荧光条 oklch(88% 0.095 85)，向页面投椭圆软影；注记必须活在同一 3D 空间、同一台相机透视（C3）——第一版平面叠字被否决后重做（平面叠字破坏 3D 空间统一）；出场早于卡片 reseat\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 选卡 | cards[3]，中心 x 恰为 960（页面中心） | 主角卡先按\"推进后构图居中\"选，不是随便挑一张 |\n| 相机 | 静止全页 zoom 0.78（82–114f）→ 16f 推进 zoom 2.6；rotY 34° 主导 + rotX 仅 8°，persp 1200；焦点取卡心左移 30px；reseat 触地后 cx/cy/zoom/rot 全锁死 ≥15f | 侧向水平机位读感优于俯拍（\"从左侧拍摄而不是从下方\"，Q6）；收尾禁用 zoom 2.6→2.58 一类尾漂，呼吸必须是真静止（R1） |\n| 聚光灯 | 4 个中间站（86→130f），光池半径 620→420→360 收拢，锁定 +6% 脉冲；vignette 0.16→0.42 | 中间站让\"随机照射\"可信；直奔目标读作程序化 |\n| 动作弧 | rise 10f（bezier(0.2,1.25,0.3,1) 过冲）→ 悬停 54f（sin bob 振幅 4px 周期 40f，z=110px·lift）→ reseat 18f，落地 press 0.997 | 锁定→落地 ≈98f≈3.3s——质感镜头要\"放慢到 3 秒\"（R3），初版几乎总是偏快 |\n| 轮廓光束 | lap1 142→156f（strokeWidth 5+2.5 双层）、lap2 162→182f（3.5+1.75、整体 opacity 0.62）；strokeDasharray \"0.14 1\" 走 dashoffset | 两圈快慢有别才读作\"持续扫描\"，一圈是眨眼；光束只给主角一次（Q4） |\n| 双层影 | `0 8·lift px …, 0 46·lift px 90·lift px` 随高度生长 | 影子不随高度长，悬浮就不成立 |\n\n## 声音\n弹起钉 whoosh-big（模板片钉在 f127），光束扫描钉 sparkle（f141），reseat 钉 transition-snap（f204）——弹起/光效/落定各有专属音（S2、S4）。\n\n## 已知坑\n- 开场多卡群舞撑不起第一印象（Q5）——开场曾反复推倒多次才收敛到单卡；直接从单主角+完整动作弧起稿\n- 推进特写下卡上文字会糊，根因是纹理栅格化分辨率不是 DoF——配套技法展开见审美准则 Q2\n- 逐卡 glint 闪烁被两次否决（\"不需要每个卡片都闪烁一下\"，Q4），光效严格只给主角\n\n## 参考实现\ndemos/opening/spotlight-hero-card/SpotlightHeroCard.tsx（原 template/src/aifl/live/SceneOpen.tsx 82–220 段）\n\n## Reference implementation (Remotion)\n\n- demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/opening/spotlight-hero-card/SpotlightHeroCard.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/opening/spotlight-hero-card/SpotlightHeroCard.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/opening/spotlight-hero-card/SpotlightHeroCard.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/opening/spotlight-hero-card/SpotlightHeroCard.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": 160,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/opening/spotlight-hero-card/SpotlightHeroCard.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/opening/spotlight-hero-card/SpotlightHeroCard.tsx =====\n// spotlight-hero-card —— 聚光灯锁定一张卡 → 斜 45° 推进 → 卡片弹起悬浮 →\n// 轮廓光束两圈 → 贴回原位。单一主角式产品开场，把核心对象立成全片主角。\n// 参考实现从 template SceneOpen 的 82–220 段剥离，self-contained：\n// 游走聚光灯经 4 个中间站后锁定卡心（光池收拢 + 锁定脉冲 + vignette 压暗）；\n// 相机全页正视 → 16f 斜侧推进（rotY 34° 主导 + rotX 8°）；卡 rise（带过冲）→\n// 悬停 sin bob（54f）→ reseat（18f 落地微压）；SVG rounded-rect 轮廓光束两圈\n// （lap1 快而亮 / lap2 慢而弱）；悬停期左侧 3D 注记 + 马克笔荧光条。\n// 主角卡 = layout.projects.cards[3]，中心 x 恰为 960（页面中心）。\nimport { AbsoluteFill, Img, interpolate, staticFile, useCurrentFrame, Easing } from 'remotion';\nimport { PageCam2D, CamKey2D } from '../../_fixtures/PageCam2D';\nimport layout from '../../_textures/live-layout.json';\n\nexport const SPOTLIGHT_HERO_CARD_DURATION = 139; // 82–220f，落在 shot 内 offset 0\n\nconst SERIF = 'ui-serif, Georgia, \"Times New Roman\", serif';\nconst INK = 'oklch(18% 0.006 82)';\nconst AMBER = 'oklch(52% 0.115 65)';\nconst SLOT_AMBER = 'oklch(58% 0.13 65)';\nconst BEAM_CORE = 'rgba(255,248,232,0.98)';\nconst PATCH = 'oklch(97.5% 0.008 82)';\n\nconst PAGE_H = layout.projects.pageH;\nconst MAIN = 3;\nconst CARD = layout.projects.cards[MAIN];\nconst MCX = CARD.x + CARD.w / 2; // ≈ 960\nconst MCY = CARD.y + CARD.h / 2; // 772\nconst RADIUS = 16;\n\n// Camera: straight-on full page (0→32, static while the spotlight roves and\n// locks), then a 16-frame push-in swinging to a LEFT-side view — rotY dominant,\n// card's left edge near, right edge recedes; slight downward tilt; then held to\n// the end with an almost-imperceptible pull. Focal point sits a touch LEFT of\n// the card centre so the card lands slightly right of screen-centre.\nconst CAM_KEYS: CamKey2D[] = [\n  { frame: 0, cx: 960, cy: 540, zoom: 0.78, rotX: 0, rotY: 0, rotZ: 0, persp: 1200 },\n  { frame: 32, cx: 960, cy: 540, zoom: 0.78, rotX: 0, rotY: 0, rotZ: 0, persp: 1200 },\n  { frame: 48, cx: MCX - 30, cy: MCY, zoom: 2.6, rotX: 8, rotY: 34, rotZ: 2, persp: 1200 },\n  { frame: 138, cx: MCX - 30, cy: MCY, zoom: 2.6, rotX: 8, rotY: 34, rotZ: 2, persp: 1200 },\n];\nconst PUSH_EASE = Easing.bezier(0.35, 0, 0.2, 1);\nconst POP_EASE = Easing.bezier(0.2, 1.25, 0.3, 1);\nconst RESEAT_EASE = Easing.bezier(0.4, 0, 0.3, 1.05);\n\nexport const SpotlightHeroCard: React.FC = () => {\n  const frame = useCurrentFrame();\n\n  // --- macro fades in (0 → 8) ---\n  const macroIn = interpolate(frame, [0, 8], [0, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0, 0.2, 1),\n  });\n\n  // --- roving spotlight (screen space): waypoints then locks on hero card\n  // centre (50%, 67% in the straight-on view), rides the push-in ---\n  const spotEase = Easing.bezier(0.4, 0, 0.3, 1);\n  const spotX = interpolate(frame, [4, 8, 16, 22, 28, 48], [25, 25, 70, 42, 50, 50], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: spotEase,\n  });\n  const spotY = interpolate(frame, [4, 8, 16, 22, 28, 48], [30, 30, 45, 60, 67, 50], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: spotEase,\n  });\n  const spotOn = interpolate(frame, [2, 10], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });\n\n  const poolBase = interpolate(frame, [22, 32, 48], [620, 420, 360], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.4, 0, 0.3, 1),\n  });\n  const poolPulse = interpolate(frame, [32, 36, 41], [0, 0.06, 0], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n  const poolRx = poolBase * (1 + poolPulse);\n  const poolRy = poolBase * 0.8 * (1 + poolPulse);\n  const vignette = interpolate(frame, [22, 32, 48], [0.16, 0.34, 0.42], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n\n  // --- hero card pop-up: rise (48→58 overshoot), hover (58→112, 54f sin bob),\n  // reseat (112→130, 18f gentle) — lock(32)→touchdown(130) ≈ 98f ≈ 3.3s ---\n  const rise = interpolate(frame, [48, 58], [0, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: POP_EASE,\n  });\n  const reseat = interpolate(frame, [112, 130], [0, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: RESEAT_EASE,\n  });\n  const lift = rise * (1 - reseat);\n  const bob = Math.sin(((frame - 58) / 40) * Math.PI * 2) * 4 * lift;\n  const z = 110 * lift + bob;\n  const landed = frame >= 130;\n  const press = interpolate(frame, [126, 129, 130], [1, 0.997, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n  const shadow = `0 ${8 * lift}px ${10 + 12 * lift}px rgba(40,30,20,${0.18 * lift}), 0 ${46 * lift}px ${90 * lift}px rgba(40,30,20,${0.22 * lift})`;\n\n  // vacated-slot amber outline: alive while airborne, brightens as it lands\n  const slotVis = Math.min(1, rise * 2) * (1 - reseat);\n  const landPulse = interpolate(frame, [126, 130, 134], [0, 1, 0], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n  const slotEdge = Math.min(1, 0.4 * (1 - reseat)) + landPulse * 0.6;\n\n  // --- perimeter beam: TWO laps. Lap 1 (60→74) fast & bright; lap 2 (80→100)\n  // slower & weaker; a faint amber ring lingers and fades before reseat. ---\n  const beam1Prog = interpolate(frame, [60, 74], [0, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.linear,\n  });\n  const beam1On = frame >= 59 && frame <= 75;\n  const beam2Prog = interpolate(frame, [80, 100], [0, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.4, 0, 0.4, 1),\n  });\n  const beam2On = frame >= 79 && frame <= 101;\n  const beamTrail = interpolate(frame, [100, 112], [0.35, 0], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n  const bw = CARD.w + 6;\n  const bh = CARD.h + 6;\n\n  // hi-res hero crossfades in as the push-in starts (32→38)\n  const hiresIn = interpolate(frame, [32, 38], [0, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n  });\n\n  return (\n    <AbsoluteFill style={{ backgroundColor: '#faf7f2' }}>\n      <AbsoluteFill style={{ opacity: macroIn }}>\n        <PageCam2D src=\"textures/live/projects-full.png\" pageH={PAGE_H} keys={CAM_KEYS} ease={PUSH_EASE}>\n          {/* rim light along the near (bottom) edge of the tilted plane */}\n          <div\n            style={{\n              position: 'absolute', left: 0, right: 0, bottom: 0, height: 8,\n              background: 'rgba(255,255,255,0.85)', filter: 'blur(6px)',\n              opacity: 0.6 * Math.min(1, lift + Math.max(0, (frame - 32) / 16)),\n              pointerEvents: 'none',\n            }}\n          />\n\n          {/* hero card: lifts off its slot, hovers, beam runs, settles back */}\n          <div style={{ transformStyle: 'preserve-3d' }}>\n            {/* vacated-slot patch + breathing amber outline */}\n            {slotVis > 0.02 ? (\n              <div\n                style={{\n                  position: 'absolute', left: CARD.x - 2, top: CARD.y - 2,\n                  width: CARD.w + 4, height: CARD.h + 4, background: PATCH,\n                  borderRadius: RADIUS,\n                  boxShadow: `inset 0 0 26px rgba(180,120,50,${0.12 * slotEdge})`,\n                  opacity: slotVis,\n                }}\n              >\n                <div\n                  style={{\n                    position: 'absolute', inset: 0, borderRadius: RADIUS,\n                    border: `1.5px solid ${SLOT_AMBER}`, opacity: slotEdge, pointerEvents: 'none',\n                  }}\n                />\n              </div>\n            ) : null}\n\n            {/* the levitating card */}\n            <div\n              style={{\n                position: 'absolute', left: CARD.x, top: CARD.y,\n                width: CARD.w, height: CARD.h,\n                transform: `translateZ(${z}px) scale(${press})`,\n                transformOrigin: 'center center', transformStyle: 'preserve-3d',\n              }}\n            >\n              <div\n                style={{\n                  position: 'absolute', inset: 0, borderRadius: RADIUS, overflow: 'hidden',\n                  boxShadow: landed ? 'none' : shadow,\n                }}\n              >\n                <Img\n                  src={staticFile(`textures/live/${CARD.file}`)}\n                  style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', display: 'block' }}\n                />\n                {/* 4x hi-res capture layered on top, crisp under push-in zoom */}\n                <Img\n                  src={staticFile('textures/live/card4-hires.png')}\n                  style={{\n                    position: 'absolute', inset: 0, width: '100%', height: '100%',\n                    display: 'block', opacity: hiresIn,\n                  }}\n                />\n                <div\n                  style={{\n                    position: 'absolute', inset: 0,\n                    background: 'linear-gradient(160deg, rgba(255,255,255,0.5), transparent 40%)',\n                    opacity: lift, pointerEvents: 'none',\n                  }}\n                />\n              </div>\n              {/* 1px white inner stroke (lit paper edge) */}\n              <div\n                style={{\n                  position: 'absolute', inset: 0, borderRadius: RADIUS,\n                  boxShadow: `inset 0 0 0 1px rgba(255,255,255,${0.7 * lift})`, pointerEvents: 'none',\n                }}\n              />\n\n              {/* perimeter beam: SVG rounded-rect, pathLength=1 travelling arc */}\n              {(beam1On || beam2On) && lift > 0.4 ? (\n                <svg\n                  width={bw} height={bh} viewBox={`0 0 ${bw} ${bh}`}\n                  style={{\n                    position: 'absolute', left: -3, top: -3, overflow: 'visible', pointerEvents: 'none',\n                    opacity: beam1On ? 1 : 0.62,\n                    filter: `drop-shadow(0 0 6px ${AMBER}) drop-shadow(0 0 18px rgba(255,240,210,0.55))`,\n                  }}\n                >\n                  <rect\n                    x={2} y={2} width={bw - 4} height={bh - 4} rx={RADIUS} fill=\"none\"\n                    stroke={AMBER} strokeWidth={beam1On ? 5 : 3.5} strokeLinecap=\"round\"\n                    pathLength={1} strokeDasharray=\"0.14 1\"\n                    strokeDashoffset={-(beam1On ? beam1Prog : beam2Prog)}\n                  />\n                  <rect\n                    x={2} y={2} width={bw - 4} height={bh - 4} rx={RADIUS} fill=\"none\"\n                    stroke={BEAM_CORE} strokeWidth={beam1On ? 2.5 : 1.75} strokeLinecap=\"round\"\n                    pathLength={1} strokeDasharray=\"0.14 1\"\n                    strokeDashoffset={-(beam1On ? beam1Prog : beam2Prog)}\n                  />\n                </svg>\n              ) : null}\n\n              {/* faint amber ring left behind, fades out */}\n              {beamTrail > 0.01 ? (\n                <div\n                  style={{\n                    position: 'absolute', inset: -3, borderRadius: RADIUS + 3,\n                    border: `1.5px solid ${AMBER}`, opacity: beamTrail, pointerEvents: 'none',\n                  }}\n                />\n              ) : null}\n            </div>\n          </div>\n\n          {/* 3D floating annotation LEFT of the hovering card */}\n          {frame >= 60 && frame <= 130 ? (() => {\n            const noteIn = interpolate(frame, [60, 70], [0, 1], {\n              extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.2, 0.75, 0.3, 1),\n            });\n            const noteOut = interpolate(frame, [116, 126], [1, 0], {\n              extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n            });\n            const noteVis = noteIn * noteOut;\n            const noteZ = 92 + Math.sin(((frame - 60) / 44) * Math.PI * 2) * 3;\n            const hl = interpolate(frame, [74, 86], [0, 1], {\n              extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0, 0.2, 1),\n            });\n            return (\n              <div style={{ transformStyle: 'preserve-3d', pointerEvents: 'none' }}>\n                <div\n                  style={{\n                    position: 'absolute', left: 566, top: 736, width: 210, height: 74,\n                    transform: 'translateZ(2px)',\n                    background: 'radial-gradient(ellipse at 50% 50%, rgba(40,30,20,0.3), transparent 70%)',\n                    filter: 'blur(12px)', opacity: 0.55 * noteVis,\n                  }}\n                />\n                <div\n                  style={{\n                    position: 'absolute', left: 556, top: 668, width: 230,\n                    transform: `translateZ(${noteZ}px) translateY(${(1 - noteIn) * 26}px)`,\n                    opacity: noteVis, filter: `blur(${(1 - noteIn) * 4}px)`,\n                  }}\n                >\n                  <div style={{ fontFamily: SERIF, fontSize: 37, fontWeight: 600, color: INK, lineHeight: 1.16, letterSpacing: '-0.012em' }}>\n                    One card,\n                  </div>\n                  <div style={{ position: 'relative', display: 'inline-block' }}>\n                    <div\n                      style={{\n                        position: 'absolute', left: -5, top: '12%', bottom: '4%',\n                        width: `calc(${hl} * (100% + 10px))`,\n                        background: 'oklch(88% 0.095 85)', borderRadius: 4,\n                      }}\n                    />\n                    <div style={{ position: 'relative', fontFamily: SERIF, fontStyle: 'italic', fontSize: 37, fontWeight: 600, color: INK, lineHeight: 1.16, letterSpacing: '-0.012em' }}>\n                      one project.\n                    </div>\n                  </div>\n                </div>\n              </div>\n            );\n          })() : null}\n        </PageCam2D>\n\n        {/* roving / locking spotlight: warm pool + dim outside */}\n        <AbsoluteFill\n          style={{\n            background: `radial-gradient(${poolRx}px ${poolRy}px at ${spotX}% ${spotY}%, rgba(255,241,214,0.42), rgba(255,241,214,0.10) 45%, rgba(70,56,38,${vignette * spotOn}) 100%)`,\n            pointerEvents: 'none', opacity: spotOn,\n          }}\n        />\n        <AbsoluteFill\n          style={{\n            background: `radial-gradient(300px 220px at ${spotX - 6}% ${spotY + 10}%, rgba(255,246,228,0.18), transparent 70%)`,\n            pointerEvents: 'none', opacity: spotOn * 0.7,\n          }}\n        />\n      </AbsoluteFill>\n    </AbsoluteFill>\n  );\n};\n\n\n"
}