diff --git a/Cargo.lock b/Cargo.lock
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,22 @@
 # It is not intended for manual editing.
 version = 4
 
+[[package]]
+name = "ab_glyph"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
+
 [[package]]
 name = "accesskit"
 version = "0.25.1"
@@ -195,6 +211,23 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
 
+[[package]]
+name = "android_log-sys"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d"
+
+[[package]]
+name = "android_logger"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3"
+dependencies = [
+ "android_log-sys",
+ "env_filter",
+ "log",
+]
+
 [[package]]
 name = "android_system_properties"
 version = "0.1.6"
@@ -1557,6 +1590,16 @@ dependencies = [
  "syn 2.0.119",
 ]
 
+[[package]]
+name = "env_filter"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2"
+dependencies = [
+ "log",
+ "regex",
+]
+
 [[package]]
 name = "epaint"
 version = "0.37.0"
@@ -2871,7 +2914,7 @@ dependencies = [
  "skrifa 0.42.1",
  "smallvec",
  "subsetter",
- "tiny-skia-path",
+ "tiny-skia-path 0.12.0",
  "xmp-writer",
  "yoke",
  "zune-jpeg",
@@ -3575,6 +3618,15 @@ dependencies = [
  "windows-sys 0.61.2",
 ]
 
+[[package]]
+name = "owned_ttf_parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
+dependencies = [
+ "ttf-parser",
+]
+
 [[package]]
 name = "parking"
 version = "2.2.1"
@@ -4557,6 +4609,19 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
+[[package]]
+name = "sctk-adwaita"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec"
+dependencies = [
+ "ab_glyph",
+ "log",
+ "memmap2",
+ "smithay-client-toolkit 0.19.2",
+ "tiny-skia",
+]
+
 [[package]]
 name = "security-framework"
 version = "3.7.0"
@@ -5368,6 +5433,31 @@ dependencies = [
  "time-core",
 ]
 
+[[package]]
+name = "tiny-skia"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "bytemuck",
+ "cfg-if",
+ "log",
+ "tiny-skia-path 0.11.4",
+]
+
+[[package]]
+name = "tiny-skia-path"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
+dependencies = [
+ "arrayref",
+ "bytemuck",
+ "strict-num",
+]
+
 [[package]]
 name = "tiny-skia-path"
 version = "0.12.0"
