diff --git a/src/components/online/RotatePrompt.tsx b/src/components/online/RotatePrompt.tsx
new file mode 100644
index 0000000..d890a33
--- /dev/null
+++ b/src/components/online/RotatePrompt.tsx
@@ -0,0 +1,58 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { RotateCcw } from "lucide-react";
+import { useI18n } from "@/lib/i18n";
+
+/**
+ * Landscape-first nudge for the game table. The Hokm table plays best wide
+ * (UNO-style), so when a phone is held in portrait we cover the table with a
+ * "rotate your device" prompt. iOS/Safari can't be force-rotated, so this is the
+ * reliable cross-platform path. A "play anyway" escape hatch avoids trapping
+ * users whose OS rotation-lock is on.
+ */
+export function RotatePrompt() {
+ const { t } = useI18n();
+ const [portrait, setPortrait] = useState(false);
+ const [dismissed, setDismissed] = useState(false);
+
+ useEffect(() => {
+ const check = () => {
+ const isPortrait = window.innerHeight > window.innerWidth;
+ // Only nudge on phone-sized screens (smaller side ≤ ~820px); desktops/wide
+ // tablets are already roomy enough.
+ const isPhone = Math.min(window.innerWidth, window.innerHeight) <= 820;
+ setPortrait(isPortrait && isPhone);
+ };
+ check();
+ window.addEventListener("resize", check);
+ window.addEventListener("orientationchange", check);
+ return () => {
+ window.removeEventListener("resize", check);
+ window.removeEventListener("orientationchange", check);
+ };
+ }, []);
+
+ if (!portrait || dismissed) return null;
+
+ return (
+
+
+
{t("rotate.title")}
+
{t("rotate.desc")}
+
+
+
+
+ );
+}
diff --git a/src/components/screens/GameScreen.tsx b/src/components/screens/GameScreen.tsx
index 4b2872e..cf87c57 100644
--- a/src/components/screens/GameScreen.tsx
+++ b/src/components/screens/GameScreen.tsx
@@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from "react";
import { GameTable } from "@/components/GameTable";
import { PostMatchRewardsModal } from "@/components/online/PostMatchRewardsModal";
import { MatchIntroOverlay } from "@/components/online/MatchIntroOverlay";
+import { RotatePrompt } from "@/components/online/RotatePrompt";
import { useGameStore } from "@/lib/game-store";
import { useSessionStore } from "@/lib/session-store";
import { useUIStore } from "@/lib/ui-store";
@@ -58,6 +59,20 @@ export function GameScreen() {
};
}, []);
+ // Landscape-first table: best-effort lock to landscape on Android/PWA (iOS &
+ // desktop reject it — harmless). Restored to portrait/auto when leaving.
+ useEffect(() => {
+ const o = (screen as unknown as { orientation?: { lock?: (m: string) => Promise; unlock?: () => void } }).orientation;
+ o?.lock?.("landscape").catch(() => {});
+ return () => {
+ try {
+ o?.unlock?.();
+ } catch {
+ /* unsupported — ignore */
+ }
+ };
+ }, []);
+
const notifyAchievements = (r: RewardResult) => {
for (const a of r.newAchievements)
pushNotification({
@@ -142,6 +157,9 @@ export function GameScreen() {
onForfeit={canForfeit ? () => useGameStore.getState().forfeit() : undefined}
/>
+ {/* Landscape-first: nudge to rotate when held in portrait on a phone */}
+
+
{/* UNO-style "players joining the table" intro (online matches, once) */}
{introPending && mode === "online" && (
diff --git a/src/lib/i18n.tsx b/src/lib/i18n.tsx
index e638e82..5145013 100644
--- a/src/lib/i18n.tsx
+++ b/src/lib/i18n.tsx
@@ -327,6 +327,9 @@ const fa: Dict = {
"settings.audio": "تنظیمات صدا",
"settings.sound": "افکت صدا",
"settings.music": "موسیقی پسزمینه",
+ "rotate.title": "گوشی را بچرخانید",
+ "rotate.desc": "برای تجربهٔ بهترِ میز بازی، گوشی را افقی (چرخانده) نگه دارید.",
+ "rotate.anyway": "همینطور عمودی بازی میکنم",
"settings.musicStyle": "سبک موسیقی",
"settings.trackSantoor": "سنتی (سنتور)",
"settings.trackPlayful": "شاد",
@@ -663,6 +666,9 @@ const en: Dict = {
"settings.audio": "Audio",
"settings.sound": "Sound effects",
"settings.music": "Background music",
+ "rotate.title": "Rotate your phone",
+ "rotate.desc": "The table plays best in landscape — turn your phone sideways for the full experience.",
+ "rotate.anyway": "Keep playing in portrait",
"settings.musicStyle": "Music style",
"settings.trackSantoor": "Traditional (Santoor)",
"settings.trackPlayful": "Playful",