# BBPA Physics Bridge · API 1.0

The supplied register/mount contract is implemented in bridge.js. It mounts the real interactive physics lab in an isolated iframe, rather than creating a static mockup. The standalone app continues to work.

## Host usage
Copy the app files into the same origin as the portal for the simplest deployment, or load bridge.js from the private Physics Lab origin when that origin permits framing and script access. Its iframe URL is derived from the script's source URL.

```html
<div id="physics-panel"></div>
<script src="/physics/bridge.js"></script>
<script>
(async () => {
  const dispose = await window.BBPAPhysicsBridge.mount(
    document.getElementById('physics-panel'),
    { skill: 'draw', coachId: 'approved-coach-id', playerName: 'Justin', verified: false }
  );
  // Call dispose() when closing the panel or leaving the host route.
})();
</script>
```

If the host already defines window.BBPAPhysicsBridge.register, the script registers this adapter with that existing registry instead of replacing it:

```js
window.BBPAPhysicsBridge.register({
  apiVersion: '1.0',
  mount: async (element, context) => { /* actual implementation in bridge.js */ }
});
```

A host-defined registry is responsible for storing the adapter and invoking its mount method. An unsupported API version is rejected by the included fallback registry.

## Context boundary
- skill accepts stop, follow, draw; unsupported values become stop.
- coachId and playerName are optional text, capped at 80 characters and rendered using textContent. Coach ID is only a display/context hook; existing artwork is not automatically resolved.
- verified is always forced to false, including when the host passes true.
- Extra context properties are discarded. Context is not saved to storage, URLs, or assessment records.
- The only messages are readiness, sanitized display context, mount acknowledgment and frame height. No simulated outcomes or scores are sent back to the host.
- Both sides check event origin and event source. Context is sent only after the real child app announces readiness; a sign-in wall does not receive player context.

## Lifecycle
await mount(element, context) resolves to an idempotent synchronous cleanup function after the child accepts context. Cleanup removes the parent message listener and iframe. Removing the iframe destroys its document, event listeners and requestAnimationFrame loop. The child also disconnects its size observer and message handler on pagehide. A second mount into the same element cancels the first. Host content outside the inserted iframe is preserved. Multiple elements can mount independently.

If the app does not initialize within 15 seconds, the promise rejects and the frame/listener are removed. A private sign-in wall, blocked cross-origin framing, blocked script delivery or third-party cookie policy can prevent embedding. The bridge does not bypass authentication or change sharing. Sign in directly to the private lab first, or deploy the app source alongside the actual portal. Cross-origin portal integration is not yet validated; the bundled same-origin integration.html demonstrates the contract.

## Demonstration
integration.html provides Mount Stop, Mount Draw, Mount Follow and Unmount. It deliberately sends verified:true to demonstrate that the app still displays Unverified. This is a test host, not an integration into an existing BBPA portal. No existing portal or assessment system has been modified.
