# Base de plataforma e navegação para Quest

Este documento descreve a configuração mínima esperada para builds Android/Quest e as limitações atuais antes de entrar em MR real.

## Player Settings esperados (Quest / Android)

Para gerar um build utilizável no Meta Quest (Android):

1. **File > Build Settings**
   - **Platform:** Android (troque se estiver em PC/Mac).
   - Inclua as cenas **Bootstrap**, **Main** e **QuestPresentation** em **Scenes In Build** (ordem: Bootstrap primeiro, depois Main e QuestPresentation).

2. **Player Settings (Edit > Project Settings > Player)**
   - **Company Name / Product Name:** conforme o produto.
   - **Default Icon:** opcional.
   - **Other Settings**
     - **Rendering**
       - **Color Space:** Linear (recomendado).
       - **Auto Graphics API:** pode deixar; para Quest, Vulkan/OpenGLES3 costumam ser escolhidos.
     - **Identification**
       - **Minimum API Level:** Android 10 (API 29) ou superior, conforme requisito do Quest.
       - **Target API Level:** conforme recomendação da Meta para o Quest em uso.
   - **XR Plug-in Management** (quando for usar Meta SDK no futuro)
     - Por ora o projeto **não** exige XR Plug-in Management instalado; a cena Quest roda em modo 2D/Canvas.
   - **Publishing Settings**
     - **Package Name:** ex.: `com.suaempresa.eventsappquest`.

3. **Qualidade**
   - Em **Edit > Project Settings > Quality**, verifique que há um nível adequado para Android/Quest (ex.: reduzir sombras ou efeitos se necessário para performance).

## Primeiro teste visual: UI espacial e câmera headset

- **QuestPresentationCameraRig** e **QuestXRPresentationRig:** configuração de câmera/orientação. **QuestXRPresentationRig** é preferido (compatível com modo de runtime: em EditorFallback/SimulatedXR aplica altura dos olhos; em RealXR deixa o XR controlar a câmera). Se não existir nenhum rig, o controlador cria **QuestXRPresentationRig** em runtime e reparenta a Main Camera.
- **UI em World Space:** **QuestWorldUIPanelLayout** monta um Canvas em World Space com três painéis no mundo: (1) **painel principal** (título da apresentação + passo/hotspot/estado atual, uma linha de conteúdo); (2) **painel lateral** (hotspot atual); (3) **painel de progresso** (progresso, modo, prontidão). Os painéis ficam à frente do rig, com hierarquia visual clara e pouco texto por painel (leitura rápida).
- **Alternar HUD / painéis espaciais:** botão **UI: HUD/Espacial** e tecla **U** (editor) alternam entre modo HUD tradicional (overlay) e modo painéis espaciais. A barra de navegação (botões) permanece visível em ambos os modos; apenas o conteúdo (HUD + views) é trocado por painéis no mundo. Se a UI espacial não for construída (ex.: sem câmera), o fluxo continua em HUD.
- **O que já é UI espacial:** painel principal (passo/hotspot/estado), painel lateral (hotspot), painel de progresso (progresso, modo, prontidão).
- **O que permanece em HUD/overlay:** barra de botões de navegação (Próx. passo, Voltar, etc.) e, no modo HUD, título, status, progresso, passo detalhado, hotspot e estado de cena (layout em overlay).
- **Limitações desta etapa (antes de MR real):** painéis são posicionados à frente da câmera/rig em um ambiente virtual (não há passthrough nem ancoragem no mundo real); posição é fixa em relação ao rig (sem scene understanding). Esta etapa é a **base para futura UI imersiva e apresentação em MR**.

## Foco e interação na apresentação Quest

