diff --git a/AGENTS.md b/AGENTS.md
index c2750ec..34da7fa 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -158,7 +158,9 @@ Established 2026-08-25, and it decides more than it looks like:
silently never matches — while the phone's own camera app, which tries
both, does. The scanner asks for `Intents.Scan.MIXED_SCAN`, which
alternates normal and inverted frames; keep it that way rather than
- making the server dictate the colours.
+ making the server dictate the colours. `EnrollmentScanActivity` also
+ turns off the library's 10% framing-rect inset (it decodes only what is
+ inside it) and its laser/result-point decorations.
- **AGP 9 refuses `Provider`s in the source-set API**: generated sources go
through `androidComponents.onVariants { it.sources.java?.addGenerated
SourceDirectory(task, Task::outputDir) }`, which also carries the task
diff --git a/app/androidApp/src/main/AndroidManifest.xml b/app/androidApp/src/main/AndroidManifest.xml
index 6e0ac26..a334a2e 100644
--- a/app/androidApp/src/main/AndroidManifest.xml
+++ b/app/androidApp/src/main/AndroidManifest.xml
@@ -40,5 +40,18 @@
+
+
+
diff --git a/app/androidApp/src/main/kotlin/com/example/aiapp/EnrollmentScanActivity.kt b/app/androidApp/src/main/kotlin/com/example/aiapp/EnrollmentScanActivity.kt
new file mode 100644
index 0000000..bd91ac1
--- /dev/null
+++ b/app/androidApp/src/main/kotlin/com/example/aiapp/EnrollmentScanActivity.kt
@@ -0,0 +1,36 @@
+package com.example.aiapp
+
+import android.view.View
+import com.journeyapps.barcodescanner.CaptureActivity
+import com.journeyapps.barcodescanner.DecoratedBarcodeView
+
+/**
+ * The screen behind the Settings screen's "Scan QR code" button:
+ * zxing-android-embedded's capture activity with two of its defaults taken
+ * off, both because they put work on the person holding the phone.
+ *
+ * - **It decodes only the framing rectangle**, which
+ * `CameraPreview.marginFraction` insets by 10% from every edge and
+ * `DecoderThread` crops each frame to before the decoder sees it. A code
+ * that fills the viewfinder keeps decoding -- measured, not assumed --
+ * but it does so having spent its quiet zone and margin for error on
+ * the crop, and anything further out is simply not looked at. Decoding
+ * the whole preview costs nothing and means the framing is never the
+ * user's problem.
+ * - **It decorates the preview** with a red laser line and the dots the
+ * detector scatters wherever it finds a candidate pattern. That is the
+ * library's house style; a plain preview is ours.
+ *
+ * Orientation is left to the sensor rather than pinned to landscape as the
+ * library's own manifest entry pins it, so the phone can be held whichever
+ * way the code is in front of it.
+ */
+class EnrollmentScanActivity : CaptureActivity() {
+ override fun initializeContent(): DecoratedBarcodeView {
+ val view = super.initializeContent()
+ view.barcodeView.marginFraction = 0.0
+ view.viewFinder.visibility = View.GONE
+ view.statusView.visibility = View.GONE
+ return view
+ }
+}
diff --git a/app/androidApp/src/main/kotlin/com/example/aiapp/SettingsScreen.kt b/app/androidApp/src/main/kotlin/com/example/aiapp/SettingsScreen.kt
index af406b9..e99820c 100644
--- a/app/androidApp/src/main/kotlin/com/example/aiapp/SettingsScreen.kt
+++ b/app/androidApp/src/main/kotlin/com/example/aiapp/SettingsScreen.kt
@@ -87,6 +87,9 @@ fun SettingsScreen(
scanLauncher.launch(
ScanOptions()
.setDesiredBarcodeFormats(ScanOptions.QR_CODE)
+ .setCaptureActivity(EnrollmentScanActivity::class.java)
+ // Follow the phone, not the library's landscape pin.
+ .setOrientationLocked(false)
// Decode both polarities: ZXing otherwise looks only for a
// dark code on a light ground, and ai-server's QR is block
// characters in the terminal's foreground colour -- so on a