## 1. UsdSceneRenderer — DrawCameraWireframes

- [ ] 1.1 Add to `UsdSceneRenderer.h`: `void DrawCameraWireframes(pxr::UsdStageRefPtr stage, const pxr::SdfPathVector& selectedPaths, const pxr::SdfPath& activeCameraPath, const pxr::GfMatrix4d& viewProjMatrix, double viewportCameraDist)` and private helpers `BuildCameraWireframeLines(const pxr::GfCamera& gfCam, double scale, std::vector<float>& outVerts)` and `pxr::SdfPathVector m_cachedCameraPaths` + `bool m_cameraCacheDirty = true`
- [ ] 1.2 In `UsdSceneRenderer.cpp` implement `DrawCameraWireframes`:
  - If `m_cameraCacheDirty`, traverse stage for `UsdGeomCamera` prims and cache paths; clear dirty flag
  - For each cached camera path, resolve world transform + lens params via `UsdGeomCamera::GetCamera(UsdTimeCode::Default())` → `GfCamera`
  - Compute `frustumScale = std::min(viewportCameraDist * 0.12, 50.0)`
  - Call `BuildCameraWireframeLines` to produce a `std::vector<float>` of XYZ vertex pairs (GL_LINES)
  - Determine colour: selected → `(1.0,0.75,0.1,1.0)`, active → `(0.2,0.9,1.0,1.0)`, inactive → `(0.55,0.55,0.55,0.85)`
  - `BindDrawTarget()`, draw all segments using the existing axis GLSL program + a dynamic VBO (same pattern as `DrawBoundingBoxes`), `UnbindDrawTarget()`
- [ ] 1.3 Implement `BuildCameraWireframeLines`: given `GfCamera` and scale, produce:
  - **Body box** — 12 edges of a small box (width × height × depth = scale×0.15 each) centred at camera origin in local space, transformed to world space by the camera's transform matrix
  - **Frustum pyramid** — 4 lines from camera origin to each corner of a near-display quad at depth `max(nearClip, 0.01)`, scaled so the quad width/height match `horizontalAperture/focalLength * nearDepth` (perspective divide)
  - **Up arrow** — one line from body-box top-centre upward by `scale * 0.2` along camera local Y
- [ ] 1.4 Set `m_cameraCacheDirty = true` inside `SetStage` (already exists) and inside the `SetForceRefresh(true)` path

## 2. UsdSceneRenderer — PickCameraAtPoint

- [ ] 2.1 Add to `UsdSceneRenderer.h`: `bool PickCameraAtPoint(pxr::UsdStageRefPtr stage, const ImVec2& mousePosAbsolute, const pxr::GfMatrix4d& viewProjMatrix, const ImVec2& imagePos, int viewW, int viewH, double viewportCameraDist, pxr::SdfPath* outCameraPath)`
- [ ] 2.2 In `UsdSceneRenderer.cpp` implement `PickCameraAtPoint`:
  - Iterate `m_cachedCameraPaths` (build cache if needed)
  - For each camera: build wireframe vertex list via `BuildCameraWireframeLines`
  - Project each vertex pair to screen space using the same `WorldToScreen` math as `TransformManipulator`
  - Compute `PointToSegmentDist` for each segment against `mousePosAbsolute`
  - Track minimum distance and corresponding path; if `< kCameraPickRadius (10 px)` return true with that path
  - If tie between multiple cameras, return the one with the smallest minimum distance

## 3. ViewportPanel — Integration

- [ ] 3.1 In `ViewportPanel.h`: add `pxr::SdfPath GetActiveCameraPath() const` (returns `m_camera.GetUsdCameraPath()` when in UsdCamera mode, else `SdfPath()`) — check `ViewportCamera` API for the getter name
- [ ] 3.2 In `ViewportPanel.cpp` `Render()`: after the `DrawBoundingBoxes` call, add:
  ```cpp
  m_renderer.DrawCameraWireframes(m_stage, m_selectedSdfPaths,
      m_camera.GetUsdCameraPath(), viewProj, m_camera.GetDist());
  ```
  (Use the local `viewProj` already computed in that scope)
- [ ] 3.3 In `ViewportPanel.cpp` `HandleInput()`, single-click path (before the existing `PickObject` call): add camera pick:
  ```cpp
  pxr::SdfPath camPath;
  if (m_renderer.PickCameraAtPoint(m_stage, mouse, viewProj,
          m_imageScreenPos, m_viewWidth, m_viewHeight,
          m_camera.GetDist(), &camPath)) {
      // fire selection callbacks with camPath
  }
  ```
  Only fall through to `PickObject` if `PickCameraAtPoint` returns false

## 4. Build and Verification

- [ ] 4.1 Run `cmake --preset default` (only needed if new `.cpp` files were added; all changes here are in existing files so this may be skipped) then `cmake --build build --config Release` — resolve any compilation errors
- [ ] 4.2 Install and launch `install/bin/App.exe`; create a `Camera` prim via Stage menu, verify its frustum wireframe appears in the viewport at the world origin
- [ ] 4.3 Verify clicking the camera wireframe selects the prim in the Scene Hierarchy panel
- [ ] 4.4 Verify the Move (W) gizmo appears on the selected camera and dragging repositions it; the wireframe moves to match
- [ ] 4.5 Switch the viewport to that camera via the camera toolbar; verify the wireframe turns cyan
- [ ] 4.6 With the camera active in the toolbar, click it in the viewport; verify it turns yellow-orange (selected overrides active colour)
