diff --git a/.gitignore b/.gitignore
index 2dcd2d0..ba0fedb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ backups/
interface.ini
settings.json
*.bak*
+publish/
diff --git a/README.md b/README.md
index 0468da1..0bd3a28 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,7 @@
- [Troubleshooting](#troubleshooting)
- [Known issues](#known-issues)
- [Building from source](#building-from-source)
+- [Linux and SteamOS handhelds (AppImage)](#linux-and-steamos-handhelds-appimage)
- [License](#license)
- [Disclaimer](#disclaimer)
- [Credits](#credits)
@@ -263,6 +264,52 @@ contain (`.git\info\banned-words.txt`, one per line) and does not run without it
+## Linux and SteamOS handhelds (AppImage)
+
+
+Build an AppImage for Steam Deck, Legion Go, ROG Ally (Bazzite/SteamOS) or desktop Linux
+
+The game runtime is cross-platform (SDL2, OpenGL, OpenAL through Silk.NET), so the port also runs natively on
+Linux, packaged as a single self-contained AppImage (no .NET install needed on the device). Tested on a Legion Go 2
+running Bazzite (Radeon 890M, OpenGL 4.6, Game Mode through Steam).
+
+**Build it** (on any x86_64 Linux machine or WSL, from your own disc image):
+
+```
+git clone --recurse-submodules
+cd KnowYourRoleRecomp
+
+# with Docker (no local .NET SDK needed)
+docker build -t kyr-linux tools/linux
+docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/src" -v "/path/to/your/disc:/disc:ro" \
+ kyr-linux "/disc/WWF SmackDown! 2 - Know Your Role (USA).cue"
+
+# or with the .NET 10 SDK installed
+tools/linux/build-appimage.sh "/path/to/your/disc/WWF SmackDown! 2 - Know Your Role (USA).cue"
+```
+
+This writes `dist/KnowYourRole-x86_64.AppImage`. It contains code generated from **your** disc: keep it to yourself.
+
+**On the device**: copy the AppImage anywhere (for example `~/Games/KnowYourRole/`) and add it to Steam as a
+non-Steam game (no Proton / compatibility tool). On first start pick your `.cue` in the game's disc picker, or
+point it at the disc beforehand:
+
+```
+mkdir -p ~/.local/share/KnowYourRoleRecomp
+echo "/path/to/WWF SmackDown! 2 - Know Your Role (USA).cue" > ~/.local/share/KnowYourRoleRecomp/disc.txt
+```
+
+Saves, settings and logs live in `~/.local/share/KnowYourRoleRecomp/`.
+
+**Handheld-friendly defaults**:
+- **Widescreen**: *Settings > Display > Widescreen* (`auto`, `off`, `16:9`, `16:10`). `auto` (default) matches the
+ screen, so a 16:10 handheld gets a 16:10 picture: 3D is rendered with a wider field of view instead of being
+ stretched, while the HUD, menus and videos keep their original 4:3 proportions, centred. Objects right at the edges
+ may pop in, since the game culls for a 4:3 view.
+- **Menu bar hidden** by default. Toggle it with **F1**, or **L3 + R3** on player 1's controller.
+
+
+
## License
The port's code is MIT licensed ([LICENSE](LICENSE)). RecompOne is MIT licensed by its authors and included as a
diff --git a/RecompOne b/RecompOne
index d54b944..58c945c 160000
--- a/RecompOne
+++ b/RecompOne
@@ -1 +1 @@
-Subproject commit d54b944fd3a7e3eaaac4cd7e62ea460440c3f9e7
+Subproject commit 58c945c0b1ab0c45bb6a8f9eb6861b7ae2e4790b
diff --git a/tools/linux/AppRun b/tools/linux/AppRun
new file mode 100755
index 0000000..5c5f6f7
--- /dev/null
+++ b/tools/linux/AppRun
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Know Your Role Recomp - AppImage launcher (Linux / SteamOS-like handhelds).
+#
+# Saves, settings and logs live in ${XDG_DATA_HOME:-~/.local/share}/KnowYourRoleRecomp (an AppImage is read-only).
+# Disc: the first argument, else $KYR_DISC, else the path in /disc.txt. The runtime validates the disc from
+# settings.json (CdPath), so a known disc is written there while CdPath is empty; with no disc known, the game shows
+# its own disc picker and remembers the choice.
+HERE="$(dirname "$(readlink -f "$0")")"
+DATA="${XDG_DATA_HOME:-$HOME/.local/share}/KnowYourRoleRecomp"
+mkdir -p "$DATA/logs"
+export RECOMPONE_DATA_DIR="${RECOMPONE_DATA_DIR:-$DATA}"
+export RECOMPONE_LOG_DIR="${RECOMPONE_LOG_DIR:-$DATA/logs}"
+
+DISC="${1:-${KYR_DISC:-}}"
+[ -z "$DISC" ] && [ -f "$DATA/disc.txt" ] && DISC="$(cat "$DATA/disc.txt")"
+
+S="$DATA/settings.json"
+if [ -n "$DISC" ] && [ -f "$S" ] && grep -q '"CdPath": ""' "$S"; then
+ ESC="$(printf '%s' "$DISC" | sed 's/[\\"]/\\&/g; s/[&|]/\\&/g')"
+ sed -i "s|\"CdPath\": \"\"|\"CdPath\": \"$ESC\"|" "$S"
+fi
+
+cd "$DATA"
+if [ -n "$DISC" ]; then
+ exec "$HERE/usr/bin/KnowYourRole.Game" "$DISC"
+else
+ exec "$HERE/usr/bin/KnowYourRole.Game"
+fi
diff --git a/tools/linux/Dockerfile b/tools/linux/Dockerfile
new file mode 100755
index 0000000..49d0ca4
--- /dev/null
+++ b/tools/linux/Dockerfile
@@ -0,0 +1,24 @@
+# Build environment for the Linux AppImage (tools/linux/build-appimage.sh), so no local .NET SDK is needed.
+#
+# docker build -t kyr-linux tools/linux
+# docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/src" -v "/path/to/your/disc:/disc:ro" \
+# kyr-linux /disc/YourDisc.cue
+#
+# The repository is mounted at /src (clone it with --recurse-submodules first); the AppImage lands in dist/.
+FROM mcr.microsoft.com/dotnet/sdk:10.0
+
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends git curl python3 file ca-certificates \
+ && rm -rf /var/lib/apt/lists/* \
+ && git config --system --add safe.directory '*'
+
+# writable homes for dotnet/NuGet when running as the host user
+ENV HOME=/tmp/home \
+ DOTNET_CLI_HOME=/tmp/home \
+ NUGET_PACKAGES=/tmp/home/.nuget/packages \
+ DOTNET_CLI_TELEMETRY_OPTOUT=1 \
+ DOTNET_NOLOGO=1
+RUN mkdir -p /tmp/home && chmod 1777 /tmp/home
+
+WORKDIR /src
+ENTRYPOINT ["bash", "tools/linux/build-appimage.sh"]
diff --git a/tools/linux/build-appimage.sh b/tools/linux/build-appimage.sh
new file mode 100755
index 0000000..5d65bcd
--- /dev/null
+++ b/tools/linux/build-appimage.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+# Builds a Linux AppImage of Know Your Role Recomp from your own disc image, for desktop Linux and SteamOS-like
+# handhelds (Steam Deck, Legion Go, ROG Ally on Bazzite, ...).
+#
+# tools/linux/build-appimage.sh [path/to/KnowYourRole.cue]
+#
+# Needs: the .NET 10 SDK (dotnet on PATH or in ~/.dotnet), git, curl, python3, and x86_64 Linux (WSL works).
+# Output: dist/KnowYourRole-x86_64.AppImage. It contains code generated from YOUR disc: do not share it.
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
+cd "$ROOT"
+export PATH="$HOME/.dotnet:$PATH" DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1
+command -v dotnet >/dev/null || { echo "dotnet not found: install the .NET 10 SDK (https://dot.net)"; exit 1; }
+
+if [ $# -ge 1 ]; then
+ CUE="$(readlink -f "$1")"
+ mkdir -p disc
+ # the config expects disc/KnowYourRole.cue; write a cue pointing at the given image's .bin
+ BIN="$(dirname "$CUE")/$(sed -n 's/^FILE "\(.*\)".*/\1/p' "$CUE" | head -1)"
+ [ -f "$BIN" ] || BIN="$(find "$(dirname "$CUE")" -maxdepth 1 -iname '*.bin' | head -1)"
+ [ -f "$BIN" ] || { echo "no .bin found next to $CUE"; exit 1; }
+ ln -sf "$BIN" disc/KnowYourRole.bin
+ printf 'FILE "KnowYourRole.bin" BINARY\r\n TRACK 01 MODE2/2352\r\n INDEX 01 00:00:00\r\n' > disc/KnowYourRole.cue
+fi
+[ -f disc/KnowYourRole.cue ] || { echo "put your disc at disc/KnowYourRole.cue (+ .bin) or pass the .cue path"; exit 1; }
+
+echo "== recompiling from your disc"
+[ -f RecompOne/RecompOne.sln ] || git submodule update --init --recursive
+dotnet build RecompOne/RecompOne.Recompiler -c Release -v quiet
+(cd RecompOne/RecompOne.Recompiler && dotnet run -c Release --no-build -- ../../config/KnowYourRole.json)
+
+echo "== publishing (self-contained linux-x64)"
+rm -rf publish
+dotnet publish KnowYourRole.csproj -c Release -r linux-x64 --self-contained true -m:1 -o publish -v quiet
+
+echo "== packaging AppImage"
+APPDIR="$ROOT/dist/AppDir"
+rm -rf "$APPDIR" && mkdir -p "$APPDIR/usr/bin"
+cp -a publish/. "$APPDIR/usr/bin/"
+rm -f "$APPDIR"/usr/bin/*.pdb
+chmod +x "$APPDIR/usr/bin/KnowYourRole.Game"
+install -m 755 tools/linux/AppRun "$APPDIR/AppRun"
+install -m 644 tools/linux/knowyourrole.desktop "$APPDIR/knowyourrole.desktop"
+# icon: the largest PNG inside Launcher/app.ico
+python3 - "$ROOT/Launcher/app.ico" "$APPDIR/knowyourrole.png" <<'PY'
+import struct, sys
+d = open(sys.argv[1], 'rb').read(); best = None
+for i in range(struct.unpack_from(' best[0]): best = (w or 256, blob)
+open(sys.argv[2], 'wb').write(best[1])
+PY
+
+TOOL="$ROOT/dist/appimagetool"
+[ -x "$TOOL" ] || { curl -fsSL -o "$TOOL" https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage; chmod +x "$TOOL"; }
+# --appimage-extract-and-run: works without FUSE (containers, WSL)
+ARCH=x86_64 "$TOOL" --appimage-extract-and-run "$APPDIR" "$ROOT/dist/KnowYourRole-x86_64.AppImage" >/dev/null
+echo "== done: dist/KnowYourRole-x86_64.AppImage"
+echo " first run: pick your .cue in the game's disc picker (or: echo /path/to/game.cue > ~/.local/share/KnowYourRoleRecomp/disc.txt)"
diff --git a/tools/linux/knowyourrole.desktop b/tools/linux/knowyourrole.desktop
new file mode 100755
index 0000000..94222b1
--- /dev/null
+++ b/tools/linux/knowyourrole.desktop
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Type=Application
+Name=WWF SmackDown! 2 Know Your Role (Recomp)
+Exec=AppRun
+Icon=knowyourrole
+Categories=Game;
+Terminal=false