- **QuestPresentationFocusTargetType:** alvos de foco (GuidedStepPanel, HotspotPanel, SceneStatePanel, ProgressPanel, ActionPanel). A sessão armazena **FocusedTargetType** e **IsInteractionModeActive**.
- **QuestPresentationFocusController:** alterna foco entre painéis (NextFocus, PreviousFocus), aplica ação contextual ao painel em foco (ActivateFocus: próximo passo/hotspot/estado, reiniciar ou alternar modo), entra/sai do modo de interação (EnterFocusMode, ExitFocusMode).
- **Destaque visual:** quando o modo de interação está ativo, o painel em foco recebe cor mais clara, escala ligeiramente maior e borda (Outline). Os painéis espaciais e a HUD reagem ao foco com dados reais (passo, hotspot, estado, progresso, modo).
- **Input de foco (editor):** Tab = próximo foco; \` (BackQuote) = foco anterior; Espaço = ativar (ação do painel em foco); Esc = sair do modo foco. Botões **Próx. foco**, **Ativar**, **Sair foco** na barra de navegação.
- **Indicador:** na HUD (overlay) e nos painéis espaciais aparece "Foco: [Passo/Hotspot/...] | Modo interação: Sim/Não | Ação: [dica]". Se o foco espacial falhar, a navegação básica (passo, hotspot, estado, botões) continua funcionando.

## Seleção e apontamento na apresentação Quest

- **QuestSelectableTarget** e **QuestSelectableTargetType:** alvos selecionáveis (GuidedStep, Hotspot, SceneState, ActionButton, ModeToggle) com TargetId, DisplayName, IsCurrentlyAvailable, SelectionHint, RelatedDataKey.
- **QuestSelectionController:** regista os alvos principais a partir da sessão (passo atual, hotspot, estado, botões, modo), alterna seleção (NextSelectable, PreviousSelectable), confirma (ConfirmSelection executa a ação do alvo) e cancela (CancelSelection). **QuestInteractionCommand** / **QuestInteractionCommandType** (NextSelectable, PreviousSelectable, ConfirmSelection, CancelSelection) permitem processar comandos de forma abstracta e preparar a futura substituição por input real.
- **Destaque visual:** o painel correspondente ao alvo selecionado recebe realce distinto do foco (cor verde, escala 1,04, borda verde); foco (Tab) e seleção (F/G) podem coexistir. Indicador "Selecionado: [nome] | Ação: [dica] | Enter = confirmar" na HUD e nos painéis espaciais.
- **Input de seleção (editor):** F = próximo alvo; G = alvo anterior; Enter = confirmar seleção (ativar); Backspace = cancelar seleção. Botões **Próx. alvo**, **Ativar sel.**, **Cancelar sel.** na barra. Navegação e foco (Tab, Espaço, Esc) continuam disponíveis.
- **Fallback:** se a seleção falhar ou não estiver disponível, a navegação básica e o foco anterior continuam a funcionar.

## Cursor e alvo no mundo (camada inicial)

- **QuestPointableTarget** e **QuestPointableTargetType:** alvos apontáveis/hoveráveis (Panel, HotspotMarker, ActionTarget, NavigationTarget, InfoTarget) com TargetId, WorldAnchorHint, HoverHint, SelectionHint, IsInteractable, RelatedSelectableTargetId. Espelham os alvos principais (passo, hotspot, estado, ações, modo) para preparar interação mais natural no headset.
- **QuestPointerPreparationController:** mantém a lista de alvos apontáveis (RefreshPointableTargets a partir da sessão) e o "alvo apontado atual". Por agora **hover simulado:** o alvo apontado acompanha o alvo selecionado (SetPointedFromSelection); a estrutura está pronta para futura substituição por resultado de raycast/gaze real (SetPointedTargetId por hit).
- **QuestWorldCursorController:** camada visual de cursor e alvo no mundo: retículo simples (círculo amarelo/âmbar) no centro do canvas World Space e label com o nome do alvo apontado (HoverHint). Construído no mesmo canvas dos painéis espaciais; visível apenas no modo UI espacial. Atualizado em cada Refresh com o alvo apontado do PointerPreparationController.
- **Distinção visual mínima:** foco = azul (cor, escala 1,03, borda azul); seleção = verde (cor, escala 1,04, borda verde); **alvo apontado/hover** = âmbar (cor, escala 1,02, borda âmbar). Nos painéis espaciais aplica-se a ordem: seleção > apontado > foco > normal.
- **Integração:** os painéis (principal, lateral, progresso) e os alvos de ação recebem estado "apontado" quando o PointerPreparationController indica o TargetId correspondente. Navegação entre alvos apontáveis = F/G (próximo/anterior selecionável); confirmar no alvo apontado = Enter (ConfirmSelection). O fluxo actual de foco e seleção não é alterado; o alvo apontado segue a seleção.
- **HUD e painéis espaciais:** linha "Alvo apontado: [nome] | Interagível: Sim/Não | Ação: [dica]" na HUD; nos painéis espaciais o indicador de seleção continua; o retículo e a label do cursor aparecem no modo UI espacial.
- **Fallback:** se a camada de cursor/alvo falhar (PointerPreparationController ou WorldCursorController nulos), o layout e a HUD chamam Refresh sem pointer; foco e seleção continuam a funcionar (Refresh(session, selectionController) sem terceiro parâmetro).

## Raycast/gaze simulado (etapa 40)

- **QuestSimulatedPointerHit:** resultado de apontamento espacial simulado (TargetId, TargetType, HitScore, IsInteractable, HitHint, SourceMode). Usado pela camada de cursor e seleção.
- **QuestPointerSourceMode:** FocusDriven (alvo = painel em foco), CenterScreenSimulated (alvo por critério espacial/situacional), ManualCycleAssisted (alvo = seleção cíclica F/G).
- **QuestSimulatedPointerController:** executa o apontamento simulado (PerformSimulatedHit) sem raycast físico: **FocusDriven** devolve o alvo principal do painel em foco; **CenterScreenSimulated** pontua alvos por centralidade (main_panel > lateral_panel > progress_panel), reforço de foco e disponibilidade; **ManualCycleAssisted** devolve o alvo selecionado. Ciclo de modo: P = TogglePointerMode (FocusDriven → CenterScreenSimulated → ManualCycleAssisted → …).
- **Integração:** PointerPreparationController actualiza o alvo apontado com SetPointedFromSimulatedHit(hit); cursor, layout e HUD mostram o apontado. **ConfirmPointedTarget** (Enter): alinha a seleção ao alvo apontado e confirma (ConfirmPointedTarget no SelectionController). Foco, apontado e seleção mantêm distinção visual (azul / âmbar / verde).
- **Indicação do modo:** HUD e painéis espaciais mostram "Modo: FocusDriven / CenterScreenSimulated / ManualCycleAssisted" e "(P = alternar)".
- **Fallback:** se PerformSimulatedHit não devolver alvo válido, SetPointedFromSimulatedHit(null) limpa o apontado; foco e seleção (Tab, F/G, Enter, Backspace) continuam a funcionar. Se SimulatedPointerController for nulo, usa-se SetPointedFromSelection como antes.

## Camada abstrata de input (etapa 41)

- **QuestPresentationInputCommand:** comando de input abstrato (CommandType, Source, Timestamp, ContextHint). Qualquer fonte (teclado, futuro controlador, mão, gaze) emite comandos deste tipo.
- **QuestPresentationInputCommandType:** enum com todos os comandos (NextFocus, PreviousFocus, ActivateFocus, ExitFocusMode, NextSelectable, PreviousSelectable, ConfirmSelection, CancelSelection, TogglePointerMode, ConfirmPointedTarget, NextStep, PreviousStep, NextHotspot, PreviousHotspot, NextSceneState, PreviousSceneState, ToggleNavigationMode, RestartPresentation).
- **QuestPresentationInputSourceType:** origem do input (KeyboardFallback, EditorTest, SimulatedHeadset, FutureController, FutureHandTracking, FutureGaze).
- **IQuestPresentationInputDispatcher** e **QuestPresentationInputDispatcher:** despacho central: Dispatch(command) roteia para navegação, foco, seleção e apontamento; mantém CurrentSource e log dos últimos 5 comandos (debug/teste); Bind(nav, focus, selection, pointerPrep, simulatedPointer).
- **QuestPresentationInputAdapter:** deixa de acionar controladores directamente; emite **QuestPresentationInputCommand** para o dispatcher (BindDispatcher). Se o dispatcher não estiver ligado, usa **FallbackUpdate** com chamadas directas para não quebrar.
- **Indicação em modo teste:** HUD mostra "Input: [origem] | Último: [comando]" quando ShowInputDebugLine e dispatcher disponível (linha compacta, não polui).
- **Fallback:** se o dispatcher falhar ou não estiver ligado, o adapter continua a funcionar com FallbackUpdate (mesmo comportamento que antes).

## Base de runtime XR real (etapa 42)

- **QuestPresentationRuntimeMode:** EditorFallback (editor/teste), SimulatedXR (build sem subsistema XR), RealXR (XR activo no headset).
- **QuestXRRuntimeStatus:** RuntimeMode, IsXRActive, IsFallbackActive, XRInitMessage; preenchido por **IQuestXRRuntimeStatusService** / **MockQuestXRRuntimeStatusService** (em editor → EditorFallback; em build Android sem SDK → SimulatedXR; quando integrar OVR/XR → registar implementação que retorne RealXR).
- **QuestXRRuntimeBootstrap:** avalia e cacheia o estado (RefreshStatus); o **QuestPresentationController** chama RefreshStatus() no Start e preenche a sessão (RuntimeMode, IsXRActive, IsFallbackActive, XRInitStatus). Opcional: colocar o componente num GameObject na cena Quest para inicialização explícita.
- **QuestXRPresentationRig:** rig de câmera compatível com XR: em EditorFallback/SimulatedXR aplica altura/offset; em RealXR não altera a câmera (o subsistema XR controla). O controlador prefere **QuestXRPresentationRig** ao criar ou procurar rig; se não existir, cria um com **QuestXRPresentationRig**.
- **QuestPresentationSession:** passou a incluir RuntimeMode, IsXRActive, IsFallbackActive, XRInitStatus (preenchidos pelo controlador a partir do serviço).
- **Indicação em modo teste:** HUD mostra "Runtime: [modo] | XR: Sim/Não | Fallback: Sim/Não | Init: [mensagem]" quando ShowXRDebugLine; painel de fallback mostra também Runtime e XR.
- **Fallback:** se XR real não estiver disponível ou o serviço falhar, a cena continua em EditorFallback ou SimulatedXR (utilizável); sem falha silenciosa.

## Ponte para input XR real (etapa 43)

### O que está integrado de forma real

- **Leitura opcional de input XR:** **QuestXRInputReader** (reflexão sobre **UnityEngine.XR.InputDevices**) tenta obter dispositivos com característica Controller e ler **triggerButton**, **secondaryButton** e **primary2DAxis**. Sem referência obrigatória ao módulo XR (se o tipo não existir, não quebra).
- **ProcessFrame():** o **QuestPresentationController** chama **IQuestXRInputBridge.ProcessFrame()** em cada **Update**. A bridge chama o leitor; se houver input XR, faz detecção de borda (trigger/secundário/eixo) e despacha comandos para o dispatcher existente.
- **Mapeamento actual:** trigger (primário) → **ConfirmSelection**; botão secundário → **ToggleNavigationMode**; eixo Y do primary2DAxis (acima/abaixo do limiar) → **NextStep** / **PreviousStep**.
- **Status honesto:** **QuestXRInputStatus** inclui **IsBridgeInFallbackOnly** (true quando a bridge não está a ler input XR real). **IsXRInputAvailable** = true só quando **TryReadControllerInput** conseguiu ler pelo menos um dispositivo. **LastXRAction** reflecte a última acção realmente despachada a partir de input XR (ou SubmitXRCommand externo). **Notes**: "XR controlador activo" quando há leitura real; "XR input real não disponível (fallback activo)" ou "Sem dispositivos XR" quando em fallback.

### O que continua a ser fallback

- **Editor sem headset:** não há dispositivos XR; a bridge não lê nada; **IsXRInputAvailable** = false, **IsBridgeInFallbackOnly** = true; teclado e botões de UI funcionam normalmente.
- **Build sem subsistema XR activo:** idem; Notes indicam que o input XR real não está disponível.
- **Módulo UnityEngine.XR ausente ou incompatível:** o leitor falha por reflexão; a bridge permanece em fallback; sem falha silenciosa (o status mostra "XR input real não disponível").

### Componentes e fluxo

- **QuestXRInputStatus:** IsXRInputAvailable, IsXRInputEnabled, ActiveXRInputMode, LastXRAction, **IsBridgeInFallbackOnly**, Notes.
- **IQuestXRInputBridge** / **QuestXRInputBridge:** GetStatus(), SetDispatcher(), SubmitXRCommand(), **ProcessFrame()** (tenta ler XR e despachar). GetStatus() reflecte disponibilidade real a partir do leitor.
- **Sessão:** **QuestPresentationSession.XRInputStatus** preenchido em cada Refresh; **IsBridgeInFallbackOnly** e **Notes** permitem inspeccionar se está em fallback ou a ler XR real.
- **HUD/debug:** linha "XR Input: disponível | activo | **fallback only** Sim/Não | modo | última" quando ShowXRInputDebugLine.

### Como testar

- **Fallback (editor ou sem XR):** abrir QuestPresentation; na HUD de debug, "XR Input: disponível Não | activo Sim | fallback only Sim | modo RuntimeFallback | última —". Navegar com teclado e botões; nada quebra.
- **Com input XR real (headset com controladores):** quando o runtime tiver **UnityEngine.XR** e dispositivos com característica Controller, a bridge passa a ler em cada frame; "disponível Sim | fallback only Não | modo XRControllerPrimary"; trigger/secundário/eixo enviam comandos; "última" mostra o último comando (ConfirmSelection, NextStep, etc.).

### Limitações actuais da integração

- Não usa Meta SDK/OVR; usa apenas a API genérica **UnityEngine.XR** (InputDevices, CommonUsages) via reflexão.
- Não implementa hand tracking nem gaze avançado; apenas trigger, botão secundário, eixo 2D e **raycast XR básico** (etapa 44).
- Se o módulo XR tiver outra assinatura ou assembly, o leitor pode não encontrar os tipos; nesse caso permanece fallback.

## Raycast XR básico real (etapa 44)

- **QuestXRRaycastHitResult:** resultado de raycast XR (HitObjectName, HitTargetId, HitTargetType, HitDistance, IsValidTarget, SourceMode, Notes).
- **QuestXRRaycastSourceMode:** CameraForward (ray a partir da câmera), ControllerPrimary/Secondary (reservado).
- **QuestXRRaycastController:** dispara **Physics.Raycast** a partir da câmera/rig (SetRayOrigin), distância máxima configurável; ao atingir um Collider com **QuestPointableTargetMarker** devolve hit válido com HitTargetId/HitTargetType.
- **QuestPointableTargetMarker:** componente em GameObjects com Collider; expõe TargetId e TargetType para o raycast. Os painéis espaciais (principal, lateral, progresso) recebem BoxCollider + marcador (guided_step, hotspot, action_next).
- **Integração:** quando XR está activo (IsXRActive ou bridge com input disponível), o **QuestPresentationController** em RefreshAll chama **PerformRaycast()**; se **IsValidTarget**, **QuestPointerPreparationController.SetPointedFromXRRaycast(hit)**; senão fallback para apontamento simulado. Confirmação do alvo apontado continua pelo **input XR** (trigger → ConfirmSelection no dispatcher).
- **Distinção visual:** alvo atingido por raycast XR usa cor/outline ligeiramente diferente do apenas focado ou selecionado (_xrRaycastTint, XRRaycastScale); **IsPointedFromXRRaycast** no pointer controller e no layout.
- **Sessão:** **LastXRRaycastHit**, **XRRaycastActive** preenchidos pelo controlador; **HUD** em modo teste mostra "Raycast XR: activo Sim/Não | último hit [id] | dist [m] | origem XR real/Fallback" (ShowXRRaycastDebugLine).
- **Fallback:** sem XR activo ou sem hit válido, mantém apontamento simulado e foco anterior; apresentação não quebra.

## Hover temporal e dwell (etapa 45)

- **QuestTargetHoverState:** estado de hover no alvo (TargetId, HoverElapsedSeconds, RequiredDwellSeconds, IsDwellReady, IsHoverStable, HoverSourceMode, Notes). Preenchido pelo **QuestHoverDwellController** e exposto na sessão.
- **QuestHoverSourceMode:** origem do apontamento para hover (SimulatedPointer, XRRaycast, FutureGaze). Preparado para futura integração com gaze real.
- **QuestTargetConfirmationMode:** modo de confirmação (ExplicitOnly, DwellOnly, DwellOrExplicit). Definido na sessão (**QuestPresentationSession.ConfirmationMode**); default **DwellOrExplicit**.
- **QuestHoverDwellController:** acompanha quanto tempo um alvo permanece apontado; reseta ao mudar de alvo; marca **IsDwellReady** quando o tempo de dwell atinge **RequiredDwellSeconds** (configurável, ex.: 1,2 s). **UpdateHover(deltaTime, pointedTargetId, sourceMode)** chamado cada frame após o pointer/raycast; **OnDwellReady** dispara uma vez por permanência; **ConsumeDwellConfirm()** reseta após confirmação (dwell ou explícita).
- **Integração:** o **QuestPresentationController** chama **UpdatePointerAndHover()** cada frame (e em RefreshAll); obtém pointedId e sourceMode (XRRaycast se **IsPointedFromXRRaycast**, senão SimulatedPointer); **session.CurrentHoverState** e **session.LastConfirmationTargetId** actualizados. Se **ConfirmationMode** for DwellOnly ou DwellOrExplicit e **IsDwellReadyToConfirm()**, confirma o alvo apontado e **ConsumeDwellConfirm()**. **OnConfirmed** no **QuestSelectionController** actualiza **LastConfirmationTargetId** e chama **ConsumeDwellConfirm()** para qualquer confirmação.
- **Visual:** painéis espaciais mostram progresso de dwell (texto "X%" ou "Pronto ✓") no alvo apontado; cor/outline distinto quando **dwell ready**; indicador de apontamento nos painéis inclui "Dwell: X% | pronto". **QuestWorldCursorController** mostra "(pronto)" ou percentagem no label do cursor quando há hover activo.
- **HUD/debug:** linha "Hover: [id] | dwell X s | pronto Sim/Não | modo [Explícita/Dwell/Dwell+Explícita] | última conf. [id]" (ShowHoverDwellDebugLine).
- **Fallback:** se dwell não estiver activo ou falhar, confirmação explícita (Enter, trigger) continua a funcionar; **ExplicitOnly** desactiva confirmação por dwell.

## Targeting estável e resolução de alvo (etapa 46)

- **QuestTargetCandidate:** candidato a alvo principal (TargetId, TargetType, PriorityScore, StabilityScore, VisibilityScore, IsWorldTarget, IsUITarget, SourceMode, Notes). Usado pelo **QuestTargetResolverController**.
- **QuestTargetSourceMode:** origem do alvo (FocusFallback, SimulatedPointer, XRRaycast, FutureGaze). Distingue alvo detectado bruto vs resolvido.
- **QuestTargetResolverController:** resolve o alvo principal a partir do raw detectado (pointer/raycast), foco e seleção. **Resolve(rawDetectedTargetId, rawSourceMode, focusTargetType, selectedTargetId, pointableTargets)** chamado cada frame; devolve **QuestTargetCandidate** resolvido. **Stickiness:** troca só após **MinStableFramesToSwitch** (ex.: 4) frames no mesmo raw; sem hit válido durante **MinFramesToLose** (ex.: 6) frames limpa o resolvido. Fallback de foco: quando raw é null, usa **GetFocusFallbackTargetId(focusTargetType)** (guided_step, hotspot, scene_state, action_next, mode_toggle). **StickinessAppliedThisFrame** indica se manteve alvo anterior.
- **Distinção:** **alvo detectado** = pointer/raycast bruto (session.DetectedTargetId); **alvo resolvido** = saída do resolver (session.ResolvedTargetId, ResolvedTargetSourceMode, StickinessAppliedLastFrame); **alvo selecionado** = SelectionController; **alvo confirmado** = LastConfirmationTargetId. Hover e dwell usam o **alvo resolvido** para maior estabilidade.
- **Integração:** em **UpdatePointerAndHover** o controlador chama o resolver após obter pointedId; preenche sessão (DetectedTargetId, ResolvedTargetId, ResolvedTargetSourceMode, StickinessAppliedLastFrame); **hover** recebe resolvedId; **dwell confirm** e **cursor/layout** usam alvo resolvido. **QuestPointerPreparationController.GetPointableTargetById(id)** permite obter **QuestPointableTarget** por id (para resolved). Layout usa **resolvedTargetId** opcional em Refresh para destaque estável.
- **HUD/debug:** linha "Detectado: [id] | Resolvido: [id] | prior. [score] | origem [modo] | stick Sim/Não" (ShowTargetingDebugLine).
- **Fallback:** se o resolver falhar ou não existir, usa-se pointedId bruto; fluxo anterior (pointer prep, foco, seleção) continua utilizável.

## Gaze-like aiming (etapa 47)

- **QuestGazeLikeState:** estado do gaze-like (IsActive, CurrentGazeTargetId, StabilityElapsedSeconds, RequiredStabilitySeconds, IsStableEnough, SourceDescription). Preenchido pelo **QuestGazeLikeController** e exposto na sessão.
- **QuestAimingMode:** modo de aiming (FallbackOnly, XRRaycastPreferred, GazeLikePreferred, Hybrid). Definido na sessão (**QuestPresentationSession.AimingMode**); default **XRRaycastPreferred**.
- **QuestGazeLikeController:** usa o alvo actualmente à frente da câmera (do raycast ou simulado) e acumula **tempo de estabilidade**. **UpdateGazeLike(currentTargetIdInFront, deltaTime, sourceDescription)** chamado cada frame; quando o mesmo alvo permanece durante **RequiredStabilitySeconds** (ex.: 0,5 s), **IsStableEnough** = true e o alvo é repassado para o resolver quando **AimingMode** é GazeLikePreferred ou Hybrid.
- **Prioridade:** **FallbackOnly** = input ao resolver é null (só foco); **XRRaycastPreferred** = usa pointedId do raycast/simulado sem exigir estabilidade; **GazeLikePreferred** / **Hybrid** = quando IsStableEnough, input ao resolver é CurrentGazeTargetId (session.CurrentTargetFromGazeLike = true), senão usa pointedId. Coexistência segura com raycast e fallback.
- **Integração:** em **UpdatePointerAndHover** o controlador actualiza o gaze-like com pointedId; preenche session.GazeLikeState e CurrentTargetFromGazeLike; o **inputToResolver** depende do AimingMode e do estado gaze-like; resolver, hover e dwell recebem o mesmo fluxo já existente.
- **Visual:** painéis espaciais indicam "Gaze X%" (progresso de estabilidade) ou "Gaze estável" no indicador de apontamento quando aplicável.
- **HUD/debug:** linha "Aiming: [modo] | alvo gaze [id] | estab. X s | estável Sim/Não | origem gaze-like Sim/Não" (ShowGazeLikeDebugLine).
- **Fallback:** se gaze-like falhar ou ficar instável, raycast/simulado e foco continuam; FallbackOnly ignora raycast e gaze-like.

## Interação contextual por hotspot (etapa 48)

- **QuestHotspotContextState:** estado contextual do hotspot (HotspotId, IsContextOpen, ContextTitle, ContextSummary, SalesMessage, RelatedGuidedStepId, RelatedSceneStateId, IsPremiumUpsell, PriorityLevel, OpenedBySource). Preenchido pelo **QuestHotspotContextController** e exposto na sessão (**CurrentHotspotContext**, **HasHotspotContextOpen**).
- **QuestHotspotContextController:** gere abertura, fecho e troca de contexto. **OpenOrToggleForCurrentHotspot(source)**: se o contexto já estiver aberto para o mesmo hotspot, fecha; senão abre para o hotspot actual da sessão. **OpenContextForHotspot(hotspot, source)** preenche o estado a partir do **ProposalVisualHotspot**, do passo guiado relacionado (**RelatedGuidedTourStepId**), do estado de cena (por **PrimaryHotspotIds**) e de **BuildContextSummary** / **BuildSalesMessage**. **CloseContext()** marca IsContextOpen = false.
- **Entrada em contexto:** seleção + confirmação (Enter ou trigger) no alvo "hotspot" abre ou alterna o contexto; confirmação por **dwell** (IsDwellReady + confirmação automática) também abre/alterna. A origem (Selection vs Dwell) fica em **OpenedBySource** para debug. O botão **Próx. hotspot** continua a avançar o hotspot; a confirmação no alvo hotspot não avança, só abre/fecha o painel contextual.
- **Painel contextual no mundo:** **QuestWorldUIPanelLayout** inclui **HotspotContextPanel** (título, resumo, mensagem comercial, indicação "Premium upsell", botão Fechar). **RefreshHotspotContextPanel(session)** activa/desactiva o painel consoante **HasHotspotContextOpen** e preenche os textos a partir de **CurrentHotspotContext**. **OnCloseHotspotContextRequested** chama **CloseContext()** no controlador.
- **Dados reais da proposta:** o contexto usa **ProposalVisualHotspot** (Title, Description, IsPremiumUpsell, RelatedGuidedTourStepId, SalesValueScore), **ProposalGuidedTourStep** (Title, SalesMessage) quando relacionado, **ProposalSceneState** (Name, Description) quando o hotspot é PrimaryHotspotIds do estado, e **ProposalDecisionSummary** / **ProposalRecommendationSummary** quando disponíveis para mensagem comercial coerente.
- **Integração:** **QuestPresentationController** cria e faz Bind do **QuestHotspotContextController** à sessão; em **OnConfirmed** quando o alvo confirmado é "hotspot", chama **OpenOrToggleForCurrentHotspot** com origem "Selection" ou "Dwell". **QuestSelectionController** não avança hotspot na confirmação; o controlador da apresentação trata apenas abertura de contexto. Targeting, hover/dwell e seleção existentes permanecem; fallback: se o contexto falhar, a navegação normal continua.
- **HUD/debug:** linha "Contexto hotspot: [título] | Aberto/Fechado | origem [Selection/Dwell] | premium Sim/Não" (ShowHotspotContextDebugLine).
- **Fallback:** se o contexto não estiver disponível ou falhar, a apresentação continua utilizável com navegação entre passos e hotspots; o painel contextual simplesmente não aparece.

## Micro-roteiro narrativo por hotspot (etapa 49)

- **QuestHotspotStoryStep:** passo da micro-narrativa contextual (Id, HotspotId, Title, Summary, SalesMessage, StepOrder, IsUpsellMoment, RelatedGuidedStepId, RelatedSceneStateId, SuggestedFocusSeconds). Construído pelo **QuestHotspotStoryController** a partir da proposta.
- **QuestHotspotContextState** (evoluído): inclui **StorySteps**, **CurrentStoryStepIndex**, **IsStoryActive** e **CurrentStoryStep** (passo actual do micro-roteiro). O contexto simples do hotspot continua; a narrativa é uma camada opcional dentro do mesmo painel.
- **QuestHotspotStoryController:** **BuildAndAssignStorySteps(hotspot)** gera o micro-roteiro (visão geral do hotspot, passo guiado relacionado, estado de cena, fechamento com DecisionSummary quando premium) e atribui ao contexto; **StartStory()** activa a narrativa (passo 0); **NextStep()** / **PreviousStep()** navegam; **CloseStory()** volta à vista simples. Integrado ao abrir contexto: após **OpenContextForHotspot** o controlador da apresentação chama **BuildAndAssignStorySteps** para o hotspot actual.
- **Painel contextual (evoluído):** quando **IsStoryActive**, o painel mostra o passo actual (título, resumo, mensagem comercial), indicador "Passo X/Y", indicação "Upsell" se **IsUpsellMoment**, e botões Ant. / Próx. / Fechar roteiro. Quando a narrativa não está activa, mostra o contexto simples e, se houver passos, botão **Iniciar micro-roteiro**. Botão **Fechar** continua a fechar todo o contexto. Callbacks: **OnStartHotspotStoryRequested**, **OnPreviousStoryStepRequested**, **OnNextStoryStepRequested**, **OnCloseHotspotStoryRequested**.
- **Input:** quando o contexto do hotspot está aberto e **IsStoryActive**, as setas (NextStep/PreviousStep) navegam no micro-roteiro em vez do roteiro guiado principal; **QuestPresentationInputDispatcher.BindStory(sessionGetter, storyController)** faz o desvio. Teclado: setas = passo (do roteiro principal ou do micro-roteiro, consoante estado).
- **Dados reais:** o micro-roteiro usa **ProposalVisualHotspot**, **ProposalGuidedTourStep** (RelatedGuidedTourStepId), **ProposalSceneState** (PrimaryHotspotIds), **ProposalDecisionSummary** (FinalSalesMessage para hotspot premium). Coerente com guided tour, scene state e recommendation.
- **HUD/debug:** a linha de contexto hotspot inclui "narrativa Sim/Não | passo X/Y | upsell passo Sim/Não" (ShowHotspotContextDebugLine).
- **Fallback:** se a narrativa contextual falhar ou não tiver passos, o contexto simples do hotspot (título, resumo, mensagem, Fechar) continua a funcionar; o utilizador pode ignorar "Iniciar micro-roteiro".

## Comparação imersiva entre hotspots (etapa 50)

- **QuestHotspotComparisonState:** estado da comparação lado a lado (LeftHotspotId, RightHotspotId, LeftTitle, RightTitle, LeftSalesMessage, RightSalesMessage, BetterForExperience, BetterForUpsell, BetterForClarity, ComparisonSummary, IsComparisonOpen, OpenedBySource). Valores "Left"/"Right" indicam o lado melhor por critério.
- **QuestHotspotComparisonController:** **OpenComparisonFromCurrentHotspot(source)** — esquerda = hotspot actual, direita = próximo na lista ou primeiro; **CloseComparison()**; **SwapSides()** troca esquerda/direita. Preenche títulos e mensagens comerciais a partir de **ProposalVisualHotspot** e **ProposalGuidedTourStep** (RelatedGuidedTourStepId); **BetterForExperience** por SalesValueScore; **BetterForUpsell** por IsPremiumUpsell; **BetterForClarity** por descrição; **ComparisonSummary** de **ProposalDecisionSummary**.ExecutiveShortSummary quando disponível.
- **Painel de comparação:** **QuestWorldUIPanelLayout** inclui **HotspotComparisonPanel** (duas colunas: título e mensagem comercial esq./dir.; linhas "Melhor experiência", "Melhor upsell", "Mais claro"; resumo comparativo; botões **Trocar lados**, **Fechar comparação**). Quando **HasHotspotComparisonOpen**, o painel de comparação é exibido e o painel de contexto do hotspot fica oculto; ao fechar comparação, volta ao contexto (ou micro narrativa). No painel de contexto (vista simples) o botão **Comparar** abre a comparação.
- **Integração:** **QuestPresentationController** cria e faz Bind do **QuestHotspotComparisonController**; callbacks **OnOpenComparisonRequested**, **OnSwapComparisonRequested**, **OnCloseComparisonRequested**. Contexto e micro narrativa permanecem; comparação é uma vista alternativa a partir do contexto.
- **HUD/debug:** linha "Comparação: Activa/Fechada | Esq. [título] | Dir. [título] | melhor exp. Esq./Dir. | upsell | claro | origem" (ShowComparisonDebugLine).
- **Fallback:** se a comparação falhar ou não houver dois hotspots, o contexto simples e o micro-roteiro continuam a funcionar; o botão Comparar só aparece quando há pelo menos um hotspot.

## Modo de fechamento imersivo (etapa 51)

- **QuestClosingExperienceState:** estado do fechamento (IsClosingOpen, RecommendedProposalId, ClosingHeadline, ClosingSummary, PrimaryCTA, SecondaryCTA, SuggestedNextAction, ConfidenceLabel, BudgetConclusion, ExperienceConclusion, OperationalConclusion, IsReadyToClose). Preenchido pelo **QuestClosingExperienceController** a partir de **ProposalDecisionSummary** e **ProposalRecommendationSummary**.
- **QuestClosingExperienceController:** **OpenClosing()** constrói o estado a partir da sessão (DecisionSummary, RecommendationSummary): headline, resumo, confiança (score da recomendação), próxima ação, conclusões orçamento/experiência/operacional; PrimaryCTA = "Fechar proposta" quando **ReadyToClose**, senão "Aceitar conclusão"; **CloseClosing()**; **ConfirmPrimaryCTA()** / **ConfirmSecondaryCTA()** actualizam **LastClosingAction** e fecham o painel. A sessão expõe **RecommendationSummary** (quando a proposta é a recomendada) além de **DecisionSummary**.
- **Painel de fechamento:** **QuestWorldUIPanelLayout** inclui **ClosingPanel** (headline, resumo, confiança, próxima ação sugerida, conclusões orçamento/experiência/operacional, botões CTA principal e secundário, **Voltar**). Quando **HasClosingOpen**, o painel de fechamento é exibido e os painéis de contexto e comparação ficam ocultos; ao fechar ou confirmar CTA, volta ao tour/contexto/comparação.
- **Entrada:** botão **Fechamento** na barra de navegação (HUD) abre o modo de fechamento. Callbacks **OnCloseClosingRequested**, **OnConfirmPrimaryCTARequested**, **OnConfirmSecondaryCTARequested**.
- **Dados reais:** headline e resumo de DecisionSummary (ExecutiveHeadline, ExecutiveShortSummary) ou RecommendationSummary (ExecutiveSummary); SuggestedNextAction, BudgetConclusion, ExperienceConclusion, OperationalConclusion; ConfidenceLabel do RecommendationConfidenceScore; IsReadyToClose = (DecisionReadinessLevel == ReadyToClose).
- **HUD/debug:** linha "Fechamento: Aberto/Fechado | CTA [texto] | proposta [id] | pronto Sim/Não | última [PrimaryCTA/SecondaryCTA]" (ShowClosingDebugLine).
- **Fallback:** se não houver DecisionSummary nem RecommendationSummary, o fechamento abre com valores default (headline "Conclusão da proposta", resumo genérico); a apresentação continua utilizável.

## Resultado comercial da sessão Quest (etapa 52)

- **Objetivo:** transformar a saída da experiência Quest num **estado comercial estruturado** (não só CTA visual), para continuidade e futura integração com web/backend/CRM. Sem backend/CRM reais nesta etapa.
- **QuestCommercialDecisionState:** enum (ExplorationOnly, Interested, NeedsFollowUp, ReadyToAdvance, ReadyToClose) para o estado final da decisão.
- **QuestCommercialOutcome:** resultado consolidado (SessionId, RecommendedProposalId, SelectedProposalId, ClosingCTAChosen, SecondaryCTAChosen, FinalDecisionState, SuggestedNextAction, ConfidenceLevel, OutcomeSummary, FollowUpPriority, Notes, Timestamp). Preenchido pelo **QuestCommercialOutcomeController** a partir do closing, RecommendationSummary e DecisionSummary.
- **QuestCommercialOutcomeController:** Bind(session); **RecordFromClosing(ctaConfirmed)** constrói o outcome quando o utilizador confirma PrimaryCTA ou SecondaryCTA (usa CurrentClosingState, DecisionSummary, RecommendationSummary); **MarkNeedsFollowUp()**, **MarkReadyToAdvance()**, **ConfirmFinalDecision()** actualizam FinalDecisionState e FollowUpPriority; **UpdateOutcomeState(state, priority)** para o painel.
- **Sessão:** **QuestPresentationSession** inclui **CurrentCommercialOutcome**, **LastConfirmedCTA**, **LastDecisionState**. Após ConfirmPrimaryCTA/ConfirmSecondaryCTA, o **QuestPresentationController** chama RecordFromClosing antes de fechar o painel de fechamento.
- **Painel de resultado:** **QuestWorldUIPanelLayout** inclui **OutcomePanel** (headline "Resultado da apresentação", decisão actual, próxima ação sugerida, prioridade follow-up; botões **Precisa follow-up**, **Pronta para avançar**, **Voltar ao fechamento**, **Confirmar decisão final**). Visível quando **CurrentCommercialOutcome != null** e o painel de fechamento está fechado. Callbacks **OnMarkNeedsFollowUpRequested**, **OnMarkReadyToAdvanceRequested**, **OnBackToClosingRequested**, **OnConfirmFinalDecisionRequested** ligados ao outcome controller e ao closing (Voltar reabre o fechamento).
- **Dados reais:** o outcome usa RecommendedProposalId, SuggestedNextAction e ConfidenceLevel do closing/DecisionSummary/RecommendationSummary; proposta escolhida = proposta actual da sessão.
- **HUD/debug:** linha "Comercial: estado [LastDecisionState] | CTA [LastConfirmedCTA] | proposta [id] | prioridade [FollowUpPriority]" (ShowCommercialOutcomeDebugLine).
- **Fallback:** se o resultado comercial falhar ou o outcome controller não estiver ligado, a apresentação e o fechamento continuam utilizáveis; o painel de resultado só aparece quando há outcome.
- **Preparação para integração:** a estrutura (QuestCommercialOutcome, SessionId, estado, prioridade, próxima ação) está pronta para futura exportação ou API para a versão web/CRM; ainda não há persistência nem envio real.

## Payload de exportação da sessão Quest (etapa 53)

- **Objetivo:** criar a base de **exportação lógica** da sessão Quest para um payload estruturado de integração futura (web/backend/CRM), mantendo tudo em memória; sem backend nem persistência reais nesta etapa.
- **QuestSessionInteractionStats:** estatísticas da sessão (GuidedStepsViewed, HotspotsOpened, ComparisonsOpened, ClosingOpened, CTAConfirmations, TotalInteractionCount, EstimatedSessionDurationSeconds). Contadores na sessão (HotspotContextOpenCount, ComparisonOpenCount, ClosingOpenCount, CTAConfirmationCount) são incrementados pelos controladores ao abrir contexto, comparação, fechamento e ao confirmar CTA; SessionStartedAtUtc em BuildSession para duração.
- **QuestSessionExportPayload:** payload exportável (SessionId, ProjectId, RecommendedProposalId, SelectedProposalId, FinalDecisionState, ConfirmedCTA, SuggestedNextAction, FollowUpPriority, OutcomeSummary, ClosingSummary, InteractionStats, Timestamp). Gerado pelo **QuestSessionExportController** a partir do outcome, closing, recomendação e proposta.
- **QuestSessionExportController:** Bind(session); **SetProjectIdGetter(getter)** para ProjectId (ex.: AppSession.CurrentProject?.Id); **GeneratePayload()** constrói o payload e actualiza CurrentExportPayload e LastExportTimestamp; **ClearPayload()** limpa payload e timestamp.
- **Sessão:** **QuestPresentationSession** inclui **CurrentExportPayload**, **LastExportTimestamp**, **SessionStartedAtUtc**, **HotspotContextOpenCount**, **ComparisonOpenCount**, **ClosingOpenCount**, **CTAConfirmationCount**.
- **Painel de exportação:** **QuestWorldUIPanelLayout** inclui **ExportPanel** (headline, ids, decisão, CTA, próxima ação, prioridade, estatísticas; botões **Gerar payload**, **Regenerar**, **Limpar**, **Voltar ao resultado**). Visível quando (CurrentCommercialOutcome != null ou CurrentExportPayload != null) e fechamento fechado. Sem payload: só "Gerar payload"; com payload: inspeção completa + Regenerar, Limpar, Voltar.
- **HUD/debug:** linha "Export: Sim/Não | [timestamp] | decisão | CTA | prior." (ShowExportDebugLine).
- **Fallback:** se a geração falhar (excepção), a apresentação continua utilizável; payload fica null ou anterior.
- **O que ainda falta para exportação real:** persistência (ficheiro, base de dados), envio HTTP para web/backend, integração com CRM real; a estrutura do payload está pronta para serialização e API futura.

## Persistência local simulada da sessão Quest (etapa 54)

- **Objetivo:** permitir **salvar e retomar** a sessão Quest localmente (storage leve com PlayerPrefs), sem backend nem persistência remota; a apresentação deixa de ser apenas efémera e prepara persistência real futura.
- **QuestSessionLocalSnapshot:** snapshot persistível (SessionId, ProjectId, ActiveProposalId, RuntimeMode, NavigationMode, CurrentGuidedStepIndex, CurrentHotspotIndex, CurrentSceneStateIndex, IsClosingOpen, LastDecisionState, LastConfirmedCTA, HasCommercialOutcome, HasExportPayload, SavedAt). Serializado com JsonUtility para PlayerPrefs.
- **QuestSessionLocalPersistenceController:** Bind(session); **SetProjectIdGetter(getter)**; **Save()** constrói snapshot a partir da sessão e grava em PlayerPrefs; **Restore(onRestoreClosing)** lê do storage, aplica índices/modo/LastDecisionState/LastConfirmedCTA à sessão e invoca onRestoreClosing(IsClosingOpen) para reabrir o painel de fechamento se necessário; **Clear()** remove snapshot do storage; **HasRestorableSnapshot()** indica se existe snapshot válido.
- **Sessão:** **QuestPresentationSession** inclui **CurrentLocalSnapshot** (cópia do último snapshot salvo ou restaurado), **LastRestoreTimestamp**, **RestorableSnapshotAvailable** (preenchido pelo controlador antes de Refresh).
- **Painel de persistência:** **QuestWorldUIPanelLayout** inclui **PersistencePanel** (headline "Persistência local", status do snapshot, botões **Salvar sessão**, **Restaurar**, **Limpar**). Visível quando a apresentação tem dados (MRPlan != null). Status mostra data do save, SessionId e se é restaurável.
- **Restauração coerente:** recupera índices de passo/hotspot/estado de cena (com clamp aos tamanhos actuais), NavigationMode, LastDecisionState, LastConfirmedCTA; se IsClosingOpen, o controlador chama OpenClosing() após restaurar. Outcome e payload completos não são persistidos no snapshot; apenas os índices e estado mínimo para retomar a navegação.
- **HUD/debug:** linha "Snapshot: Sim/Não | [SavedAt] | restaurado Sim/Não | sessão | proposta" (ShowPersistenceDebugLine).
- **Fallback:** se Save/Restore/Clear falharem (excepção), a apresentação continua utilizável.
- **O que ainda falta para persistência completa:** persistência em ficheiro ou base de dados, sincronização com backend, restauração de outcome/payload completos, múltiplos snapshots por utilizador.

## O que já está preparado para futura substituição por input real

- **QuestPresentationInputCommand** e **IQuestPresentationInputDispatcher:** qualquer fonte (teclado, controlador, mão, gaze) pode criar comandos e chamar Dispatch(); os controladores não dependem da fonte. **QuestPresentationInputSourceType** permite identificar a origem (FutureController, FutureHandTracking, FutureGaze).
- **IQuestXRInputBridge** e **QuestXRInputBridge:** ponte com leitura real opcional via **QuestXRInputReader** (reflexão UnityEngine.XR); ProcessFrame() lê trigger/secondary/eixo e despacha ConfirmSelection, ToggleNavigationMode, NextStep, PreviousStep. SubmitXRCommand(cmd) continua disponível para injeção externa. GetStatus() e IsBridgeInFallbackOnly reflectem se está a ler XR real ou em fallback.
- **QuestInteractionCommand** (seleção) continua a ser usado pelo dispatcher ao rotear NextSelectable, ConfirmSelection, etc.
- **QuestSelectableTarget**, **QuestPointableTarget**, **QuestSimulatedPointerHit:** listas e hit; **ConfirmPointedTarget**, **SetSelectedTargetById**. **QuestXRRaycastController** e **QuestXRRaycastHitResult** (etapa 44): raycast real a partir da câmera; **QuestPointableTargetMarker** nos painéis; **SetPointedFromXRRaycast**; confirmação via trigger XR. **QuestHoverDwellController**, **QuestTargetHoverState**, **QuestHoverSourceMode**, **QuestTargetConfirmationMode** (etapa 45): hover temporal, dwell configurável, confirmação Explícita/Dwell/Dwell+Explícita; preparação para gaze real. **QuestTargetCandidate**, **QuestTargetSourceMode**, **QuestTargetResolverController** (etapa 46): resolução de alvo estável com stickiness; alvo detectado vs resolvido; hover/dwell e cursor usam resolvido. **QuestGazeLikeState**, **QuestAimingMode**, **QuestGazeLikeController** (etapa 47): primeira camada gaze-like com tempo de estabilidade; direção câmera/headset como base; AimingMode (FallbackOnly, XRRaycastPreferred, GazeLikePreferred, Hybrid). **QuestSimulatedPointerController** permanece para fallback. **QuestHotspotContextState**, **QuestHotspotContextController** (etapa 48): interação contextual por hotspot; abertura/fecho por seleção ou dwell; painel no mundo com título, resumo, mensagem comercial e premium upsell; dados da proposta (ProposalVisualHotspot, GuidedTourStep, SceneState, DecisionSummary). **QuestHotspotStoryStep**, **QuestHotspotStoryController** (etapa 49): micro-roteiro de venda imersiva por hotspot; StorySteps, CurrentStoryStepIndex, IsStoryActive; BuildAndAssignStorySteps, StartStory, NextStep, PreviousStep, CloseStory; setas navegam no micro-roteiro quando narrativa activa; painel com passo actual, progresso e upsell. **QuestHotspotComparisonState**, **QuestHotspotComparisonController** (etapa 50): comparação lado a lado entre dois hotspots; OpenComparisonFromCurrentHotspot, CloseComparison, SwapSides; painel com colunas esq./dir., melhor experiência/upsell/clareza, resumo; botão Comparar no contexto. **QuestClosingExperienceState**, **QuestClosingExperienceController** (etapa 51): modo de fechamento imersivo; OpenClosing, CloseClosing, ConfirmPrimaryCTA, ConfirmSecondaryCTA; painel com headline, resumo, CTA, conclusões; botão Fechamento na barra.
- **FocusController**, **SelectionController**, **PointerPreparationController**, **SimulatedPointerController** recebem apenas via dispatcher (não acoplados à fonte de input).

## Relatório final / handoff comercial (etapa 55)

- **Objetivo:** consolidar o resultado da sessão Quest num **relatório final/handoff** utilizável por vendedor/consultor ou sistema de acompanhamento, usando apenas dados em memória; sem backend/CRM/persistência remota nesta etapa.
- **QuestSessionHandoffReport:** relatório final (SessionId, ProjectId, RecommendedProposalId, SelectedProposalId, FinalDecisionState, ConfirmedCTA, SuggestedNextAction, FollowUpPriority, OutcomeSummary, ClosingSummary, KeyHotspotsVisited, KeyComparisonsViewed, FinalConfidenceLabel, HandoffNotes, GeneratedAt). Fonte de dados: QuestCommercialOutcome, QuestSessionExportPayload (e InteractionStats), QuestClosingExperienceState, ProposalRecommendationSummary, ProposalDecisionSummary.
- **QuestSessionHandoffReportController:** Bind(session); **SetProjectIdGetter(getter)**; **GenerateReport()** consolida o relatório a partir do outcome/export/fechamento/recomendação/decisão (sem duplicar lógica de negócio); **ClearReport()** limpa o relatório actual. Mantém tudo em memória; preparado para futura exportação web/CRM.
- **Sessão:** **QuestPresentationSession** inclui **CurrentHandoffReport**, **LastHandoffGeneratedAt**, **HasGeneratedHandoffReport**. O relatório não é persistido localmente nesta etapa; pode ser regenerado quando necessário a partir de outcome/export.
- **Painel de relatório/handoff:** **QuestWorldUIPanelLayout** inclui **HandoffPanel** (headline "Relatório da sessão", propostas recomendada/escolhida, decisão final, CTA confirmado, próxima ação, prioridade, hotspots/comparações, confiança, observações; botões **Gerar relatório**, **Regenerar**, **Limpar**, **Voltar**). Visível quando a apresentação tem dados (MRPlan != null); utiliza o relatório actual quando disponível; caso contrário, orienta o utilizador a gerar o relatório.
- **HUD/debug:** linha "Handoff: Sim/Não | [timestamp] | decisão [FinalDecisionState] | prior. [FollowUpPriority]" (ShowHandoffDebugLine), usando dados do relatório quando disponível (senão do estado comercial).
- **Fallback:** se GenerateReport falhar (excepção), a apresentação continua utilizável; o relatório fica null ou anterior.
- **O que ainda falta para handoff persistido/compartilhável:** persistência real do relatório (ficheiro/BD), envio para backend e CRM, histórico de sessões/handoffs, edição colaborativa e exportações formatadas (PDF/e-mail).

## O que ainda falta antes de uma experiência comercial XR mais madura

- **Contexto, micro-roteiro, comparação, fechamento, resultado comercial, export, persistência, handoff, resumo compartilhável, diagnóstico de qualidade, plano de melhorias, presets e onboarding (etapas 48–56, 60–63):** a apresentação Quest suporta **pontos de venda imersivos**, **modo de fechamento imersivo**, **resultado comercial** (QuestCommercialOutcome), **payload exportável** (QuestSessionExportPayload), **persistência local simulada** (QuestSessionLocalSnapshot em PlayerPrefs), **relatório final/handoff** (QuestSessionHandoffReport), **resumo compartilhável** (QuestSessionShareableSummary), **diagnóstico de qualidade** (QuestSessionQualityAssessment), **plano de melhorias** (QuestSessionImprovementPlan), **presets de apresentação** (QuestPresentationPreset) e **onboarding imersivo** (QuestPresentationOnboardingState: entrada guiada com proposta, preset, modo, objetivo e dicas; abre no arranque quando não visto; Iniciar apresentação / Fechar; botão Contexto para reabrir). Isto permite **retomar a apresentação**, **encerrar a sessão com visão comercial consolidada**, **avaliar qualidade**, **obter recomendações de otimização**, **conduzir em estilos diferentes** e **melhorar a entrada do utilizador** na experiência; ainda não há backend real, persistência remota, CRM, **analytics persistidos/reais**, **recomendações persistidas**, **presets persistidos ou configuráveis externamente**, **onboarding mais premium/persistido** nem avaliação mais avançada. MR real, passthrough, scene understanding, anchors espaciais, hand tracking real e gaze real avançado também não implementados.
- **Input real do Quest:** a bridge lê input de controlador XR (trigger, secundário, eixo); **raycast XR básico** (etapa 44) permite apontar e confirmar em painéis com Collider + **QuestPointableTargetMarker**. Teclado e botões de UI permanecem como fallback. Hand tracking e gaze avançado ainda não implementados.
- **Meta SDK:** sem OVR Plugin; a camada de comandos, dispatcher e raycast básico estão prontos para evoluir.
- **Raycast/gaze:** raycast **básico real** a partir da câmera (Physics.Raycast + marcadores); **hover/dwell** (etapa 45), **targeting estável** (etapa 46) e **gaze-like aiming** (etapa 47, estabilidade na direção câmera) aproximam de “olhar para apontar”; gaze real avançado ou raycast 6DoF ainda não implementados.

## Modos de execução e como testar (editor / XR simulado / XR real)

- **Editor fallback:** abrir a cena QuestPresentation no editor (ou Main → Abrir apresentação Quest). Runtime: EditorFallback; XR: Não; Fallback: Sim. Câmera e UI funcionam como antes; HUD e painéis espaciais utilizáveis.
- **XR simulado:** build Android sem XR Plug-in: instalar no Quest e abrir o app. Runtime: SimulatedXR; XR: Não; Fallback: Sim. Mesma experiência que no editor (botões na tela, sem tracking real).
- **XR real básico:** com subsistema XR activo, a bridge lê trigger/secondary/eixo e o **QuestXRRaycastController** executa raycast a partir da câmera; alvos com **QuestPointableTargetMarker** (painéis principal, lateral, progresso) podem ser atingidos; trigger confirma o alvo apontado. HUD de debug: "Raycast XR: activo Sim | último hit [id] | dist [m] | origem XR real". **Fallback:** sem XR ou sem hit válido, apontamento simulado e teclado/UI continuam.

## Limitações atuais (antes de MR real)

- **Sem Meta SDK avançado:** não há integração com OVR Plugin, passthrough nem scene understanding.
- **Sem MR real:** a cena Quest usa Canvas 2D (overlay e World Space no ambiente virtual); não há ancoragem no mundo real nem overlay em passthrough.
- **Sem 3D espacial real:** não há colocação de objetos 3D no ambiente; os painéis são UI em World Space.
- **Input:** navegação por botões grandes na UI e, no editor, fallback por teclado (setas, H, E, M, R, U; Tab, \`, Espaço, Esc para foco; F, G, Enter, Backspace para seleção; **P** = alternar modo de apontamento; **Enter** = confirmar alvo apontado).
- **Build no headset:** o app pode ser buildado para Android e instalado no Quest; a apresentação pode ser vista em overlay (HUD) ou em painéis espaciais no mundo virtual.

## Navegação e input na apresentação Quest

- **Modos:** Guiado (sequência de passos), Manual (livre), Foco hotspot.
- **Ações:** Próximo / anterior passo, próximo / anterior hotspot, hotspot principal, próximo / anterior estado de cena, alternar modo, reiniciar apresentação, **Fechamento** (abre painel de fechamento imersivo), Voltar (Main).
- **No headset:** use os botões grandes na HUD (toque na tela).
- **Teclado (editor / teste):** setas = passo (roteiro principal ou micro-roteiro do hotspot, se narrativa contextual activa); H/J = hotspot; E/Q = estado; M = modo; R = reiniciar; **Tab**/\` = foco; **Espaço** = ativar foco; **Esc** = sair foco; **F**/**G** = próximo/anterior alvo; **Enter** = confirmar alvo apontado (ou seleção); **Backspace** = cancelar seleção; **P** = alternar modo de apontamento; U = alternar UI. O **QuestPresentationInputAdapter** traduz teclas em **QuestPresentationInputCommand** e envia ao **IQuestPresentationInputDispatcher**; o dispatcher roteia para navegação, foco, seleção e apontamento. Em modo teste a HUD mostra "Input: [origem] | Último: [comando]".

## Como rodar o primeiro build Quest

1. Configurar **Player Settings** como acima e incluir as cenas **Bootstrap**, **Main** e **QuestPresentation** no Build Settings (ordem: Bootstrap primeiro).
2. Gerar **Build Android** (File > Build Settings > Build) e instalar o APK no Quest (ADB ou distribuição).
3. Abrir o app no Quest: Bootstrap carrega; em seguida abre a cena **Main** (ou **QuestPresentation** se a flag **Open Quest Presentation On Startup** estiver ativa no AppConfig).
4. Na Main, toque em **Abrir apresentação Quest** para ir à cena de apresentação.
5. Na apresentação: use os **botões grandes** na tela para navegar (próximo/anterior passo, hotspots, estados de cena, modo, reiniciar, Voltar). A HUD mostra **Modo: Teste Quest inicial**, status de prontidão (Build, Dados, Nav) e o bloco em destaque.

## Requisitos mínimos já cobertos

- Bootstrap e cenas (Bootstrap, Main, QuestPresentation) no build.
- Sessão de apresentação montada a partir da proposta ativa (MRPresentationPlan, roteiro, hotspots, estados de cena).
- Avaliação de prontidão de runtime (**QuestRuntimeReadinessStatus**: IsBuildReady, HasPresentationData, HasFallbackMode, HasNavigationControls) exibida na HUD e no painel de fallback.
- Modo de teste claro: com dados completos a proposta é exibida; sem dados suficientes entra **fallback** com mensagem objetiva e botão Voltar (o app não falha em silêncio).
- Input básico validável: **QuestPresentationInputAdapter** (teclado no editor; estrutura pronta para input do headset) e botões na UI para uso no Quest.
- Flag **Open Quest Presentation On Startup** no AppConfig para abrir direto a QuestPresentation após o bootstrap (útil para teste rápido no headset).

## Bootstrap de plataforma (opcional)

O script **QuestPlatformBootstrap** (Core) define `Application.targetFrameRate` em builds Android (ex.: 72 fps para Quest). Adicione o componente a um GameObject na cena Bootstrap ou na cena Quest para ativar; no editor não altera nada.

## Como testar input e navegação no headset

- Instale o build Android no Quest e abra o app.
- Vá à apresentação (Main → Abrir apresentação Quest, ou use Open Quest Presentation On Startup no AppConfig).
- Se houver dados da proposta: navegue com os botões (Próx. passo, Ant. passo, Próx. hotspot, etc.). A HUD mostra "Modo: Teste Quest inicial", Build/Dados/Nav e o conteúdo atual.
- Se não houver dados: o painel de fallback mostra a mensagem e o status (Build / Dados); use **Voltar** para retornar à Main. Não há falha silenciosa.

## Estrutura preparada para evolução

- **QuestPresentationCameraRig** e **QuestXRPresentationRig:** câmera/orientação; **QuestXRPresentationRig** compatível com modo de runtime (EditorFallback/SimulatedXR aplica altura; RealXR deixa XR controlar).
- **QuestPresentationRuntimeMode**, **QuestXRRuntimeStatus**, **IQuestXRRuntimeStatusService** e **MockQuestXRRuntimeStatusService:** modo de execução (EditorFallback, SimulatedXR, RealXR); avaliação de XR activo e fallback; HUD e painel de fallback mostram "Runtime | XR | Fallback | Init" em modo teste.
- **QuestXRRuntimeBootstrap:** avalia e cacheia estado XR (RefreshStatus); controlador preenche a sessão no Start.
- **QuestPresentationFocusTargetType** e **QuestPresentationFocusController:** foco entre painéis, destaque visual (cor, escala, borda), ActivateFocus contextual.
- **QuestSelectableTarget**, **QuestSelectableTargetType** e **QuestSelectionController:** alvos selecionáveis (passo, hotspot, estado, ação, modo), registro a partir da sessão, Next/PreviousSelectable, ConfirmSelection, CancelSelection; **QuestInteractionCommand** para comandos abstractos (preparação para input real).
- **QuestPointableTarget**, **QuestPointableTargetType**, **QuestPointerPreparationController** e **QuestWorldCursorController:** camada inicial de cursor/alvo no mundo (alvos apontáveis, retículo e label no canvas espacial).
- **QuestSimulatedPointerHit**, **QuestPointerSourceMode** e **QuestSimulatedPointerController:** raycast/gaze simulado; SetPointedFromSimulatedHit; ConfirmPointedTarget (Enter) e TogglePointerMode (P).
- **QuestPresentationInputCommand**, **QuestPresentationInputCommandType**, **QuestPresentationInputSourceType**, **IQuestPresentationInputDispatcher** e **QuestPresentationInputDispatcher:** camada abstrata de input; adapter emite comandos; dispatcher roteia; log dos últimos comandos e indicação "Input | Último" na HUD (modo teste); fallback sem dispatcher.
- **QuestXRInputMode**, **QuestXRInputStatus**, **IQuestXRInputBridge** e **QuestXRInputBridge:** ponte opcional para input XR real; SubmitXRCommand → dispatcher; GetStatus → sessão; HUD mostra "XR Input: disponível | activo | fallback only | modo | última"; convivência com fallback (etapa 43).
- **QuestXRRaycastHitResult**, **QuestXRRaycastSourceMode**, **QuestXRRaycastController**, **QuestPointableTargetMarker:** raycast XR básico real (etapa 44); Physics.Raycast da câmera; painéis com BoxCollider + marcador; SetPointedFromXRRaycast; HUD "Raycast XR: activo | último hit | dist | origem"; fallback para simulado.
- **QuestTargetHoverState**, **QuestHoverSourceMode**, **QuestTargetConfirmationMode**, **QuestHoverDwellController:** hover temporal e dwell (etapa 45); UpdateHover por frame; ConfirmationMode (ExplicitOnly, DwellOnly, DwellOrExplicit); CurrentHoverState e LastConfirmationTargetId na sessão; confirmação por dwell ou explícita; HUD "Hover | dwell | pronto | modo | última conf."; preparação para gaze real.
- **QuestTargetCandidate**, **QuestTargetSourceMode**, **QuestTargetResolverController:** targeting estável (etapa 46); Resolve(raw, focus, selection) com stickiness (min frames para trocar, min frames para perder); DetectedTargetId vs ResolvedTargetId na sessão; hover/dwell e layout usam alvo resolvido; HUD "Detectado | Resolvido | prior. | origem | stick".
- **QuestGazeLikeState**, **QuestAimingMode**, **QuestGazeLikeController:** gaze-like aiming (etapa 47); UpdateGazeLike(alvo à frente, deltaTime); RequiredStabilitySeconds; quando estável em GazeLikePreferred/Hybrid o alvo vai ao resolver; AimingMode (FallbackOnly, XRRaycastPreferred, GazeLikePreferred, Hybrid); sessão GazeLikeState e CurrentTargetFromGazeLike; HUD "Aiming | alvo gaze | estab. | estável | origem gaze-like"; painéis "Gaze X%" / "Gaze estável".
- **QuestHotspotContextState**, **QuestHotspotContextController:** interação contextual por hotspot (etapa 48); OpenOrToggleForCurrentHotspot(source), OpenContextForHotspot(hotspot, source), CloseContext(); dados da proposta (ProposalVisualHotspot, GuidedTourStep, SceneState, DecisionSummary). **QuestHotspotStoryStep**, **QuestHotspotStoryController** (etapa 49): micro-roteiro narrativo; StorySteps, CurrentStoryStepIndex, IsStoryActive, CurrentStoryStep; BuildAndAssignStorySteps, StartStory, NextStep, PreviousStep, CloseStory. **QuestWorldUIPanelLayout** inclui **HotspotContextPanel** (título, resumo, mensagem, premium, **progresso Passo X/Y**, **botões Iniciar micro-roteiro / Ant. / Próx. / Fechar roteiro**, Fechar); **OnStartHotspotStoryRequested**, **OnPreviousStoryStepRequested**, **OnNextStoryStepRequested**, **OnCloseHotspotStoryRequested**. **QuestPresentationInputDispatcher** **BindStory(sessionGetter, storyController)**: quando narrativa activa, NextStep/PreviousStep navegam no micro-roteiro. **QuestHotspotComparisonState**, **QuestHotspotComparisonController** (etapa 50): OpenComparisonFromCurrentHotspot, CloseComparison, SwapSides; **HotspotComparisonPanel** (colunas esq./dir., melhor exp./upsell/clareza, resumo, Trocar lados, Fechar comparação); botão **Comparar** no painel de contexto; **OnOpenComparisonRequested**, **OnSwapComparisonRequested**, **OnCloseComparisonRequested**. **QuestClosingExperienceState**, **QuestClosingExperienceController** (etapa 51): **ClosingPanel** (headline, resumo, confiança, próxima ação, conclusões, CTA principal/secundário, Voltar); **OnCloseClosingRequested**, **OnConfirmPrimaryCTARequested**, **OnConfirmSecondaryCTARequested**; botão **Fechamento** na barra de navegação. **QuestCommercialOutcome**, **QuestCommercialDecisionState**, **QuestCommercialOutcomeController** (etapa 52): resultado comercial; RecordFromClosing(cta); **OutcomePanel** (decisão, próxima ação, prioridade; botões Precisa follow-up, Pronta para avançar, Voltar ao fechamento, Confirmar decisão final); **OnMarkNeedsFollowUpRequested**, **OnMarkReadyToAdvanceRequested**, **OnBackToClosingRequested**, **OnConfirmFinalDecisionRequested**. **QuestSessionInteractionStats**, **QuestSessionExportPayload**, **QuestSessionExportController** (etapa 53): GeneratePayload, ClearPayload, SetProjectIdGetter; **ExportPanel** (inspeção do payload, Gerar/Regenerar/Limpar/Voltar ao resultado); contadores de interação na sessão. **QuestSessionLocalSnapshot**, **QuestSessionLocalPersistenceController** (etapa 54): Save, Restore(onRestoreClosing), Clear, HasRestorableSnapshot; **PersistencePanel** (Salvar sessão, Restaurar, Limpar; status do snapshot); restauração de índices, modo e closing.
- **QuestWorldUIPanelLayout:** painéis em World Space; destaque de foco (azul), seleção (verde), apontado (âmbar), apontado por XR e **dwell pronto** (cor/outline); **resolvedTargetId** opcional; indicador **Gaze X%** ou **Gaze estável**; texto de progresso de dwell (X% / "Pronto ✓"); **painel de contexto de hotspot** (etapa 48) com **micro-roteiro** (etapa 49) e botão **Comparar**; **painel de comparação** (etapa 50); **painel de fechamento** (etapa 51); **painel de resultado comercial** (etapa 52); **painel de exportação** (etapa 53); **painel de persistência local** (etapa 54); **painel de handoff** (etapa 55); **painel de resumo compartilhável** (etapa 56); **painel de diagnóstico de qualidade** (etapa 60: score geral e sub-scores, label, pontos fortes/fracos, sugestões; Calcular/Recalcular/Limpar/Voltar); **painel de plano de melhorias** (etapa 61: resumo, recomendação prioritária, categoria, lista de recomendações; Gerar plano/Regenerar/Limpar/Voltar); **painel de presets de apresentação** (etapa 62: preset actual, duração, foco; Pitch rápido, Vendas guiadas, Showcase premium, Modo comparação, Revisão executiva, Limpar preset); **onboarding** (etapa 63: overlay em HUD e painel em World Space com título, proposta, preset, runtime, objetivo, dicas, duração; Iniciar apresentação, Fechar; abre no arranque se não visto; botão **Contexto** para reabrir); indicadores e GetCursorParent() para cursor.
- **QuestRuntimeReadinessStatus** e **IQuestRuntimeReadinessService** (MockQuestRuntimeReadinessService): avaliam IsBuildReady, HasPresentationData, HasFallbackMode, HasNavigationControls; integrados à sessão e à HUD.
- **QuestPresentationSession:** guarda NavigationMode, CurrentGuidedStepIndex, CurrentHotspotIndex, CurrentSceneStateIndex, IsPresentationReady, RuntimeReadiness; RuntimeMode, IsXRActive, IsFallbackActive, XRInitStatus (etapa 42); XRInputStatus (etapa 43); ConfirmationMode, CurrentHoverState, LastConfirmationTargetId (etapa 45); DetectedTargetId, ResolvedTargetId, ResolvedTargetSourceMode, StickinessAppliedLastFrame (etapa 46); **AimingMode**, **GazeLikeState**, **CurrentTargetFromGazeLike** (etapa 47); **CurrentHotspotContext**, **HasHotspotContextOpen** (etapa 48); contexto inclui **StorySteps**, **CurrentStoryStepIndex**, **IsStoryActive** (etapa 49); **CurrentHotspotComparison**, **HasHotspotComparisonOpen** (etapa 50); **CurrentClosingState**, **HasClosingOpen**, **LastClosingAction** (etapa 51); **CurrentCommercialOutcome**, **LastConfirmedCTA**, **LastDecisionState** (etapa 52); **CurrentExportPayload**, **LastExportTimestamp**, **SessionStartedAtUtc**, contadores de interação (etapa 53); **CurrentLocalSnapshot**, **LastRestoreTimestamp**, **RestorableSnapshotAvailable** (etapa 54); **CurrentHandoffReport**, **LastHandoffGeneratedAt**, **HasGeneratedHandoffReport** (etapa 55); **CurrentShareableSummary**, **LastShareableSummaryGeneratedAt**, **HasShareableSummary** (etapa 56); **CurrentQualityAssessment**, **LastQualityAssessmentGeneratedAt**, **HasQualityAssessment** (etapa 60); **CurrentImprovementPlan**, **LastImprovementPlanGeneratedAt**, **HasImprovementPlan** (etapa 61); **CurrentPresentationPreset**, **LastAppliedPresentationPresetAt**, **HasPresentationPreset** (etapa 62); **CurrentOnboardingState**, **HasSeenOnboarding**, **IsOnboardingOpen** (etapa 63); **RecommendationSummary** (quando proposta é a recomendada).
- **QuestPresentationNavigationController:** executa ações de navegação (sem polling de input).
- **QuestPresentationInputAdapter:** camada de input (teclado no editor; preparada para input real do Quest) que envia comandos ao nav controller.
- **QuestPresentationHUD:** exibe status (modo, prontidão), linha de prontidão de runtime (Modo teste Quest, Build, Dados, Nav), progresso e bloco em destaque (modo overlay); linhas de debug Input, Runtime/XR, XR Input, Raycast XR, Hover/Dwell, Targeting, **Gaze-like** (ShowGazeLikeDebugLine) e **Contexto hotspot** (ShowHotspotContextDebugLine: hotspot, aberto/fechado, origem, premium, narrativa, passo, upsell passo) e **Comparação** (ShowComparisonDebugLine: activa/fechada, Esq./Dir., melhor exp./upsell/clareza, origem) e **Fechamento** (ShowClosingDebugLine: aberto/fechado, CTA, proposta, pronto, última acção) e **Comercial** (ShowCommercialOutcomeDebugLine: estado, CTA confirmado, proposta, prioridade follow-up) e **Export** (ShowExportDebugLine: payload gerado Sim/Não, timestamp, decisão, CTA, prioridade) e **Snapshot** (ShowPersistenceDebugLine: snapshot salvo, SavedAt, restaurado, sessão, proposta) e **Handoff** (ShowHandoffDebugLine: relatório gerado, timestamp, decisão, prioridade) e **Resumo** (ShowShareableDebugLine: resumo gerado, timestamp, decisão, próxima ação) e **Qualidade** (ShowQualityDebugLine: diagnóstico Sim/Não, timestamp, score, label, principal força, principal fraqueza) e **Melhorias** (ShowImprovementDebugLine: plano Sim/Não, quantidade de recomendações, prioritária, categoria) e **Preset** (ShowPresetDebugLine: preset actual, tipo, foco, duração estimada) e **Onboarding** (ShowOnboardingDebugLine: Aberto/Fechado, preset, proposta, pronto para iniciar).
