diff --git a/src/__tests__/GoogleOAuthButton.test.tsx b/src/__tests__/GoogleOAuthButton.test.tsx
index 4606ce5..5e84a46 100644
--- a/src/__tests__/GoogleOAuthButton.test.tsx
+++ b/src/__tests__/GoogleOAuthButton.test.tsx
@@ -3,7 +3,7 @@ import { render, screen, waitFor, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
-// ── Mocks ─────────────────────────────────────────────────────────────────────
+// -- Mocks ---------------------------------------------------------------------
const MockApiError = vi.hoisted(() =>
class extends Error {
@@ -24,11 +24,11 @@ vi.mock('@/lib/api-client', () => ({
ApiError: MockApiError,
}));
-// ── Import after mocks ────────────────────────────────────────────────────────
+// -- Import after mocks --------------------------------------------------------
import { GoogleOAuthButton } from '@/features/auth/GoogleOAuthButton';
-// ── Google GIS mock helpers ───────────────────────────────────────────────────
+// -- Google GIS mock helpers ---------------------------------------------------
function setupGoogleMock() {
const mockPrompt = vi.fn();
@@ -56,7 +56,7 @@ afterEach(() => {
clearGoogleMock();
});
-// ── Tests ─────────────────────────────────────────────────────────────────────
+// -- Tests ---------------------------------------------------------------------
describe('GoogleOAuthButton', () => {
it('renders the Google sign-in button', () => {
diff --git a/src/__tests__/ResetPasswordPage.test.tsx b/src/__tests__/ResetPasswordPage.test.tsx
index 67528b8..93a2ce3 100644
--- a/src/__tests__/ResetPasswordPage.test.tsx
+++ b/src/__tests__/ResetPasswordPage.test.tsx
@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { ResetPasswordPage } from '@/features/auth/ResetPasswordPage';
-// ── Mocks ─────────────────────────────────────────────────────────────────────
+// -- Mocks ---------------------------------------------------------------------
const MockApiError = vi.hoisted(() =>
class extends Error {
@@ -36,7 +36,7 @@ vi.mock('@/lib/routes', () => ({
ROUTES: { login: '/login', dashboard: '/dashboard' },
}));
-// ── Helpers ───────────────────────────────────────────────────────────────────
+// -- Helpers -------------------------------------------------------------------
function renderPage(token = 'valid-token') {
return render();
@@ -47,7 +47,7 @@ beforeEach(() => {
mockResetPassword.mockResolvedValue(undefined);
});
-// ── Tests ─────────────────────────────────────────────────────────────────────
+// -- Tests ---------------------------------------------------------------------
describe('ResetPasswordPage', () => {
it('renders the form with disabled submit when token is missing', () => {
diff --git a/src/__tests__/VerifyEmailConfirmPage.test.tsx b/src/__tests__/VerifyEmailConfirmPage.test.tsx
index fe87de7..f2c8a2a 100644
--- a/src/__tests__/VerifyEmailConfirmPage.test.tsx
+++ b/src/__tests__/VerifyEmailConfirmPage.test.tsx
@@ -3,7 +3,7 @@ import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
-// ── Mocks ─────────────────────────────────────────────────────────────────────
+// -- Mocks ---------------------------------------------------------------------
const MockApiError = vi.hoisted(() =>
class extends Error {
@@ -35,7 +35,7 @@ vi.mock('@/lib/routes', () => ({
ROUTES: { dashboard: '/dashboard', login: '/login', verifyEmail: '/verify-email' },
}));
-// ── Import after mocks ────────────────────────────────────────────────────────
+// -- Import after mocks --------------------------------------------------------
import { VerifyEmailConfirmPage } from '@/features/auth/VerifyEmailConfirmPage';
@@ -48,7 +48,7 @@ afterEach(() => {
vi.useRealTimers();
});
-// ── Tests ─────────────────────────────────────────────────────────────────────
+// -- Tests ---------------------------------------------------------------------
describe('VerifyEmailConfirmPage', () => {
it('shows verifying spinner immediately', () => {
diff --git a/src/__tests__/mobileResponsive.test.ts b/src/__tests__/mobileResponsive.test.ts
new file mode 100644
index 0000000..5f95bdf
--- /dev/null
+++ b/src/__tests__/mobileResponsive.test.ts
@@ -0,0 +1,35 @@
+import { describe, expect, it } from 'vitest';
+import { readFileSync } from 'node:fs';
+import { join } from 'node:path';
+
+const css = readFileSync(join(process.cwd(), 'src/styles/globals.css'), 'utf8');
+
+describe('mobile responsive safety rules', () => {
+ it('keeps app shell and page containers constrained on small screens', () => {
+ expect(css).toContain('max-width: 100vw');
+ expect(css).toContain('.app__main');
+ expect(css).toContain('min-width: 0');
+ expect(css).toContain('@media (max-width: 720px)');
+ expect(css).toContain('.app__sidebar { display: none; }');
+ expect(css).toContain('.bottomnav { display: flex; }');
+ });
+
+ it('stacks major desktop grids and converts wide rows for mobile', () => {
+ expect(css).toContain('.responsive-split');
+ expect(css).toContain('.responsive-sidebar');
+ expect(css).toContain('.responsive-room');
+ expect(css).toContain('grid-template-columns: minmax(0, 1fr) !important');
+ expect(css).toContain('.leaderboard-row');
+ expect(css).toContain('.match-history-row');
+ expect(css).toContain('.friends-row');
+ });
+
+ it('keeps dropdowns, modals, tabs, and toasts inside the viewport', () => {
+ expect(css).toContain('.dropdown-panel');
+ expect(css).toContain('max-width: calc(100vw - 24px)');
+ expect(css).toContain('.modal, .modal-lg');
+ expect(css).toContain('.tabs');
+ expect(css).toContain('overflow-x: auto');
+ expect(css).toContain('.toast-stack');
+ });
+});
diff --git a/src/__tests__/profileApi.test.ts b/src/__tests__/profileApi.test.ts
index 9302665..a76888b 100644
--- a/src/__tests__/profileApi.test.ts
+++ b/src/__tests__/profileApi.test.ts
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
-// ── profileApi unit tests ─────────────────────────────────────────────────────
+// -- profileApi unit tests -----------------------------------------------------
// Tests use fetch mocking to verify the API client builds correct requests.
const mockFetch = vi.fn();
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 735a713..7f452a3 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -4,28 +4,37 @@ import { Geist, Geist_Mono } from 'next/font/google';
import './globals.css';
import { ToastProvider } from '@/components/ui/Toast';
import { AuthProvider } from '@/features/auth/AuthProvider';
+import { ThemeProvider } from '@/lib/theme';
const geistSans = Geist({ variable: '--font-geist-sans', subsets: ['latin'] });
const geistMono = Geist_Mono({ variable: '--font-geist-mono', subsets: ['latin'] });
export const metadata: Metadata = {
- title: 'SimPle — Social Gaming Platform',
+ title: 'SimPle - Social Gaming Platform',
description: 'A premium social gaming platform. Play sharp little games with friends, climb leaderboards, and build your profile.',
};
+const themeBootScript = `(function(){try{var s=localStorage.getItem('simple.theme');if(!s){s=(window.matchMedia&&window.matchMedia('(prefers-color-scheme: light)').matches)?'light':'dark';}document.documentElement.setAttribute('data-theme',s);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();`;
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
-
+
- {/* Google Identity Services — loaded once for the whole app */}
+
-
- {children}
-
+
+
+ {children}
+
+
);
diff --git a/src/components/friends/InviteFriendModal.tsx b/src/components/friends/InviteFriendModal.tsx
index 715d951..2a2f5bd 100644
--- a/src/components/friends/InviteFriendModal.tsx
+++ b/src/components/friends/InviteFriendModal.tsx
@@ -58,7 +58,7 @@ export function InviteFriendModal({ open, onClose, preselectedGameId }: Props) {
Game
-
+
{GAMES.slice(0,6).map(g => (