{
  "_comment": "TEMPLATE. Baked into the image read-only at /etc/thinclient-agent/gesture-config.json. On first boot configs/gesture-control/gesture_pointer.py copies it to /var/lib/thinclient-agent/gesture-config.json (thinclient_agent/runtime_state.py, the same split audio-config.json and rdp-vnc.json use) and reads only that copy from then on, so the camera can be turned on and off on a running machine without rebuilding an ISO.",

  "_comment_privacy": "'enabled': false is the whole point of this file. While it is false the camera device is NEVER OPENED — gesture_pointer.py checks this before it imports OpenCV or MediaPipe at all, so a disabled image does not merely ignore the camera, it never touches the video stack. Turning it on means a camera continuously captures and analyses video of this room for as long as the session is up. That is a per-room decision, exactly like the microphone in the wyoming-satellite rooms (project-plan Phase 11.8). See the privacy section in hosts/thin-client/README.md before flipping it.",

  "enabled": false,

  "_comment_camera": "A V4L2 device path, not an index — indices are reassigned across reboots and hotplugs, the same trap audio-config.json's preferred_sink documents for WirePlumber IDs. Find yours on the booted machine with `v4l2-ctl --list-devices`. If the box has an internal camera as well as a USB one, being explicit here is what stops the wrong one being opened.",
  "camera_device": "/dev/video0",

  "_comment_capture": "640x480 is what the hand model wants anyway; capturing higher and downscaling only costs CPU. capture_fps is what the camera is asked to deliver, inference_fps is how often a frame is actually run through the model — frames in between are read and dropped to keep the V4L2 queue drained rather than letting a backlog of stale frames build up latency. Inference is by far the expensive half, so inference_fps is the knob to turn down if the CPU load on the real hardware is too high (see README).",
  "capture_fps": 30,
  "inference_fps": 12,
  "frame_width": 640,
  "frame_height": 480,

  "_comment_mirror": "true when the camera faces the user (the normal wall/display-mounted case): moving your hand right should move the pointer right, and an unmirrored front-facing camera does the opposite.",
  "mirror": true,

  "_comment_pointer": "Velocity control, not trackpad control: the hand's offset from the centre of the frame sets a pointer SPEED, so holding still in the middle stops the pointer and you never run out of frame. dead_zone is that centre rest area in normalised frame units (0.0-0.5) — below it nothing moves at all, which is what stops an idle hand drifting the pointer across the screen. pointer_speed is pixels per second at the edge of the frame.",
  "dead_zone": 0.08,
  "pointer_speed": 900,

  "_comment_click": "A fist has to be held for fist_hold_seconds before it clicks, and the hand has to open again before another click can fire — a single mis-detected frame must never be able to click something. click_cooldown_seconds is a second floor under the repeat rate. Raise fist_hold_seconds if the false-positive click rate on the real camera is annoying.",
  "fist_hold_seconds": 0.4,
  "click_cooldown_seconds": 1.0,

  "_comment_model": "Downloaded by 1100-gesture-control.hook.chroot at build time. If the file is missing, gesture_pointer.py logs and exits instead of starting — nothing else in the session is affected.",
  "model_path": "/opt/gesture-control/hand_landmarker.task"
}