@@ -5600,6 +5690,12 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
+[[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+
 [[package]]
 name = "type-map"
 version = "0.5.1"
@@ -6547,6 +6643,7 @@ dependencies = [
  "raw-window-handle",
  "redox_syscall 0.4.1",
  "rustix 0.38.44",
+ "sctk-adwaita",
  "smithay-client-toolkit 0.19.2",
  "smol_str",
  "tracing",
@@ -6680,6 +6777,19 @@ dependencies = [
  "zbus",
 ]
 
+[[package]]
+name = "wordcraft-android"
+version = "0.11.0"
+dependencies = [
+ "android_logger",
+ "eframe",
+ "egui",
+ "log",
+ "winit",
+ "wordcraft-engine",
+ "wordcraft-ui-egui",
+]
+
 [[package]]
 name = "wordcraft-cli"
 version = "0.11.0"
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -153,6 +153,8 @@ wordcraft-cli run --script job.json                 # the same from a file: comm
 
 Web: `cd apps/wordcraft-web && trunk serve`, then open <http://127.0.0.1:8771/?sample>.
 
+Experimental Android ARM64: build the native APK from the full workspace with the [Android build guide](apps/wordcraft-android/README.md).
+
 ## For agents: CLI and MCP
 
 WordCraft was designed to be driven by people *and* by AI agents.
diff --git a/apps/wordcraft-android/AndroidManifest.xml b/apps/wordcraft-android/AndroidManifest.xml
new file mode 100644
--- /dev/null
+++ b/apps/wordcraft-android/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ai.storyteller.wordcraft">
+    <uses-sdk android:minSdkVersion="26" android:targetSdkVersion="35" />
+    <application
+        android:label="WordCraft"
+        android:hasCode="false"
+        android:theme="@android:style/Theme.Material.Light.NoActionBar">
+        <activity
+            android:name="android.app.NativeActivity"
+            android:label="WordCraft"
+            android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|uiMode"
+            android:exported="true"
+            android:theme="@android:style/Theme.Material.Light.NoActionBar">
+            <meta-data android:name="android.app.lib_name" android:value="wordcraft_android" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/apps/wordcraft-android/Cargo.toml b/apps/wordcraft-android/Cargo.toml
new file mode 100644
--- /dev/null
+++ b/apps/wordcraft-android/Cargo.toml
@@ -0,0 +1,48 @@
+[package]
+name = "wordcraft-android"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+publish = false
+description = "WordCraft native Android app"
+[lints.rust]
+unsafe_code = "allow"
+[lints.clippy]
+dbg_macro = "warn"
+todo = "warn"
+too_many_arguments = "allow"
+type_complexity = "allow"
+[lib]
+crate-type = ["cdylib"]
+[dependencies]
+wordcraft-engine = { workspace = true }
+wordcraft-ui-egui = { workspace = true }
+eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit"] }
+egui = { workspace = true }
+winit = { version = "0.30", features = ["android-native-activity"] }
+android_logger = "0.15.1"
+log = { workspace = true }
+[package.metadata.android]
+package = "ai.storyteller.wordcraft"
+apk_name = "wordcraft"
+use_aapt2 = false
+manifest_override = "AndroidManifest.xml"
+build_targets = ["aarch64-linux-android"]
+include_cplusplus_shared = true
+[package.metadata.android.sdk]
+min_sdk_version = 26
+target_sdk_version = 35
+
+[package.metadata.android.application]
+has_code = false
+
+[[package.metadata.android.application.activity]]
+name = "android.app.NativeActivity"
+
+[[package.metadata.android.application.activity.meta_data]]
+name = "android.app.lib_name"
+value = "wordcraft_android"
+
+[[package.metadata.android.application.activity.intent_filter]]
+actions = ["android.intent.action.MAIN"]
+categories = ["android.intent.category.LAUNCHER"]
diff --git a/apps/wordcraft-android/README.md b/apps/wordcraft-android/README.md
new file mode 100644
--- /dev/null
+++ b/apps/wordcraft-android/README.md
@@ -0,0 +1,76 @@
+# WordCraft para Android ARM64 (experimental)
+
+Este é o alvo Android nativo do WordCraft. Ele reutiliza o motor e a interface egui do workspace completo, sem WebView. O APK é compilado para `arm64-v8a` (`aarch64-linux-android`) e requer Android 8.0/API 26 ou posterior.
+
+## Compilar do zero no Linux ou WSL 2
+
+### 1. Instale as ferramentas
+
+Instale Rust pelo [rustup](https://rustup.rs/), o **JDK 21 completo** (precisa incluir `java`, `javac` e `keytool`) e as Android Command-line Tools. Com `ANDROID_HOME` apontando para o SDK, aceite a licença Android interativamente e instale as versões usadas por este alvo:
+
+```sh
+export ANDROID_HOME="$HOME/Android/Sdk"
+export ANDROID_SDK_ROOT="$ANDROID_HOME"
+"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --licenses
+"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --install \
+  "platform-tools" \
+  "platforms;android-35" \
+  "build-tools;35.0.0" \
+  "ndk;30.0.16248370"
+export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/30.0.16248370"
+```
+
+O projeto fixa o API de compilação em 35, o Build Tools em 35.0.0 e o NDK em 30.0.16248370. As licenças precisam ser aceitas no computador antes do primeiro build.
+
+### 2. Clone a branch Android
+
+```sh
+git clone --branch android-native-arm64 https://github.com/anonymous3210/wordcraft.git
+cd wordcraft
+```
+
+Se o repositório já estiver clonado, entre na pasta e faça checkout da branch `android-native-arm64`.
+
+### 3. Gere o APK
+
+```sh
+./apps/wordcraft-android/build.sh
+```
+
+O script verifica as ferramentas, instala `cargo-apk2` 1.5.0 se estiver faltando, adiciona o target Rust ARM64 e compila usando o `Cargo.lock` do repositório. O arquivo de saída é:
+
+```text
+target/debug/apk/wordcraft.apk
+```
+
+Para instalar num aparelho ARM64 conectado com depuração USB:
+
+```sh
+adb install -r target/debug/apk/wordcraft.apk
+```
+
+Também é possível executar manualmente, sem o script, depois de preparar as ferramentas:
+
+```sh
+rustup target add aarch64-linux-android
+cargo install cargo-apk2 --version 1.5.0 --locked
+cargo apk2 build --manifest-path apps/wordcraft-android/Cargo.toml
+```
+
+## Build release
+
+Uma build release otimizada exige uma chave de assinatura própria. Guarde o arquivo e a senha fora do repositório e não reutilize a chave de depuração:
+
+```sh
+export CARGO_APK_RELEASE_KEYSTORE="$HOME/.android/wordcraft-release.jks"
+read -rsp "Senha do keystore: " CARGO_APK_RELEASE_KEYSTORE_PASSWORD; echo
+export CARGO_APK_RELEASE_KEYSTORE_PASSWORD
+cargo apk2 build --release --manifest-path apps/wordcraft-android/Cargo.toml
+unset CARGO_APK_RELEASE_KEYSTORE CARGO_APK_RELEASE_KEYSTORE_PASSWORD
+```
+
+O APK otimizado sai em `target/release/apk/wordcraft.apk`. O APK gerado pelo script padrão é de depuração e serve para instalação/teste; não o publique numa loja.
+
+## Escopo atual
+
+A inicialização, o renderizador e a interface são nativos. Ainda não estão conectados o seletor Android de documentos nem os serviços de impressão e compartilhamento. A interface existente foi desenhada para ribbon de desktop, portanto a ergonomia precisa ser validada num aparelho. Esta integração compila e empacota, mas não foi testada em dispositivo físico.
diff --git a/apps/wordcraft-android/build.sh b/apps/wordcraft-android/build.sh
new file mode 100755
--- /dev/null
+++ b/apps/wordcraft-android/build.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
+cd "$ROOT"
+
+fail() {
+  printf 'ERRO: %s\n' "$1" >&2
+  exit 1
+}
+
+[[ "$(uname -s)" == "Linux" ]] || fail "Este script suporta Linux e WSL. No Windows, use WSL 2 com a toolchain Android Linux."
+command -v cargo >/dev/null 2>&1 || fail "Rust/Cargo não encontrado. Instale Rust pelo rustup: https://rustup.rs/"
+command -v rustup >/dev/null 2>&1 || fail "rustup não encontrado. Instale Rust pelo rustup: https://rustup.rs/"
+command -v java >/dev/null 2>&1 || fail "Java não encontrado. Instale JDK 21 (incluindo java, javac e keytool)."
+command -v javac >/dev/null 2>&1 || fail "javac não encontrado. Instale o JDK completo, não apenas o Java Runtime."
+command -v keytool >/dev/null 2>&1 || fail "keytool não encontrado. Instale o JDK completo."
+
+export ANDROID_HOME="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}}"
+export ANDROID_SDK_ROOT="$ANDROID_HOME"
+export ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$ANDROID_HOME/ndk/30.0.16248370}"
+export NDK_HOME="$ANDROID_NDK_HOME"
+
+[[ -f "$ANDROID_HOME/platforms/android-35/android.jar" ]] || fail "Android SDK API 35 ausente. Instale: platforms;android-35"
+[[ -x "$ANDROID_HOME/build-tools/35.0.0/aapt" ]] || fail "Android Build Tools 35.0.0 ausentes. Instale: build-tools;35.0.0"
+[[ -x "$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android26-clang" ]] || fail "NDK 30.0.16248370 ausente ou inválido em $ANDROID_NDK_HOME"
+
+if ! command -v cargo-apk2 >/dev/null 2>&1; then
+  printf 'Instalando cargo-apk2 1.5.0...\n'
+  cargo install cargo-apk2 --version 1.5.0 --locked
+fi
+
+rustup target add aarch64-linux-android
+cargo apk2 build --manifest-path "$ROOT/apps/wordcraft-android/Cargo.toml"
+
+APK="$ROOT/target/debug/apk/wordcraft.apk"
+[[ -s "$APK" ]] || fail "O build terminou sem produzir $APK"
+printf '\nAPK criado com sucesso:\n%s\n' "$APK"
diff --git a/apps/wordcraft-android/src/lib.rs b/apps/wordcraft-android/src/lib.rs
new file mode 100644
--- /dev/null
+++ b/apps/wordcraft-android/src/lib.rs
@@ -0,0 +1,43 @@
+#![cfg(target_os = "android")]
+
+use eframe::{App, Frame, NativeOptions, egui};
+use winit::platform::android::activity::AndroidApp;
+use wordcraft_engine::Session;
+use wordcraft_ui_egui::{Services, WordApp};
+
+struct AndroidWordCraft(WordApp);
+
+impl App for AndroidWordCraft {
+    fn logic(&mut self, ctx: &egui::Context, _frame: &mut Frame) {
+        self.0.logic(ctx);
+    }
+
+    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut Frame) {
+        self.0.ui(ui);
+    }
+}
+
+#[unsafe(no_mangle)]
+fn android_main(android_app: AndroidApp) {
+    android_logger::init_once(android_logger::Config::default().with_max_level(log::LevelFilter::Info));
+
+    let options =
+        NativeOptions { android_app: Some(android_app), viewport: egui::ViewportBuilder::default().with_title("WordCraft"), ..Default::default() };
+
+    let result = eframe::run_native(
+        "WordCraft",
+        options,
+        Box::new(|_creation_context| {
+            let region = wordcraft_ui_egui::i18n::system_region();
+            let mut session = Session::new(wordcraft_engine::blank_document(&region));
+            session.system_region = region;
+            let mut app = WordApp::new(session, Services::default());
+            app.start_screen();
+            Ok(Box::new(AndroidWordCraft(app)))
+        }),
+    );
+
+    if let Err(error) = result {
+        log::error!("WordCraft Android startup failed: {error}");
+    }
+}
