## Objetivo

Alinhar o comportamento dos controles Touch Plus (Meta Quest 3) ao padrão pedido:

- **Grip** = único responsável por mover a peça (pan, zoom, orbit).
- **Trigger** = apenas seleção, medição e ações pontuais (nunca move/rotaciona a peça).
- **Thumbstick** = navegação do usuário (teletransporte/recuar/girar a câmera), nunca mais ajuste fino do modelo.
- **Botão Menu** = mantém comportamento nativo (não tocar).

## Mapeamento alvo

```text
Mão               Botão              Ação
─────────────────────────────────────────────────────────
Esquerda/Direita  Grip (1 mão)       Pan da peça (translação 6DOF, sem rotação)
Ambas as mãos     Grip + afastar     Zoom uniforme (escala)
Ambas as mãos     Grip + giro oposto Orbitar/rotacionar a peça
Direita           Trigger            Selecionar componente / adicionar ponto de medida
Esquerda          Trigger            Alternar snap de vértice (já existe)
Direita           Botão A            Desfazer última medida (já existe)
Direita           Botão B            Limpar medidas (já existe)
Thumbstick        Frente + soltar    Teletransporte para o ponto apontado
Thumbstick        Lados              Snap-rotate da câmera (±30°)
Thumbstick        Trás               Recuar o usuário (passo)
```

## Mudanças no código

### 1. `src/components/three/XRGrabbable.tsx`

- **One-hand grip**: passar a aplicar **apenas translação** (pan). Hoje aplica matriz completa do grip → leva rotação junto. Vou capturar o offset de posição e atualizar só `group.position`, mantendo `quaternion` e `scale`.
- **Two-hand grip**: manter rotação pelo eixo entre as mãos + **forçar `allowScale = true` sempre** (zoom passa a vir do grip, não depende mais do toggle do HUD).
- Remover a prop `allowScale` da assinatura (ou ignorar e tratar como `true`).

### 2. `src/pages/XRSession.tsx`

- Remover o estado `allowScale` e os toggles correspondentes nas duas instâncias de `<XRGrabbable>`.
- Substituir `<ControllerFineTuning />` por um novo `<ControllerLocomotion />` (ver item 4) — o joystick não deve mais editar `fineTuning` do modelo.
- Garantir que `XRHitTestPlacement` só consome `onSelect` (trigger) quando `placementMode === true`; fora disso o trigger é roteado integralmente para `XRControllerMeasure`.

### 3. `src/components/XRHud.tsx` e `src/components/three/XRHudInWorld.tsx`

- Remover botão/toggle “Permitir escala” (`allowScale`) — agora é sempre via grip duplo.
- Remover/relabelar o toggle “Movimento livre” (`freeMove`) já que o joystick deixa de ajustar o modelo; manter apenas se ainda fizer sentido para outra função (caso não, remover prop também de `XRSession.tsx`).

### 4. Novo `src/components/three/ControllerLocomotion.tsx`

Componente que substitui `ControllerFineTuning`. Lê `useXR().session` e implementa:

- **Joystick esquerdo (ou ambos, configurável)**:
  - `axes[1] < -0.7` mantido por ~200 ms → dispara teleport: raycast a partir do controle direito contra `scene.children` no plano horizontal mais próximo; aplica `xrOrigin.position` para reposicionar.
  - `axes[1] > 0.7` → recua o `XROrigin` em passos de 0.3 m por trigger.
  - `axes[0]` cruzando ±0.7 (com histerese) → snap-rotate de ±30° no `XROrigin.rotation.y`.
- Usar a API `XROrigin` já montada em `XRSession.tsx` (referência via `useRef` ou store) para escrever pose.
- Desligar tudo se `lockedActive` (modelo travado) — locomoção do usuário ainda funciona, só não move o modelo.

### 5. `src/components/three/XRControllerMeasure.tsx`

- Sem mudanças funcionais — já usa apenas trigger e botões A/B. Apenas confirmar que o early-return `(!measureMode && !selectionMode)` continua: quando nenhum dos modos está ativo, o trigger não dispara nada relacionado à medição (e não interfere em `XRHitTestPlacement`, que respeita `placementMode`).

### 6. `src/components/three/XRHitTestPlacement.tsx`

- Garantir que o handler `onSelect` (trigger) verifica `placementMode` antes de colocar o modelo. Hoje o tap-plane invisível pode estar capturando o select mesmo fora do placement — adicionar guarda explícita e `visible={placementMode}`.

## Validação

1. **Desktop SimXR** (devkit): pressionar Q/W (grip simulado) move a peça; pressionar A/S (trigger simulado) não muda pose nenhuma.
2. **Quest 3 real (passthrough)**:
   - Grip único arrasta a peça sem rotacionar.
   - Dois grips afastando = zoom; dois grips em rotação oposta = orbit.
   - Trigger direito fora dos modos medir/selecionar/colocar = nada acontece.
   - Trigger direito em modo Selecionar = seleciona componente IFC.
   - Trigger direito em modo Medir = adiciona ponto com snap.
   - Joystick esquerdo para frente = teletransporte; lados = snap-rotate; trás = recua.
3. Logs `[XR][grab]` continuam mostrando início/fim de one-hand e two-hand.

## Fora do escopo

- Não mexer no botão Menu (Universal Menu do Quest é nativo).
- Não alterar handlers DOM/mouse do modo desktop (Viewer).
- Não tocar em RLS, store de dados ou backend.
