diff --git a/CLAUDE.md b/CLAUDE.md
index 47dc3e3..c317064 120000
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1 +1 @@
-AGENTS.md
\ No newline at end of file
+AGENTS.md
diff --git a/src/content/docs/robot/gamepad.mdx b/src/content/docs/robot/gamepad.mdx
new file mode 100644
index 0000000..f47a64f
--- /dev/null
+++ b/src/content/docs/robot/gamepad.mdx
@@ -0,0 +1,91 @@
+---
+title: CommandGamepad
+description: Wrapping a standard FTC Gamepad to expose its buttons and sticks as Triggers and RangeTriggers.
+sidebar:
+ order: 4
+---
+
+import { Tabs, TabItem } from "@astrojs/starlight/components";
+
+`CommandGamepad` wraps a standard FTC `Gamepad` and exposes every button, bumper, d-pad direction,
+and stick as a [`Trigger`](/reference/triggers) or [`RangeTrigger`](/reference/triggers), so you can
+bind controller input directly to Ivy commands without manually polling the gamepad each loop.
+
+## Construction
+
+`CommandGamepad` takes the `Gamepad` instance to wrap, and an optional `EventLoop` to bind its
+triggers to.
+If no event loop is provided, it defaults to `Trigger.defaultEventLoop`.
+
+
+
+ ```kotlin val driver = CommandGamepad(gamepad = gamepad1) ```
+
+
+ ```java CommandGamepad driver = new CommandGamepad(gamepad1); ```
+
+
+
+## Buttons
+
+Each button is exposed as a `Trigger` that is true while the button is held down.
+Button names
+follow Xbox-style naming (`a`, `b`, `x`, `y`), with PlayStation-style aliases (`cross`, `circle`,
+`square`, `triangle`) mapped to the same underlying inputs.
+
+| Property | True while... |
+| ------------------ | ------------------------- |
+| `a` / `cross` | A (cross) is held |
+| `b` / `circle` | B (circle) is held |
+| `x` / `square` | X (square) is held |
+| `y` / `triangle` | Y (triangle) is held |
+| `leftBumper` | Left bumper is held |
+| `rightBumper` | Right bumper is held |
+| `dpadUp` | D-pad up is held |
+| `dpadDown` | D-pad down is held |
+| `dpadLeft` | D-pad left is held |
+| `dpadRight` | D-pad right is held |
+| `back` | Back button is held |
+| `start` | Start button is held |
+| `leftStickButton` | Left stick is clicked in |
+| `rightStickButton` | Right stick is clicked in |
+
+
+
+ ```kotlin driver.a.onTrue(claw.close())
+ driver.leftBumper.whileTrue(intake.run()) ```
+
+
+ ```java driver.a().onTrue(claw.close());
+ driver.leftBumper().whileTrue(intake.run()); ```
+
+
+
+## Sticks and analog triggers
+
+Stick axes and the analog triggers are exposed as `RangeTrigger`s, giving you both the raw analog
+value and threshold-based triggers derived from it.
+
+| Property | Value represents |
+| -------------- | ---------------------------------------------- |
+| `leftStickX` | How far the left stick is moved on the x-axis |
+| `leftStickY` | How far the left stick is moved on the y-axis |
+| `rightStickX` | How far the right stick is moved on the x-axis |
+| `rightStickY` | How far the right stick is moved on the y-axis |
+| `leftTrigger` | How far the left analog trigger is pressed |
+| `rightTrigger` | How far the right analog trigger is pressed |
+
+
+
+ ```kotlin driver.rightTrigger.isOver(0.5).onTrue(intake.run()) val forward =
+ driver.leftStickY.value ```
+
+
+ ```java driver.rightTrigger().isOver(0.5).onTrue(intake.run()); double
+ forward = driver.leftStickY().getValue(); ```
+
+
+
+See [Triggers and RangeTriggers](/reference/triggers) for the full list of binding methods
+(`onTrue`, `whileTrue`, `isOver`, `isBetween`, `debounce`, `multiPress`, and more) available on
+each of these.