diff --git a/playwright.config.ts b/playwright.config.ts
index 02db5bd47..020df9ead 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -18,28 +18,23 @@ export default defineConfig({
   forbidOnly: !!process.env.CI,
   /* 1 retry catches timing-sensitive tests without hiding consistent issues */
   retries: 1,
-  /* Conservative approach for CI stability - 1 worker ensures each test gets full resources */
-  workers: isCI ? 1 : undefined,
+  workers: 1,
   /* Reporter to use. See https://playwright.dev/docs/test-reporters */
   reporter: 'list',
-  /* Test timeout */
-  timeout: isCI ? 60 * 1000 : 30 * 1000, // 60 seconds in CI, 30 locally
+  timeout: 60 * 1000,
+
+  expect: {
+    timeout: 10 * 1000,
+  },
 
-  /* Global setup - runs once before all tests */
   globalSetup: require.resolve('./tests/global.setup.ts'),
 
-  /* Shared settings for all the projects below. */
   use: {
-    /* Base URL to use in actions like `await page.goto('/')`. */
     baseURL: 'http://localhost:3000',
-
-    /* Collect trace when retrying the failed test. */
+    actionTimeout: 10 * 1000,
+    navigationTimeout: 30 * 1000,
     trace: 'on-first-retry',
-
-    /* Screenshot on failure */
     screenshot: 'only-on-failure',
-
-    /* Video on failure */
     video: 'retain-on-failure',
   },
 
@@ -50,11 +45,17 @@ export default defineConfig({
       name: 'setup',
       testMatch: /auth\.setup\.ts/,
     },
+    // Data setup - creates listings, reports, etc. that other tests depend on
+    {
+      name: 'data-setup',
+      testMatch: /data-setup\.spec\.ts/,
+      dependencies: ['setup'],
+    },
     {
       name: 'chromium',
       use: { ...devices['Desktop Chrome'] },
-      testIgnore: ['**/auth.setup.ts', '**/global.setup.ts'],
-      dependencies: ['setup'], // Ensure auth is set up before running tests
+      testIgnore: ['**/auth.setup.ts', '**/global.setup.ts', '**/data-setup.spec.ts'],
+      dependencies: ['data-setup'],
     },
   ],
 
diff --git a/prisma/migrations/20260414175740_add_vote_change_reversal_trust_action/migration.sql b/prisma/migrations/20260414175740_add_vote_change_reversal_trust_action/migration.sql
new file mode 100644
index 000000000..834b83420
--- /dev/null
+++ b/prisma/migrations/20260414175740_add_vote_change_reversal_trust_action/migration.sql
@@ -0,0 +1,14 @@
+-- AlterEnum
+ALTER TYPE "trust_action" ADD VALUE 'VOTE_CHANGE_REVERSAL';
+
+-- CreateIndex
+CREATE INDEX "CommentVote_userId_nullifiedAt_idx" ON "CommentVote"("userId", "nullifiedAt");
+
+-- CreateIndex
+CREATE INDEX "Vote_userId_nullifiedAt_idx" ON "Vote"("userId", "nullifiedAt");
+
+-- CreateIndex
+CREATE INDEX "pc_listing_comment_votes_userId_nullifiedAt_idx" ON "pc_listing_comment_votes"("userId", "nullifiedAt");
+
+-- CreateIndex
+CREATE INDEX "pc_listing_votes_userId_nullifiedAt_idx" ON "pc_listing_votes"("userId", "nullifiedAt");
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 10cb2f900..1555e5fc4 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -505,6 +505,7 @@ model Vote {
   @@unique([userId, listingId])
   @@index([userId])
   @@index([listingId])
+  @@index([userId, nullifiedAt])
 }
 
 model Comment {
@@ -541,6 +542,7 @@ model CommentVote {
 
   @@unique([userId, commentId])
   @@index([userId])
+  @@index([userId, nullifiedAt])
 }
 
 model ListingCustomFieldValue {
@@ -735,6 +737,7 @@ enum TrustAction {
   ADMIN_ADJUSTMENT_POSITIVE
   ADMIN_ADJUSTMENT_NEGATIVE
   VOTE_NULLIFICATION_REVERSAL
+  VOTE_CHANGE_REVERSAL
 
   @@map("trust_action")
 }
@@ -1123,6 +1126,7 @@ model PcListingVote {
   @@unique([userId, pcListingId])
   @@index([pcListingId])
   @@index([userId])
+  @@index([userId, nullifiedAt])
   @@map("pc_listing_votes")
 }
 
@@ -1166,6 +1170,7 @@ model PcListingCommentVote {
   @@unique([userId, commentId])
   @@index([commentId])
   @@index([userId])
+  @@index([userId, nullifiedAt])
   @@map("pc_listing_comment_votes")
 }
 
diff --git a/prisma/seed.ts b/prisma/seed.ts
index e10128cd2..63596a09e 100644
--- a/prisma/seed.ts
+++ b/prisma/seed.ts
@@ -6,6 +6,7 @@ import customFieldTemplatesSeeder from './seeders/customFieldTemplatesSeeder'
 import devicesSeeder from './seeders/devicesSeeder'
 import edenCustomFieldsSeeder from './seeders/edenCustomFieldsSeeder'
 import emulatorsSeeder from './seeders/emulatorsSeeder'
+import gamenativeCustomFieldsSeeder from './seeders/gamenativeCustomFieldsSeeder'
 import gamesSeeder from './seeders/gamesSeeder'
 import gpuSeeder from './seeders/gpuSeeder'
 import listingsSeeder from './seeders/listingsSeeder'
@@ -129,6 +130,7 @@ async function main() {
       await customFieldTemplatesSeeder(prisma)
       await azaharCustomFieldsSeeder(prisma)
       await edenCustomFieldsSeeder(prisma)
+      await gamenativeCustomFieldsSeeder(prisma)
       console.info('✅ Custom fields seeded successfully!')
     } catch (error) {
       console.error('❌ Error seeding custom fields:', error)
@@ -188,6 +190,7 @@ async function main() {
     await emulatorsSeeder(prisma)
     await azaharCustomFieldsSeeder(prisma)
     await edenCustomFieldsSeeder(prisma)
+    await gamenativeCustomFieldsSeeder(prisma)
     await customFieldTemplatesSeeder(prisma)
     await socSeeder(prisma)
     await cpuSeeder(prisma)
diff --git a/prisma/seeders/gamenativeCustomFieldsSeeder.ts b/prisma/seeders/gamenativeCustomFieldsSeeder.ts
new file mode 100644
index 000000000..4e67ce7bd
--- /dev/null
+++ b/prisma/seeders/gamenativeCustomFieldsSeeder.ts
@@ -0,0 +1,486 @@
+import { CustomFieldType, Prisma, type PrismaClient } from '@orm'
+
+interface SelectOption {
+  value: string
+  label: string
+}
+
+interface GameNativeCustomFieldSeed {
+  name: string
+  label: string
+  type: CustomFieldType
+  required: boolean
+  displayOrder: number
+  defaultValue?: string | number | boolean | null
+  placeholder?: string | null
+  options?: SelectOption[]
+}
+
+const GAMENATIVE_EMULATOR_NAME = 'GameNative'
+
+const GAMENATIVE_CUSTOM_FIELDS: GameNativeCustomFieldSeed[] = [
+  {
+    name: 'emulator_version',
+    label: 'Emulator Version',
+    type: CustomFieldType.TEXT,
+    required: true,
+    displayOrder: 0,
+    defaultValue: null,
+  },
+  {
+    name: 'graphics_driver',
+    label: 'Graphics Driver',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 1,
+    defaultValue: 'Vortek (Universal)',
+    options: [
+      { value: 'Vortek (Universal)', label: 'Vortek (Universal)' },
+      { value: 'Turnip (Adreno)', label: 'Turnip (Adreno)' },
+      { value: 'VirGL (Universal)', label: 'VirGL (Universal)' },
+      { value: 'Adreno (Adreno)', label: 'Adreno (Adreno)' },
+      { value: 'SD 8 Elite (SD 8 Elite)', label: 'SD 8 Elite (SD 8 Elite)' },
+      { value: 'Wrapper', label: 'Wrapper (Bionic)' },
+      { value: 'Wrapper-v2', label: 'Wrapper-v2 (Bionic)' },
+      { value: 'Wrapper-leegao', label: 'Wrapper-leegao (Bionic)' },
+      { value: 'Wrapper-legacy', label: 'Wrapper-legacy (Bionic)' },
+    ],
+  },
+  {
+    name: 'dx_wrapper',
+    label: 'DX Wrapper',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 2,
+    defaultValue: 'DXVK',
+    options: [
+      { value: 'WineD3D', label: 'WineD3D' },
+      { value: 'DXVK', label: 'DXVK' },
+      { value: 'VKD3D', label: 'VKD3D' },
+      { value: 'CNC DDraw', label: 'CNC DDraw' },
+      { value: 'Other', label: 'Other' },
+    ],
+  },
+  {
+    name: 'dxvk_version',
+    label: 'DXVK Version',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 3,
+    defaultValue: '2.6.1-gplasync',
+    options: [
+      { value: '1.10.1', label: '1.10.1' },
+      { value: '1.10.3', label: '1.10.3' },
+      { value: 'async-1.10.3', label: 'async-1.10.3' },
+      { value: '1.10.9-sarek', label: '1.10.9-sarek' },
+      { value: '1.11.1-sarek', label: '1.11.1-sarek' },
+      { value: '1.9.2', label: '1.9.2' },
+      { value: '2.3.1', label: '2.3.1' },
+      { value: '2.4-gplasync', label: '2.4-gplasync' },
+      { value: '2.4.1', label: '2.4.1' },
+      { value: '2.4.1-gplasync', label: '2.4.1-gplasync' },
+      { value: '2.6.1-gplasync', label: '2.6.1-gplasync' },
+      { value: '2.6-arm64ec', label: '2.6-arm64ec' },
+      { value: '2.7.1', label: '2.7.1' },
+      { value: 'other', label: 'Other (specify in notes)' },
+    ],
+  },
+  {
+    name: 'dx_wrapper_config',
+    label: 'DX Wrapper Config (Version, Frame Rate, Max Memory)',
+    type: CustomFieldType.TEXTAREA,
+    required: false,
+    displayOrder: 4,
+    defaultValue: null,
+  },
+  {
+    name: 'audio_driver',
+    label: 'Audio Driver',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 5,
+    defaultValue: 'alsa',
+    options: [
+      { value: 'alsa', label: 'ALSA' },
+      { value: 'pulse', label: 'PulseAudio' },
+      { value: 'other', label: 'Other' },
+    ],
+  },
+  {
+    name: 'env_variables',
+    label: 'Environment Variables',
+    type: CustomFieldType.TEXTAREA,
+    required: false,
+    displayOrder: 6,
+    defaultValue: null,
+  },
+  {
+    name: 'box64_version',
+    label: 'Box64 Version',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 7,
+    defaultValue: '0.3.6',
+    options: [
+      { value: '0.3.2', label: '0.3.2' },
+      { value: '0.3.4', label: '0.3.4' },
+      { value: '0.3.6', label: '0.3.6' },
+      { value: '0.3.7', label: '0.3.7' },
+      { value: '0.3.8', label: '0.3.8' },
+      { value: '0.4.0', label: '0.4.0' },
+    ],
+  },
+  {
+    name: 'box64_preset',
+    label: 'Box64 Preset',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 8,
+    defaultValue: 'compatibility',
+    options: [
+      { value: 'stability', label: 'Stability' },
+      { value: 'compatibility', label: 'Compatibility' },
+      { value: 'intermediate', label: 'Intermediate' },
+      { value: 'performance', label: 'Performance' },
+      { value: 'unity', label: 'Unity' },
+      { value: 'unity_mono_bleeding_edge', label: 'Unity Mono Bleeding Edge' },
+      { value: 'denuvo', label: 'Denuvo' },
+      { value: 'other', label: 'Other/Custom' },
+    ],
+  },
+  {
+    name: 'startup_selection',
+    label: 'Startup Selection',
+    type: CustomFieldType.SELECT,
+    required: false,
+    displayOrder: 9,
+    defaultValue: 'Aggressive (Stop services on startup)',
+    options: [
+      { value: 'Normal (Load all services)', label: 'Normal (Load all services)' },
+      {
+        value: 'Essential (Load only essential services)',
+        label: 'Essential (Load only essential services)',
+      },
+      {
+        value: 'Aggressive (Stop services on startup)',
+        label: 'Aggressive (Stop services on startup)',
+      },
+      { value: 'Other', label: 'Other' },
+    ],
+  },
+  {
+    name: 'resolution',
+    label: 'Resolution (Screen Size)',
+    type: CustomFieldType.TEXT,
+    required: true,
+    displayOrder: 10,
+    defaultValue: '1280x720 (16:9)',
+  },
+  {
+    name: 'youtube',
+    label: 'YouTube',
+    type: CustomFieldType.URL,
+    required: false,
+    displayOrder: 11,
+    defaultValue: null,
+  },
+  {
+    name: 'media_url',
+    label: 'Screenshots, Blog Post, etc',
+    type: CustomFieldType.URL,
+    required: false,
+    displayOrder: 12,
+    defaultValue: null,
+  },
+  {
+    name: 'exec_arguments',
+    label: 'Exec Arguments',
+    type: CustomFieldType.TEXT,
+    required: false,
+    displayOrder: 13,
+    defaultValue: null,
+    placeholder:
+      '-noverifyfiles -nobootstrapupdate -skipinitialbootstrap -norepairfiles -nocrashmonitor -noshaders',
+  },
+  {
+    name: 'game_version',
+    label: 'Game Version',
+    type: CustomFieldType.TEXT,
+    required: false,
+    displayOrder: 14,
+    defaultValue: null,
+  },
+  {
+    name: 'average_fps',
+    label: 'Average FPS',
+    type: CustomFieldType.TEXT,
+    required: false,
+    displayOrder: 15,
+    defaultValue: null,
+  },
+  {
+    name: 'container_variant',
+    label: 'Container Variant',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 16,
+    defaultValue: 'bionic',
+    options: [
+      { value: 'bionic', label: 'bionic' },
+      { value: 'glibc', label: 'glibc' },
+    ],
+  },
+  {
+    name: 'wine_version',
+    label: 'Wine Version',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 17,
+    defaultValue: 'proton-9.0-arm64ec',
+    options: [
+      { value: 'wine-9.2-x86_64', label: 'wine-9.2-x86_64 (Glibc)' },
+      { value: 'proton-9.0-arm64ec', label: 'proton-9.0-arm64ec (Bionic)' },
+      { value: 'proton-9.0-x86_64', label: 'proton-9.0-x86_64 (Bionic)' },
+      { value: 'proton-10.0-arm64ec', label: 'proton-10.0-arm64ec (Bionic)' },
+    ],
+  },
+  {
+    name: 'steam_type',
+    label: 'Steam Type',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 18,
+    defaultValue: 'normal',
+    options: [
+      { value: 'normal', label: 'Normal' },
+      { value: 'light', label: 'Light' },
+      { value: 'ultra_light', label: 'Ultra Light' },
+    ],
+  },
+  {
+    name: 'dynamic_driver_version',
+    label: 'Dynamic Driver Version',
+    type: CustomFieldType.TEXT,
+    required: true,
+    displayOrder: 19,
+    defaultValue: null,
+  },
+  {
+    name: 'max_device_memory',
+    label: 'Max Device Memory',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 20,
+    defaultValue: '0',
+    options: [
+      { value: '0', label: '0 MB' },
+      { value: '512', label: '512 MB' },
+      { value: '1024', label: '1024 MB' },
+      { value: '2048', label: '2048 MB' },
+      { value: '4096', label: '4096 MB' },
+    ],
+  },
+  {
+    name: 'use_adrenotools_turnip',
+    label: 'Use Adrenotools Turnip',
+    type: CustomFieldType.BOOLEAN,
+    required: true,
+    displayOrder: 21,
+    defaultValue: true,
+  },
+  {
+    name: 'fex_core_version',
+    label: 'FEXCore Version',
+    type: CustomFieldType.TEXT,
+    required: true,
+    displayOrder: 22,
+    defaultValue: '2603',
+  },
+  {
+    name: '32_bit_emulator',
+    label: '32-bit Emulator',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 23,
+    defaultValue: 'fex',
+    options: [
+      { value: 'fex', label: 'FEXCore' },
+      { value: 'box', label: 'Box32' },
+    ],
+  },
+  {
+    name: '64_bit_emulator',
+    label: '64-bit Emulator',
+    type: CustomFieldType.SELECT,
+    required: false,
+    displayOrder: 24,
+    defaultValue: 'fex',
+    options: [
+      { value: 'fex', label: 'FEXCore' },
+      { value: 'box', label: 'Box64' },
+    ],
+  },
+  {
+    name: 'fex_core_preset',
+    label: 'FEXCore Preset',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 25,
+    defaultValue: 'intermediate',
+    options: [
+      { value: 'stability', label: 'Stability' },
+      { value: 'compatibility', label: 'Compatibility' },
+      { value: 'intermediate', label: 'Intermediate' },
+      { value: 'performance', label: 'Performance' },
+      { value: 'extreme', label: 'Extreme' },
+      { value: 'denuvo', label: 'Denuvo' },
+      { value: 'other', label: 'Other' },
+    ],
+  },
+  {
+    name: 'use_steam_input',
+    label: 'Use Steam Input',
+    type: CustomFieldType.BOOLEAN,
+    required: false,
+    displayOrder: 26,
+    defaultValue: false,
+  },
+  {
+    name: 'enable_x_input_api',
+    label: 'Enable XInput API',
+    type: CustomFieldType.BOOLEAN,
+    required: false,
+    displayOrder: 27,
+    defaultValue: true,
+  },
+  {
+    name: 'enable_direct_input_api',
+    label: 'Enable DirectInput API',
+    type: CustomFieldType.BOOLEAN,
+    required: false,
+    displayOrder: 28,
+    defaultValue: true,
+  },
+  {
+    name: 'direct_input_mapper_type',
+    label: 'DirectInput Mapper Type',
+    type: CustomFieldType.SELECT,
+    required: true,
+    displayOrder: 29,
+    defaultValue: 'standard',
+    options: [
+      { value: 'standard', label: 'Standard' },
+      { value: 'xinput_mapper', label: 'XInput Mapper' },
+    ],
+  },
+]
+
+export default async function gamenativeCustomFieldsSeeder(prisma: PrismaClient) {
+  console.info('🌱 Seeding GameNative custom fields...')
+
+  const gamenative = await prisma.emulator.findUnique({
+    where: { name: GAMENATIVE_EMULATOR_NAME },
+    select: { id: true },
+  })
+
+  if (!gamenative) {
+    console.warn(
+      `⚠️ Emulator "${GAMENATIVE_EMULATOR_NAME}" not found. Skipping custom field seeding.`,
+    )
+    return
+  }
+
+  const fieldNames = GAMENATIVE_CUSTOM_FIELDS.map((field) => field.name)
+
+  for (const field of GAMENATIVE_CUSTOM_FIELDS) {
+    await prisma.customFieldDefinition.upsert({
+      where: {
+        emulatorId_name: {
+          emulatorId: gamenative.id,
+          name: field.name,
+        },
+      },
+      create: buildDefinitionCreate(gamenative.id, field),
+      update: buildDefinitionUpdate(field),
+    })
+  }
+
+  const staleDefinitions = await prisma.customFieldDefinition.findMany({
+    where: {
+      emulatorId: gamenative.id,
+      name: { notIn: fieldNames },
+    },
+    select: { id: true, name: true },
+  })
+
+  if (staleDefinitions.length > 0) {
+    const valueCount = await prisma.listingCustomFieldValue.count({
+      where: { customFieldDefinitionId: { in: staleDefinitions.map((d) => d.id) } },
+    })
+    const pcValueCount = await prisma.pcListingCustomFieldValue.count({
+      where: { customFieldDefinitionId: { in: staleDefinitions.map((d) => d.id) } },
+    })
+
+    if (valueCount > 0 || pcValueCount > 0) {
+      console.warn(
+        `⚠️ Skipping removal of ${staleDefinitions.length} stale field(s) ` +
+          `(${staleDefinitions.map((d) => d.name).join(', ')}) — ` +
+          `${valueCount + pcValueCount} user-submitted values would be cascade-deleted. ` +
+          `Remove manually after migrating data.`,
+      )
+    } else {
+      await prisma.customFieldDefinition.deleteMany({
+        where: { id: { in: staleDefinitions.map((d) => d.id) } },
+      })
+      console.info(`✅ Removed ${staleDefinitions.length} unused field definition(s).`)
+    }
+  }
+
+  console.info(
+    `✅ GameNative custom fields synced. Updated ${GAMENATIVE_CUSTOM_FIELDS.length} definitions.`,
+  )
+}
+
+function buildDefinitionCreate(emulatorId: string, field: GameNativeCustomFieldSeed) {
+  const { options, defaultValue, placeholder, ...base } = field
+
+  return {
+    emulatorId,
+    name: base.name,
+    label: base.label,
+    type: base.type,
+    options: normalizeJsonInput(options),
+    defaultValue: normalizeJsonInput(defaultValue),
+    placeholder: placeholder ?? null,
+    rangeMin: null,
+    rangeMax: null,
+    rangeDecimals: null,
+    rangeUnit: null,
+    isRequired: base.required,
+    displayOrder: base.displayOrder,
+  }
+}
+
+function buildDefinitionUpdate(field: GameNativeCustomFieldSeed) {
+  const { options, defaultValue, placeholder, ...base } = field
+
+  return {
+    label: base.label,
+    type: base.type,
+    options: normalizeJsonInput(options),
+    defaultValue: normalizeJsonInput(defaultValue),
+    placeholder: placeholder ?? null,
+    rangeMin: null,
+    rangeMax: null,
+    rangeDecimals: null,
+    rangeUnit: null,
+    isRequired: base.required,
+    displayOrder: base.displayOrder,
+  }
+}
+
+function normalizeJsonInput(
+  value: unknown,
+): Prisma.NullableJsonNullValueInput | Prisma.InputJsonValue | undefined {
+  return value === null || value === undefined ? Prisma.JsonNull : (value as Prisma.InputJsonValue)
+}
diff --git a/src/app/admin/users/components/UserDetailsModal.tsx b/src/app/admin/users/components/UserDetailsModal.tsx
index 1b7eaf15f..fbcc990b6 100644
--- a/src/app/admin/users/components/UserDetailsModal.tsx
+++ b/src/app/admin/users/components/UserDetailsModal.tsx
@@ -368,7 +368,10 @@ function UserDetailsModal(props: Props) {
                     Trust Score
                   </span>
                 </div>
-                <p className="text-2xl font-bold text-orange-900 dark:text-orange-100">
+                <p
+                  className="text-2xl font-bold text-orange-900 dark:text-orange-100"
+                  aria-label="Trust score value"
+                >
                   {userQuery.data.trustScore}
                 </p>
                 <p className="text-xs text-orange-700 dark:text-orange-300">
@@ -669,6 +672,7 @@ function UserDetailsModal(props: Props) {
                     <Input
                       type="number"
                       placeholder="±points"
+                      aria-label="Custom trust adjustment value"
                       value={customAdjustment}
                       onChange={(e) => setCustomAdjustment(e.target.value)}
                       disabled={isAdjusting}
@@ -698,6 +702,7 @@ function UserDetailsModal(props: Props) {
                   <div>
                     <Input
                       placeholder="Reason for adjustment (required)"
+                      aria-label="Reason for trust adjustment"
                       value={adjustmentReason}
                       onChange={(e) => setAdjustmentReason(e.target.value)}
                       disabled={isAdjusting}
diff --git a/src/app/admin/users/components/UserRoleModal.tsx b/src/app/admin/users/components/UserRoleModal.tsx
index df1949d44..1a2928842 100644
--- a/src/app/admin/users/components/UserRoleModal.tsx
+++ b/src/app/admin/users/components/UserRoleModal.tsx
@@ -64,7 +64,12 @@ function UserRoleModal(props: Props) {
 
   return (
     <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center">
-      <div className="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md w-full">
+      <div
+        role="dialog"
+        aria-modal="true"
+        aria-label="Edit User Role"
+        className="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md w-full"
+      >
         <h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white flex items-center gap-2">
           <ShieldCheck className="h-5 w-5 text-purple-500" />
           Edit User Role
diff --git a/src/app/listings/ListingsPage.tsx b/src/app/listings/ListingsPage.tsx
index 2bdafb26e..fd6147aa8 100644
--- a/src/app/listings/ListingsPage.tsx
+++ b/src/app/listings/ListingsPage.tsx
@@ -12,7 +12,7 @@ import {
 } from '@/app/listings/shared/components'
 import CommunitySupportBanner from '@/components/banners/CommunitySupportBanner'
 import { EmulatorIcon, SystemIcon } from '@/components/icons'
-import { Badge } from '@/components/ui/Badge'
+import { BannedUserBadge } from '@/components/ui/BannedUserBadge'
 import { Button } from '@/components/ui/Button'
 import { ColumnVisibilityControl } from '@/components/ui/ColumnVisibilityControl'
 import { DisplayToggleButton } from '@/components/ui/DisplayToggleButton'
@@ -516,20 +516,12 @@ function ListingsPage() {
                               </Tooltip>
                             )}
 
-                            {isModerator &&
-                              listing.author &&
-                              'userBans' in listing.author &&
-                              Array.isArray(listing.author.userBans) &&
-                              listing.author.userBans.length > 0 && (
-                                <Tooltip>
-                                  <TooltipTrigger>
-                                    <Badge className="bg-red-100 text-red-700 dark:bg-red-900/20 dark:text-red-400 border-red-200 dark:border-red-800">
-                                      BANNED
-                                    </Badge>
-                                  </TooltipTrigger>
-                                  <TooltipContent>This user has been banned</TooltipContent>
-                                </Tooltip>
-                              )}
+                            <BannedUserBadge
+                              author={listing.author}
+                              canView={isModerator}
+                              label="BANNED"
+                              tooltip="This user has been banned"
+                            />
 
                             {listing.developerVerifications &&
                               listing.developerVerifications.length > 0 && (
diff --git a/src/app/listings/[id]/components/ListingDetailsClient.tsx b/src/app/listings/[id]/components/ListingDetailsClient.tsx
index 5932e9470..63a0c0fbc 100644
--- a/src/app/listings/[id]/components/ListingDetailsClient.tsx
+++ b/src/app/listings/[id]/components/ListingDetailsClient.tsx
@@ -11,16 +11,16 @@ import { AuthorPanel } from '@/app/listings/components/shared/details/AuthorPane
 import BookmarkButton from '@/app/listings/components/shared/details/BookmarkButton'
 import { DetailsHeader } from '@/app/listings/components/shared/details/DetailsHeader'
 import { DetailsHeaderBadges } from '@/app/listings/components/shared/details/DetailsHeaderBadges'
+import { ModeratorInfoPanel } from '@/app/listings/components/shared/details/ModeratorInfoPanel'
+import { logHandheldVoteError } from '@/app/listings/components/shared/details/utils/logVoteError'
+import { refreshHandheldListingDetail } from '@/app/listings/components/shared/details/utils/refreshListingDetail'
 import { VotingSection } from '@/app/listings/components/shared/details/VotingSection'
 import { NotesSection } from '@/app/listings/components/shared/NotesSection'
 import { GameImage } from '@/app/listings/shared/components'
 import CommunitySupportBanner from '@/components/banners/CommunitySupportBanner'
-import { Card, Button, Badge } from '@/components/ui'
+import { BannedUserBadge, Button, Card } from '@/components/ui'
 import { api } from '@/lib/api'
-import { logger } from '@/lib/logger'
-import toast from '@/lib/toast'
 import { type RouterOutput } from '@/types/trpc'
-import getErrorMessage from '@/utils/getErrorMessage'
 import { roleIncludesRole } from '@/utils/permission-system'
 import { Role } from '@orm'
 import CommentThread from './CommentThread'
@@ -46,10 +46,7 @@ function ListingDetailsClient(props: Props) {
   const currentUserQuery = api.users.me.useQuery()
   const canViewBannedUsers = roleIncludesRole(currentUserQuery.data?.role, Role.MODERATOR)
 
-  const refreshData = async () => {
-    await utils.listings.byId.invalidate({ id: props.listing.id })
-    await utils.listings.byId.refetch({ id: props.listing.id })
-  }
+  const refreshData = () => refreshHandheldListingDetail({ utils, listingId: props.listing.id })
 
   const scrollToVoteSection = () => {
     voteSectionRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' })
@@ -57,10 +54,7 @@ function ListingDetailsClient(props: Props) {
 
   const voteMutation = api.listings.vote.useMutation({
     onSuccess: refreshData,
-    onError: (error) => {
-      logger.error('[ListingDetailsClient] handleVoting:', error)
-      toast.error(`Failed to vote: ${getErrorMessage(error)}`)
-    },
+    onError: (error) => logHandheldVoteError({ error, listingId: props.listing.id }),
   })
 
   const handleVote = async (value: boolean | null) => {
@@ -142,15 +136,11 @@ function ListingDetailsClient(props: Props) {
                   authorId={props.listing?.author?.id}
                   postedAt={props.listing.createdAt}
                   bannedBadge={
-                    canViewBannedUsers &&
-                    props.listing?.author &&
-                    'userBans' in props.listing.author &&
-                    Array.isArray(props.listing.author.userBans) &&
-                    props.listing.author.userBans.length > 0 ? (
-                      <Badge variant="danger" size="sm" className="mt-1">
-                        BANNED USER
-                      </Badge>
-                    ) : undefined
+                    <BannedUserBadge
+                      author={props.listing?.author}
+                      canView={canViewBannedUsers}
+                      className="mt-1"
+                    />
                   }
                 />
                 <BookmarkButton type="listing" listingId={props.listing.id} />
@@ -205,6 +195,10 @@ function ListingDetailsClient(props: Props) {
             />
           </VotingSection>
 
+          {canViewBannedUsers && (
+            <ModeratorInfoPanel listingId={props.listing.id} listingType="handheld" />
+          )}
+
           <div className="mt-10 border-t border-gray-200 dark:border-gray-700 pt-8">
             <CommentThread
               listingId={props.listing?.id}
diff --git a/src/app/listings/components/shared/details/AdminUserLink.tsx b/src/app/listings/components/shared/details/AdminUserLink.tsx
new file mode 100644
index 000000000..98dfe8e7e
--- /dev/null
+++ b/src/app/listings/components/shared/details/AdminUserLink.tsx
@@ -0,0 +1,19 @@
+import { ExternalLink } from 'lucide-react'
+import Link from 'next/link'
+
+interface Props {
+  userId: string
+  name: string | null
+}
+
+export function AdminUserLink(props: Props) {
+  return (
+    <Link
+      href={`/admin/users?userId=${props.userId}`}
+      className="inline-flex items-center gap-1 text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 hover:underline"
+    >
+      {props.name ?? 'Unknown User'}
+      <ExternalLink className="h-3 w-3" />
+    </Link>
+  )
+}
diff --git a/src/app/listings/components/shared/details/ApprovalSection.tsx b/src/app/listings/components/shared/details/ApprovalSection.tsx
new file mode 100644
index 000000000..35d6b67a6
--- /dev/null
+++ b/src/app/listings/components/shared/details/ApprovalSection.tsx
@@ -0,0 +1,43 @@
+import { ApprovalStatusBadge, LocalizedDate } from '@/components/ui'
+import { type RouterOutput } from '@/types/trpc'
+import { ApprovalStatus } from '@orm'
+import { AdminUserLink } from './AdminUserLink'
+
+type ModeratorInfo = NonNullable<RouterOutput['listings']['moderatorInfo']>
+
+interface Props {
+  approval: ModeratorInfo['approval']
+}
+
+export function ApprovalSection(props: Props) {
+  return (
+    <div className="space-y-2">
+      <h4 className="text-sm font-semibold text-gray-700 dark:text-gray-300">Approval</h4>
+      <div className="flex flex-wrap items-center gap-3 text-sm">
+        <ApprovalStatusBadge status={props.approval.status} />
+        {props.approval.processedBy && (
+          <span className="text-gray-600 dark:text-gray-400">
+            by{' '}
+            <AdminUserLink
+              userId={props.approval.processedBy.id}
+              name={props.approval.processedBy.name}
+            />
+          </span>
+        )}
+        {props.approval.processedAt && (
+          <span className="text-gray-500 dark:text-gray-400">
+            <LocalizedDate date={props.approval.processedAt} format="timeAgo" />
+          </span>
+        )}
+      </div>
+      {props.approval.processedNotes && (
+        <p className="text-sm text-gray-500 dark:text-gray-400 italic bg-gray-50 dark:bg-gray-800 rounded px-3 py-2">
+          {props.approval.processedNotes}
+        </p>
+      )}
+      {props.approval.status === ApprovalStatus.PENDING && (
+        <p className="text-sm text-amber-600 dark:text-amber-400">Not yet reviewed</p>
+      )}
+    </div>
+  )
+}
diff --git a/src/app/listings/components/shared/details/ModeratorInfoPanel.tsx b/src/app/listings/components/shared/details/ModeratorInfoPanel.tsx
new file mode 100644
index 000000000..e6c0dc030
--- /dev/null
+++ b/src/app/listings/components/shared/details/ModeratorInfoPanel.tsx
@@ -0,0 +1,66 @@
+'use client'
+
+import { ChevronDown, ChevronRight } from 'lucide-react'
+import { useState } from 'react'
+import { LoadingSpinner } from '@/components/ui'
+import { api } from '@/lib/api'
+import { type ListingType } from '@/schemas/common'
+import { ApprovalSection } from './ApprovalSection'
+import { VotesSection } from './VotesSection'
+
+interface Props {
+  listingId: string
+  listingType: ListingType
+}
+
+export function ModeratorInfoPanel(props: Props) {
+  const [isExpanded, setIsExpanded] = useState(false)
+
+  const moderatorInfoQuery = api.listings.moderatorInfo.useQuery(
+    { id: props.listingId, type: props.listingType },
+    { enabled: isExpanded, refetchOnWindowFocus: false },
+  )
+
+  const ChevronIcon = isExpanded ? ChevronDown : ChevronRight
+
+  return (
+    <div className="mt-8 border border-amber-200 dark:border-amber-800 rounded-xl overflow-hidden bg-amber-50/50 dark:bg-amber-950/20">
+      <button
+        type="button"
+        onClick={() => setIsExpanded((prevValue) => !prevValue)}
+        className="w-full flex items-center gap-2 px-4 py-3 text-sm font-semibold text-amber-800 dark:text-amber-200 hover:bg-amber-100/50 dark:hover:bg-amber-900/30 transition-colors"
+      >
+        <ChevronIcon className="h-4 w-4" />
+        Moderator Info
+      </button>
+
+      {isExpanded && (
+        <div className="px-4 pb-4 space-y-6 border-t border-amber-200 dark:border-amber-800">
+          {moderatorInfoQuery.isLoading && (
+            <div className="flex justify-center py-6">
+              <LoadingSpinner />
+            </div>
+          )}
+
+          {moderatorInfoQuery.data && (
+            <>
+              <div className="pt-4">
+                <ApprovalSection approval={moderatorInfoQuery.data.approval} />
+              </div>
+              <VotesSection
+                votes={moderatorInfoQuery.data.votes}
+                voteCounts={moderatorInfoQuery.data.voteCounts}
+              />
+            </>
+          )}
+
+          {moderatorInfoQuery.isError && (
+            <p className="text-sm text-red-500 dark:text-red-400 py-4">
+              Failed to load moderator info.
+            </p>
+          )}
+        </div>
+      )}
+    </div>
+  )
+}
diff --git a/src/app/listings/components/shared/details/VoteDirectionIcon.test.tsx b/src/app/listings/components/shared/details/VoteDirectionIcon.test.tsx
new file mode 100644
index 000000000..52ade6ac3
--- /dev/null
+++ b/src/app/listings/components/shared/details/VoteDirectionIcon.test.tsx
@@ -0,0 +1,31 @@
+import { render, screen } from '@testing-library/react'
+import { describe, expect, it } from 'vitest'
+import { VoteDirectionIcon } from './VoteDirectionIcon'
+
+describe('VoteDirectionIcon', () => {
+  it('renders upvote icon with aria-label "Upvote" when value is true', () => {
+    render(<VoteDirectionIcon value={true} />)
+    const icon = screen.getByLabelText('Upvote')
+    expect(icon).toBeInTheDocument()
+    expect(icon).toHaveAttribute('role', 'img')
+  })
+
+  it('renders downvote icon with aria-label "Downvote" when value is false', () => {
+    render(<VoteDirectionIcon value={false} />)
+    const icon = screen.getByLabelText('Downvote')
+    expect(icon).toBeInTheDocument()
+    expect(icon).toHaveAttribute('role', 'img')
+  })
+
+  it('applies green color class for upvote', () => {
+    render(<VoteDirectionIcon value={true} />)
+    const icon = screen.getByLabelText('Upvote')
+    expect(icon).toHaveClass('text-green-600')
+  })
+
+  it('applies red color class for downvote', () => {
+    render(<VoteDirectionIcon value={false} />)
+    const icon = screen.getByLabelText('Downvote')
+    expect(icon).toHaveClass('text-red-600')
+  })
+})
diff --git a/src/app/listings/components/shared/details/VoteDirectionIcon.tsx b/src/app/listings/components/shared/details/VoteDirectionIcon.tsx
new file mode 100644
index 000000000..4fe1146a3
--- /dev/null
+++ b/src/app/listings/components/shared/details/VoteDirectionIcon.tsx
@@ -0,0 +1,14 @@
+import { ArrowDown, ArrowUp } from 'lucide-react'
+
+interface Props {
+  value: boolean
+}
+
+export function VoteDirectionIcon(props: Props) {
+  const label = props.value ? 'Upvote' : 'Downvote'
+  return props.value ? (
+    <ArrowUp className="h-4 w-4 text-green-600 dark:text-green-400" aria-label={label} role="img" />
+  ) : (
+    <ArrowDown className="h-4 w-4 text-red-600 dark:text-red-400" aria-label={label} role="img" />
+  )
+}
diff --git a/src/app/listings/components/shared/details/VoteRow.test.tsx b/src/app/listings/components/shared/details/VoteRow.test.tsx
new file mode 100644
index 000000000..996de715d
--- /dev/null
+++ b/src/app/listings/components/shared/details/VoteRow.test.tsx
@@ -0,0 +1,86 @@
+import { render, screen } from '@testing-library/react'
+import { describe, expect, it } from 'vitest'
+import { VoteRow } from './VoteRow'
+
+function makeVote(
+  overrides: Partial<{
+    id: string
+    value: boolean
+    nullifiedAt: Date | null
+    userName: string | null
+  }> = {},
+) {
+  return {
+    id: overrides.id ?? 'vote-1',
+    value: overrides.value ?? true,
+    nullifiedAt: overrides.nullifiedAt ?? null,
+    createdAt: new Date('2026-01-01T00:00:00Z'),
+    user: {
+      id: 'user-1',
+      name: 'userName' in overrides ? (overrides.userName ?? null) : 'Alice',
+      trustScore: 50,
+    },
+  }
+}
+
+function renderRow(vote: ReturnType<typeof makeVote>) {
+  return render(
+    <table>
+      <tbody>
+        <VoteRow vote={vote} />
+      </tbody>
+    </table>,
+  )
+}
+
+describe('VoteRow', () => {
+  it('renders an active (non-nullified) upvote row without strike-through or Nullified badge', () => {
+    const vote = makeVote({ value: true, nullifiedAt: null })
+    renderRow(vote)
+
+    expect(screen.queryByText('Nullified')).not.toBeInTheDocument()
+
+    const row = screen.getByRole('row')
+    expect(row.className).not.toContain('line-through')
+    expect(row.className).not.toContain('opacity-60')
+  })
+
+  it('renders a nullified vote row with reduced opacity and a Nullified badge, but no line-through', () => {
+    const vote = makeVote({ value: true, nullifiedAt: new Date('2026-01-02T00:00:00Z') })
+    renderRow(vote)
+
+    expect(screen.getByText('Nullified')).toBeInTheDocument()
+
+    const row = screen.getByRole('row')
+    expect(row.className).toContain('opacity-60')
+    expect(row.innerHTML).not.toContain('line-through')
+  })
+
+  it('links to the voter user in admin', () => {
+    const vote = makeVote({ userName: 'Alice' })
+    renderRow(vote)
+
+    expect(screen.getByText('Alice')).toBeInTheDocument()
+  })
+
+  it('falls back to "Unknown User" when voter name is null', () => {
+    const vote = makeVote({ userName: null })
+    renderRow(vote)
+
+    expect(screen.getByText('Unknown User')).toBeInTheDocument()
+  })
+
+  it('renders upvote icon with Upvote aria-label for value=true', () => {
+    const vote = makeVote({ value: true })
+    renderRow(vote)
+
+    expect(screen.getByLabelText('Upvote')).toBeInTheDocument()
+  })
+
+  it('renders downvote icon with Downvote aria-label for value=false', () => {
+    const vote = makeVote({ value: false })
+    renderRow(vote)
+
+    expect(screen.getByLabelText('Downvote')).toBeInTheDocument()
+  })
+})
diff --git a/src/app/listings/components/shared/details/VoteRow.tsx b/src/app/listings/components/shared/details/VoteRow.tsx
new file mode 100644
index 000000000..7621b7a65
--- /dev/null
+++ b/src/app/listings/components/shared/details/VoteRow.tsx
@@ -0,0 +1,42 @@
+import { Badge, LocalizedDate, TrustLevelBadge } from '@/components/ui'
+import { cn } from '@/lib/utils'
+import { type RouterOutput } from '@/types/trpc'
+import { AdminUserLink } from './AdminUserLink'
+import { VoteDirectionIcon } from './VoteDirectionIcon'
+
+type ModeratorInfo = NonNullable<RouterOutput['listings']['moderatorInfo']>
+export type VoteEntry = ModeratorInfo['votes'][number]
+
+interface Props {
+  vote: VoteEntry
+}
+
+export function VoteRow(props: Props) {
+  const isNullified = props.vote.nullifiedAt !== null
+
+  return (
+    <tr
+      className={cn('border-b border-gray-100 dark:border-gray-800', isNullified && 'opacity-60')}
+    >
+      <td className="py-2 pr-3">
+        <VoteDirectionIcon value={props.vote.value} />
+      </td>
+      <td className="py-2 pr-3">
+        <AdminUserLink userId={props.vote.user.id} name={props.vote.user.name} />
+      </td>
+      <td className="py-2 pr-3">
+        <TrustLevelBadge trustScore={props.vote.user.trustScore} size="sm" />
+      </td>
+      <td className="py-2 pr-3 text-sm text-gray-500 dark:text-gray-400">
+        <LocalizedDate date={props.vote.createdAt} format="timeAgo" />
+      </td>
+      <td className="py-2">
+        {isNullified && (
+          <Badge variant="warning" size="sm">
+            Nullified
+          </Badge>
+        )}
+      </td>
+    </tr>
+  )
+}
diff --git a/src/app/listings/components/shared/details/VotesSection.tsx b/src/app/listings/components/shared/details/VotesSection.tsx
new file mode 100644
index 000000000..2c2754061
--- /dev/null
+++ b/src/app/listings/components/shared/details/VotesSection.tsx
@@ -0,0 +1,43 @@
+import { type RouterOutput } from '@/types/trpc'
+import { VoteRow } from './VoteRow'
+
+type ModeratorInfo = NonNullable<RouterOutput['listings']['moderatorInfo']>
+
+interface Props {
+  votes: ModeratorInfo['votes']
+  voteCounts: ModeratorInfo['voteCounts']
+}
+
+export function VotesSection(props: Props) {
+  const { up, down, nullified } = props.voteCounts
+
+  return (
+    <div className="space-y-2">
+      <h4 className="text-sm font-semibold text-gray-700 dark:text-gray-300">
+        Votes ({up} up, {down} down{nullified > 0 && `, ${nullified} nullified`})
+      </h4>
+      {props.votes.length === 0 ? (
+        <p className="text-sm text-gray-500 dark:text-gray-400">No votes yet</p>
+      ) : (
+        <div className="overflow-x-auto">
+          <table className="w-full text-sm">
+            <thead>
+              <tr className="text-left text-xs text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700">
+                <th className="pb-2 pr-3 w-8" />
+                <th className="pb-2 pr-3">User</th>
+                <th className="pb-2 pr-3">Trust</th>
+                <th className="pb-2 pr-3">When</th>
+                <th className="pb-2" />
+              </tr>
+            </thead>
+            <tbody>
+              {props.votes.map((vote) => (
+                <VoteRow key={vote.id} vote={vote} />
+              ))}
+            </tbody>
+          </table>
+        </div>
+      )}
+    </div>
+  )
+}
diff --git a/src/app/listings/components/shared/details/utils/logVoteError.test.ts b/src/app/listings/components/shared/details/utils/logVoteError.test.ts
new file mode 100644
index 000000000..8722b1544
--- /dev/null
+++ b/src/app/listings/components/shared/details/utils/logVoteError.test.ts
@@ -0,0 +1,77 @@
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import { logger } from '@/lib/logger'
+import toast from '@/lib/toast'
+import { logHandheldVoteError, logPcVoteError } from './logVoteError'
+
+vi.mock('@/lib/logger', () => ({
+  logger: {
+    error: vi.fn(),
+  },
+}))
+
+vi.mock('@/lib/toast', () => ({
+  default: {
+    error: vi.fn(),
+  },
+}))
+
+vi.mock('@/utils/getErrorMessage', () => ({
+  default: (error: unknown) => (error instanceof Error ? error.message : String(error)),
+}))
+
+afterEach(() => vi.clearAllMocks())
+
+describe('logHandheldVoteError', () => {
+  const LISTING_ID = '00000000-0000-4000-a000-000000000010'
+
+  it('passes listingId as extra context to logger.error', () => {
+    const error = new Error('Network down')
+
+    logHandheldVoteError({ error, listingId: LISTING_ID })
+
+    expect(logger.error).toHaveBeenCalledTimes(1)
+    expect(logger.error).toHaveBeenCalledWith('[ListingDetailsClient] handleVoting:', error, {
+      listingId: LISTING_ID,
+    })
+  })
+
+  it('calls toast.error with a user-facing message derived from the error', () => {
+    logHandheldVoteError({ error: new Error('Forbidden'), listingId: LISTING_ID })
+
+    expect(toast.error).toHaveBeenCalledTimes(1)
+    expect(toast.error).toHaveBeenCalledWith('Failed to vote: Forbidden')
+  })
+
+  it('handles non-Error thrown values without crashing', () => {
+    logHandheldVoteError({ error: 'bare string', listingId: LISTING_ID })
+
+    expect(logger.error).toHaveBeenCalledWith(
+      '[ListingDetailsClient] handleVoting:',
+      'bare string',
+      { listingId: LISTING_ID },
+    )
+    expect(toast.error).toHaveBeenCalledWith('Failed to vote: bare string')
+  })
+})
+
+describe('logPcVoteError', () => {
+  const PC_LISTING_ID = '00000000-0000-4000-a000-000000000011'
+
+  it('passes pcListingId as extra context to logger.error', () => {
+    const error = new Error('CAPTCHA failed')
+
+    logPcVoteError({ error, pcListingId: PC_LISTING_ID })
+
+    expect(logger.error).toHaveBeenCalledTimes(1)
+    expect(logger.error).toHaveBeenCalledWith('[PcListingDetailsClient] handleVoting:', error, {
+      pcListingId: PC_LISTING_ID,
+    })
+  })
+
+  it('uses the PC-specific prefix (not the handheld one)', () => {
+    logPcVoteError({ error: new Error('err'), pcListingId: PC_LISTING_ID })
+
+    const firstArg = vi.mocked(logger.error).mock.calls[0]?.[0]
+    expect(firstArg).toBe('[PcListingDetailsClient] handleVoting:')
+  })
+})
diff --git a/src/app/listings/components/shared/details/utils/logVoteError.ts b/src/app/listings/components/shared/details/utils/logVoteError.ts
new file mode 100644
index 000000000..a4533d9ad
--- /dev/null
+++ b/src/app/listings/components/shared/details/utils/logVoteError.ts
@@ -0,0 +1,27 @@
+import { logger } from '@/lib/logger'
+import toast from '@/lib/toast'
+import getErrorMessage from '@/utils/getErrorMessage'
+
+interface HandheldParams {
+  error: unknown
+  listingId: string
+}
+
+export function logHandheldVoteError(params: HandheldParams): void {
+  logger.error('[ListingDetailsClient] handleVoting:', params.error, {
+    listingId: params.listingId,
+  })
+  toast.error(`Failed to vote: ${getErrorMessage(params.error)}`)
+}
+
+interface PcParams {
+  error: unknown
+  pcListingId: string
+}
+
+export function logPcVoteError(params: PcParams): void {
+  logger.error('[PcListingDetailsClient] handleVoting:', params.error, {
+    pcListingId: params.pcListingId,
+  })
+  toast.error(`Failed to vote: ${getErrorMessage(params.error)}`)
+}
diff --git a/src/app/listings/components/shared/details/utils/refreshListingDetail.test.ts b/src/app/listings/components/shared/details/utils/refreshListingDetail.test.ts
new file mode 100644
index 000000000..20030893f
--- /dev/null
+++ b/src/app/listings/components/shared/details/utils/refreshListingDetail.test.ts
@@ -0,0 +1,101 @@
+import { describe, expect, it, vi } from 'vitest'
+import { refreshHandheldListingDetail, refreshPcListingDetail } from './refreshListingDetail'
+
+function createMockUtils() {
+  return {
+    listings: {
+      byId: {
+        invalidate: vi.fn().mockResolvedValue(undefined),
+        refetch: vi.fn().mockResolvedValue(undefined),
+      },
+      moderatorInfo: {
+        invalidate: vi.fn().mockResolvedValue(undefined),
+      },
+    },
+    pcListings: {
+      byId: {
+        invalidate: vi.fn().mockResolvedValue(undefined),
+      },
+    },
+  }
+}
+
+describe('refreshHandheldListingDetail', () => {
+  const LISTING_ID = '00000000-0000-4000-a000-000000000010'
+
+  it('invalidates listings.byId and listings.moderatorInfo with type=handheld', async () => {
+    const utils = createMockUtils()
+
+    await refreshHandheldListingDetail({ utils, listingId: LISTING_ID })
+
+    expect(utils.listings.byId.invalidate).toHaveBeenCalledWith({ id: LISTING_ID })
+    expect(utils.listings.moderatorInfo.invalidate).toHaveBeenCalledWith({
+      id: LISTING_ID,
+      type: 'handheld',
+    })
+  })
+
+  it('refetches listings.byId after invalidation completes', async () => {
+    const utils = createMockUtils()
+    const refetchOrder: string[] = []
+    vi.mocked(utils.listings.byId.invalidate).mockImplementation(async () => {
+      refetchOrder.push('invalidate-byId')
+    })
+    vi.mocked(utils.listings.moderatorInfo.invalidate).mockImplementation(async () => {
+      refetchOrder.push('invalidate-moderatorInfo')
+    })
+    vi.mocked(utils.listings.byId.refetch).mockImplementation(async () => {
+      refetchOrder.push('refetch-byId')
+    })
+
+    await refreshHandheldListingDetail({ utils, listingId: LISTING_ID })
+
+    expect(refetchOrder.indexOf('refetch-byId')).toBeGreaterThan(
+      refetchOrder.indexOf('invalidate-byId'),
+    )
+    expect(refetchOrder.indexOf('refetch-byId')).toBeGreaterThan(
+      refetchOrder.indexOf('invalidate-moderatorInfo'),
+    )
+  })
+
+  it('does NOT touch the pcListings utils', async () => {
+    const utils = createMockUtils()
+
+    await refreshHandheldListingDetail({ utils, listingId: LISTING_ID })
+
+    expect(utils.pcListings.byId.invalidate).not.toHaveBeenCalled()
+  })
+})
+
+describe('refreshPcListingDetail', () => {
+  const PC_LISTING_ID = '00000000-0000-4000-a000-000000000011'
+
+  it('invalidates pcListings.byId and listings.moderatorInfo with type=pc', async () => {
+    const utils = createMockUtils()
+
+    await refreshPcListingDetail({ utils, pcListingId: PC_LISTING_ID })
+
+    expect(utils.pcListings.byId.invalidate).toHaveBeenCalledWith({ id: PC_LISTING_ID })
+    expect(utils.listings.moderatorInfo.invalidate).toHaveBeenCalledWith({
+      id: PC_LISTING_ID,
+      type: 'pc',
+    })
+  })
+
+  it('does NOT touch the handheld listings.byId utils', async () => {
+    const utils = createMockUtils()
+
+    await refreshPcListingDetail({ utils, pcListingId: PC_LISTING_ID })
+
+    expect(utils.listings.byId.invalidate).not.toHaveBeenCalled()
+    expect(utils.listings.byId.refetch).not.toHaveBeenCalled()
+  })
+
+  it('does NOT call refetch', async () => {
+    const utils = createMockUtils()
+
+    await refreshPcListingDetail({ utils, pcListingId: PC_LISTING_ID })
+
+    expect(utils.listings.byId.refetch).not.toHaveBeenCalled()
+  })
+})
diff --git a/src/app/listings/components/shared/details/utils/refreshListingDetail.ts b/src/app/listings/components/shared/details/utils/refreshListingDetail.ts
new file mode 100644
index 000000000..4503ed469
--- /dev/null
+++ b/src/app/listings/components/shared/details/utils/refreshListingDetail.ts
@@ -0,0 +1,40 @@
+import { type api } from '@/lib/api'
+
+type Utils = ReturnType<typeof api.useUtils>
+
+interface RefreshHandheldParams {
+  utils: {
+    listings: {
+      byId: Pick<Utils['listings']['byId'], 'invalidate' | 'refetch'>
+      moderatorInfo: Pick<Utils['listings']['moderatorInfo'], 'invalidate'>
+    }
+  }
+  listingId: string
+}
+
+export async function refreshHandheldListingDetail(params: RefreshHandheldParams): Promise<void> {
+  await Promise.all([
+    params.utils.listings.byId.invalidate({ id: params.listingId }),
+    params.utils.listings.moderatorInfo.invalidate({ id: params.listingId, type: 'handheld' }),
+  ])
+  await params.utils.listings.byId.refetch({ id: params.listingId })
+}
+
+interface RefreshPcParams {
+  utils: {
+    pcListings: {
+      byId: Pick<Utils['pcListings']['byId'], 'invalidate'>
+    }
+    listings: {
+      moderatorInfo: Pick<Utils['listings']['moderatorInfo'], 'invalidate'>
+    }
+  }
+  pcListingId: string
+}
+
+export async function refreshPcListingDetail(params: RefreshPcParams): Promise<void> {
+  await Promise.all([
+    params.utils.pcListings.byId.invalidate({ id: params.pcListingId }),
+    params.utils.listings.moderatorInfo.invalidate({ id: params.pcListingId, type: 'pc' }),
+  ])
+}
diff --git a/src/app/listings/new/NewListingPage.tsx b/src/app/listings/new/NewListingPage.tsx
index 42c85151b..6d49ea125 100644
--- a/src/app/listings/new/NewListingPage.tsx
+++ b/src/app/listings/new/NewListingPage.tsx
@@ -16,6 +16,7 @@ import {
 import { useForm, Controller } from 'react-hook-form'
 import '@/shared/emulator-config/eden'
 import '@/shared/emulator-config/azahar'
+import '@/shared/emulator-config/gamenative'
 import { Button, LoadingSpinner } from '@/components/ui'
 import analytics from '@/lib/analytics'
 import { api } from '@/lib/api'
@@ -27,6 +28,7 @@ import { type RouterInput } from '@/types/trpc'
 import { parseCustomFieldOptions, getCustomFieldDefaultValue } from '@/utils/custom-fields'
 import getErrorMessage from '@/utils/getErrorMessage'
 import { formatCountLabel } from '@/utils/text'
+import { ms } from '@/utils/time'
 import {
   CustomFieldsFormSection,
   type DeviceOption,
@@ -49,6 +51,8 @@ import { reconcileDriverValue } from '../components/shared/custom-fields/driverV
 
 export type ListingFormValues = RouterInput['listings']['create']
 
+const HIGHLIGHT_DURATION_MS = 1800
+
 function AddListingPage() {
   const router = useRouter()
   const searchParams = useSearchParams()
@@ -99,7 +103,7 @@ function AddListingPage() {
   }, [availableEmulators, selectedEmulatorId])
   // Prefetch driver versions so an imported Eden driver filename can be resolved immediately
   const driverVersionsQuery = api.listings.driverVersions.useQuery(undefined, {
-    staleTime: 30 * 60 * 1000,
+    staleTime: ms.minutes(30),
     refetchOnWindowFocus: false,
     refetchOnReconnect: false,
   })
@@ -165,15 +169,14 @@ function AddListingPage() {
       }
       importHighlightTimeoutRef.current = window.setTimeout(() => {
         setHighlightedFieldIds([])
-      }, 1800)
-
-      if (changedCount > 0) {
-        toast.success(
-          `Imported Eden configuration. Filled ${formatCountLabel('field', changedCount)}.`,
-        )
-      } else {
-        toast.success('Imported Eden configuration.')
-      }
+      }, HIGHLIGHT_DURATION_MS)
+
+      const changedFieldsMessage =
+        changedCount > 0
+          ? `Filled ${formatCountLabel('field', changedCount)}.`
+          : 'No matching fields were filled.'
+
+      toast.success(`Imported configuration. ${changedFieldsMessage}`)
 
       if (uniqueMissing.length > 0) {
         toast.info(`Review manually: ${uniqueMissing.join(', ')}`)
@@ -202,9 +205,10 @@ function AddListingPage() {
     .trim()
     .toLowerCase()
 
-  const importerSlugMap: Record<string, 'eden' | 'azahar'> = {
+  const importerSlugMap: Record<string, 'eden' | 'azahar' | 'gamenative'> = {
     eden: 'eden',
     azahar: 'azahar',
+    gamenative: 'gamenative',
   }
 
   const selectedEmulatorSlug = importerSlugMap[normalizedEmulatorName] ?? null
@@ -412,10 +416,8 @@ function AddListingPage() {
   })
 
   useEffect(() => {
-    if (selectedEmulatorSlug !== 'eden') {
-      setImportSummary(null)
-      setHighlightedFieldIds([])
-    }
+    setImportSummary(null)
+    setHighlightedFieldIds([])
   }, [selectedEmulatorSlug])
 
   const onSubmit = async (data: ListingFormValues) => {
diff --git a/src/app/pc-listings/PcListingsPage.tsx b/src/app/pc-listings/PcListingsPage.tsx
index f6e51349c..f1e47f637 100644
--- a/src/app/pc-listings/PcListingsPage.tsx
+++ b/src/app/pc-listings/PcListingsPage.tsx
@@ -13,23 +13,23 @@ import {
 import CommunitySupportBanner from '@/components/banners/CommunitySupportBanner'
 import { EmulatorIcon, SystemIcon } from '@/components/icons'
 import {
-  PerformanceBadge,
-  PageSizeSelector,
-  Pagination,
-  LoadingSpinner,
-  LocalizedDate,
-  SortableHeader,
+  BannedUserBadge,
   Button,
   ColumnVisibilityControl,
+  DisplayToggleButton,
+  EditButton,
+  LoadingSpinner,
+  LocalizedDate,
   MobileColumnVisibilityControl,
+  PageSizeSelector,
+  Pagination,
+  PerformanceBadge,
+  SortableHeader,
+  SuccessRateBar,
   Tooltip,
-  TooltipTrigger,
   TooltipContent,
-  EditButton,
+  TooltipTrigger,
   ViewButton,
-  Badge,
-  DisplayToggleButton,
-  SuccessRateBar,
 } from '@/components/ui'
 import storageKeys from '@/data/storageKeys'
 import {
@@ -485,20 +485,12 @@ function PcListingsPage() {
                               </Tooltip>
                             )}
 
-                            {isModerator &&
-                              listing.author &&
-                              'userBans' in listing.author &&
-                              Array.isArray(listing.author.userBans) &&
-                              listing.author.userBans.length > 0 && (
-                                <Tooltip>
-                                  <TooltipTrigger>
-                                    <Badge className="bg-red-100 text-red-700 dark:bg-red-900/20 dark:text-red-400 border-red-200 dark:border-red-800">
-                                      BANNED
-                                    </Badge>
-                                  </TooltipTrigger>
-                                  <TooltipContent>This user has been banned</TooltipContent>
-                                </Tooltip>
-                              )}
+                            <BannedUserBadge
+                              author={listing.author}
+                              canView={isModerator}
+                              label="BANNED"
+                              tooltip="This user has been banned"
+                            />
                           </div>
                         </td>
                       )}
diff --git a/src/app/pc-listings/[id]/components/PcListingDetailsClient.tsx b/src/app/pc-listings/[id]/components/PcListingDetailsClient.tsx
index 2c5ffd249..9d58104b4 100644
--- a/src/app/pc-listings/[id]/components/PcListingDetailsClient.tsx
+++ b/src/app/pc-listings/[id]/components/PcListingDetailsClient.tsx
@@ -13,17 +13,17 @@ import BookmarkButton from '@/app/listings/components/shared/details/BookmarkBut
 import { DetailFieldRow } from '@/app/listings/components/shared/details/DetailFieldRow'
 import { DetailsHeader } from '@/app/listings/components/shared/details/DetailsHeader'
 import { DetailsHeaderBadges } from '@/app/listings/components/shared/details/DetailsHeaderBadges'
+import { ModeratorInfoPanel } from '@/app/listings/components/shared/details/ModeratorInfoPanel'
+import { logPcVoteError } from '@/app/listings/components/shared/details/utils/logVoteError'
+import { refreshPcListingDetail } from '@/app/listings/components/shared/details/utils/refreshListingDetail'
 import { VotingSection } from '@/app/listings/components/shared/details/VotingSection'
 import { NotesSection } from '@/app/listings/components/shared/NotesSection'
 import { GameImage } from '@/app/listings/shared/components'
 import CommunitySupportBanner from '@/components/banners/CommunitySupportBanner'
-import { Card, Button, Badge } from '@/components/ui'
+import { BannedUserBadge, Button, Card } from '@/components/ui'
 import { PC_OS_LABELS } from '@/data/pc-os'
 import { api } from '@/lib/api'
-import { logger } from '@/lib/logger'
-import toast from '@/lib/toast'
 import { type RouterOutput } from '@/types/trpc'
-import getErrorMessage from '@/utils/getErrorMessage'
 import { roleIncludesRole } from '@/utils/permission-system'
 import { type PcOs, Role } from '@orm'
 import EditPcListingButton from './EditPcListingButton'
@@ -89,9 +89,7 @@ function PcListingDetailsClient(props: Props) {
     },
   ] as const
 
-  const refreshData = async () => {
-    await utils.pcListings.byId.invalidate({ id: props.pcListing.id })
-  }
+  const refreshData = () => refreshPcListingDetail({ utils, pcListingId: props.pcListing.id })
 
   const scrollToVoteSection = () => {
     voteSectionRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' })
@@ -99,10 +97,7 @@ function PcListingDetailsClient(props: Props) {
 
   const voteMutation = api.pcListings.vote.useMutation({
     onSuccess: refreshData,
-    onError: (error) => {
-      logger.error('[PcListingDetailsClient] handleVoting:', error)
-      toast.error(`Failed to vote: ${getErrorMessage(error)}`)
-    },
+    onError: (error) => logPcVoteError({ error, pcListingId: props.pcListing.id }),
   })
 
   const handleVote = async (value: boolean | null) => {
@@ -205,15 +200,11 @@ function PcListingDetailsClient(props: Props) {
                   authorId={props.pcListing.author?.id}
                   postedAt={props.pcListing.createdAt}
                   bannedBadge={
-                    canViewBannedUsers &&
-                    props.pcListing?.author &&
-                    'userBans' in props.pcListing.author &&
-                    Array.isArray(props.pcListing.author.userBans) &&
-                    props.pcListing.author.userBans.length > 0 ? (
-                      <Badge variant="danger" size="sm" className="mt-1">
-                        BANNED USER
-                      </Badge>
-                    ) : undefined
+                    <BannedUserBadge
+                      author={props.pcListing?.author}
+                      canView={canViewBannedUsers}
+                      className="mt-1"
+                    />
                   }
                 />
                 <BookmarkButton type="pcListing" pcListingId={props.pcListing.id} />
@@ -269,6 +260,10 @@ function PcListingDetailsClient(props: Props) {
             />
           </VotingSection>
 
+          {canViewBannedUsers && (
+            <ModeratorInfoPanel listingId={props.pcListing.id} listingType="pc" />
+          )}
+
           {/* Comments Section */}
           <div className="mt-8 pt-8 border-t border-gray-200 dark:border-gray-700">
             <PcCommentThread
diff --git a/src/app/users/[id]/UserProfilePage.tsx b/src/app/users/[id]/UserProfilePage.tsx
index 63e7b19e5..fcef1226f 100644
--- a/src/app/users/[id]/UserProfilePage.tsx
+++ b/src/app/users/[id]/UserProfilePage.tsx
@@ -23,6 +23,7 @@ import { isArray, isString } from 'remeda'
 import { z } from 'zod'
 import {
   Badge,
+  BannedUserBadge,
   Button,
   Input,
   Pagination,
@@ -324,14 +325,12 @@ function UserDetailsPage() {
                     <Badge variant={getRoleVariant(userQuery.data.role)} className="text-sm">
                       {formatUserRole(userQuery.data.role)}
                     </Badge>
-                    {canViewBannedUsers &&
-                      'userBans' in userQuery.data &&
-                      Array.isArray(userQuery.data.userBans) &&
-                      userQuery.data.userBans.length > 0 && (
-                        <Badge variant="danger" className="text-sm font-bold">
-                          BANNED USER
-                        </Badge>
-                      )}
+                    <BannedUserBadge
+                      author={userQuery.data}
+                      canView={canViewBannedUsers}
+                      size="md"
+                      className="text-sm font-bold"
+                    />
                     <TrustLevelBadge trustScore={userQuery.data.trustScore} size="sm" />
                   </div>
                   <div className="flex items-center gap-2 text-gray-600 dark:text-gray-400 justify-center lg:justify-start mb-3">
diff --git a/src/components/listings/AuthorDisplay.tsx b/src/components/listings/AuthorDisplay.tsx
index 95c96b0f5..663415ee5 100644
--- a/src/components/listings/AuthorDisplay.tsx
+++ b/src/components/listings/AuthorDisplay.tsx
@@ -1,5 +1,5 @@
 import Link from 'next/link'
-import { Badge } from '@/components/ui'
+import { BannedUserBadge } from '@/components/ui'
 
 interface Author {
   id?: string | null
@@ -25,15 +25,7 @@ export function AuthorDisplay(props: Props) {
       ) : (
         <span>{props.author?.name ?? 'Anonymous'}</span>
       )}
-      {props.canSeeBannedUsers &&
-        props.author &&
-        'userBans' in props.author &&
-        Array.isArray(props.author.userBans) &&
-        props.author.userBans.length > 0 && (
-          <Badge variant="danger" size="sm">
-            BANNED
-          </Badge>
-        )}
+      <BannedUserBadge author={props.author} canView={props.canSeeBannedUsers} label="BANNED" />
     </div>
   )
 }
diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx
index ed7d03a0a..d3c0223b4 100644
--- a/src/components/ui/Badge.tsx
+++ b/src/components/ui/Badge.tsx
@@ -3,7 +3,7 @@
 import { type PropsWithChildren } from 'react'
 import { cn } from '@/lib/utils'
 
-type BadgeSize = 'sm' | 'md' | 'lg'
+export type BadgeSize = 'sm' | 'md' | 'lg'
 export type BadgeVariant = 'default' | 'success' | 'warning' | 'danger' | 'info' | 'primary'
 
 const variantClasses: Record<BadgeVariant, string> = {
diff --git a/src/components/ui/BannedUserBadge.test.tsx b/src/components/ui/BannedUserBadge.test.tsx
new file mode 100644
index 000000000..0d3a8c2dc
--- /dev/null
+++ b/src/components/ui/BannedUserBadge.test.tsx
@@ -0,0 +1,54 @@
+import { render, screen } from '@testing-library/react'
+import { describe, expect, it } from 'vitest'
+import { BannedUserBadge } from './BannedUserBadge'
+
+describe('BannedUserBadge', () => {
+  const bannedAuthor = { userBans: [{ id: 'ban-1' }] }
+  const cleanAuthor = { userBans: [] }
+
+  it('renders nothing when canView is false (even if author is banned)', () => {
+    const { container } = render(<BannedUserBadge author={bannedAuthor} canView={false} />)
+    expect(container).toBeEmptyDOMElement()
+  })
+
+  it('renders nothing when author has no active bans', () => {
+    const { container } = render(<BannedUserBadge author={cleanAuthor} canView={true} />)
+    expect(container).toBeEmptyDOMElement()
+  })
+
+  it('renders nothing for null / undefined authors', () => {
+    const { container: nullContainer } = render(<BannedUserBadge author={null} canView={true} />)
+    expect(nullContainer).toBeEmptyDOMElement()
+
+    const { container: undefContainer } = render(
+      <BannedUserBadge author={undefined} canView={true} />,
+    )
+    expect(undefContainer).toBeEmptyDOMElement()
+  })
+
+  it('renders the default BANNED USER label when author is banned and viewer can see', () => {
+    render(<BannedUserBadge author={bannedAuthor} canView={true} />)
+    expect(screen.getByText('BANNED USER')).toBeInTheDocument()
+  })
+
+  it('renders a custom label when provided (AuthorDisplay compact variant)', () => {
+    render(<BannedUserBadge author={bannedAuthor} canView={true} label="BANNED" />)
+    expect(screen.getByText('BANNED')).toBeInTheDocument()
+    expect(screen.queryByText('BANNED USER')).not.toBeInTheDocument()
+  })
+
+  it('forwards className through to the underlying Badge', () => {
+    render(<BannedUserBadge author={bannedAuthor} canView={true} className="mt-1 custom-class" />)
+    const badge = screen.getByText('BANNED USER')
+    expect(badge.className).toContain('mt-1')
+    expect(badge.className).toContain('custom-class')
+  })
+
+  it('respects the size prop (profile-header md variant)', () => {
+    render(<BannedUserBadge author={bannedAuthor} canView={true} size="md" />)
+    const badge = screen.getByText('BANNED USER')
+    // size="md" → px-2 py-1 per Badge's sizeClasses map
+    expect(badge.className).toContain('px-2')
+    expect(badge.className).toContain('py-1')
+  })
+})
diff --git a/src/components/ui/BannedUserBadge.tsx b/src/components/ui/BannedUserBadge.tsx
new file mode 100644
index 000000000..ace6426f7
--- /dev/null
+++ b/src/components/ui/BannedUserBadge.tsx
@@ -0,0 +1,34 @@
+'use client'
+
+import { Badge, type BadgeSize } from '@/components/ui/Badge'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/Tooltip'
+import { hasActiveBans } from '@/utils/user-bans'
+
+interface Props {
+  author: unknown
+  canView: boolean
+  label?: string
+  size?: BadgeSize
+  className?: string
+  tooltip?: string
+}
+
+export function BannedUserBadge(props: Props) {
+  if (!props.canView) return null
+  if (!hasActiveBans(props.author)) return null
+
+  const badge = (
+    <Badge variant="danger" size={props.size ?? 'sm'} className={props.className}>
+      {props.label ?? 'BANNED USER'}
+    </Badge>
+  )
+
+  if (!props.tooltip) return badge
+
+  return (
+    <Tooltip>
+      <TooltipTrigger>{badge}</TooltipTrigger>
+      <TooltipContent>{props.tooltip}</TooltipContent>
+    </Tooltip>
+  )
+}
diff --git a/src/components/ui/TrustLevelBadge.test.tsx b/src/components/ui/TrustLevelBadge.test.tsx
new file mode 100644
index 000000000..b4e2f96c0
--- /dev/null
+++ b/src/components/ui/TrustLevelBadge.test.tsx
@@ -0,0 +1,56 @@
+import { render, screen } from '@testing-library/react'
+import { describe, expect, it } from 'vitest'
+import { TrustLevelBadge } from './TrustLevelBadge'
+
+describe('TrustLevelBadge', () => {
+  it('shows Newcomer level for score 0', () => {
+    render(<TrustLevelBadge trustScore={0} />)
+    expect(screen.getByText('Newcomer')).toBeInTheDocument()
+  })
+
+  it('shows Newcomer level for negative score (no crash)', () => {
+    render(<TrustLevelBadge trustScore={-50} />)
+    expect(screen.getByText('Newcomer')).toBeInTheDocument()
+  })
+
+  it('shows Contributor level at 100', () => {
+    render(<TrustLevelBadge trustScore={100} />)
+    expect(screen.getByText('Contributor')).toBeInTheDocument()
+  })
+
+  it('shows Trusted level at 250', () => {
+    render(<TrustLevelBadge trustScore={250} />)
+    expect(screen.getByText('Trusted')).toBeInTheDocument()
+  })
+
+  describe('progress display', () => {
+    it('shows 0% for negative scores (clamped, no "-5%")', () => {
+      render(<TrustLevelBadge trustScore={-5} showProgress />)
+      expect(screen.getByText('0%')).toBeInTheDocument()
+      expect(screen.queryByText(/-\d+%/)).not.toBeInTheDocument()
+    })
+
+    it('shows 0% at the bottom of a level', () => {
+      render(<TrustLevelBadge trustScore={0} showProgress />)
+      expect(screen.getByText('0%')).toBeInTheDocument()
+    })
+
+    it('shows 50% at the midpoint between levels', () => {
+      // Newcomer (0) → Contributor (100), midpoint is 50
+      render(<TrustLevelBadge trustScore={50} showProgress />)
+      expect(screen.getByText('50%')).toBeInTheDocument()
+    })
+
+    it('does not render progress at the highest level (Core)', () => {
+      render(<TrustLevelBadge trustScore={2000} showProgress />)
+      expect(screen.getByText('Core')).toBeInTheDocument()
+      expect(screen.queryByText(/Progress to/)).not.toBeInTheDocument()
+    })
+
+    it('shows points needed (can be larger than typical when score is negative)', () => {
+      render(<TrustLevelBadge trustScore={-10} showProgress />)
+      // Next level Contributor at 100, so 100 - (-10) = 110 points needed
+      expect(screen.getByText('110 points needed')).toBeInTheDocument()
+    })
+  })
+})
diff --git a/src/components/ui/TrustLevelBadge.tsx b/src/components/ui/TrustLevelBadge.tsx
index 1d7ed805d..4c831c954 100644
--- a/src/components/ui/TrustLevelBadge.tsx
+++ b/src/components/ui/TrustLevelBadge.tsx
@@ -75,7 +75,7 @@ export function TrustLevelBadge(props: Props): JSX.Element {
         <div className="flex flex-col gap-1">
           <div className="flex justify-between items-center text-xs text-gray-600 dark:text-gray-400">
             <span>Progress to {nextLevel.name}</span>
-            <span>{Math.round(progress * 100)}%</span>
+            <span>{Math.round(Math.min(Math.max(progress, 0), 1) * 100)}%</span>
           </div>
           <div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
             <div
diff --git a/src/components/ui/VoteButtons.tsx b/src/components/ui/VoteButtons.tsx
index e98b109a0..1bdf3cd69 100644
--- a/src/components/ui/VoteButtons.tsx
+++ b/src/components/ui/VoteButtons.tsx
@@ -149,6 +149,7 @@ export function VoteButtons(props: VoteButtonsProps) {
             type="button"
             onClick={() => handleVote(true)}
             disabled={!isAuthenticated || props.isLoading}
+            aria-pressed={optimisticVote === true}
             className={`flex flex-col items-center p-3 rounded-lg transition-colors border-2 ${
               optimisticVote === true
                 ? 'bg-green-50 border-green-200 text-green-700 dark:bg-green-900/20 dark:border-green-700 dark:text-green-400'
@@ -173,6 +174,7 @@ export function VoteButtons(props: VoteButtonsProps) {
             type="button"
             onClick={() => handleVote(false)}
             disabled={!isAuthenticated || props.isLoading}
+            aria-pressed={optimisticVote === false}
             className={`flex flex-col items-center p-3 rounded-lg transition-colors border-2 ${
               optimisticVote === false
                 ? 'bg-red-50 border-red-200 text-red-700 dark:bg-red-900/20 dark:border-red-700 dark:text-red-400'
diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts
index 44e27728f..02088ad15 100644
--- a/src/components/ui/index.ts
+++ b/src/components/ui/index.ts
@@ -3,6 +3,7 @@ export * from './ApprovalStatusBadge'
 export * from './AuthorRiskIndicator'
 export * from './AuthorRiskWarningBanner'
 export * from './Badge'
+export * from './BannedUserBadge'
 export * from './BulkActions'
 export * from './Button'
 export * from './Card'
diff --git a/src/components/ui/modals/Modal.tsx b/src/components/ui/modals/Modal.tsx
index b1dd7a173..186a117e5 100644
--- a/src/components/ui/modals/Modal.tsx
+++ b/src/components/ui/modals/Modal.tsx
@@ -65,6 +65,9 @@ export function Modal({ onClose, ...props }: Props) {
       onClick={handleBackdropClick}
     >
       <div
+        role="dialog"
+        aria-modal="true"
+        aria-label={props.title}
         className={cn(
           'relative bg-white dark:bg-gray-800 rounded-xl shadow-2xl flex flex-col max-h-[90vh] transform transition-all duration-300 ease-out animate-slide-up',
           sizeClasses[size],
diff --git a/src/lib/errors.ts b/src/lib/errors.ts
index 952a8f0f1..8b2193191 100644
--- a/src/lib/errors.ts
+++ b/src/lib/errors.ts
@@ -554,4 +554,8 @@ export class ResourceError {
         `${userName} must have the DEVELOPER role or higher to be removed as a verified developer. Current role: ${currentRole}`,
       ),
   }
+
+  static trust = {
+    adjustmentCannotBeZero: () => AppError.badRequest('Trust adjustment cannot be zero'),
+  }
 }
diff --git a/src/lib/trust/config.ts b/src/lib/trust/config.ts
index 3b320df02..3ab72bbc3 100644
--- a/src/lib/trust/config.ts
+++ b/src/lib/trust/config.ts
@@ -58,9 +58,13 @@ export const TRUST_ACTIONS = {
     description: 'Manual trust score decrease by admin',
   },
   [TrustAction.VOTE_NULLIFICATION_REVERSAL]: {
-    weight: 0, // Dynamic weight set during nullification
+    weight: 0,
     description: 'Trust reversal due to vote nullification or restoration',
   },
+  [TrustAction.VOTE_CHANGE_REVERSAL]: {
+    weight: 0,
+    description: 'Trust reversal due to vote change or removal',
+  },
 } as const
 
 export const TRUST_LEVELS = [
diff --git a/src/lib/trust/service.test.ts b/src/lib/trust/service.test.ts
new file mode 100644
index 000000000..c456fc8e7
--- /dev/null
+++ b/src/lib/trust/service.test.ts
@@ -0,0 +1,595 @@
+import { describe, expect, it, beforeEach, vi } from 'vitest'
+import { TrustAction } from '@orm'
+
+vi.mock('@/lib/analytics', () => ({
+  default: {
+    trust: {
+      trustScoreChanged: vi.fn(),
+      trustLevelChanged: vi.fn(),
+    },
+  },
+}))
+
+function createMockPrismaCtx() {
+  return {
+    user: {
+      findMany: vi.fn().mockResolvedValue([]),
+      findUnique: vi.fn().mockResolvedValue(null),
+      update: vi.fn().mockResolvedValue({}),
+    },
+    trustActionLog: {
+      create: vi.fn().mockResolvedValue({}),
+      createMany: vi.fn().mockResolvedValue({ count: 0 }),
+    },
+  }
+}
+
+type MockPrismaCtx = ReturnType<typeof createMockPrismaCtx>
+
+describe('TrustService.applyBulkManualAdjustments', () => {
+  let prismaCtx: MockPrismaCtx
+
+  beforeEach(async () => {
+    vi.clearAllMocks()
+    prismaCtx = createMockPrismaCtx()
+  })
+
+  async function createService() {
+    const { TrustService } = await import('./service')
+    return new TrustService(prismaCtx as never)
+  }
+
+  it('returns 0 and makes no DB calls for an empty map', async () => {
+    const service = await createService()
+
+    const result = await service.applyBulkManualAdjustments({
+      adjustments: new Map(),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    expect(result).toBe(0)
+    expect(prismaCtx.user.findMany).not.toHaveBeenCalled()
+    expect(prismaCtx.trustActionLog.createMany).not.toHaveBeenCalled()
+  })
+
+  it('returns 0 and makes no DB calls when all adjustments are zero', async () => {
+    const service = await createService()
+
+    const result = await service.applyBulkManualAdjustments({
+      adjustments: new Map([
+        ['user-1', 0],
+        ['user-2', 0],
+      ]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    expect(result).toBe(0)
+    expect(prismaCtx.user.findMany).not.toHaveBeenCalled()
+  })
+
+  it('skips non-existent users gracefully', async () => {
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 50 }])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: 'Admin', email: 'admin@test.com' })
+
+    const service = await createService()
+
+    const result = await service.applyBulkManualAdjustments({
+      adjustments: new Map([
+        ['user-1', -10],
+        ['deleted-user', -5],
+      ]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    expect(result).toBe(1)
+    expect(prismaCtx.user.update).toHaveBeenCalledTimes(1)
+    expect(prismaCtx.user.update).toHaveBeenCalledWith({
+      where: { id: 'user-1' },
+      data: { trustScore: 40, lastActiveAt: expect.any(Date) },
+    })
+  })
+
+  it('returns 0 when all users in map are non-existent', async () => {
+    prismaCtx.user.findMany.mockResolvedValue([])
+
+    const service = await createService()
+
+    const result = await service.applyBulkManualAdjustments({
+      adjustments: new Map([['deleted-user', -5]]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    expect(result).toBe(0)
+    expect(prismaCtx.user.findUnique).not.toHaveBeenCalled()
+    expect(prismaCtx.user.update).not.toHaveBeenCalled()
+    expect(prismaCtx.trustActionLog.createMany).not.toHaveBeenCalled()
+  })
+
+  it('applies a single adjustment correctly', async () => {
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 50 }])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: 'Admin', email: 'admin@test.com' })
+
+    const service = await createService()
+
+    const result = await service.applyBulkManualAdjustments({
+      adjustments: new Map([['user-1', -10]]),
+      reason: 'Vote nullification: Spam',
+      adminUserId: 'admin-1',
+    })
+
+    expect(result).toBe(1)
+
+    expect(prismaCtx.user.update).toHaveBeenCalledWith({
+      where: { id: 'user-1' },
+      data: { trustScore: 40, lastActiveAt: expect.any(Date) },
+    })
+
+    expect(prismaCtx.trustActionLog.createMany).toHaveBeenCalledWith({
+      data: [
+        {
+          userId: 'user-1',
+          action: TrustAction.ADMIN_ADJUSTMENT_NEGATIVE,
+          weight: -10,
+          metadata: {
+            reason: 'Vote nullification: Spam',
+            adminUserId: 'admin-1',
+            adminName: 'Admin',
+            adjustment: -10,
+          },
+        },
+      ],
+    })
+  })
+
+  it('applies multiple adjustments with one createMany call', async () => {
+    prismaCtx.user.findMany.mockResolvedValue([
+      { id: 'user-1', trustScore: 100 },
+      { id: 'user-2', trustScore: 200 },
+    ])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: 'Admin', email: 'admin@test.com' })
+
+    const service = await createService()
+
+    const result = await service.applyBulkManualAdjustments({
+      adjustments: new Map([
+        ['user-1', -5],
+        ['user-2', 10],
+      ]),
+      reason: 'Vote nullification: test',
+      adminUserId: 'admin-1',
+    })
+
+    expect(result).toBe(2)
+    expect(prismaCtx.user.update).toHaveBeenCalledTimes(2)
+    expect(prismaCtx.trustActionLog.createMany).toHaveBeenCalledTimes(1)
+
+    const logData = prismaCtx.trustActionLog.createMany.mock.calls[0][0].data as {
+      userId: string
+      action: TrustAction
+    }[]
+    expect(logData).toHaveLength(2)
+    expect(logData[0].userId).toBe('user-1')
+    expect(logData[0].action).toBe(TrustAction.ADMIN_ADJUSTMENT_NEGATIVE)
+    expect(logData[1].userId).toBe('user-2')
+    expect(logData[1].action).toBe(TrustAction.ADMIN_ADJUSTMENT_POSITIVE)
+  })
+
+  it('allows negative trust scores when adjustment exceeds current score (no clamping)', async () => {
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 5 }])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: 'Admin', email: 'admin@test.com' })
+
+    const service = await createService()
+
+    await service.applyBulkManualAdjustments({
+      adjustments: new Map([['user-1', -100]]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    // 5 + (-100) = -95 — negative scores are allowed (used for author-risk signals)
+    expect(prismaCtx.user.update).toHaveBeenCalledWith({
+      where: { id: 'user-1' },
+      data: { trustScore: -95, lastActiveAt: expect.any(Date) },
+    })
+
+    const logData = prismaCtx.trustActionLog.createMany.mock.calls[0][0].data as {
+      weight: number
+      metadata: { adjustment: number }
+    }[]
+    expect(logData[0].weight).toBe(-100)
+    expect(logData[0].metadata.adjustment).toBe(-100)
+  })
+
+  it('emits trustLevelChanged analytics when level changes', async () => {
+    const analytics = (await import('@/lib/analytics')).default
+
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 95 }])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: 'Admin', email: 'admin@test.com' })
+
+    const service = await createService()
+
+    await service.applyBulkManualAdjustments({
+      adjustments: new Map([['user-1', 10]]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    // 95 + 10 = 105, crosses from Newcomer (0) to Contributor (100)
+    expect(analytics.trust.trustLevelChanged).toHaveBeenCalledWith({
+      userId: 'user-1',
+      oldLevel: 'Newcomer',
+      newLevel: 'Contributor',
+      score: 105,
+    })
+  })
+
+  it('does not emit trustLevelChanged when level stays the same', async () => {
+    const analytics = (await import('@/lib/analytics')).default
+
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 50 }])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: 'Admin', email: 'admin@test.com' })
+
+    const service = await createService()
+
+    await service.applyBulkManualAdjustments({
+      adjustments: new Map([['user-1', 10]]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    // 50 + 10 = 60, still Newcomer
+    expect(analytics.trust.trustLevelChanged).not.toHaveBeenCalled()
+  })
+
+  it('wraps in $transaction when prisma has it', async () => {
+    const mockTransaction = vi.fn(async (fn: (ctx: MockPrismaCtx) => Promise<number>) =>
+      fn(prismaCtx),
+    )
+    const prismaWithTx = {
+      ...prismaCtx,
+      $transaction: mockTransaction,
+    }
+
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 50 }])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: 'Admin', email: 'admin@test.com' })
+
+    const { TrustService } = await import('./service')
+    const service = new TrustService(prismaWithTx as never)
+
+    await service.applyBulkManualAdjustments({
+      adjustments: new Map([['user-1', 5]]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    expect(mockTransaction).toHaveBeenCalledTimes(1)
+  })
+
+  it('calls execute directly when prisma is a transaction client (no $transaction)', async () => {
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 50 }])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: 'Admin', email: 'admin@test.com' })
+
+    const service = await createService()
+
+    const result = await service.applyBulkManualAdjustments({
+      adjustments: new Map([['user-1', 5]]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    expect(result).toBe(1)
+    expect(prismaCtx.user.update).toHaveBeenCalled()
+  })
+
+  it('uses positive action type for positive adjustments', async () => {
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 50 }])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: 'Admin', email: 'admin@test.com' })
+
+    const service = await createService()
+
+    await service.applyBulkManualAdjustments({
+      adjustments: new Map([['user-1', 10]]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    const logData = prismaCtx.trustActionLog.createMany.mock.calls[0][0].data as {
+      action: TrustAction
+    }[]
+    expect(logData[0].action).toBe(TrustAction.ADMIN_ADJUSTMENT_POSITIVE)
+  })
+
+  it('falls back to email when admin has no name', async () => {
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 50 }])
+    prismaCtx.user.findUnique.mockResolvedValue({ name: null, email: 'admin@test.com' })
+
+    const service = await createService()
+
+    await service.applyBulkManualAdjustments({
+      adjustments: new Map([['user-1', -5]]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    const logData = prismaCtx.trustActionLog.createMany.mock.calls[0][0].data as {
+      metadata: { adminName: string }
+    }[]
+    expect(logData[0].metadata.adminName).toBe('admin@test.com')
+  })
+
+  it('uses "Unknown Admin" when admin not found', async () => {
+    prismaCtx.user.findMany.mockResolvedValue([{ id: 'user-1', trustScore: 50 }])
+    prismaCtx.user.findUnique.mockResolvedValue(null)
+
+    const service = await createService()
+
+    await service.applyBulkManualAdjustments({
+      adjustments: new Map([['user-1', -5]]),
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    const logData = prismaCtx.trustActionLog.createMany.mock.calls[0][0].data as {
+      metadata: { adminName: string }
+    }[]
+    expect(logData[0].metadata.adminName).toBe('Unknown Admin')
+  })
+})
+
+describe('TrustService.applyManualAdjustment', () => {
+  let prismaCtx: MockPrismaCtx
+
+  beforeEach(async () => {
+    vi.clearAllMocks()
+    prismaCtx = createMockPrismaCtx()
+  })
+
+  async function createService() {
+    const { TrustService } = await import('./service')
+    return new TrustService(prismaCtx as never)
+  }
+
+  it('throws when adjustment is zero', async () => {
+    const service = await createService()
+
+    await expect(
+      service.applyManualAdjustment({
+        userId: 'user-1',
+        adjustment: 0,
+        reason: 'test',
+        adminUserId: 'admin-1',
+      }),
+    ).rejects.toThrow()
+  })
+
+  it('throws when user not found', async () => {
+    prismaCtx.user.findUnique.mockResolvedValueOnce(null)
+    const service = await createService()
+
+    await expect(
+      service.applyManualAdjustment({
+        userId: 'user-1',
+        adjustment: 5,
+        reason: 'test',
+        adminUserId: 'admin-1',
+      }),
+    ).rejects.toThrow()
+  })
+
+  it('allows negative trust score when adjustment goes below zero (no clamping)', async () => {
+    prismaCtx.user.findUnique
+      .mockResolvedValueOnce({ trustScore: 5, name: 'User', email: 'user@test.com' })
+      .mockResolvedValueOnce({ name: 'Admin', email: 'admin@test.com' })
+    const service = await createService()
+
+    await service.applyManualAdjustment({
+      userId: 'user-1',
+      adjustment: -100,
+      reason: 'test',
+      adminUserId: 'admin-1',
+    })
+
+    // 5 + (-100) = -95, not clamped to 0
+    expect(prismaCtx.user.update).toHaveBeenCalledWith({
+      where: { id: 'user-1' },
+      data: { trustScore: -95, lastActiveAt: expect.any(Date) },
+    })
+  })
+
+  it('logs the raw adjustment weight (no clamping adjustment)', async () => {
+    prismaCtx.user.findUnique
+      .mockResolvedValueOnce({ trustScore: 10, name: 'User', email: 'user@test.com' })
+      .mockResolvedValueOnce({ name: 'Admin', email: 'admin@test.com' })
+    const service = await createService()
+
+    await service.applyManualAdjustment({
+      userId: 'user-1',
+      adjustment: -50,
+      reason: 'Vote nullification',
+      adminUserId: 'admin-1',
+    })
+
+    expect(prismaCtx.trustActionLog.create).toHaveBeenCalledWith({
+      data: {
+        userId: 'user-1',
+        action: TrustAction.ADMIN_ADJUSTMENT_NEGATIVE,
+        weight: -50,
+        metadata: {
+          reason: 'Vote nullification',
+          adminUserId: 'admin-1',
+          adminName: 'Admin',
+          adjustment: -50,
+        },
+      },
+    })
+  })
+})
+
+describe('TrustService.reverseLogAction', () => {
+  let prismaCtx: MockPrismaCtx
+
+  beforeEach(async () => {
+    vi.clearAllMocks()
+    prismaCtx = createMockPrismaCtx()
+  })
+
+  async function createService() {
+    const { TrustService } = await import('./service')
+    return new TrustService(prismaCtx as never)
+  }
+
+  it('negates positive action weight (LISTING_RECEIVED_UPVOTE: +2 → -2)', async () => {
+    prismaCtx.user.findUnique.mockResolvedValue({ trustScore: 10 })
+    const service = await createService()
+
+    await service.reverseLogAction({
+      userId: 'author-1',
+      originalAction: TrustAction.LISTING_RECEIVED_UPVOTE,
+      metadata: { listingId: 'listing-1' },
+    })
+
+    expect(prismaCtx.user.update).toHaveBeenCalledWith({
+      where: { id: 'author-1' },
+      data: { trustScore: 8, lastActiveAt: expect.any(Date) },
+    })
+    expect(prismaCtx.trustActionLog.create).toHaveBeenCalledWith({
+      data: {
+        userId: 'author-1',
+        action: TrustAction.VOTE_CHANGE_REVERSAL,
+        weight: -2,
+        metadata: {
+          listingId: 'listing-1',
+          originalAction: TrustAction.LISTING_RECEIVED_UPVOTE,
+          reversed: true,
+        },
+      },
+    })
+  })
+
+  it('negates negative action weight (COMMENT_RECEIVED_DOWNVOTE: -1 → +1)', async () => {
+    prismaCtx.user.findUnique.mockResolvedValue({ trustScore: 5 })
+    const service = await createService()
+
+    await service.reverseLogAction({
+      userId: 'author-1',
+      originalAction: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
+      metadata: { commentId: 'comment-1' },
+    })
+
+    expect(prismaCtx.user.update).toHaveBeenCalledWith({
+      where: { id: 'author-1' },
+      data: { trustScore: 6, lastActiveAt: expect.any(Date) },
+    })
+    expect(prismaCtx.trustActionLog.create).toHaveBeenCalledWith({
+      data: {
+        userId: 'author-1',
+        action: TrustAction.VOTE_CHANGE_REVERSAL,
+        weight: 1,
+        metadata: {
+          commentId: 'comment-1',
+          originalAction: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
+          reversed: true,
+        },
+      },
+    })
+  })
+
+  it('allows negative trust score on reversal (no clamping)', async () => {
+    prismaCtx.user.findUnique.mockResolvedValue({ trustScore: 1 })
+    const service = await createService()
+
+    await service.reverseLogAction({
+      userId: 'author-1',
+      originalAction: TrustAction.LISTING_RECEIVED_UPVOTE, // weight +2, reversal = -2
+      metadata: {},
+    })
+
+    // 1 + (-2) = -1, allowed
+    expect(prismaCtx.user.update).toHaveBeenCalledWith({
+      where: { id: 'author-1' },
+      data: { trustScore: -1, lastActiveAt: expect.any(Date) },
+    })
+  })
+
+  it('no-ops when original action has weight 0', async () => {
+    const service = await createService()
+
+    await service.reverseLogAction({
+      userId: 'user-1',
+      originalAction: TrustAction.ADMIN_ADJUSTMENT_POSITIVE, // weight 0
+      metadata: {},
+    })
+
+    expect(prismaCtx.user.update).not.toHaveBeenCalled()
+    expect(prismaCtx.trustActionLog.create).not.toHaveBeenCalled()
+  })
+
+  it('throws on invalid action', async () => {
+    const service = await createService()
+
+    await expect(
+      service.reverseLogAction({
+        userId: 'user-1',
+        originalAction: 'NONEXISTENT_ACTION' as TrustAction,
+        metadata: {},
+      }),
+    ).rejects.toThrow('Invalid trust action')
+  })
+
+  it('wraps in $transaction when prisma has it', async () => {
+    const mockTransaction = vi.fn(async (fn: (ctx: MockPrismaCtx) => Promise<void>) =>
+      fn(prismaCtx),
+    )
+    const prismaWithTx = { ...prismaCtx, $transaction: mockTransaction }
+    prismaCtx.user.findUnique.mockResolvedValue({ trustScore: 10 })
+
+    const { TrustService } = await import('./service')
+    const service = new TrustService(prismaWithTx as never)
+
+    await service.reverseLogAction({
+      userId: 'user-1',
+      originalAction: TrustAction.LISTING_RECEIVED_UPVOTE,
+      metadata: {},
+    })
+
+    expect(mockTransaction).toHaveBeenCalledTimes(1)
+  })
+
+  it('calls execute directly when prisma is a transaction client (no $transaction)', async () => {
+    prismaCtx.user.findUnique.mockResolvedValue({ trustScore: 10 })
+    const service = await createService()
+
+    await service.reverseLogAction({
+      userId: 'user-1',
+      originalAction: TrustAction.LISTING_RECEIVED_UPVOTE,
+      metadata: {},
+    })
+
+    expect(prismaCtx.user.update).toHaveBeenCalled()
+  })
+
+  it('emits trustLevelChanged when crossing a level boundary', async () => {
+    const analytics = (await import('@/lib/analytics')).default
+
+    prismaCtx.user.findUnique.mockResolvedValue({ trustScore: 101 })
+    const service = await createService()
+
+    await service.reverseLogAction({
+      userId: 'user-1',
+      originalAction: TrustAction.LISTING_RECEIVED_UPVOTE, // -2
+      metadata: {},
+    })
+
+    // 101 + (-2) = 99, drops from Contributor (>=100) back to Newcomer
+    expect(analytics.trust.trustLevelChanged).toHaveBeenCalledWith({
+      userId: 'user-1',
+      oldLevel: 'Contributor',
+      newLevel: 'Newcomer',
+      score: 99,
+    })
+  })
+})
diff --git a/src/lib/trust/service.ts b/src/lib/trust/service.ts
index 7ebc3880d..f399f5620 100644
--- a/src/lib/trust/service.ts
+++ b/src/lib/trust/service.ts
@@ -1,5 +1,6 @@
 import { z } from 'zod'
 import analytics from '@/lib/analytics'
+import { ResourceError } from '@/lib/errors'
 import { prisma } from '@/server/db'
 import { validateData } from '@/server/utils/validation'
 import { TrustAction, type Prisma, type PrismaClient } from '@orm'
@@ -13,6 +14,7 @@ function resolveTrustLevelName(level: ReturnType<typeof getTrustLevel> | null):
 
 interface TrustActionContext {
   listingId?: string
+  pcListingId?: string
   targetUserId?: string
   voteType?: 'up' | 'down'
   adminUserId?: string
@@ -112,7 +114,7 @@ export async function reverseTrustAction(params: {
     })
 
     const currentTrustLevel = currentUser ? getTrustLevel(currentUser.trustScore) : null
-    const newTrustScore = Math.max(0, (currentUser?.trustScore || 0) + reversalWeight)
+    const newTrustScore = (currentUser?.trustScore ?? 0) + reversalWeight
     const newTrustLevel = getTrustLevel(newTrustScore)
 
     await tx.user.update({
@@ -228,44 +230,44 @@ export async function applyMonthlyActiveBonus(): Promise<{
   return { processedUsers, errors }
 }
 
-export async function applyManualTrustAdjustment(params: {
-  userId: string
-  adjustment: number
-  reason: string
-  adminUserId: string
-}): Promise<void> {
-  const { userId, adjustment, reason, adminUserId } = params
+type PrismaTransaction = Omit<
+  PrismaClient,
+  '$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends'
+>
 
-  if (adjustment === 0) {
-    // TODO: Custom Error, or not to Custom Error, that is the question
-    throw new Error('Adjustment cannot be zero')
-  }
+/**
+ * TrustService class for managing trust actions.
+ * Accepts either a PrismaClient (creates its own transactions) or a
+ * TransactionClient (participates in an outer transaction).
+ */
+export class TrustService {
+  constructor(private readonly prisma: PrismaClient | PrismaTransaction) {}
 
-  try {
-    // Get user's current trust score before applying adjustment
-    const currentUser = await prisma.user.findUnique({
-      where: { id: userId },
-      select: { trustScore: true, name: true, email: true },
-    })
+  async logAction(params: {
+    userId: string
+    action: TrustAction
+    targetUserId?: string
+    metadata?: Record<string, unknown>
+  }): Promise<void> {
+    const { userId, action, metadata } = params
 
-    if (!currentUser) {
-      // TODO: use proper error later, now i just want to sleep
-      throw new Error('User not found')
+    if (!TRUST_ACTIONS[action]) {
+      throw new Error(`Invalid trust action: ${action}`)
     }
 
-    const currentTrustLevel = getTrustLevel(currentUser.trustScore)
-    const newTrustScore = Math.max(0, currentUser.trustScore + adjustment) // Prevent negative trust scores
-    const newTrustLevel = getTrustLevel(newTrustScore)
-    const actualAdjustment = newTrustScore - currentUser.trustScore
+    const weight = TRUST_ACTIONS[action].weight
 
-    // Determine action type based on adjustment direction
-    const action =
-      adjustment > 0 ? TrustAction.ADMIN_ADJUSTMENT_POSITIVE : TrustAction.ADMIN_ADJUSTMENT_NEGATIVE
+    const executeInTransaction = async (prismaCtx: PrismaTransaction) => {
+      const currentUser = await prismaCtx.user.findUnique({
+        where: { id: userId },
+        select: { trustScore: true },
+      })
+
+      const currentTrustLevel = currentUser ? getTrustLevel(currentUser.trustScore) : null
+      const newTrustScore = (currentUser?.trustScore || 0) + weight
+      const newTrustLevel = getTrustLevel(newTrustScore)
 
-    // Use a transaction to ensure atomicity
-    await prisma.$transaction(async (tx) => {
-      // Update user's trust score
-      await tx.user.update({
+      await prismaCtx.user.update({
         where: { id: userId },
         data: {
           trustScore: newTrustScore,
@@ -273,77 +275,58 @@ export async function applyManualTrustAdjustment(params: {
         },
       })
 
-      // Create audit log entry with admin context
-      await tx.trustActionLog.create({
+      await prismaCtx.trustActionLog.create({
         data: {
           userId,
           action,
-          weight: actualAdjustment,
-          metadata: {
-            reason,
-            adminUserId,
-            adminName: await tx.user
-              .findUnique({
-                where: { id: adminUserId },
-                select: { name: true, email: true },
-              })
-              .then((admin) => admin?.name || admin?.email || 'Unknown Admin'),
-            originalAdjustment: adjustment,
-            actualAdjustment,
-          },
+          weight,
+          metadata: metadata as Prisma.InputJsonValue,
         },
       })
-    })
-
-    analytics.trust.trustScoreChanged({
-      userId,
-      oldScore: currentUser.trustScore,
-      newScore: newTrustScore,
-      action,
-      weight: actualAdjustment,
-    })
 
-    // Track trust level achievement if level changed
-    if ((currentTrustLevel.name ?? null) !== (newTrustLevel.name ?? null)) {
-      analytics.trust.trustLevelChanged({
+      analytics.trust.trustScoreChanged({
         userId,
-        oldLevel: resolveTrustLevelName(currentTrustLevel),
-        newLevel: resolveTrustLevelName(newTrustLevel),
-        score: newTrustScore,
+        oldScore: currentUser?.trustScore || 0,
+        newScore: newTrustScore,
+        action,
+        weight,
       })
-    }
-  } catch (error) {
-    console.error('Failed to apply manual trust adjustment:', error)
-    throw new Error('Failed to update trust score')
-  }
-}
 
-type PrismaTransaction = Omit<
-  PrismaClient,
-  '$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends'
->
+      const previousLevel = currentTrustLevel?.name ?? null
+      const nextLevel = newTrustLevel.name ?? null
 
-/**
- * TrustService class for managing trust actions
- */
-export class TrustService {
-  constructor(private readonly prisma: PrismaClient | PrismaTransaction) {}
+      if (previousLevel !== nextLevel) {
+        analytics.trust.trustLevelChanged({
+          userId,
+          oldLevel: resolveTrustLevelName(currentTrustLevel),
+          newLevel: resolveTrustLevelName(newTrustLevel),
+          score: newTrustScore,
+        })
+      }
+    }
 
-  async logAction(params: {
+    if ('$transaction' in this.prisma) {
+      await this.prisma.$transaction(executeInTransaction)
+    } else {
+      await executeInTransaction(this.prisma)
+    }
+  }
+
+  async reverseLogAction(params: {
     userId: string
-    action: TrustAction
+    originalAction: TrustAction
     targetUserId?: string
     metadata?: Record<string, unknown>
   }): Promise<void> {
-    const { userId, action, metadata } = params
+    const { userId, originalAction, metadata = {} } = params
 
-    if (!TRUST_ACTIONS[action]) {
-      throw new Error(`Invalid trust action: ${action}`)
+    if (!TRUST_ACTIONS[originalAction]) {
+      throw new Error(`Invalid trust action: ${originalAction}`)
     }
 
-    const weight = TRUST_ACTIONS[action].weight
+    const reversalWeight = -TRUST_ACTIONS[originalAction].weight
+    if (reversalWeight === 0) return
 
-    // Handle both regular prisma client and transaction context
     const executeInTransaction = async (prismaCtx: PrismaTransaction) => {
       const currentUser = await prismaCtx.user.findUnique({
         where: { id: userId },
@@ -351,7 +334,7 @@ export class TrustService {
       })
 
       const currentTrustLevel = currentUser ? getTrustLevel(currentUser.trustScore) : null
-      const newTrustScore = (currentUser?.trustScore || 0) + weight
+      const newTrustScore = (currentUser?.trustScore ?? 0) + reversalWeight
       const newTrustLevel = getTrustLevel(newTrustScore)
 
       await prismaCtx.user.update({
@@ -365,19 +348,18 @@ export class TrustService {
       await prismaCtx.trustActionLog.create({
         data: {
           userId,
-          action,
-          weight,
-          metadata: metadata as Prisma.InputJsonValue,
+          action: TrustAction.VOTE_CHANGE_REVERSAL,
+          weight: reversalWeight,
+          metadata: { ...metadata, originalAction, reversed: true } as Prisma.InputJsonValue,
         },
       })
 
-      // Track analytics
       analytics.trust.trustScoreChanged({
         userId,
-        oldScore: currentUser?.trustScore || 0,
+        oldScore: currentUser?.trustScore ?? 0,
         newScore: newTrustScore,
-        action,
-        weight,
+        action: TrustAction.VOTE_CHANGE_REVERSAL,
+        weight: reversalWeight,
       })
 
       const previousLevel = currentTrustLevel?.name ?? null
@@ -393,11 +375,199 @@ export class TrustService {
       }
     }
 
-    // If already in a transaction, use it; otherwise create a new one
     if ('$transaction' in this.prisma) {
       await this.prisma.$transaction(executeInTransaction)
     } else {
       await executeInTransaction(this.prisma)
     }
   }
+
+  async applyManualAdjustment(params: {
+    userId: string
+    adjustment: number
+    reason: string
+    adminUserId: string
+  }): Promise<void> {
+    const { userId, adjustment, reason, adminUserId } = params
+
+    if (adjustment === 0) {
+      throw ResourceError.trust.adjustmentCannotBeZero()
+    }
+
+    const executeAdjustment = async (prismaCtx: PrismaTransaction) => {
+      const currentUser = await prismaCtx.user.findUnique({
+        where: { id: userId },
+        select: { trustScore: true, name: true, email: true },
+      })
+
+      if (!currentUser) {
+        throw ResourceError.user.notFound()
+      }
+
+      const currentTrustLevel = getTrustLevel(currentUser.trustScore)
+      const newTrustScore = currentUser.trustScore + adjustment
+      const newTrustLevel = getTrustLevel(newTrustScore)
+
+      const action =
+        adjustment > 0
+          ? TrustAction.ADMIN_ADJUSTMENT_POSITIVE
+          : TrustAction.ADMIN_ADJUSTMENT_NEGATIVE
+
+      await prismaCtx.user.update({
+        where: { id: userId },
+        data: {
+          trustScore: newTrustScore,
+          lastActiveAt: new Date(),
+        },
+      })
+
+      const admin = await prismaCtx.user.findUnique({
+        where: { id: adminUserId },
+        select: { name: true, email: true },
+      })
+
+      await prismaCtx.trustActionLog.create({
+        data: {
+          userId,
+          action,
+          weight: adjustment,
+          metadata: {
+            reason,
+            adminUserId,
+            adminName: admin?.name || admin?.email || 'Unknown Admin',
+            adjustment,
+          },
+        },
+      })
+
+      analytics.trust.trustScoreChanged({
+        userId,
+        oldScore: currentUser.trustScore,
+        newScore: newTrustScore,
+        action,
+        weight: adjustment,
+      })
+
+      if ((currentTrustLevel.name ?? null) !== (newTrustLevel.name ?? null)) {
+        analytics.trust.trustLevelChanged({
+          userId,
+          oldLevel: resolveTrustLevelName(currentTrustLevel),
+          newLevel: resolveTrustLevelName(newTrustLevel),
+          score: newTrustScore,
+        })
+      }
+    }
+
+    if ('$transaction' in this.prisma) {
+      await this.prisma.$transaction(executeAdjustment)
+    } else {
+      await executeAdjustment(this.prisma)
+    }
+  }
+
+  async applyBulkManualAdjustments(params: {
+    adjustments: Map<string, number>
+    reason: string
+    adminUserId: string
+  }): Promise<number> {
+    const { adjustments, reason, adminUserId } = params
+
+    const nonZeroEntries = [...adjustments.entries()].filter(([, adj]) => adj !== 0)
+    if (nonZeroEntries.length === 0) return 0
+
+    const userIds = nonZeroEntries.map(([id]) => id)
+
+    const executeBulk = async (prismaCtx: PrismaTransaction) => {
+      const users = await prismaCtx.user.findMany({
+        where: { id: { in: userIds } },
+        select: { id: true, trustScore: true },
+      })
+
+      const userMap = new Map(users.map((u) => [u.id, u.trustScore]))
+      const foundEntries = nonZeroEntries.filter(([id]) => userMap.has(id))
+      if (foundEntries.length === 0) return 0
+
+      const admin = await prismaCtx.user.findUnique({
+        where: { id: adminUserId },
+        select: { name: true, email: true },
+      })
+      const adminName = admin?.name || admin?.email || 'Unknown Admin'
+
+      const logEntries: {
+        userId: string
+        action: TrustAction
+        weight: number
+        metadata: Prisma.InputJsonValue
+      }[] = []
+
+      const levelChanges: {
+        userId: string
+        oldLevel: string
+        newLevel: string
+        score: number
+      }[] = []
+
+      for (const [userId, adjustment] of foundEntries) {
+        const currentScore = userMap.get(userId) ?? 0
+        const newTrustScore = currentScore + adjustment
+
+        const action =
+          adjustment > 0
+            ? TrustAction.ADMIN_ADJUSTMENT_POSITIVE
+            : TrustAction.ADMIN_ADJUSTMENT_NEGATIVE
+
+        await prismaCtx.user.update({
+          where: { id: userId },
+          data: {
+            trustScore: newTrustScore,
+            lastActiveAt: new Date(),
+          },
+        })
+
+        logEntries.push({
+          userId,
+          action,
+          weight: adjustment,
+          metadata: {
+            reason,
+            adminUserId,
+            adminName,
+            adjustment,
+          },
+        })
+
+        analytics.trust.trustScoreChanged({
+          userId,
+          oldScore: currentScore,
+          newScore: newTrustScore,
+          action,
+          weight: adjustment,
+        })
+
+        const currentTrustLevel = getTrustLevel(currentScore)
+        const newTrustLevel = getTrustLevel(newTrustScore)
+        if ((currentTrustLevel.name ?? null) !== (newTrustLevel.name ?? null)) {
+          levelChanges.push({
+            userId,
+            oldLevel: resolveTrustLevelName(currentTrustLevel),
+            newLevel: resolveTrustLevelName(newTrustLevel),
+            score: newTrustScore,
+          })
+        }
+      }
+
+      await prismaCtx.trustActionLog.createMany({ data: logEntries })
+
+      for (const change of levelChanges) {
+        analytics.trust.trustLevelChanged(change)
+      }
+
+      return foundEntries.length
+    }
+
+    if ('$transaction' in this.prisma) {
+      return this.prisma.$transaction(executeBulk)
+    }
+    return executeBulk(this.prisma)
+  }
 }
diff --git a/src/schemas/listing.ts b/src/schemas/listing.ts
index 9ff49acab..03173cd69 100644
--- a/src/schemas/listing.ts
+++ b/src/schemas/listing.ts
@@ -1,6 +1,6 @@
 import { z } from 'zod'
 import { PAGINATION } from '@/data/constants'
-import { JsonValueSchema } from '@/schemas/common'
+import { JsonValueSchema, ListingType } from '@/schemas/common'
 import { ApprovalStatus } from '@orm'
 
 export const CreateListingSchema = z.object({
@@ -50,6 +50,11 @@ export const GetListingsSchema = z.object({
 
 export const GetListingByIdSchema = z.object({ id: z.string().uuid() })
 
+export const GetListingModeratorInfoSchema = z.object({
+  id: z.string().uuid(),
+  type: ListingType,
+})
+
 export const GetPendingListingsSchema = z
   .object({
     search: z.string().nullable().optional(),
@@ -77,6 +82,20 @@ export const GetProcessedSchema = z.object({
   limit: z.number().default(10),
   filterStatus: z.nativeEnum(ApprovalStatus).nullable().optional(),
   search: z.string().nullable().optional(),
+  sortField: z
+    .enum([
+      'processedAt',
+      'createdAt',
+      'status',
+      'game.title',
+      'game.system.name',
+      'device',
+      'emulator.name',
+      'author.name',
+    ])
+    .nullable()
+    .optional(),
+  sortDirection: z.enum(['asc', 'desc']).nullable().optional(),
 })
 
 export const OverrideApprovalStatusSchema = z.object({
diff --git a/src/schemas/pcListing.ts b/src/schemas/pcListing.ts
index 963a72900..441102ff7 100644
--- a/src/schemas/pcListing.ts
+++ b/src/schemas/pcListing.ts
@@ -78,6 +78,16 @@ export const GetPendingPcListingsSchema = z
 
 export const DeletePcListingSchema = z.object({ id: z.string().uuid() })
 
+// TODO: Wire up a PC admin processed-listings page + router procedure for
+// parity with handheld (`admin.getProcessed` + `src/app/admin/processed-listings/`).
+// When doing so, extend this schema with `sortField` / `sortDirection` using the
+// same shape as `GetProcessedSchema` in `./listing.ts`, and ideally share as much
+// of the admin router logic as possible (the two codebases are drifting — fixes
+// applied to handheld listings often miss their PC counterpart). Candidates for
+// shared code: `buildProcessedOrderBy`, the search `where` builder, the
+// approval-flow branches. See also: `src/server/api/utils/listingHelpers.ts`
+// (handheld) vs `pcListingHelpers.ts` (PC) — these helpers already exist and
+// should be the basis for a shared abstraction.
 export const GetProcessedPcSchema = z.object({
   page: z.number().default(1),
   limit: z.number().default(10),
@@ -225,6 +235,7 @@ export const GetPcPresetsSchema = z.object({
 export const VotePcListingSchema = z.object({
   pcListingId: z.string().uuid(),
   value: z.boolean(), // true = upvote, false = downvote
+  recaptchaToken: z.string().nullable().optional(),
 })
 
 export const GetPcListingUserVoteSchema = z.object({
diff --git a/src/server/api/routers/listings.ts b/src/server/api/routers/listings.ts
index 9d75104c7..77922ec7c 100644
--- a/src/server/api/routers/listings.ts
+++ b/src/server/api/routers/listings.ts
@@ -18,6 +18,7 @@ export const listingsRouter = createTRPCRouter({
   unpinComment: commentsRouter.unpinComment,
 
   // Admin operations
+  moderatorInfo: adminRouter.moderatorInfo,
   getPending: adminRouter.getPending,
   approveListing: adminRouter.approve,
   rejectListing: adminRouter.reject,
diff --git a/src/server/api/routers/listings/admin.ts b/src/server/api/routers/listings/admin.ts
index 79afa3273..ac06029da 100644
--- a/src/server/api/routers/listings/admin.ts
+++ b/src/server/api/routers/listings/admin.ts
@@ -1,10 +1,12 @@
 import { ResourceError, AppError } from '@/lib/errors'
 import { applyTrustAction } from '@/lib/trust/service'
+import { ListingType } from '@/schemas/common'
 import {
   ApproveListingSchema,
   RejectListingSchema,
   GetProcessedSchema,
   GetPendingListingsSchema,
+  GetListingModeratorInfoSchema,
   OverrideApprovalStatusSchema,
   DeleteListingSchema,
   BulkApproveListingsSchema,
@@ -24,6 +26,7 @@ import {
   viewStatisticsProcedure,
   protectedProcedure,
 } from '@/server/api/trpc'
+import { buildProcessedOrderBy } from '@/server/api/utils/listingHelpers'
 import {
   invalidateListing,
   invalidateListPages,
@@ -31,6 +34,8 @@ import {
   revalidateByTag,
 } from '@/server/cache/invalidation'
 import { notificationEventEmitter, NOTIFICATION_EVENTS } from '@/server/notifications/eventEmitter'
+import { ListingsRepository } from '@/server/repositories/listings.repository'
+import { PcListingsRepository } from '@/server/repositories/pc-listings.repository'
 import { computeAuthorRiskProfiles } from '@/server/services/author-risk.service'
 import { listingStatsCache } from '@/server/utils/cache/instances'
 import { generateEmulatorConfig } from '@/server/utils/emulator-config/emulator-detector'
@@ -43,6 +48,15 @@ const LISTING_STATS_CACHE_KEY = 'listing-stats'
 const mode = Prisma.QueryMode.insensitive
 
 export const adminRouter = createTRPCRouter({
+  moderatorInfo: moderatorProcedure
+    .input(GetListingModeratorInfoSchema)
+    .query(async ({ ctx, input }) => {
+      return input.type === ListingType.enum.handheld
+        ? new ListingsRepository(ctx.prisma).getModeratorInfo(input.id)
+        : new PcListingsRepository(ctx.prisma).getModeratorInfo(input.id)
+    }),
+
+  // TODO: abstract to service or repository
   getPending: developerProcedure.input(GetPendingListingsSchema).query(async ({ ctx, input }) => {
     const { search, page = 1, limit = 20, sortField, sortDirection } = input ?? {}
     const skip = (page - 1) * limit
@@ -123,7 +137,6 @@ export const adminRouter = createTRPCRouter({
           select: {
             id: true,
             name: true,
-            email: true,
             userBans: {
               where: {
                 isActive: true,
@@ -267,7 +280,7 @@ export const adminRouter = createTRPCRouter({
         },
       })
 
-      ResourceError.listing.cannotApproveBannedUser(banReason)
+      return ResourceError.listing.cannotApproveBannedUser(banReason)
     }
 
     // Update listing status
@@ -450,7 +463,7 @@ export const adminRouter = createTRPCRouter({
     }),
 
   getProcessed: superAdminProcedure.input(GetProcessedSchema).query(async ({ ctx, input }) => {
-    const { page, limit, filterStatus, search } = input
+    const { page, limit, filterStatus, search, sortField, sortDirection } = input
     const skip = (page - 1) * limit
 
     const baseWhere: Prisma.ListingWhereInput = {
@@ -474,19 +487,19 @@ export const adminRouter = createTRPCRouter({
       ...searchWhere,
     }
 
+    const orderBy = buildProcessedOrderBy(sortField, sortDirection)
+
     const listings = await ctx.prisma.listing.findMany({
       where: whereClause,
       include: {
         game: { include: { system: true } },
         device: { include: { brand: true } },
         emulator: true,
-        author: { select: { id: true, name: true, email: true } },
+        author: { select: { id: true, name: true } },
         performance: true,
-        processedByUser: { select: { id: true, name: true, email: true } }, // Admin who processed
-      },
-      orderBy: {
-        processedAt: 'desc', // Show most recently processed first
+        processedByUser: { select: { id: true, name: true } },
       },
+      orderBy,
       skip,
       take: limit,
     })
@@ -497,7 +510,7 @@ export const adminRouter = createTRPCRouter({
 
     return {
       listings,
-      pagination: paginate({ total: totalListings, page, limit: limit }),
+      pagination: paginate({ total: totalListings, page, limit }),
     }
   }),
 
@@ -666,22 +679,9 @@ export const adminRouter = createTRPCRouter({
           })
         }
 
-        // Apply trust actions for approved listings only
-        for (const listing of validListings) {
-          if (listing.authorId) {
-            await applyTrustAction({
-              userId: listing.authorId,
-              action: TrustAction.LISTING_APPROVED,
-              context: {
-                listingId: listing.id,
-                adminUserId,
-                reason: 'bulk_listing_approved',
-              },
-            })
-          }
-        }
-
-        // Return data needed for post-transaction operations
+        // Return data needed for post-transaction operations.
+        // Trust actions intentionally run AFTER the transaction commits — applyTrustAction
+        // opens its own internal transaction, so nesting would deadlock or surprise.
         return {
           validListings,
           bannedUserListings,
@@ -689,6 +689,24 @@ export const adminRouter = createTRPCRouter({
         }
       })
 
+      // Apply trust actions in parallel for approved listings (post-commit, distinct authors).
+      const approvedListingsWithAuthor = transactionResult.validListings.filter(
+        (l): l is typeof l & { authorId: string } => l.authorId !== null,
+      )
+      await Promise.all(
+        approvedListingsWithAuthor.map((listing) =>
+          applyTrustAction({
+            userId: listing.authorId,
+            action: TrustAction.LISTING_APPROVED,
+            context: {
+              listingId: listing.id,
+              adminUserId,
+              reason: 'bulk_listing_approved',
+            },
+          }),
+        ),
+      )
+
       // Emit notification events AFTER transaction completes successfully
       try {
         const { validListings } = transactionResult
@@ -827,28 +845,33 @@ export const adminRouter = createTRPCRouter({
           },
         })
 
-        // Apply trust actions for all rejected listings
-        for (const listing of listingsToReject) {
-          if (listing.authorId) {
-            await applyTrustAction({
-              userId: listing.authorId,
-              action: TrustAction.LISTING_REJECTED,
-              context: {
-                listingId: listing.id,
-                adminUserId,
-                reason: notes || 'bulk_listing_rejected',
-              },
-            })
-          }
-        }
-
-        // Return data needed for post-transaction operations
+        // Return data needed for post-transaction operations.
+        // Trust actions intentionally run AFTER the transaction commits — applyTrustAction
+        // opens its own internal transaction, so nesting would deadlock or surprise.
         return {
           listingsToReject,
           notFoundOrNotPendingIds,
         }
       })
 
+      // Apply trust actions in parallel for rejected listings (post-commit, distinct authors).
+      const rejectedListingsWithAuthor = transactionResult.listingsToReject.filter(
+        (l): l is typeof l & { authorId: string } => l.authorId !== null,
+      )
+      await Promise.all(
+        rejectedListingsWithAuthor.map((listing) =>
+          applyTrustAction({
+            userId: listing.authorId,
+            action: TrustAction.LISTING_REJECTED,
+            context: {
+              listingId: listing.id,
+              adminUserId,
+              reason: notes || 'bulk_listing_rejected',
+            },
+          }),
+        ),
+      )
+
       // NOTE: Emit notification events AFTER transaction completes successfully
       try {
         const { listingsToReject } = transactionResult
@@ -990,7 +1013,7 @@ export const adminRouter = createTRPCRouter({
         game: { include: { system: true } },
         device: { include: { brand: true, soc: true } },
         emulator: true,
-        author: { select: { id: true, name: true, email: true } },
+        author: { select: { id: true, name: true } },
         performance: true,
       },
       orderBy,
@@ -1027,7 +1050,7 @@ export const adminRouter = createTRPCRouter({
             },
           },
         },
-        author: { select: { id: true, name: true, email: true } },
+        author: { select: { id: true, name: true } },
         performance: true,
         customFieldValues: {
           include: { customFieldDefinition: { include: { category: true } } },
@@ -1055,7 +1078,7 @@ export const adminRouter = createTRPCRouter({
 
       if (!existingListing) return ResourceError.listing.notFound()
 
-      return ctx.prisma.$transaction(async (tx) => {
+      return await ctx.prisma.$transaction(async (tx) => {
         // Update the main listing fields
         const updatedListing = await tx.listing.update({
           where: { id },
diff --git a/src/server/api/routers/listings/comments.test.ts b/src/server/api/routers/listings/comments.test.ts
new file mode 100644
index 000000000..d25b3afec
--- /dev/null
+++ b/src/server/api/routers/listings/comments.test.ts
@@ -0,0 +1,199 @@
+import { describe, expect, it, beforeEach, vi } from 'vitest'
+import { Role } from '@orm'
+
+vi.unmock('@/server/api/trpc')
+vi.unmock('@/server/api/root')
+
+const mockHandleCommentVoteTrustEffects = vi.fn().mockResolvedValue(undefined)
+const mockEmitNotificationEvent = vi.fn()
+
+vi.mock('@/server/utils/vote-trust-effects', () => ({
+  handleCommentVoteTrustEffects: (...args: unknown[]) => mockHandleCommentVoteTrustEffects(...args),
+  handleListingVoteTrustEffects: vi.fn(),
+  handleVoteTrustEffects: vi.fn(),
+}))
+
+vi.mock('@/server/notifications/eventEmitter', () => ({
+  notificationEventEmitter: { emitNotificationEvent: mockEmitNotificationEvent },
+  NOTIFICATION_EVENTS: {
+    COMMENT_VOTED: 'COMMENT_VOTED',
+    LISTING_COMMENTED: 'LISTING_COMMENTED',
+    COMMENT_REPLIED: 'COMMENT_REPLIED',
+  },
+}))
+
+vi.mock('@/server/utils/query-builders', () => ({
+  isUserBanned: vi.fn().mockResolvedValue(false),
+}))
+
+vi.mock('@/lib/analytics', () => ({
+  default: {
+    engagement: { commentVote: vi.fn() },
+  },
+}))
+
+const { commentsRouter } = await import('./comments')
+
+const USER_ID = '00000000-0000-4000-a000-000000000001'
+const AUTHOR_ID = '00000000-0000-4000-a000-000000000002'
+const LISTING_ID = '00000000-0000-4000-a000-000000000010'
+const COMMENT_ID = '00000000-0000-4000-a000-000000000020'
+
+function createMockPrisma() {
+  const mockTx = {
+    commentVote: {
+      create: vi.fn().mockResolvedValue({ userId: USER_ID, commentId: COMMENT_ID, value: true }),
+      delete: vi.fn().mockResolvedValue(undefined),
+      update: vi.fn().mockResolvedValue({ userId: USER_ID, commentId: COMMENT_ID, value: false }),
+      findUnique: vi.fn().mockResolvedValue(null),
+    },
+    comment: {
+      findUnique: vi.fn(),
+      update: vi.fn().mockResolvedValue({ id: COMMENT_ID, score: 1 }),
+    },
+  }
+
+  return {
+    ...mockTx,
+    $transaction: vi.fn(async (cb: (tx: typeof mockTx) => Promise<unknown>) => cb(mockTx)),
+  }
+}
+
+type MockPrisma = ReturnType<typeof createMockPrisma>
+
+function createCaller(overrides: { userId?: string; role?: Role; prisma?: MockPrisma } = {}) {
+  const prisma = overrides.prisma ?? createMockPrisma()
+  return {
+    caller: commentsRouter.createCaller({
+      session: {
+        user: {
+          id: overrides.userId ?? USER_ID,
+          email: 'test@test.com',
+          name: 'Test User',
+          role: overrides.role ?? Role.USER,
+          permissions: [],
+          showNsfw: false,
+        },
+      },
+      prisma: prisma as never,
+      headers: new Headers(),
+    }),
+    prisma,
+  }
+}
+
+describe('handheld comments router — voteComment', () => {
+  beforeEach(() => {
+    vi.clearAllMocks()
+  })
+
+  function setupCommentMocks(prisma: MockPrisma) {
+    prisma.comment.findUnique.mockResolvedValue({
+      id: COMMENT_ID,
+      userId: AUTHOR_ID,
+      listingId: LISTING_ID,
+    })
+    prisma.commentVote.findUnique.mockResolvedValue(null)
+    prisma.comment.update.mockResolvedValue({ id: COMMENT_ID, score: 1 })
+  }
+
+  it('dispatches trust effects with listingType handheld on new upvote', async () => {
+    const { caller, prisma } = createCaller()
+    setupCommentMocks(prisma)
+
+    await caller.vote({ commentId: COMMENT_ID, value: true })
+
+    expect(mockHandleCommentVoteTrustEffects).toHaveBeenCalledWith(
+      expect.objectContaining({
+        trustAction: 'upvote',
+        commentAuthorId: AUTHOR_ID,
+        voterId: USER_ID,
+        commentId: COMMENT_ID,
+        parentEntityId: LISTING_ID,
+        listingType: 'handheld',
+      }),
+    )
+  })
+
+  it('dispatches trust effects with change action on vote flip', async () => {
+    const { caller, prisma } = createCaller()
+    setupCommentMocks(prisma)
+    prisma.commentVote.findUnique.mockResolvedValue({
+      userId: USER_ID,
+      commentId: COMMENT_ID,
+      value: true,
+    })
+
+    await caller.vote({ commentId: COMMENT_ID, value: false })
+
+    expect(mockHandleCommentVoteTrustEffects).toHaveBeenCalledWith(
+      expect.objectContaining({
+        trustAction: 'change',
+        previousValue: true,
+        newValue: false,
+        listingType: 'handheld',
+      }),
+    )
+  })
+
+  it('dispatches trust effects with remove action on toggle-off', async () => {
+    const { caller, prisma } = createCaller()
+    setupCommentMocks(prisma)
+    prisma.commentVote.findUnique.mockResolvedValue({
+      userId: USER_ID,
+      commentId: COMMENT_ID,
+      value: true,
+    })
+
+    await caller.vote({ commentId: COMMENT_ID, value: true })
+
+    expect(mockHandleCommentVoteTrustEffects).toHaveBeenCalledWith(
+      expect.objectContaining({
+        trustAction: 'remove',
+        listingType: 'handheld',
+      }),
+    )
+  })
+
+  it('emits COMMENT_VOTED on new upvote', async () => {
+    const { caller, prisma } = createCaller()
+    setupCommentMocks(prisma)
+
+    await caller.vote({ commentId: COMMENT_ID, value: true })
+
+    expect(mockEmitNotificationEvent).toHaveBeenCalledWith(
+      expect.objectContaining({
+        eventType: 'COMMENT_VOTED',
+        entityId: COMMENT_ID,
+        payload: expect.objectContaining({ voteValue: true, commentId: COMMENT_ID }),
+      }),
+    )
+  })
+
+  it('does NOT emit COMMENT_VOTED on toggle-off', async () => {
+    const { caller, prisma } = createCaller()
+    setupCommentMocks(prisma)
+    prisma.commentVote.findUnique.mockResolvedValue({
+      userId: USER_ID,
+      commentId: COMMENT_ID,
+      value: true,
+    })
+
+    await caller.vote({ commentId: COMMENT_ID, value: true })
+
+    expect(mockEmitNotificationEvent).not.toHaveBeenCalled()
+  })
+
+  it('fetches existingVote inside the $transaction callback', async () => {
+    const { caller, prisma } = createCaller()
+    setupCommentMocks(prisma)
+
+    await caller.vote({ commentId: COMMENT_ID, value: true })
+
+    const txCall = vi.mocked(prisma.$transaction).mock.invocationCallOrder[0]
+    const findUniqueCall = prisma.commentVote.findUnique.mock.invocationCallOrder[0]
+    expect(txCall).toBeDefined()
+    expect(findUniqueCall).toBeDefined()
+    expect(findUniqueCall).toBeGreaterThan(txCall as number)
+  })
+})
diff --git a/src/server/api/routers/listings/comments.ts b/src/server/api/routers/listings/comments.ts
index 878f89d7b..884d28900 100644
--- a/src/server/api/routers/listings/comments.ts
+++ b/src/server/api/routers/listings/comments.ts
@@ -2,7 +2,6 @@ import analytics from '@/lib/analytics'
 import { RECAPTCHA_CONFIG } from '@/lib/captcha/config'
 import { getClientIP, verifyRecaptcha } from '@/lib/captcha/verify'
 import { AppError, ResourceError } from '@/lib/errors'
-import { TrustService } from '@/lib/trust/service'
 import {
   CreateCommentSchema,
   EditCommentSchema,
@@ -20,15 +19,20 @@ import { notificationEventEmitter, NOTIFICATION_EVENTS } from '@/server/notifica
 import { CommentsRepository } from '@/server/repositories/comments.repository'
 import { logAudit } from '@/server/services/audit.service'
 import { isUserBanned } from '@/server/utils/query-builders'
+import { handleCommentVoteTrustEffects } from '@/server/utils/vote-trust-effects'
 import { roleIncludesRole } from '@/utils/permission-system'
 import { canDeleteComment, canEditComment } from '@/utils/permissions'
-import { AuditAction, AuditEntityType, Role, TrustAction } from '@orm'
+import { AuditAction, AuditEntityType, Role } from '@orm'
 
 export const commentsRouter = createTRPCRouter({
   create: protectedProcedure.input(CreateCommentSchema).mutation(async ({ ctx, input }) => {
     const { listingId, content, parentId, recaptchaToken } = input
     const userId = ctx.session.user.id
 
+    // TODO: Add spam detection via `checkSpamContent` from
+    // `@/server/utils/spam-check` (currently only applied in mobile routes).
+    // Block: UX/product sign-off needed since existing web users would start
+    // seeing spam-block errors. Mirror mobile: `{ userId, content, entityType: 'comment' }`.
     // Verify CAPTCHA if token is provided
     if (recaptchaToken) {
       const clientIP = ctx.headers ? getClientIP(ctx.headers) : undefined
@@ -322,13 +326,15 @@ export const commentsRouter = createTRPCRouter({
 
     if (!comment) return ResourceError.comment.notFound()
 
-    // Check if user already voted on this comment
-    const existingVote = await ctx.prisma.commentVote.findUnique({
-      where: { userId_commentId: { userId, commentId } },
-    })
+    // Fetch `existingVote` inside the transaction: two concurrent votes from
+    // the same user could both read null and both attempt to insert,
+    // producing a Prisma P2002 on the second. Keeping the read and write
+    // under the same isolation avoids the race.
+    return await ctx.prisma.$transaction(async (tx) => {
+      const existingVote = await tx.commentVote.findUnique({
+        where: { userId_commentId: { userId, commentId } },
+      })
 
-    // Start a transaction to handle both the vote and score update
-    return ctx.prisma.$transaction(async (tx) => {
       let voteResult
       let scoreChange: number
       let trustActionNeeded: 'upvote' | 'downvote' | 'change' | 'remove' | null = null
@@ -370,126 +376,26 @@ export const commentsRouter = createTRPCRouter({
         data: { score: { increment: scoreChange } },
       })
 
-      // Award trust points to the comment author (not the voter)
-      if (comment && comment.userId !== userId) {
-        // Don't award points for self-votes
-        const trustService = new TrustService(tx)
-
-        if (trustActionNeeded === 'upvote') {
-          // New upvote: +2 points to comment author
-          await trustService.logAction({
-            userId: comment.userId,
-            action: TrustAction.COMMENT_RECEIVED_UPVOTE,
-            targetUserId: userId,
-            metadata: {
-              commentId,
-              voterId: userId,
-              listingId: comment.listingId,
-            },
-          })
-        } else if (trustActionNeeded === 'downvote') {
-          // New downvote: -1 point to comment author
-          await trustService.logAction({
-            userId: comment.userId,
-            action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
-            targetUserId: userId,
-            metadata: {
-              commentId,
-              voterId: userId,
-              listingId: comment.listingId,
-            },
-          })
-        } else if (trustActionNeeded === 'change') {
-          // Vote changed: reverse previous and apply new
-          if (value) {
-            // Changed from downvote to upvote: +1 (reverse) +2 (new) = +3 net
-            await trustService.logAction({
-              userId: comment.userId,
-              action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
-              targetUserId: userId,
-              metadata: {
-                commentId,
-                voterId: userId,
-                listingId: comment.listingId,
-                reversed: true,
-              },
-            })
-            await trustService.logAction({
-              userId: comment.userId,
-              action: TrustAction.COMMENT_RECEIVED_UPVOTE,
-              targetUserId: userId,
-              metadata: {
-                commentId,
-                voterId: userId,
-                listingId: comment.listingId,
-              },
-            })
-          } else {
-            // Changed from upvote to downvote: -2 (reverse) -1 (new) = -3 net
-            await trustService.logAction({
-              userId: comment.userId,
-              action: TrustAction.COMMENT_RECEIVED_UPVOTE,
-              targetUserId: userId,
-              metadata: {
-                commentId,
-                voterId: userId,
-                listingId: comment.listingId,
-                reversed: true,
-              },
-            })
-            await trustService.logAction({
-              userId: comment.userId,
-              action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
-              targetUserId: userId,
-              metadata: {
-                commentId,
-                voterId: userId,
-                listingId: comment.listingId,
-              },
-            })
-          }
-        } else if (trustActionNeeded === 'remove' && existingVote) {
-          // Vote removed: reverse the previous vote's effect
-          const action = existingVote.value
-            ? TrustAction.COMMENT_RECEIVED_UPVOTE
-            : TrustAction.COMMENT_RECEIVED_DOWNVOTE
-          await trustService.logAction({
-            userId: comment.userId,
-            action,
-            targetUserId: userId,
-            metadata: {
-              commentId,
-              voterId: userId,
-              listingId: comment.listingId,
-              reversed: true,
-            },
-          })
-        }
-
-        // Check if comment has reached the helpful threshold (5+ score)
-        // Award bonus only when crossing the threshold for the first time
-        const HELPFUL_THRESHOLD = 5
-        const previousScore = updatedComment.score - scoreChange
-
-        if (previousScore < HELPFUL_THRESHOLD && updatedComment.score >= HELPFUL_THRESHOLD) {
-          // Comment just crossed the threshold - award bonus
-          await trustService.logAction({
-            userId: comment.userId,
-            action: TrustAction.HELPFUL_COMMENT,
-            metadata: {
-              commentId,
-              listingId: comment.listingId,
-              score: updatedComment.score,
-              threshold: HELPFUL_THRESHOLD,
-            },
-          })
-        }
+      if (trustActionNeeded) {
+        await handleCommentVoteTrustEffects({
+          tx,
+          trustAction: trustActionNeeded,
+          newValue: value,
+          previousValue: existingVote?.value ?? null,
+          commentAuthorId: comment.userId,
+          voterId: userId,
+          commentId,
+          parentEntityId: comment.listingId,
+          listingType: 'handheld',
+          updatedScore: updatedComment.score,
+          scoreChange,
+        })
       }
 
-      // Emit notification event
-      if (comment) {
+      // Notify comment author on new votes / direction changes; skip on toggle-off.
+      if (comment && trustActionNeeded !== null && trustActionNeeded !== 'remove') {
         notificationEventEmitter.emitNotificationEvent({
-          eventType: value ? NOTIFICATION_EVENTS.COMMENT_VOTED : NOTIFICATION_EVENTS.COMMENT_VOTED,
+          eventType: NOTIFICATION_EVENTS.COMMENT_VOTED,
           entityType: 'comment',
           entityId: comment.id,
           triggeredBy: ctx.session.user.id,
diff --git a/src/server/api/routers/listings/core.test.ts b/src/server/api/routers/listings/core.test.ts
new file mode 100644
index 000000000..3a8d54798
--- /dev/null
+++ b/src/server/api/routers/listings/core.test.ts
@@ -0,0 +1,233 @@
+import { describe, expect, it, beforeEach, vi } from 'vitest'
+import { Role } from '@orm'
+
+vi.unmock('@/server/api/trpc')
+vi.unmock('@/server/api/root')
+
+const mockApplyTrustAction = vi.fn().mockResolvedValue(undefined)
+const mockHandleListingVoteTrustEffects = vi.fn().mockResolvedValue(undefined)
+const mockHandleCommentVoteTrustEffects = vi.fn().mockResolvedValue(undefined)
+const mockLogAction = vi.fn().mockResolvedValue(undefined)
+const mockReverseLogAction = vi.fn().mockResolvedValue(undefined)
+
+vi.mock('@/lib/trust/service', () => ({
+  applyTrustAction: (...args: unknown[]) => mockApplyTrustAction(...args),
+  TrustService: vi.fn().mockImplementation(() => ({
+    logAction: mockLogAction,
+    reverseLogAction: mockReverseLogAction,
+  })),
+}))
+
+vi.mock('@/server/utils/vote-trust-effects', () => ({
+  handleListingVoteTrustEffects: (...args: unknown[]) => mockHandleListingVoteTrustEffects(...args),
+  handleCommentVoteTrustEffects: (...args: unknown[]) => mockHandleCommentVoteTrustEffects(...args),
+}))
+
+vi.mock('@/server/utils/vote-counts', () => ({
+  updateListingVoteCounts: vi.fn().mockResolvedValue(undefined),
+}))
+
+vi.mock('@/server/notifications/eventEmitter', () => ({
+  notificationEventEmitter: { emitNotificationEvent: vi.fn() },
+  NOTIFICATION_EVENTS: {
+    LISTING_VOTED: 'LISTING_VOTED',
+    COMMENT_VOTED: 'COMMENT_VOTED',
+    LISTING_APPROVED: 'LISTING_APPROVED',
+    LISTING_REJECTED: 'LISTING_REJECTED',
+    COMMENT_REPLIED: 'COMMENT_REPLIED',
+    LISTING_COMMENTED: 'LISTING_COMMENTED',
+  },
+}))
+
+vi.mock('@/server/utils/query-builders', () => ({
+  isUserBanned: vi.fn().mockResolvedValue(false),
+  listingWhereClause: vi.fn(() => ({})),
+}))
+
+vi.mock('@/lib/captcha/verify', () => ({
+  verifyRecaptcha: vi.fn().mockResolvedValue({ success: true }),
+  getClientIP: vi.fn().mockReturnValue('127.0.0.1'),
+}))
+
+vi.mock('@/lib/analytics', () => ({
+  default: {
+    engagement: { vote: vi.fn(), commentVote: vi.fn() },
+    userJourney: { firstTimeAction: vi.fn() },
+    listing: { created: vi.fn() },
+  },
+}))
+
+vi.mock('@/server/services/audit.service', () => ({
+  logAudit: vi.fn().mockResolvedValue(undefined),
+  buildDiff: vi.fn().mockReturnValue({}),
+}))
+
+vi.mock('@/server/utils/security-validation', () => ({
+  validatePagination: vi.fn((page, limit, max) => ({ page: page ?? 1, limit: limit ?? max ?? 20 })),
+  sanitizeInput: vi.fn((s: string) => s),
+}))
+
+vi.mock('@/server/repositories/listings.repository', () => ({
+  ListingsRepository: vi.fn().mockImplementation(() => ({
+    getExistingVote: vi.fn().mockResolvedValue(null),
+  })),
+}))
+
+const { coreRouter } = await import('./core')
+
+const USER_ID = '00000000-0000-4000-a000-000000000001'
+const AUTHOR_ID = '00000000-0000-4000-a000-000000000002'
+const LISTING_ID = '00000000-0000-4000-a000-000000000010'
+
+function createMockPrisma() {
+  const mockTx = {
+    vote: {
+      create: vi
+        .fn()
+        .mockResolvedValue({ id: 'vote-1', value: true, userId: USER_ID, listingId: LISTING_ID }),
+      delete: vi.fn().mockResolvedValue(undefined),
+      update: vi
+        .fn()
+        .mockResolvedValue({ id: 'vote-1', value: false, userId: USER_ID, listingId: LISTING_ID }),
+      findUnique: vi.fn().mockResolvedValue(null),
+      count: vi.fn().mockResolvedValue(1),
+    },
+    listing: {
+      findUnique: vi.fn(),
+      update: vi.fn().mockResolvedValue({}),
+    },
+    user: {
+      findUnique: vi.fn().mockResolvedValue({ id: USER_ID }),
+    },
+  }
+
+  return {
+    ...mockTx,
+    $transaction: vi.fn(async (cb: (tx: typeof mockTx) => Promise<unknown>) => cb(mockTx)),
+  }
+}
+
+type MockPrisma = ReturnType<typeof createMockPrisma>
+
+function createCaller(overrides: { userId?: string; role?: Role; prisma?: MockPrisma } = {}) {
+  const prisma = overrides.prisma ?? createMockPrisma()
+  return {
+    caller: coreRouter.createCaller({
+      session: {
+        user: {
+          id: overrides.userId ?? USER_ID,
+          email: 'test@test.com',
+          name: 'Test User',
+          role: overrides.role ?? Role.USER,
+          permissions: [],
+          showNsfw: false,
+        },
+      },
+      prisma: prisma as never,
+      headers: new Headers(),
+    }),
+    prisma,
+  }
+}
+
+describe('handheld listings trust integration (core.ts)', () => {
+  beforeEach(() => {
+    vi.clearAllMocks()
+  })
+
+  describe('vote', () => {
+    it('calls handleListingVoteTrustEffects with tx on new upvote', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+
+      await caller.vote({ listingId: LISTING_ID, value: true })
+
+      expect(mockHandleListingVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          action: 'created',
+          currentValue: true,
+          previousValue: null,
+          userId: USER_ID,
+          listingId: LISTING_ID,
+          listingType: 'handheld',
+          authorId: AUTHOR_ID,
+          tx: expect.any(Object),
+        }),
+      )
+    })
+
+    it('calls handleListingVoteTrustEffects with listingType handheld (not pc)', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+
+      await caller.vote({ listingId: LISTING_ID, value: true })
+
+      const firstCall = mockHandleListingVoteTrustEffects.mock.calls[0][0] as {
+        listingType: string
+      }
+      expect(firstCall.listingType).toBe('handheld')
+    })
+
+    it('calls handleListingVoteTrustEffects with action deleted on vote toggle', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+      prisma.vote.findUnique.mockResolvedValue({
+        id: 'vote-1',
+        value: true,
+        userId: USER_ID,
+        listingId: LISTING_ID,
+      })
+
+      await caller.vote({ listingId: LISTING_ID, value: true })
+
+      expect(mockHandleListingVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          action: 'deleted',
+          previousValue: true,
+          listingType: 'handheld',
+          tx: expect.any(Object),
+        }),
+      )
+    })
+
+    it('calls handleListingVoteTrustEffects with action updated on vote change', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+      prisma.vote.findUnique.mockResolvedValue({
+        id: 'vote-1',
+        value: true,
+        userId: USER_ID,
+        listingId: LISTING_ID,
+      })
+
+      await caller.vote({ listingId: LISTING_ID, value: false }) // change to downvote
+
+      expect(mockHandleListingVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          action: 'updated',
+          currentValue: false,
+          previousValue: true,
+          listingType: 'handheld',
+          tx: expect.any(Object),
+        }),
+      )
+    })
+
+    it('passes the transaction client to handleListingVoteTrustEffects', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+
+      await caller.vote({ listingId: LISTING_ID, value: true })
+
+      expect(prisma.$transaction).toHaveBeenCalledTimes(1)
+
+      const trustCall = mockHandleListingVoteTrustEffects.mock.calls[0][0] as {
+        tx: unknown
+      }
+      // The tx passed should be the same mockTx object passed to the transaction callback
+      expect(trustCall.tx).toBeDefined()
+      expect(trustCall.tx).toHaveProperty('vote')
+      expect(trustCall.tx).toHaveProperty('listing')
+    })
+  })
+})
diff --git a/src/server/api/routers/listings/core.ts b/src/server/api/routers/listings/core.ts
index 4200d69e2..5bc6d54f4 100644
--- a/src/server/api/routers/listings/core.ts
+++ b/src/server/api/routers/listings/core.ts
@@ -32,7 +32,7 @@ import { isUserBanned } from '@/server/utils/query-builders'
 import { sanitizeInput, validatePagination } from '@/server/utils/security-validation'
 import { withSavepoint } from '@/server/utils/transactions'
 import { updateListingVoteCounts } from '@/server/utils/vote-counts'
-import { handleVoteTrustEffects } from '@/server/utils/vote-trust-effects'
+import { handleListingVoteTrustEffects } from '@/server/utils/vote-trust-effects'
 import { roleIncludesRole } from '@/utils/permission-system'
 import { ms } from '@/utils/time'
 import { ApprovalStatus, Prisma, Role, TrustAction } from '@orm'
@@ -145,6 +145,10 @@ export const coreRouter = createTRPCRouter({
     const { recaptchaToken, ...payload } = input
     const authorId = ctx.session.user.id
 
+    // TODO: Add spam detection via `checkSpamContent` from
+    // `@/server/utils/spam-check` (currently only applied in mobile routes).
+    // Block: UX/product sign-off needed since existing web users would start
+    // seeing spam-block errors. Mirror mobile: `{ userId, content: notes, entityType: 'listing' }`.
     // Verify CAPTCHA if token is provided
     if (recaptchaToken) {
       const clientIP = ctx.headers ? getClientIP(ctx.headers) : undefined
@@ -269,6 +273,12 @@ export const coreRouter = createTRPCRouter({
         where: { userId_listingId: { userId, listingId: input.listingId } },
       })
 
+      let result: {
+        vote: { id: string; value: boolean; userId: string; listingId: string } | null
+        action: 'created' | 'updated' | 'deleted'
+        previousValue: boolean | null
+      }
+
       if (!existingVote) {
         // Create new vote
         const newVote = await tx.vote.create({
@@ -277,39 +287,44 @@ export const coreRouter = createTRPCRouter({
 
         await updateListingVoteCounts(tx, input.listingId, 'create', input.value)
 
-        return { vote: newVote, action: 'created' as const, previousValue: null }
-      }
-
-      // If value is the same, remove the vote (toggle)
-      if (existingVote.value === input.value) {
+        result = { vote: newVote, action: 'created', previousValue: null }
+      } else if (existingVote.value === input.value) {
         await tx.vote.delete({
           where: { userId_listingId: { userId, listingId: input.listingId } },
         })
 
         await updateListingVoteCounts(tx, input.listingId, 'delete', undefined, existingVote.value)
 
-        return { vote: null, action: 'deleted' as const, previousValue: existingVote.value }
-      }
+        result = { vote: null, action: 'deleted', previousValue: existingVote.value }
+      } else {
+        const updatedVote = await tx.vote.update({
+          where: { userId_listingId: { userId, listingId: input.listingId } },
+          data: { value: input.value },
+        })
 
-      // Update vote to new value
-      const updatedVote = await tx.vote.update({
-        where: { userId_listingId: { userId, listingId: input.listingId } },
-        data: { value: input.value },
-      })
+        await updateListingVoteCounts(
+          tx,
+          input.listingId,
+          'update',
+          input.value,
+          existingVote.value,
+        )
 
-      await updateListingVoteCounts(tx, input.listingId, 'update', input.value, existingVote.value)
+        result = { vote: updatedVote, action: 'updated', previousValue: existingVote.value }
+      }
 
-      return { vote: updatedVote, action: 'updated' as const, previousValue: existingVote.value }
-    })
+      await handleListingVoteTrustEffects({
+        tx,
+        action: result.action,
+        currentValue: input.value,
+        previousValue: result.previousValue,
+        userId,
+        listingId: input.listingId,
+        listingType: 'handheld',
+        authorId: listing.authorId,
+      })
 
-    // Handle trust effects for all vote actions
-    await handleVoteTrustEffects({
-      action: voteResult.action,
-      currentValue: input.value,
-      previousValue: voteResult.previousValue,
-      userId,
-      listingId: input.listingId,
-      authorId: listing.authorId,
+      return result
     })
 
     // Handle post-transaction side effects for created/updated votes (TODO: consider abstracting)
@@ -342,7 +357,7 @@ export const coreRouter = createTRPCRouter({
       }
     }
 
-    return voteResult.vote || { id: '', value: false, listingId: input.listingId, userId }
+    return voteResult.vote
   }),
 
   performanceScales: publicProcedure.query(async ({ ctx }) =>
diff --git a/src/server/api/routers/mobile/listings.test.ts b/src/server/api/routers/mobile/listings.test.ts
new file mode 100644
index 000000000..d3d24cf5e
--- /dev/null
+++ b/src/server/api/routers/mobile/listings.test.ts
@@ -0,0 +1,370 @@
+import { describe, expect, it, beforeEach, vi } from 'vitest'
+import { Role } from '@orm'
+
+vi.unmock('@/server/api/trpc')
+vi.unmock('@/server/api/root')
+
+// Stub the api-keys schema so it doesn't pull unmocked enums from @orm
+vi.mock('@/schemas/apiAccess', () => ({
+  GetApiKeyUsageSchema: {},
+  CreateApiKeySchema: {},
+  UpdateApiKeySchema: {},
+  RevokeApiKeySchema: {},
+  ListApiKeysSchema: {},
+}))
+
+vi.mock('@/server/repositories/api-keys.repository', () => ({
+  ApiKeysRepository: vi.fn().mockImplementation(() => ({})),
+}))
+
+const mockApplyTrustAction = vi.fn().mockResolvedValue(undefined)
+const mockHandleListingVoteTrustEffects = vi.fn().mockResolvedValue(undefined)
+const mockHandleCommentVoteTrustEffects = vi.fn().mockResolvedValue(undefined)
+const mockLogAction = vi.fn().mockResolvedValue(undefined)
+
+vi.mock('@/lib/trust/service', () => ({
+  applyTrustAction: (...args: unknown[]) => mockApplyTrustAction(...args),
+  TrustService: vi.fn().mockImplementation(() => ({ logAction: mockLogAction })),
+}))
+
+vi.mock('@/server/utils/vote-trust-effects', () => ({
+  handleListingVoteTrustEffects: (...args: unknown[]) => mockHandleListingVoteTrustEffects(...args),
+  handleCommentVoteTrustEffects: (...args: unknown[]) => mockHandleCommentVoteTrustEffects(...args),
+}))
+
+vi.mock('@/server/utils/vote-counts', () => ({
+  updateListingVoteCounts: vi.fn().mockResolvedValue(undefined),
+}))
+
+vi.mock('@/lib/analytics', () => ({
+  default: {
+    engagement: { vote: vi.fn(), commentVote: vi.fn() },
+  },
+}))
+
+vi.mock('@/server/repositories/comments.repository', () => ({
+  CommentsRepository: vi.fn().mockImplementation(() => ({
+    listByListing: vi.fn().mockResolvedValue([]),
+  })),
+}))
+
+const mockEmitNotificationEvent = vi.fn()
+vi.mock('@/server/notifications/eventEmitter', () => ({
+  notificationEventEmitter: { emitNotificationEvent: mockEmitNotificationEvent },
+  NOTIFICATION_EVENTS: {
+    LISTING_VOTED: 'LISTING_VOTED',
+    COMMENT_VOTED: 'COMMENT_VOTED',
+  },
+}))
+
+const { mobileListingsRouter } = await import('./listings')
+
+const USER_ID = '00000000-0000-4000-a000-000000000001'
+const AUTHOR_ID = '00000000-0000-4000-a000-000000000002'
+const LISTING_ID = '00000000-0000-4000-a000-000000000010'
+const COMMENT_ID = '00000000-0000-4000-a000-000000000020'
+
+function createMockPrisma() {
+  const mockTx = {
+    vote: {
+      create: vi
+        .fn()
+        .mockResolvedValue({ id: 'vote-1', value: true, userId: USER_ID, listingId: LISTING_ID }),
+      delete: vi.fn().mockResolvedValue(undefined),
+      update: vi
+        .fn()
+        .mockResolvedValue({ id: 'vote-1', value: false, userId: USER_ID, listingId: LISTING_ID }),
+      findUnique: vi.fn().mockResolvedValue(null),
+    },
+    commentVote: {
+      create: vi.fn().mockResolvedValue({ userId: USER_ID, commentId: COMMENT_ID, value: true }),
+      delete: vi.fn().mockResolvedValue(undefined),
+      update: vi.fn().mockResolvedValue({ userId: USER_ID, commentId: COMMENT_ID, value: false }),
+      findUnique: vi.fn().mockResolvedValue(null),
+    },
+    comment: {
+      findUnique: vi.fn(),
+      update: vi.fn().mockResolvedValue({ id: COMMENT_ID, score: 1 }),
+    },
+    listing: {
+      findUnique: vi.fn(),
+    },
+  }
+  return {
+    ...mockTx,
+    $transaction: vi.fn(async (cb: (tx: typeof mockTx) => Promise<unknown>) => cb(mockTx)),
+  }
+}
+
+type MockPrisma = ReturnType<typeof createMockPrisma>
+
+function createCaller(overrides: { userId?: string; role?: Role; prisma?: MockPrisma } = {}) {
+  const prisma = overrides.prisma ?? createMockPrisma()
+  return {
+    caller: mobileListingsRouter.createCaller({
+      session: {
+        user: {
+          id: overrides.userId ?? USER_ID,
+          email: 'test@test.com',
+          name: 'Test User',
+          role: overrides.role ?? Role.USER,
+          permissions: [],
+          showNsfw: false,
+        },
+      },
+      prisma: prisma as never,
+      headers: new Headers(),
+      apiKey: null,
+    }),
+    prisma,
+  }
+}
+
+describe('mobile listings trust integration', () => {
+  beforeEach(() => {
+    vi.clearAllMocks()
+  })
+
+  describe('vote', () => {
+    it('calls handleListingVoteTrustEffects with tx on new upvote', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ authorId: AUTHOR_ID })
+
+      await caller.vote({ listingId: LISTING_ID, value: true })
+
+      expect(mockHandleListingVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          action: 'created',
+          currentValue: true,
+          previousValue: null,
+          userId: USER_ID,
+          listingId: LISTING_ID,
+          listingType: 'handheld',
+          authorId: AUTHOR_ID,
+          tx: expect.any(Object),
+        }),
+      )
+    })
+
+    it('calls handleListingVoteTrustEffects with action deleted on vote toggle', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ authorId: AUTHOR_ID })
+      prisma.vote.findUnique.mockResolvedValue({
+        id: 'vote-1',
+        value: true,
+        userId: USER_ID,
+        listingId: LISTING_ID,
+      })
+
+      await caller.vote({ listingId: LISTING_ID, value: true })
+
+      expect(mockHandleListingVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          action: 'deleted',
+          previousValue: true,
+          listingType: 'handheld',
+          tx: expect.any(Object),
+        }),
+      )
+    })
+
+    it('calls handleListingVoteTrustEffects with action updated on vote change', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ authorId: AUTHOR_ID })
+      prisma.vote.findUnique.mockResolvedValue({
+        id: 'vote-1',
+        value: true,
+        userId: USER_ID,
+        listingId: LISTING_ID,
+      })
+
+      await caller.vote({ listingId: LISTING_ID, value: false })
+
+      expect(mockHandleListingVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          action: 'updated',
+          currentValue: false,
+          previousValue: true,
+          tx: expect.any(Object),
+        }),
+      )
+    })
+
+    it('runs trust call inside the transaction', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ authorId: AUTHOR_ID })
+
+      await caller.vote({ listingId: LISTING_ID, value: true })
+
+      expect(prisma.$transaction).toHaveBeenCalledTimes(1)
+      const trustCall = mockHandleListingVoteTrustEffects.mock.calls[0][0] as { tx: unknown }
+      expect(trustCall.tx).toHaveProperty('vote')
+    })
+
+    it('emits LISTING_VOTED on a new vote', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ authorId: AUTHOR_ID })
+
+      await caller.vote({ listingId: LISTING_ID, value: true })
+
+      expect(mockEmitNotificationEvent).toHaveBeenCalledWith(
+        expect.objectContaining({
+          eventType: 'LISTING_VOTED',
+          entityType: 'listing',
+          entityId: LISTING_ID,
+          payload: expect.objectContaining({ voteValue: true, listingId: LISTING_ID }),
+        }),
+      )
+    })
+
+    it('does NOT emit LISTING_VOTED on toggle-off', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.listing.findUnique.mockResolvedValue({ authorId: AUTHOR_ID })
+      prisma.vote.findUnique.mockResolvedValue({
+        id: 'vote-1',
+        value: true,
+        userId: USER_ID,
+        listingId: LISTING_ID,
+      })
+
+      await caller.vote({ listingId: LISTING_ID, value: true })
+
+      expect(mockEmitNotificationEvent).not.toHaveBeenCalled()
+    })
+  })
+
+  describe('voteComment', () => {
+    it('calls handleCommentVoteTrustEffects with listingType handheld on new upvote', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.comment.findUnique.mockResolvedValue({
+        id: COMMENT_ID,
+        userId: AUTHOR_ID,
+        listingId: LISTING_ID,
+      })
+      prisma.commentVote.findUnique.mockResolvedValue(null)
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      expect(mockHandleCommentVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          trustAction: 'upvote',
+          newValue: true,
+          commentAuthorId: AUTHOR_ID,
+          voterId: USER_ID,
+          commentId: COMMENT_ID,
+          parentEntityId: LISTING_ID,
+          listingType: 'handheld',
+        }),
+      )
+    })
+
+    it('calls handleCommentVoteTrustEffects with change action on vote flip', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.comment.findUnique.mockResolvedValue({
+        id: COMMENT_ID,
+        userId: AUTHOR_ID,
+        listingId: LISTING_ID,
+      })
+      prisma.commentVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        commentId: COMMENT_ID,
+        value: true,
+      })
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: false })
+
+      expect(mockHandleCommentVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          trustAction: 'change',
+          newValue: false,
+          previousValue: true,
+          listingType: 'handheld',
+        }),
+      )
+    })
+
+    it('calls handleCommentVoteTrustEffects with remove action on vote toggle', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.comment.findUnique.mockResolvedValue({
+        id: COMMENT_ID,
+        userId: AUTHOR_ID,
+        listingId: LISTING_ID,
+      })
+      prisma.commentVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        commentId: COMMENT_ID,
+        value: true,
+      })
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      expect(mockHandleCommentVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          trustAction: 'remove',
+          listingType: 'handheld',
+        }),
+      )
+    })
+
+    it('emits COMMENT_VOTED on a new upvote', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.comment.findUnique.mockResolvedValue({
+        id: COMMENT_ID,
+        userId: AUTHOR_ID,
+        listingId: LISTING_ID,
+      })
+      prisma.commentVote.findUnique.mockResolvedValue(null)
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      expect(mockEmitNotificationEvent).toHaveBeenCalledWith(
+        expect.objectContaining({
+          eventType: 'COMMENT_VOTED',
+          entityType: 'comment',
+          entityId: COMMENT_ID,
+          payload: expect.objectContaining({
+            commentId: COMMENT_ID,
+            listingId: LISTING_ID,
+            voteValue: true,
+          }),
+        }),
+      )
+    })
+
+    it('does NOT emit COMMENT_VOTED on toggle-off (remove)', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.comment.findUnique.mockResolvedValue({
+        id: COMMENT_ID,
+        userId: AUTHOR_ID,
+        listingId: LISTING_ID,
+      })
+      prisma.commentVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        commentId: COMMENT_ID,
+        value: true,
+      })
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      expect(mockEmitNotificationEvent).not.toHaveBeenCalled()
+    })
+
+    it('fetches existingVote inside the $transaction callback', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.comment.findUnique.mockResolvedValue({
+        id: COMMENT_ID,
+        userId: AUTHOR_ID,
+        listingId: LISTING_ID,
+      })
+      prisma.commentVote.findUnique.mockResolvedValue(null)
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      const txCall = vi.mocked(prisma.$transaction).mock.invocationCallOrder[0]
+      const findUniqueCall = prisma.commentVote.findUnique.mock.invocationCallOrder[0]
+      expect(txCall).toBeDefined()
+      expect(findUniqueCall).toBeDefined()
+      expect(findUniqueCall).toBeGreaterThan(txCall as number)
+    })
+  })
+})
diff --git a/src/server/api/routers/mobile/listings.ts b/src/server/api/routers/mobile/listings.ts
index ffe38d6e0..0312b56ee 100644
--- a/src/server/api/routers/mobile/listings.ts
+++ b/src/server/api/routers/mobile/listings.ts
@@ -1,6 +1,5 @@
 import analytics from '@/lib/analytics'
 import { AppError, ResourceError } from '@/lib/errors'
-import { TrustService } from '@/lib/trust/service'
 import { CreateListingSchema } from '@/schemas/listing'
 import {
   CreateCommentSchema,
@@ -26,6 +25,7 @@ import {
   mobileProtectedProcedure,
   mobilePublicProcedure,
 } from '@/server/api/mobileContext'
+import { NOTIFICATION_EVENTS, notificationEventEmitter } from '@/server/notifications/eventEmitter'
 import { CommentsRepository } from '@/server/repositories/comments.repository'
 import { ListingsRepository } from '@/server/repositories/listings.repository'
 import { getDriverVersions } from '@/server/utils/driver-versions'
@@ -34,10 +34,14 @@ import {
   detectEmulatorConfigType,
   generateEmulatorConfig,
 } from '@/server/utils/emulator-config/emulator-detector'
-import { SpamDetectionService } from '@/server/utils/spamDetection'
+import { checkSpamContent } from '@/server/utils/spam-check'
 import { updateListingVoteCounts } from '@/server/utils/vote-counts'
+import {
+  handleCommentVoteTrustEffects,
+  handleListingVoteTrustEffects,
+} from '@/server/utils/vote-trust-effects'
 import { isModerator } from '@/utils/permissions'
-import { ApprovalStatus, Prisma, TrustAction, type PrismaClient, type Role } from '@orm'
+import { ApprovalStatus, Prisma, type PrismaClient, type Role } from '@orm'
 
 // Helper for getting listings using the repository
 async function getListingsHelper(
@@ -144,28 +148,13 @@ export const mobileListingsRouter = createMobileTRPCRouter({
     const { ...payload } = input
     const repository = new ListingsRepository(ctx.prisma)
 
-    // Spam detection
-    const spamDetector = new SpamDetectionService(ctx.prisma)
-    const spamResult = await spamDetector.detectSpam({
+    await checkSpamContent({
+      prisma: ctx.prisma,
       userId: ctx.session.user.id,
       content: payload.notes || '',
       entityType: 'listing',
     })
 
-    if (spamResult.isSpam) {
-      // Track spam detection in analytics
-      analytics.contentQuality.spamDetected({
-        entityType: 'listing',
-        entityId: ctx.session.user.id, // Use userId as entityId since listing not created yet
-        confidence: spamResult.confidence,
-        method: spamResult.method,
-      })
-
-      throw AppError.badRequest(
-        `Spam detected: ${spamResult.reason || 'Your content appears to be spam. Please review our community guidelines.'}`,
-      )
-    }
-
     return await repository.create({
       authorId: ctx.session.user.id,
       userRole: ctx.session.user.role,
@@ -239,68 +228,89 @@ export const mobileListingsRouter = createMobileTRPCRouter({
    * Vote on a listing
    */
   vote: mobileProtectedProcedure.input(VoteListingSchema).mutation(async ({ ctx, input }) => {
-    return await ctx.prisma.$transaction(async (tx) => {
-      // Check if user already voted
-      const existingVote = await tx.vote.findUnique({
-        where: {
-          userId_listingId: {
-            userId: ctx.session.user.id,
-            listingId: input.listingId,
-          },
-        },
-      })
+    const userId = ctx.session.user.id
 
-      let vote
+    const listing = await ctx.prisma.listing.findUnique({
+      where: { id: input.listingId },
+      select: { authorId: true },
+    })
 
-      if (existingVote) {
-        if (existingVote.value === input.value) {
-          // Same vote = toggle off (remove vote)
-          await tx.vote.delete({
-            where: { id: existingVote.id },
-          })
+    if (!listing) return ResourceError.listing.notFound()
 
-          // Update counts for deletion
-          await updateListingVoteCounts(
-            tx,
-            input.listingId,
-            'delete',
-            undefined,
-            existingVote.value,
-          )
+    const voteResult = await ctx.prisma.$transaction(async (tx) => {
+      const existingVote = await tx.vote.findUnique({
+        where: { userId_listingId: { userId, listingId: input.listingId } },
+      })
 
-          return { id: existingVote.id, value: null, removed: true }
-        } else {
-          // Different vote = update
-          vote = await tx.vote.update({
-            where: { id: existingVote.id },
-            data: { value: input.value },
-          })
+      let result: {
+        vote:
+          | { id: string; value: boolean; userId: string; listingId: string }
+          | { id: string; value: null; removed: true }
+        action: 'created' | 'updated' | 'deleted'
+        previousValue: boolean | null
+      }
 
-          // Update counts for change
-          await updateListingVoteCounts(
-            tx,
-            input.listingId,
-            'update',
-            input.value,
-            existingVote.value,
-          )
+      if (!existingVote) {
+        const vote = await tx.vote.create({
+          data: { value: input.value, listingId: input.listingId, userId },
+        })
+        await updateListingVoteCounts(tx, input.listingId, 'create', input.value)
+        result = { vote, action: 'created', previousValue: null }
+      } else if (existingVote.value === input.value) {
+        await tx.vote.delete({ where: { id: existingVote.id } })
+        await updateListingVoteCounts(tx, input.listingId, 'delete', undefined, existingVote.value)
+        result = {
+          vote: { id: existingVote.id, value: null, removed: true },
+          action: 'deleted',
+          previousValue: existingVote.value,
         }
       } else {
-        // New vote
-        vote = await tx.vote.create({
-          data: {
-            value: input.value,
+        const vote = await tx.vote.update({
+          where: { id: existingVote.id },
+          data: { value: input.value },
+        })
+        await updateListingVoteCounts(
+          tx,
+          input.listingId,
+          'update',
+          input.value,
+          existingVote.value,
+        )
+        result = { vote, action: 'updated', previousValue: existingVote.value }
+      }
+
+      await handleListingVoteTrustEffects({
+        tx,
+        action: result.action,
+        currentValue: input.value,
+        previousValue: result.previousValue,
+        userId,
+        listingId: input.listingId,
+        listingType: 'handheld',
+        authorId: listing.authorId,
+      })
+
+      return result
+    })
+
+    // Notify listing author on new votes / direction changes; skip on toggle-off.
+    if (voteResult.action === 'created' || voteResult.action === 'updated') {
+      if (voteResult.vote && 'id' in voteResult.vote) {
+        notificationEventEmitter.emitNotificationEvent({
+          eventType: NOTIFICATION_EVENTS.LISTING_VOTED,
+          entityType: 'listing',
+          entityId: input.listingId,
+          triggeredBy: userId,
+          payload: {
             listingId: input.listingId,
-            userId: ctx.session.user.id,
+            voteId: voteResult.vote.id,
+            voteValue: input.value,
           },
         })
-
-        // Update counts for new vote
-        await updateListingVoteCounts(tx, input.listingId, 'create', input.value)
       }
+    }
 
-      return vote
-    })
+    return voteResult.vote
   }),
 
   /**
@@ -371,28 +381,13 @@ export const mobileListingsRouter = createMobileTRPCRouter({
     .mutation(async ({ ctx, input }) => {
       const { listingId, content, parentId } = input
 
-      // Spam detection
-      const spamDetector = new SpamDetectionService(ctx.prisma)
-      const spamResult = await spamDetector.detectSpam({
+      await checkSpamContent({
+        prisma: ctx.prisma,
         userId: ctx.session.user.id,
         content,
         entityType: 'comment',
       })
 
-      if (spamResult.isSpam) {
-        // Track spam detection in analytics
-        analytics.contentQuality.spamDetected({
-          entityType: 'comment',
-          entityId: ctx.session.user.id, // Use userId as entityId since comment not created yet
-          confidence: spamResult.confidence,
-          method: spamResult.method,
-        })
-
-        throw AppError.badRequest(
-          `Spam detected: ${spamResult.reason || 'Your content appears to be spam. Please review our community guidelines.'}`,
-        )
-      }
-
       // If replying to a comment, validate parent existence and ownership
       if (parentId) {
         const parent = await ctx.prisma.comment.findUnique({
@@ -526,11 +521,15 @@ export const mobileListingsRouter = createMobileTRPCRouter({
       const comment = await ctx.prisma.comment.findUnique({ where: { id: input.commentId } })
       if (!comment) return ResourceError.comment.notFound()
 
-      const existingVote = await ctx.prisma.commentVote.findUnique({
-        where: { userId_commentId: { userId, commentId: input.commentId } },
-      })
+      // Fetch `existingVote` inside the transaction: two concurrent votes
+      // from the same user could both read null and both attempt to insert,
+      // producing a Prisma P2002 on the second. Keeping the read and write
+      // under the same isolation avoids the race.
+      return await ctx.prisma.$transaction(async (tx) => {
+        const existingVote = await tx.commentVote.findUnique({
+          where: { userId_commentId: { userId, commentId: input.commentId } },
+        })
 
-      return ctx.prisma.$transaction(async (tx) => {
         let voteResult: unknown
         let scoreChange: number
         let trustActionNeeded: 'upvote' | 'downvote' | 'change' | 'remove' | null
@@ -568,110 +567,37 @@ export const mobileListingsRouter = createMobileTRPCRouter({
           data: { score: { increment: scoreChange } },
         })
 
-        // Award trust points to the comment author (not the voter)
-        if (comment && comment.userId !== userId) {
-          const trustService = new TrustService(tx)
-
-          if (trustActionNeeded === 'upvote') {
-            await trustService.logAction({
-              userId: comment.userId,
-              action: TrustAction.COMMENT_RECEIVED_UPVOTE,
-              targetUserId: userId,
-              metadata: {
-                commentId: input.commentId,
-                voterId: userId,
-                listingId: comment.listingId,
-              },
-            })
-          } else if (trustActionNeeded === 'downvote') {
-            await trustService.logAction({
-              userId: comment.userId,
-              action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
-              targetUserId: userId,
-              metadata: {
-                commentId: input.commentId,
-                voterId: userId,
-                listingId: comment.listingId,
-              },
-            })
-          } else if (trustActionNeeded === 'change') {
-            if (input.value) {
-              await trustService.logAction({
-                userId: comment.userId,
-                action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
-                targetUserId: userId,
-                metadata: {
-                  commentId: input.commentId,
-                  voterId: userId,
-                  listingId: comment.listingId,
-                  reversed: true,
-                },
-              })
-              await trustService.logAction({
-                userId: comment.userId,
-                action: TrustAction.COMMENT_RECEIVED_UPVOTE,
-                targetUserId: userId,
-                metadata: {
-                  commentId: input.commentId,
-                  voterId: userId,
-                  listingId: comment.listingId,
-                },
-              })
-            } else {
-              await trustService.logAction({
-                userId: comment.userId,
-                action: TrustAction.COMMENT_RECEIVED_UPVOTE,
-                targetUserId: userId,
-                metadata: {
-                  commentId: input.commentId,
-                  voterId: userId,
-                  listingId: comment.listingId,
-                  reversed: true,
-                },
-              })
-              await trustService.logAction({
-                userId: comment.userId,
-                action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
-                targetUserId: userId,
-                metadata: {
-                  commentId: input.commentId,
-                  voterId: userId,
-                  listingId: comment.listingId,
-                },
-              })
-            }
-          } else if (trustActionNeeded === 'remove' && existingVote) {
-            const action = existingVote.value
-              ? TrustAction.COMMENT_RECEIVED_UPVOTE
-              : TrustAction.COMMENT_RECEIVED_DOWNVOTE
-            await trustService.logAction({
-              userId: comment.userId,
-              action,
-              targetUserId: userId,
-              metadata: {
-                commentId: input.commentId,
-                voterId: userId,
-                listingId: comment.listingId,
-                reversed: true,
-              },
-            })
-          }
+        if (trustActionNeeded) {
+          await handleCommentVoteTrustEffects({
+            tx,
+            trustAction: trustActionNeeded,
+            newValue: !!input.value,
+            previousValue: existingVote?.value ?? null,
+            commentAuthorId: comment.userId,
+            voterId: userId,
+            commentId: input.commentId,
+            parentEntityId: comment.listingId,
+            listingType: 'handheld',
+            updatedScore: updatedComment.score,
+            scoreChange,
+          })
+        }
 
-          // Helpful threshold bonus
-          const HELPFUL_THRESHOLD = 5
-          const previousScore = updatedComment.score - scoreChange
-          if (previousScore < HELPFUL_THRESHOLD && updatedComment.score >= HELPFUL_THRESHOLD) {
-            await trustService.logAction({
-              userId: comment.userId,
-              action: TrustAction.HELPFUL_COMMENT,
-              metadata: {
-                commentId: input.commentId,
-                listingId: comment.listingId,
-                score: updatedComment.score,
-                threshold: HELPFUL_THRESHOLD,
-              },
-            })
-          }
+        // Notify comment author on new votes / direction changes; skip on toggle-off.
+        // When trustActionNeeded is upvote/downvote/change, input.value is guaranteed non-null
+        // (null input.value always maps to 'remove' in the branching above).
+        if (trustActionNeeded !== null && trustActionNeeded !== 'remove' && input.value !== null) {
+          notificationEventEmitter.emitNotificationEvent({
+            eventType: NOTIFICATION_EVENTS.COMMENT_VOTED,
+            entityType: 'comment',
+            entityId: comment.id,
+            triggeredBy: userId,
+            payload: {
+              listingId: comment.listingId,
+              commentId: comment.id,
+              voteValue: input.value,
+            },
+          })
         }
 
         // Analytics
diff --git a/src/server/api/routers/mobile/pcListings.ts b/src/server/api/routers/mobile/pcListings.ts
index c549222c6..afa8496a4 100644
--- a/src/server/api/routers/mobile/pcListings.ts
+++ b/src/server/api/routers/mobile/pcListings.ts
@@ -1,4 +1,5 @@
 import { ResourceError } from '@/lib/errors'
+import { applyTrustAction } from '@/lib/trust/service'
 import {
   GetCpusSchema,
   GetGpusSchema,
@@ -21,7 +22,7 @@ import { PcListingsRepository } from '@/server/repositories/pc-listings.reposito
 import { listingStatsCache } from '@/server/utils/cache'
 import { paginate } from '@/server/utils/pagination'
 import { isModerator } from '@/utils/permissions'
-import { Prisma, ApprovalStatus } from '@orm'
+import { Prisma, ApprovalStatus, TrustAction } from '@orm'
 
 export const mobilePcListingsRouter = createMobileTRPCRouter({
   /**
@@ -166,6 +167,12 @@ export const mobilePcListingsRouter = createMobileTRPCRouter({
         : null) as { customFieldDefinitionId: string; value: unknown }[] | null,
     })
 
+    await applyTrustAction({
+      userId: ctx.session.user.id,
+      action: TrustAction.LISTING_CREATED,
+      context: { pcListingId: created.id },
+    })
+
     // Invalidate stats cache
     listingStatsCache.delete('pc-listing-stats')
 
diff --git a/src/server/api/routers/pcListings.test.ts b/src/server/api/routers/pcListings.test.ts
new file mode 100644
index 000000000..6aeb4af9e
--- /dev/null
+++ b/src/server/api/routers/pcListings.test.ts
@@ -0,0 +1,627 @@
+import { describe, expect, it, beforeEach, vi } from 'vitest'
+import { ApprovalStatus, Role, TrustAction } from '@orm'
+
+vi.unmock('@/server/api/trpc')
+vi.unmock('@/server/api/root')
+
+const mockApplyTrustAction = vi.fn().mockResolvedValue(undefined)
+const mockHandleListingVoteTrustEffects = vi.fn().mockResolvedValue(undefined)
+const mockHandleCommentVoteTrustEffects = vi.fn().mockResolvedValue(undefined)
+const mockLogAction = vi.fn().mockResolvedValue(undefined)
+
+vi.mock('@/lib/trust/service', () => ({
+  applyTrustAction: (...args: unknown[]) => mockApplyTrustAction(...args),
+  TrustService: vi.fn().mockImplementation(() => ({ logAction: mockLogAction })),
+}))
+
+vi.mock('@/server/utils/vote-trust-effects', () => ({
+  handleListingVoteTrustEffects: (...args: unknown[]) => mockHandleListingVoteTrustEffects(...args),
+  handleCommentVoteTrustEffects: (...args: unknown[]) => mockHandleCommentVoteTrustEffects(...args),
+  handleVoteTrustEffects: vi.fn(),
+}))
+
+vi.mock('@/server/utils/vote-counts', () => ({
+  updatePcListingVoteCounts: vi.fn().mockResolvedValue(undefined),
+}))
+
+const mockEmitNotificationEvent = vi.fn()
+vi.mock('@/server/notifications/eventEmitter', () => ({
+  notificationEventEmitter: { emitNotificationEvent: mockEmitNotificationEvent },
+  NOTIFICATION_EVENTS: {
+    LISTING_VOTED: 'LISTING_VOTED',
+    COMMENT_VOTED: 'COMMENT_VOTED',
+    PC_LISTING_APPROVED: 'PC_LISTING_APPROVED',
+    PC_LISTING_REJECTED: 'PC_LISTING_REJECTED',
+  },
+}))
+
+const mockVerifyRecaptcha = vi.fn().mockResolvedValue({ success: true })
+vi.mock('@/lib/captcha/verify', () => ({
+  verifyRecaptcha: (...args: unknown[]) => mockVerifyRecaptcha(...args),
+  getClientIP: vi.fn().mockReturnValue('127.0.0.1'),
+}))
+
+vi.mock('@/lib/captcha/config', () => ({
+  RECAPTCHA_CONFIG: { actions: { VOTE: 'VOTE' } },
+}))
+
+vi.mock('@/server/utils/query-builders', () => ({
+  isUserBanned: vi.fn().mockResolvedValue(false),
+}))
+
+vi.mock('@/server/utils/cache', () => ({
+  listingStatsCache: { delete: vi.fn() },
+}))
+
+vi.mock('@/server/cache/invalidation', () => ({
+  invalidateListPages: vi.fn().mockResolvedValue(undefined),
+  invalidateSitemap: vi.fn().mockResolvedValue(undefined),
+  revalidateByTag: vi.fn().mockResolvedValue(undefined),
+}))
+
+vi.mock('@/lib/analytics', () => ({
+  default: {
+    engagement: { vote: vi.fn(), commentVote: vi.fn() },
+    listing: { created: vi.fn() },
+  },
+}))
+
+vi.mock('@/server/services/audit.service', () => ({
+  logAudit: vi.fn().mockResolvedValue(undefined),
+}))
+
+vi.mock('@/server/services/author-risk.service', () => ({
+  computeAuthorRiskProfiles: vi.fn().mockResolvedValue(new Map()),
+}))
+
+vi.mock('@/server/api/utils/pinPermissions', () => ({
+  canManageCommentPins: vi.fn().mockReturnValue(false),
+}))
+
+vi.mock('@/server/utils/security-validation', () => ({
+  validatePagination: vi.fn((page, limit, max) => ({ page: page ?? 1, limit: limit ?? max ?? 20 })),
+}))
+
+const mockRepositoryCreate = vi.fn()
+const mockRepositoryGetById = vi.fn()
+const mockRepositoryApprove = vi.fn()
+const mockRepositoryReject = vi.fn()
+const mockRepositoryGetExistingVote = vi.fn()
+const mockIsDeveloperVerified = vi.fn()
+
+vi.mock('@/server/repositories/pc-listings.repository', () => ({
+  PcListingsRepository: vi.fn().mockImplementation(() => ({
+    create: mockRepositoryCreate,
+    getById: mockRepositoryGetById,
+    approve: mockRepositoryApprove,
+    reject: mockRepositoryReject,
+    getExistingVote: mockRepositoryGetExistingVote,
+    isDeveloperVerifiedForEmulator: mockIsDeveloperVerified,
+    list: vi.fn().mockResolvedValue({ pcListings: [], pagination: {} }),
+    getUserVote: vi.fn().mockResolvedValue(null),
+  })),
+}))
+
+vi.mock('@/server/repositories/user-pc-presets.repository', () => ({
+  UserPcPresetsRepository: vi.fn().mockImplementation(() => ({})),
+}))
+
+const { pcListingsRouter } = await import('./pcListings')
+
+const USER_ID = '00000000-0000-4000-a000-000000000001'
+const AUTHOR_ID = '00000000-0000-4000-a000-000000000002'
+const ADMIN_ID = '00000000-0000-4000-a000-000000000003'
+const LISTING_ID = '00000000-0000-4000-a000-000000000010'
+const COMMENT_ID = '00000000-0000-4000-a000-000000000020'
+
+function createMockPrisma() {
+  const mockTx = {
+    pcListingVote: {
+      create: vi.fn().mockResolvedValue({ userId: USER_ID, pcListingId: LISTING_ID, value: true }),
+      delete: vi.fn().mockResolvedValue(undefined),
+      update: vi.fn().mockResolvedValue({ userId: USER_ID, pcListingId: LISTING_ID, value: false }),
+      findUnique: vi.fn().mockResolvedValue(null),
+    },
+    pcListingCommentVote: {
+      create: vi.fn().mockResolvedValue({ userId: USER_ID, commentId: COMMENT_ID, value: true }),
+      delete: vi.fn().mockResolvedValue(undefined),
+      update: vi.fn().mockResolvedValue({ userId: USER_ID, commentId: COMMENT_ID, value: false }),
+      findUnique: vi.fn().mockResolvedValue(null),
+    },
+    pcListingComment: {
+      findUnique: vi.fn(),
+      update: vi.fn().mockResolvedValue({ id: COMMENT_ID, score: 1 }),
+    },
+    pcListing: {
+      findUnique: vi.fn(),
+      findMany: vi.fn().mockResolvedValue([]),
+      updateMany: vi.fn().mockResolvedValue({ count: 0 }),
+    },
+  }
+
+  return {
+    ...mockTx,
+    $transaction: vi.fn(async (cb: (tx: typeof mockTx) => Promise<unknown>) => cb(mockTx)),
+  }
+}
+
+type MockPrisma = ReturnType<typeof createMockPrisma>
+
+function createCaller(overrides: { userId?: string; role?: Role; prisma?: MockPrisma } = {}) {
+  const prisma = overrides.prisma ?? createMockPrisma()
+  return {
+    caller: pcListingsRouter.createCaller({
+      session: {
+        user: {
+          id: overrides.userId ?? USER_ID,
+          email: 'test@test.com',
+          name: 'Test User',
+          role: overrides.role ?? Role.USER,
+          permissions: [],
+          showNsfw: false,
+        },
+      },
+      prisma: prisma as never,
+      headers: new Headers(),
+    }),
+    prisma,
+  }
+}
+
+describe('pcListings trust integration', () => {
+  beforeEach(() => {
+    vi.clearAllMocks()
+    mockRepositoryGetExistingVote.mockResolvedValue(null)
+  })
+
+  describe('vote', () => {
+    it('calls handleListingVoteTrustEffects with listingType pc on new vote', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.pcListing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+      await caller.vote({ pcListingId: LISTING_ID, value: true })
+
+      expect(mockHandleListingVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          action: 'created',
+          currentValue: true,
+          previousValue: null,
+          userId: USER_ID,
+          listingId: LISTING_ID,
+          listingType: 'pc',
+          authorId: AUTHOR_ID,
+          tx: expect.any(Object),
+        }),
+      )
+    })
+
+    it('calls handleListingVoteTrustEffects with action deleted on vote toggle', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.pcListing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+      // Simulate an existing upvote inside the transaction.
+      prisma.pcListingVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        pcListingId: LISTING_ID,
+        value: true,
+      })
+
+      await caller.vote({ pcListingId: LISTING_ID, value: true })
+
+      expect(mockHandleListingVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          action: 'deleted',
+          previousValue: true,
+          listingType: 'pc',
+        }),
+      )
+    })
+
+    it('fetches existingVote via the transaction client, not the repository', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.pcListing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+
+      await caller.vote({ pcListingId: LISTING_ID, value: true })
+
+      expect(prisma.pcListingVote.findUnique).toHaveBeenCalledWith({
+        where: { userId_pcListingId: { userId: USER_ID, pcListingId: LISTING_ID } },
+      })
+      expect(mockRepositoryGetExistingVote).not.toHaveBeenCalled()
+    })
+
+    it('emits LISTING_VOTED on a new vote', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.pcListing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+
+      await caller.vote({ pcListingId: LISTING_ID, value: true })
+
+      expect(mockEmitNotificationEvent).toHaveBeenCalledWith(
+        expect.objectContaining({
+          eventType: 'LISTING_VOTED',
+          entityType: 'pcListing',
+          entityId: LISTING_ID,
+          payload: expect.objectContaining({ voteValue: true }),
+        }),
+      )
+    })
+
+    it('emits LISTING_VOTED on a vote direction change', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.pcListing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+      prisma.pcListingVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        pcListingId: LISTING_ID,
+        value: true,
+      })
+
+      await caller.vote({ pcListingId: LISTING_ID, value: false })
+
+      expect(mockEmitNotificationEvent).toHaveBeenCalledWith(
+        expect.objectContaining({
+          eventType: 'LISTING_VOTED',
+          payload: expect.objectContaining({ voteValue: false }),
+        }),
+      )
+    })
+
+    it('does NOT emit LISTING_VOTED on toggle-off (delete)', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.pcListing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+      prisma.pcListingVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        pcListingId: LISTING_ID,
+        value: true,
+      })
+
+      await caller.vote({ pcListingId: LISTING_ID, value: true })
+
+      expect(mockEmitNotificationEvent).not.toHaveBeenCalled()
+    })
+
+    it('verifies recaptcha when token is provided', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.pcListing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+
+      await caller.vote({
+        pcListingId: LISTING_ID,
+        value: true,
+        recaptchaToken: 'valid-token',
+      })
+
+      expect(mockVerifyRecaptcha).toHaveBeenCalledWith({
+        token: 'valid-token',
+        expectedAction: 'VOTE',
+        userIP: expect.any(String),
+      })
+    })
+
+    it('throws CAPTCHA error when recaptcha verification fails', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.pcListing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+      mockVerifyRecaptcha.mockResolvedValueOnce({ success: false, error: 'invalid-token' })
+
+      // AppError.captcha throws a TRPCError; assert the procedure rejects.
+      await expect(
+        caller.vote({
+          pcListingId: LISTING_ID,
+          value: true,
+          recaptchaToken: 'bad-token',
+        }),
+      ).rejects.toThrow(/CAPTCHA/)
+
+      // Vote write must NOT have happened
+      expect(prisma.pcListingVote.create).not.toHaveBeenCalled()
+      expect(prisma.pcListingVote.update).not.toHaveBeenCalled()
+      expect(prisma.pcListingVote.delete).not.toHaveBeenCalled()
+    })
+
+    it('proceeds without verifying recaptcha when no token provided (optional)', async () => {
+      const { caller, prisma } = createCaller()
+      prisma.pcListing.findUnique.mockResolvedValue({ id: LISTING_ID, authorId: AUTHOR_ID })
+
+      await caller.vote({ pcListingId: LISTING_ID, value: true })
+
+      expect(mockVerifyRecaptcha).not.toHaveBeenCalled()
+      expect(prisma.pcListingVote.create).toHaveBeenCalled()
+    })
+  })
+
+  describe('voteComment notifications', () => {
+    function setupComment(prisma: MockPrisma) {
+      prisma.pcListingComment.findUnique.mockResolvedValue({
+        id: COMMENT_ID,
+        userId: AUTHOR_ID,
+        pcListingId: LISTING_ID,
+      })
+      prisma.pcListingComment.update.mockResolvedValue({ id: COMMENT_ID, score: 1 })
+    }
+
+    it('emits COMMENT_VOTED on a new upvote', async () => {
+      const { caller, prisma } = createCaller()
+      setupComment(prisma)
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      expect(mockEmitNotificationEvent).toHaveBeenCalledWith(
+        expect.objectContaining({
+          eventType: 'COMMENT_VOTED',
+          entityType: 'comment',
+          entityId: COMMENT_ID,
+          payload: expect.objectContaining({
+            commentId: COMMENT_ID,
+            pcListingId: LISTING_ID,
+            voteValue: true,
+          }),
+        }),
+      )
+    })
+
+    it('emits COMMENT_VOTED on a vote change', async () => {
+      const { caller, prisma } = createCaller()
+      setupComment(prisma)
+      prisma.pcListingCommentVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        commentId: COMMENT_ID,
+        value: true,
+      })
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: false })
+
+      expect(mockEmitNotificationEvent).toHaveBeenCalledWith(
+        expect.objectContaining({
+          eventType: 'COMMENT_VOTED',
+          payload: expect.objectContaining({ voteValue: false }),
+        }),
+      )
+    })
+
+    it('does NOT emit COMMENT_VOTED on toggle-off (remove)', async () => {
+      const { caller, prisma } = createCaller()
+      setupComment(prisma)
+      prisma.pcListingCommentVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        commentId: COMMENT_ID,
+        value: true,
+      })
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      expect(mockEmitNotificationEvent).not.toHaveBeenCalled()
+    })
+  })
+
+  describe('create', () => {
+    it('calls applyTrustAction with LISTING_CREATED after creation', async () => {
+      const newListingId = '00000000-0000-4000-a000-000000000099'
+      mockRepositoryCreate.mockResolvedValue({
+        id: newListingId,
+        status: ApprovalStatus.PENDING,
+      })
+
+      const { caller } = createCaller()
+
+      await caller.create({
+        gameId: '00000000-0000-4000-a000-000000000030',
+        cpuId: '00000000-0000-4000-a000-000000000031',
+        emulatorId: '00000000-0000-4000-a000-000000000032',
+        performanceId: 1,
+        memorySize: 16,
+        os: 'WINDOWS' as never,
+        osVersion: '11',
+      })
+
+      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+        userId: USER_ID,
+        action: TrustAction.LISTING_CREATED,
+        context: { pcListingId: newListingId },
+      })
+    })
+  })
+
+  describe('approve', () => {
+    it('calls applyTrustAction with LISTING_APPROVED for author', async () => {
+      mockRepositoryGetById.mockResolvedValue({
+        id: LISTING_ID,
+        authorId: AUTHOR_ID,
+        status: ApprovalStatus.PENDING,
+        emulatorId: '00000000-0000-4000-a000-000000000060',
+      })
+      mockRepositoryApprove.mockResolvedValue({
+        id: LISTING_ID,
+        status: ApprovalStatus.APPROVED,
+      })
+
+      const { caller } = createCaller({ userId: ADMIN_ID, role: Role.MODERATOR })
+
+      await caller.approve({ pcListingId: LISTING_ID })
+
+      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+        userId: AUTHOR_ID,
+        action: TrustAction.LISTING_APPROVED,
+        context: {
+          pcListingId: LISTING_ID,
+          adminUserId: ADMIN_ID,
+          reason: 'listing_approved',
+        },
+      })
+    })
+  })
+
+  describe('reject', () => {
+    it('calls applyTrustAction with LISTING_REJECTED for author', async () => {
+      mockRepositoryGetById.mockResolvedValue({
+        id: LISTING_ID,
+        authorId: AUTHOR_ID,
+        status: ApprovalStatus.PENDING,
+        emulatorId: '00000000-0000-4000-a000-000000000060',
+      })
+      mockRepositoryReject.mockResolvedValue({
+        id: LISTING_ID,
+        status: ApprovalStatus.REJECTED,
+      })
+
+      const { caller } = createCaller({ userId: ADMIN_ID, role: Role.MODERATOR })
+
+      await caller.reject({ pcListingId: LISTING_ID, notes: 'Incomplete report' })
+
+      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+        userId: AUTHOR_ID,
+        action: TrustAction.LISTING_REJECTED,
+        context: {
+          pcListingId: LISTING_ID,
+          adminUserId: ADMIN_ID,
+          reason: 'Incomplete report',
+        },
+      })
+    })
+  })
+
+  describe('bulkApprove', () => {
+    it('calls applyTrustAction with LISTING_APPROVED for each listing author', async () => {
+      const listing1 = {
+        id: LISTING_ID,
+        gameId: '00000000-0000-4000-a000-000000000040',
+        authorId: AUTHOR_ID,
+      }
+      const listing2 = {
+        id: '00000000-0000-4000-a000-000000000011',
+        gameId: '00000000-0000-4000-a000-000000000041',
+        authorId: '00000000-0000-4000-a000-000000000050',
+      }
+
+      const { caller, prisma } = createCaller({ userId: ADMIN_ID, role: Role.MODERATOR })
+      prisma.pcListing.findMany.mockResolvedValue([listing1, listing2])
+      prisma.pcListing.updateMany.mockResolvedValue({ count: 2 })
+
+      await caller.bulkApprove({ pcListingIds: [listing1.id, listing2.id] })
+
+      expect(mockApplyTrustAction).toHaveBeenCalledTimes(2)
+      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+        userId: AUTHOR_ID,
+        action: TrustAction.LISTING_APPROVED,
+        context: expect.objectContaining({ pcListingId: LISTING_ID }),
+      })
+      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+        userId: '00000000-0000-4000-a000-000000000050',
+        action: TrustAction.LISTING_APPROVED,
+        context: expect.objectContaining({ pcListingId: '00000000-0000-4000-a000-000000000011' }),
+      })
+    })
+  })
+
+  describe('bulkReject', () => {
+    it('calls applyTrustAction with LISTING_REJECTED for each listing author', async () => {
+      const listing1 = { id: LISTING_ID, authorId: AUTHOR_ID }
+      const listing2 = {
+        id: '00000000-0000-4000-a000-000000000011',
+        authorId: '00000000-0000-4000-a000-000000000050',
+      }
+
+      const { caller, prisma } = createCaller({ userId: ADMIN_ID, role: Role.MODERATOR })
+      prisma.pcListing.findMany.mockResolvedValue([listing1, listing2])
+      prisma.pcListing.updateMany.mockResolvedValue({ count: 2 })
+
+      await caller.bulkReject({ pcListingIds: [listing1.id, listing2.id], notes: 'Spam' })
+
+      expect(mockApplyTrustAction).toHaveBeenCalledTimes(2)
+      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+        userId: AUTHOR_ID,
+        action: TrustAction.LISTING_REJECTED,
+        context: expect.objectContaining({
+          pcListingId: LISTING_ID,
+          reason: 'Spam',
+        }),
+      })
+      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+        userId: '00000000-0000-4000-a000-000000000050',
+        action: TrustAction.LISTING_REJECTED,
+        context: expect.objectContaining({
+          pcListingId: '00000000-0000-4000-a000-000000000011',
+          reason: 'Spam',
+        }),
+      })
+    })
+  })
+
+  describe('voteComment', () => {
+    function setupCommentMocks(prisma: MockPrisma) {
+      prisma.pcListingComment.findUnique.mockResolvedValue({
+        id: COMMENT_ID,
+        userId: AUTHOR_ID,
+        pcListingId: LISTING_ID,
+      })
+      prisma.pcListingCommentVote.findUnique.mockResolvedValue(null)
+      prisma.pcListingComment.update.mockResolvedValue({ id: COMMENT_ID, score: 1 })
+    }
+
+    it('calls handleCommentVoteTrustEffects with listingType pc on new upvote', async () => {
+      const { caller, prisma } = createCaller()
+      setupCommentMocks(prisma)
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      expect(mockHandleCommentVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          trustAction: 'upvote',
+          newValue: true,
+          commentAuthorId: AUTHOR_ID,
+          voterId: USER_ID,
+          commentId: COMMENT_ID,
+          parentEntityId: LISTING_ID,
+          listingType: 'pc',
+        }),
+      )
+    })
+
+    it('calls handleCommentVoteTrustEffects with change action on vote flip', async () => {
+      const { caller, prisma } = createCaller()
+      setupCommentMocks(prisma)
+      prisma.pcListingCommentVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        commentId: COMMENT_ID,
+        value: true,
+      })
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: false })
+
+      expect(mockHandleCommentVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          trustAction: 'change',
+          newValue: false,
+          previousValue: true,
+          listingType: 'pc',
+        }),
+      )
+    })
+
+    it('calls handleCommentVoteTrustEffects with remove action on vote toggle', async () => {
+      const { caller, prisma } = createCaller()
+      setupCommentMocks(prisma)
+      prisma.pcListingCommentVote.findUnique.mockResolvedValue({
+        userId: USER_ID,
+        commentId: COMMENT_ID,
+        value: true,
+      })
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      expect(mockHandleCommentVoteTrustEffects).toHaveBeenCalledWith(
+        expect.objectContaining({
+          trustAction: 'remove',
+          listingType: 'pc',
+        }),
+      )
+    })
+
+    it('fetches existingVote inside the $transaction callback', async () => {
+      const { caller, prisma } = createCaller()
+      setupCommentMocks(prisma)
+
+      await caller.voteComment({ commentId: COMMENT_ID, value: true })
+
+      const txCall = vi.mocked(prisma.$transaction).mock.invocationCallOrder[0]
+      const findUniqueCall = prisma.pcListingCommentVote.findUnique.mock.invocationCallOrder[0]
+      expect(txCall).toBeDefined()
+      expect(findUniqueCall).toBeDefined()
+      expect(findUniqueCall).toBeGreaterThan(txCall as number)
+    })
+  })
+})
diff --git a/src/server/api/routers/pcListings.ts b/src/server/api/routers/pcListings.ts
index b588f387e..c6dd3d2b0 100644
--- a/src/server/api/routers/pcListings.ts
+++ b/src/server/api/routers/pcListings.ts
@@ -1,6 +1,8 @@
 import analytics from '@/lib/analytics'
+import { RECAPTCHA_CONFIG } from '@/lib/captcha/config'
+import { getClientIP, verifyRecaptcha } from '@/lib/captcha/verify'
 import { AppError, ResourceError } from '@/lib/errors'
-import { TrustService } from '@/lib/trust/service'
+import { applyTrustAction, TrustService } from '@/lib/trust/service'
 import {
   ApprovePcListingSchema,
   BulkApprovePcListingsSchema,
@@ -68,6 +70,10 @@ import { paginate } from '@/server/utils/pagination'
 import { isUserBanned } from '@/server/utils/query-builders'
 import { validatePagination } from '@/server/utils/security-validation'
 import { updatePcListingVoteCounts } from '@/server/utils/vote-counts'
+import {
+  handleCommentVoteTrustEffects,
+  handleListingVoteTrustEffects,
+} from '@/server/utils/vote-trust-effects'
 import { PERMISSIONS, roleIncludesRole } from '@/utils/permission-system'
 import {
   canDeleteComment,
@@ -251,6 +257,10 @@ export const pcListingsRouter = createTRPCRouter({
     }),
 
   create: protectedProcedure.input(CreatePcListingSchema).mutation(async ({ ctx, input }) => {
+    // TODO: Add spam detection via `checkSpamContent` from
+    // `@/server/utils/spam-check` (currently only applied in mobile routes).
+    // Block: UX/product sign-off needed since existing web users would start
+    // seeing spam-block errors. Mirror mobile: `{ userId, content: notes, entityType: 'listing' }`.
     const authorId = ctx.session.user.id
     const repository = new PcListingsRepository(ctx.prisma)
     const newListing = await repository.create({
@@ -270,6 +280,12 @@ export const pcListingsRouter = createTRPCRouter({
         : null) as { customFieldDefinitionId: string; value: unknown }[] | null,
     })
 
+    await applyTrustAction({
+      userId: authorId,
+      action: TrustAction.LISTING_CREATED,
+      context: { pcListingId: newListing.id },
+    })
+
     // Invalidate stats cache when PC listing is created
     listingStatsCache.delete('pc-listing-stats')
 
@@ -513,6 +529,18 @@ export const pcListingsRouter = createTRPCRouter({
 
     const approvedListing = await repository.approve(input.pcListingId, ctx.session.user.id)
 
+    if (pcListing.authorId) {
+      await applyTrustAction({
+        userId: pcListing.authorId,
+        action: TrustAction.LISTING_APPROVED,
+        context: {
+          pcListingId: input.pcListingId,
+          adminUserId: ctx.session.user.id,
+          reason: 'listing_approved',
+        },
+      })
+    }
+
     // Invalidate stats cache when PC listing is approved
     listingStatsCache.delete('pc-listing-stats')
 
@@ -567,6 +595,18 @@ export const pcListingsRouter = createTRPCRouter({
       input.notes,
     )
 
+    if (pcListing.authorId) {
+      await applyTrustAction({
+        userId: pcListing.authorId,
+        action: TrustAction.LISTING_REJECTED,
+        context: {
+          pcListingId: input.pcListingId,
+          adminUserId: ctx.session.user.id,
+          reason: input.notes || 'listing_rejected',
+        },
+      })
+    }
+
     // Invalidate stats cache when PC listing is rejected
     listingStatsCache.delete('pc-listing-stats')
 
@@ -627,7 +667,7 @@ export const pcListingsRouter = createTRPCRouter({
 
       const pendingListings = await ctx.prisma.pcListing.findMany({
         where: { id: { in: input.pcListingIds }, status: ApprovalStatus.PENDING },
-        select: { id: true, gameId: true },
+        select: { id: true, gameId: true, authorId: true },
       })
 
       const result = await ctx.prisma.pcListing.updateMany({
@@ -639,6 +679,24 @@ export const pcListingsRouter = createTRPCRouter({
         },
       })
 
+      // Apply trust actions in parallel — distinct user adjustments, independent.
+      const listingsWithAuthor = pendingListings.filter(
+        (l): l is typeof l & { authorId: string } => l.authorId !== null,
+      )
+      await Promise.all(
+        listingsWithAuthor.map((listing) =>
+          applyTrustAction({
+            userId: listing.authorId,
+            action: TrustAction.LISTING_APPROVED,
+            context: {
+              pcListingId: listing.id,
+              adminUserId: ctx.session.user.id,
+              reason: 'bulk_listing_approved',
+            },
+          }),
+        ),
+      )
+
       listingStatsCache.delete('pc-listing-stats')
 
       for (const listing of pendingListings) {
@@ -671,7 +729,7 @@ export const pcListingsRouter = createTRPCRouter({
 
       const pendingListings = await ctx.prisma.pcListing.findMany({
         where: { id: { in: input.pcListingIds }, status: ApprovalStatus.PENDING },
-        select: { id: true },
+        select: { id: true, authorId: true },
       })
 
       const result = await ctx.prisma.pcListing.updateMany({
@@ -686,6 +744,24 @@ export const pcListingsRouter = createTRPCRouter({
         },
       })
 
+      // Apply trust actions in parallel — distinct user adjustments, independent.
+      const listingsWithAuthor = pendingListings.filter(
+        (l): l is typeof l & { authorId: string } => l.authorId !== null,
+      )
+      await Promise.all(
+        listingsWithAuthor.map((listing) =>
+          applyTrustAction({
+            userId: listing.authorId,
+            action: TrustAction.LISTING_REJECTED,
+            context: {
+              pcListingId: listing.id,
+              adminUserId: ctx.session.user.id,
+              reason: input.notes || 'bulk_listing_rejected',
+            },
+          }),
+        ),
+      )
+
       // Invalidate stats cache when PC listings are bulk rejected
       listingStatsCache.delete('pc-listing-stats')
 
@@ -886,74 +962,95 @@ export const pcListingsRouter = createTRPCRouter({
       return AppError.shadowBanned()
     }
 
+    if (input.recaptchaToken) {
+      const clientIP = ctx.headers ? getClientIP(ctx.headers) : undefined
+      const captchaResult = await verifyRecaptcha({
+        token: input.recaptchaToken,
+        expectedAction: RECAPTCHA_CONFIG.actions.VOTE,
+        userIP: clientIP,
+      })
+
+      if (!captchaResult.success) return AppError.captcha(captchaResult.error)
+    }
+
     const pcListing = await ctx.prisma.pcListing.findUnique({
       where: { id: pcListingId },
     })
 
     if (!pcListing) return ResourceError.pcListing.notFound()
 
-    // Check if user already voted on this PC listing
-    const repository = new PcListingsRepository(ctx.prisma)
-    const existingVote = await repository.getExistingVote(userId, pcListingId)
-
-    let voteResult
-
-    if (existingVote) {
-      // If vote is the same, remove the vote (toggle)
-      if (existingVote.value === value) {
-        // Delete vote and update counts in transaction
-        voteResult = await ctx.prisma.$transaction(async (tx) => {
-          await tx.pcListingVote.delete({
-            where: { userId_pcListingId: { userId, pcListingId } },
-          })
-
-          await updatePcListingVoteCounts(tx, pcListingId, 'delete', undefined, existingVote.value)
-
-          return { message: 'Vote removed' }
-        })
-      } else {
-        // Update vote and counts in transaction
-        voteResult = await ctx.prisma.$transaction(async (tx) => {
-          const vote = await tx.pcListingVote.update({
-            where: { userId_pcListingId: { userId, pcListingId } },
-            data: { value },
-          })
-
-          await updatePcListingVoteCounts(tx, pcListingId, 'update', value, existingVote.value)
+    // Fetch existingVote INSIDE the transaction to avoid race conditions between
+    // concurrent votes on the same (user, pcListing) pair.
+    const voteResult = await ctx.prisma.$transaction(async (tx) => {
+      const existingVote = await tx.pcListingVote.findUnique({
+        where: { userId_pcListingId: { userId, pcListingId } },
+      })
 
-          return vote
-        })
+      let result: {
+        vote: { userId: string; pcListingId: string; value: boolean } | null
+        action: 'created' | 'updated' | 'deleted'
+        previousValue: boolean | null
       }
-    } else {
-      // Create new vote and update counts in transaction
-      voteResult = await ctx.prisma.$transaction(async (tx) => {
+
+      if (!existingVote) {
         const vote = await tx.pcListingVote.create({
           data: { userId, pcListingId, value },
         })
-
         await updatePcListingVoteCounts(tx, pcListingId, 'create', value)
+        result = { vote, action: 'created', previousValue: null }
+      } else if (existingVote.value === value) {
+        await tx.pcListingVote.delete({
+          where: { userId_pcListingId: { userId, pcListingId } },
+        })
+        await updatePcListingVoteCounts(tx, pcListingId, 'delete', undefined, existingVote.value)
+        result = { vote: null, action: 'deleted', previousValue: existingVote.value }
+      } else {
+        const vote = await tx.pcListingVote.update({
+          where: { userId_pcListingId: { userId, pcListingId } },
+          data: { value },
+        })
+        await updatePcListingVoteCounts(tx, pcListingId, 'update', value, existingVote.value)
+        result = { vote, action: 'updated', previousValue: existingVote.value }
+      }
 
-        return vote
+      await handleListingVoteTrustEffects({
+        tx,
+        action: result.action,
+        currentValue: value,
+        previousValue: result.previousValue,
+        userId,
+        listingId: pcListingId,
+        listingType: 'pc',
+        authorId: pcListing.authorId,
       })
-    }
 
-    // Emit notification event
-    notificationEventEmitter.emitNotificationEvent({
-      eventType: value ? NOTIFICATION_EVENTS.LISTING_VOTED : NOTIFICATION_EVENTS.LISTING_VOTED,
-      entityType: 'pcListing',
-      entityId: pcListingId,
-      triggeredBy: userId,
-      payload: { pcListingId, voteValue: value },
+      return result
     })
 
-    const finalVoteValue = existingVote?.value === value ? null : value
+    // Only notify the author when a vote was created or updated — toggle-off should not fire.
+    if (voteResult.action === 'created' || voteResult.action === 'updated') {
+      if (voteResult.vote) {
+        notificationEventEmitter.emitNotificationEvent({
+          eventType: NOTIFICATION_EVENTS.LISTING_VOTED,
+          entityType: 'pcListing',
+          entityId: pcListingId,
+          triggeredBy: userId,
+          payload: {
+            pcListingId,
+            voteValue: value,
+          },
+        })
+      }
+    }
+
+    const finalVoteValue = voteResult.action === 'deleted' ? null : value
     analytics.engagement.vote({
       listingId: pcListingId,
       voteValue: finalVoteValue,
-      previousVote: existingVote?.value,
+      previousVote: voteResult.previousValue,
     })
 
-    return voteResult
+    return voteResult.vote
   }),
 
   getUserVote: protectedProcedure
@@ -1072,6 +1169,10 @@ export const pcListingsRouter = createTRPCRouter({
   createComment: protectedProcedure
     .input(CreatePcListingCommentSchema)
     .mutation(async ({ ctx, input }) => {
+      // TODO: Add spam detection via `checkSpamContent` from
+      // `@/server/utils/spam-check` (currently only applied in mobile routes).
+      // Block: UX/product sign-off needed since existing web users would start
+      // seeing spam-block errors. Mirror mobile: `{ userId, content, entityType: 'comment' }`.
       const { pcListingId, content, parentId } = input
       const userId = ctx.session.user.id
 
@@ -1143,7 +1244,7 @@ export const pcListingsRouter = createTRPCRouter({
         return ResourceError.comment.noPermission('edit')
       }
 
-      return ctx.prisma.pcListingComment.update({
+      return await ctx.prisma.pcListingComment.update({
         where: { id: input.commentId },
         data: {
           content: input.content,
@@ -1238,44 +1339,79 @@ export const pcListingsRouter = createTRPCRouter({
         return ResourceError.comment.notFound()
       }
 
-      // Check if user already voted on this comment
-      const existingVote = await ctx.prisma.pcListingCommentVote.findUnique({
-        where: { userId_commentId: { userId, commentId } },
-      })
+      // Fetch `existingVote` inside the transaction: two concurrent votes
+      // from the same user could both read null and both attempt to insert,
+      // producing a Prisma P2002 on the second. Keeping the read and write
+      // under the same isolation avoids the race.
+      return await ctx.prisma.$transaction(async (tx) => {
+        const existingVote = await tx.pcListingCommentVote.findUnique({
+          where: { userId_commentId: { userId, commentId } },
+        })
 
-      // Start a transaction to handle both the vote and score update
-      return ctx.prisma.$transaction(async (tx) => {
         let voteResult
         let scoreChange: number
+        let trustAction: 'upvote' | 'downvote' | 'change' | 'remove' | null = null
 
         if (existingVote) {
-          // If vote is the same, remove the vote (toggle)
           if (existingVote.value === value) {
             await tx.pcListingCommentVote.delete({
               where: { userId_commentId: { userId, commentId } },
             })
             scoreChange = existingVote.value ? -1 : 1
             voteResult = { message: 'Vote removed' }
+            trustAction = 'remove'
           } else {
             voteResult = await tx.pcListingCommentVote.update({
               where: { userId_commentId: { userId, commentId } },
               data: { value },
             })
             scoreChange = value ? 2 : -2
+            trustAction = 'change'
           }
         } else {
           voteResult = await tx.pcListingCommentVote.create({
             data: { userId, commentId, value },
           })
           scoreChange = value ? 1 : -1
+          trustAction = value ? 'upvote' : 'downvote'
         }
 
-        // Update the comment score
-        await tx.pcListingComment.update({
+        const updatedComment = await tx.pcListingComment.update({
           where: { id: commentId },
           data: { score: { increment: scoreChange } },
         })
 
+        if (trustAction) {
+          await handleCommentVoteTrustEffects({
+            tx,
+            trustAction,
+            newValue: value,
+            previousValue: existingVote?.value ?? null,
+            commentAuthorId: comment.userId,
+            voterId: userId,
+            commentId,
+            parentEntityId: comment.pcListingId,
+            listingType: 'pc',
+            updatedScore: updatedComment.score,
+            scoreChange,
+          })
+        }
+
+        // Notify comment author on new votes / direction changes; skip on toggle-off.
+        if (trustAction !== null && trustAction !== 'remove') {
+          notificationEventEmitter.emitNotificationEvent({
+            eventType: NOTIFICATION_EVENTS.COMMENT_VOTED,
+            entityType: 'comment',
+            entityId: comment.id,
+            triggeredBy: userId,
+            payload: {
+              pcListingId: comment.pcListingId,
+              commentId: comment.id,
+              voteValue: value,
+            },
+          })
+        }
+
         return voteResult
       })
     }),
@@ -1428,7 +1564,7 @@ export const pcListingsRouter = createTRPCRouter({
 
       // Prevent users from reporting their own listings
       if (pcListing.authorId === userId) {
-        AppError.badRequest('You cannot report your own listing')
+        return AppError.badRequest('You cannot report your own listing')
       }
 
       // Check if user already reported this listing
@@ -1442,10 +1578,10 @@ export const pcListingsRouter = createTRPCRouter({
       })
 
       if (existingReport) {
-        AppError.badRequest('You have already reported this listing')
+        return AppError.badRequest('You have already reported this listing')
       }
 
-      return ctx.prisma.pcListingReport.create({
+      return await ctx.prisma.pcListingReport.create({
         data: {
           pcListingId,
           reportedById: userId,
@@ -1565,7 +1701,7 @@ export const pcListingsRouter = createTRPCRouter({
         })
       }
 
-      return ctx.prisma.pcListingReport.update({
+      return await ctx.prisma.pcListingReport.update({
         where: { id: reportId },
         data: {
           status,
@@ -1599,7 +1735,7 @@ export const pcListingsRouter = createTRPCRouter({
       })
 
       if (!pcListing) {
-        ResourceError.pcListing.notFound()
+        return ResourceError.pcListing.notFound()
       }
 
       // Check if user already verified this listing
@@ -1613,10 +1749,10 @@ export const pcListingsRouter = createTRPCRouter({
       })
 
       if (existingVerification) {
-        AppError.badRequest('You have already verified this listing')
+        return AppError.badRequest('You have already verified this listing')
       }
 
-      return ctx.prisma.pcListingDeveloperVerification.create({
+      return await ctx.prisma.pcListingDeveloperVerification.create({
         data: {
           pcListingId,
           verifiedBy: verifierId,
@@ -1644,7 +1780,7 @@ export const pcListingsRouter = createTRPCRouter({
         return ResourceError.verification.canOnlyRemoveOwn()
       }
 
-      return ctx.prisma.pcListingDeveloperVerification.delete({
+      return await ctx.prisma.pcListingDeveloperVerification.delete({
         where: { id: input.verificationId },
       })
     }),
@@ -1652,7 +1788,7 @@ export const pcListingsRouter = createTRPCRouter({
   getVerifications: publicProcedure
     .input(GetPcListingVerificationsSchema)
     .query(async ({ ctx, input }) => {
-      return ctx.prisma.pcListingDeveloperVerification.findMany({
+      return await ctx.prisma.pcListingDeveloperVerification.findMany({
         where: { pcListingId: input.pcListingId },
         include: {
           developer: { select: { id: true, name: true } },
diff --git a/src/server/api/routers/trust.test.ts b/src/server/api/routers/trust.test.ts
new file mode 100644
index 000000000..5961ca722
--- /dev/null
+++ b/src/server/api/routers/trust.test.ts
@@ -0,0 +1,179 @@
+import { describe, expect, it, beforeEach, vi } from 'vitest'
+import { Role } from '@orm'
+
+vi.unmock('@/server/api/trpc')
+vi.unmock('@/server/api/root')
+
+const mockApplyMonthlyActiveBonus = vi.fn().mockResolvedValue({ processedUsers: 0, errors: [] })
+const mockApplyManualAdjustment = vi.fn().mockResolvedValue(undefined)
+
+vi.mock('@/lib/trust/service', () => ({
+  applyMonthlyActiveBonus: (...args: unknown[]) => mockApplyMonthlyActiveBonus(...args),
+  TrustService: vi.fn().mockImplementation(() => ({
+    applyManualAdjustment: (...args: unknown[]) => mockApplyManualAdjustment(...args),
+  })),
+}))
+
+const mockPrismaTrustActionLogFindMany = vi.fn().mockResolvedValue([])
+const mockPrismaTrustActionLogCount = vi.fn().mockResolvedValue(0)
+const mockPrismaUserCount = vi.fn().mockResolvedValue(0)
+const mockPrismaUserGroupBy = vi.fn().mockResolvedValue([])
+
+vi.mock('@/server/db', () => ({
+  prisma: {
+    trustActionLog: {
+      findMany: (...args: unknown[]) => mockPrismaTrustActionLogFindMany(...args),
+      count: (...args: unknown[]) => mockPrismaTrustActionLogCount(...args),
+    },
+    user: {
+      count: (...args: unknown[]) => mockPrismaUserCount(...args),
+      groupBy: (...args: unknown[]) => mockPrismaUserGroupBy(...args),
+    },
+  },
+}))
+
+const { trustRouter } = await import('./trust')
+
+const USER_ID = '00000000-0000-4000-a000-000000000001'
+const TARGET_USER_ID = '00000000-0000-4000-a000-000000000002'
+
+function createCaller(role: Role) {
+  return trustRouter.createCaller({
+    session: {
+      user: {
+        id: USER_ID,
+        email: 'test@test.com',
+        name: 'Test User',
+        role,
+        permissions: [],
+        showNsfw: false,
+      },
+    },
+    prisma: {} as never,
+    headers: new Headers(),
+  })
+}
+
+const VALID_PAGINATION = { page: 1, limit: 20 }
+
+const NON_SUPER_ADMIN_ROLES: Role[] = [
+  Role.USER,
+  Role.AUTHOR,
+  Role.DEVELOPER,
+  Role.MODERATOR,
+  Role.ADMIN,
+]
+
+describe('trust router permission checks', () => {
+  beforeEach(() => {
+    vi.clearAllMocks()
+  })
+
+  describe('getTrustLogs (SUPER_ADMIN only)', () => {
+    NON_SUPER_ADMIN_ROLES.forEach((role) => {
+      it(`rejects with FORBIDDEN for ${role} and does not query the DB`, async () => {
+        const caller = createCaller(role)
+
+        await expect(caller.getTrustLogs(VALID_PAGINATION)).rejects.toThrow(
+          /Super Admin|insufficient|forbidden/i,
+        )
+
+        expect(mockPrismaTrustActionLogFindMany).not.toHaveBeenCalled()
+        expect(mockPrismaTrustActionLogCount).not.toHaveBeenCalled()
+      })
+    })
+
+    it('proceeds for SUPER_ADMIN', async () => {
+      const caller = createCaller(Role.SUPER_ADMIN)
+
+      const result = await caller.getTrustLogs(VALID_PAGINATION)
+
+      expect(result).toHaveProperty('logs')
+      expect(result).toHaveProperty('pagination')
+      expect(mockPrismaTrustActionLogFindMany).toHaveBeenCalledTimes(1)
+    })
+  })
+
+  describe('getTrustStats (SUPER_ADMIN only)', () => {
+    NON_SUPER_ADMIN_ROLES.forEach((role) => {
+      it(`rejects with FORBIDDEN for ${role} and does not query the DB`, async () => {
+        const caller = createCaller(role)
+
+        await expect(caller.getTrustStats({})).rejects.toThrow(
+          /Super Admin|insufficient|forbidden/i,
+        )
+
+        expect(mockPrismaTrustActionLogCount).not.toHaveBeenCalled()
+        expect(mockPrismaUserCount).not.toHaveBeenCalled()
+        expect(mockPrismaUserGroupBy).not.toHaveBeenCalled()
+      })
+    })
+
+    it('proceeds for SUPER_ADMIN', async () => {
+      const caller = createCaller(Role.SUPER_ADMIN)
+
+      const result = await caller.getTrustStats({})
+
+      expect(result).toHaveProperty('totalActions')
+      expect(mockPrismaTrustActionLogCount).toHaveBeenCalledTimes(1)
+    })
+  })
+
+  describe('runMonthlyActiveBonus (SUPER_ADMIN only)', () => {
+    NON_SUPER_ADMIN_ROLES.forEach((role) => {
+      it(`rejects with FORBIDDEN for ${role} and does not invoke the bonus job`, async () => {
+        const caller = createCaller(role)
+
+        await expect(caller.runMonthlyActiveBonus({})).rejects.toThrow(
+          /Super Admin|insufficient|forbidden/i,
+        )
+
+        expect(mockApplyMonthlyActiveBonus).not.toHaveBeenCalled()
+      })
+    })
+
+    it('invokes the bonus job for SUPER_ADMIN', async () => {
+      const caller = createCaller(Role.SUPER_ADMIN)
+
+      await caller.runMonthlyActiveBonus({})
+
+      expect(mockApplyMonthlyActiveBonus).toHaveBeenCalledTimes(1)
+    })
+  })
+
+  describe('adjustTrustScore (SUPER_ADMIN only)', () => {
+    NON_SUPER_ADMIN_ROLES.forEach((role) => {
+      it(`rejects with FORBIDDEN for ${role} and does not adjust trust`, async () => {
+        const caller = createCaller(role)
+
+        await expect(
+          caller.adjustTrustScore({
+            userId: TARGET_USER_ID,
+            adjustment: 50,
+            reason: 'attempted bypass',
+          }),
+        ).rejects.toThrow(/Super Admin|insufficient|forbidden/i)
+
+        expect(mockApplyManualAdjustment).not.toHaveBeenCalled()
+      })
+    })
+
+    it('invokes the adjustment for SUPER_ADMIN', async () => {
+      const caller = createCaller(Role.SUPER_ADMIN)
+
+      await caller.adjustTrustScore({
+        userId: TARGET_USER_ID,
+        adjustment: 50,
+        reason: 'legitimate adjustment',
+      })
+
+      expect(mockApplyManualAdjustment).toHaveBeenCalledTimes(1)
+      expect(mockApplyManualAdjustment).toHaveBeenCalledWith({
+        userId: TARGET_USER_ID,
+        adjustment: 50,
+        reason: 'legitimate adjustment',
+        adminUserId: USER_ID,
+      })
+    })
+  })
+})
diff --git a/src/server/api/routers/trust.ts b/src/server/api/routers/trust.ts
index 104651deb..84f066f13 100644
--- a/src/server/api/routers/trust.ts
+++ b/src/server/api/routers/trust.ts
@@ -1,6 +1,6 @@
 import { AppError } from '@/lib/errors'
 import { TRUST_LEVELS } from '@/lib/trust/config'
-import { applyManualTrustAdjustment, applyMonthlyActiveBonus } from '@/lib/trust/service'
+import { TrustService, applyMonthlyActiveBonus } from '@/lib/trust/service'
 import {
   GetTrustLogsSchema,
   GetTrustStatsSchema,
@@ -17,7 +17,7 @@ export const trustRouter = createTRPCRouter({
   // Get trust logs for admin dashboard (SUPER_ADMIN only)
   getTrustLogs: protectedProcedure.input(GetTrustLogsSchema).query(async ({ ctx, input }) => {
     if (!hasRolePermission(ctx.session.user.role, Role.SUPER_ADMIN)) {
-      AppError.insufficientRole(Role.SUPER_ADMIN)
+      return AppError.insufficientRole(Role.SUPER_ADMIN)
     }
 
     const { page, limit, sortField, sortDirection, search, action } = input
@@ -79,7 +79,7 @@ export const trustRouter = createTRPCRouter({
   // Get trust system statistics (SUPER_ADMIN only)
   getTrustStats: protectedProcedure.input(GetTrustStatsSchema).query(async ({ ctx }) => {
     if (!hasRolePermission(ctx.session.user.role, Role.SUPER_ADMIN)) {
-      AppError.insufficientRole(Role.SUPER_ADMIN)
+      return AppError.insufficientRole(Role.SUPER_ADMIN)
     }
 
     const [totalActions, totalUsers, levelDistribution] = await Promise.all([
@@ -127,7 +127,7 @@ export const trustRouter = createTRPCRouter({
     .input(RunMonthlyActiveBonusSchema)
     .mutation(async ({ ctx }) => {
       if (!hasRolePermission(ctx.session.user.role, Role.SUPER_ADMIN)) {
-        AppError.insufficientRole(Role.SUPER_ADMIN)
+        return AppError.insufficientRole(Role.SUPER_ADMIN)
       }
 
       return await applyMonthlyActiveBonus()
@@ -138,10 +138,10 @@ export const trustRouter = createTRPCRouter({
     .input(ManualTrustAdjustmentSchema)
     .mutation(async ({ ctx, input }) => {
       if (!hasRolePermission(ctx.session.user.role, Role.SUPER_ADMIN)) {
-        AppError.insufficientRole(Role.SUPER_ADMIN)
+        return AppError.insufficientRole(Role.SUPER_ADMIN)
       }
 
-      await applyManualTrustAdjustment({
+      await new TrustService(ctx.prisma).applyManualAdjustment({
         userId: input.userId,
         adjustment: input.adjustment,
         reason: input.reason,
diff --git a/src/server/api/routers/userBans.ts b/src/server/api/routers/userBans.ts
index 81ddd1dfb..fd27221c5 100644
--- a/src/server/api/routers/userBans.ts
+++ b/src/server/api/routers/userBans.ts
@@ -19,6 +19,7 @@ import {
 import { UserBansRepository } from '@/server/repositories/user-bans.repository'
 import { logAudit, buildDiff } from '@/server/services/audit.service'
 import { nullifyUserVotes, restoreUserVotes } from '@/server/services/vote-nullification.service'
+import { withRetryTransaction } from '@/server/utils/transactions'
 import { PERMISSIONS } from '@/utils/permission-system'
 import { hasRolePermission } from '@/utils/permissions'
 import { AuditAction, AuditEntityType, Role } from '@orm'
@@ -85,32 +86,38 @@ export const userBansRouter = createTRPCRouter({
       return AppError.badRequest('Expiration date must be in the future')
     }
 
-    const created = await repository.create({
-      userId,
-      bannedById,
-      reason,
-      notes,
-      expiresAt,
-    })
-
-    // Nullify votes if requested
-    let nullificationResult: Awaited<ReturnType<typeof nullifyUserVotes>> | null = null
-    if (shouldNullifyVotes) {
-      nullificationResult = await nullifyUserVotes(ctx.prisma, {
-        userId,
-        adminUserId: ctx.session.user.id,
-        reason: `Ban: ${reason}`,
-        includeCommentVotes: true,
-        headers: ctx.headers,
-      })
-
-      await ctx.prisma.userBan.update({
-        where: { id: created.id },
-        data: { votesNullified: true },
-      })
-    }
+    const { created, nullificationResult } = await withRetryTransaction(
+      ctx.prisma,
+      async (tx) => {
+        const ban = await new UserBansRepository(tx).create({
+          userId,
+          bannedById,
+          reason,
+          notes,
+          expiresAt,
+        })
+
+        let nullResult: Awaited<ReturnType<typeof nullifyUserVotes>> | null = null
+        if (shouldNullifyVotes) {
+          nullResult = await nullifyUserVotes(tx, {
+            userId,
+            adminUserId: ctx.session.user.id,
+            reason: `Ban: ${reason}`,
+            includeCommentVotes: true,
+            headers: ctx.headers,
+          })
+
+          await tx.userBan.update({
+            where: { id: ban.id },
+            data: { votesNullified: true },
+          })
+        }
+
+        return { created: ban, nullificationResult: nullResult }
+      },
+      { timeout: 120_000 },
+    )
 
-    // Fire-and-forget audit log
     void logAudit(ctx.prisma, {
       actorId: ctx.session.user.id,
       action: AuditAction.BAN,
@@ -217,7 +224,26 @@ export const userBansRouter = createTRPCRouter({
       isActive: ban.isActive,
     }
 
-    const lifted = await repository.lift(id, unbannedById, notes)
+    const { lifted, restorationResult } = await withRetryTransaction(
+      ctx.prisma,
+      async (tx) => {
+        const liftedBan = await new UserBansRepository(tx).lift(id, unbannedById, notes)
+
+        let restoreResult: Awaited<ReturnType<typeof restoreUserVotes>> | null = null
+        if (ban.votesNullified) {
+          restoreResult = await restoreUserVotes(tx, {
+            userId: ban.userId,
+            adminUserId: ctx.session.user.id,
+            reason: `Ban lifted: ${ban.reason}`,
+            headers: ctx.headers,
+          })
+        }
+
+        return { lifted: liftedBan, restorationResult: restoreResult }
+      },
+      { timeout: 120_000 },
+    )
+
     const next = {
       reason: lifted.reason,
       notes: lifted.notes ?? null,
@@ -225,17 +251,6 @@ export const userBansRouter = createTRPCRouter({
       isActive: lifted.isActive,
     }
 
-    // Auto-restore votes if they were nullified during this ban
-    let restorationResult: Awaited<ReturnType<typeof restoreUserVotes>> | null = null
-    if (ban.votesNullified) {
-      restorationResult = await restoreUserVotes(ctx.prisma, {
-        userId: ban.userId,
-        adminUserId: ctx.session.user.id,
-        reason: `Ban lifted: ${ban.reason}`,
-        headers: ctx.headers,
-      })
-    }
-
     void logAudit(ctx.prisma, {
       actorId: ctx.session.user.id,
       action: AuditAction.UNBAN,
@@ -324,8 +339,8 @@ export const userBansRouter = createTRPCRouter({
         ctx.prisma.listingReport.findMany({
           where: { listing: { authorId: userId } },
           include: {
-            reportedBy: { select: { id: true, name: true, email: true } },
-            reviewedBy: { select: { id: true, name: true, email: true } },
+            reportedBy: { select: { id: true, name: true } },
+            reviewedBy: { select: { id: true, name: true } },
             listing: {
               select: {
                 id: true,
@@ -339,8 +354,8 @@ export const userBansRouter = createTRPCRouter({
         ctx.prisma.pcListingReport.findMany({
           where: { pcListing: { authorId: userId } },
           include: {
-            reportedBy: { select: { id: true, name: true, email: true } },
-            reviewedBy: { select: { id: true, name: true, email: true } },
+            reportedBy: { select: { id: true, name: true } },
+            reviewedBy: { select: { id: true, name: true } },
             pcListing: {
               select: {
                 id: true,
diff --git a/src/server/api/utils/listingHelpers.test.ts b/src/server/api/utils/listingHelpers.test.ts
new file mode 100644
index 000000000..2f3abb647
--- /dev/null
+++ b/src/server/api/utils/listingHelpers.test.ts
@@ -0,0 +1,57 @@
+import { describe, expect, it } from 'vitest'
+import { buildProcessedOrderBy } from './listingHelpers'
+
+describe('buildProcessedOrderBy', () => {
+  it('defaults to processedAt desc when both args are null', () => {
+    expect(buildProcessedOrderBy(null, null)).toEqual({ processedAt: 'desc' })
+  })
+
+  it('defaults to processedAt desc when both args are undefined', () => {
+    expect(buildProcessedOrderBy(undefined, undefined)).toEqual({ processedAt: 'desc' })
+  })
+
+  it('honours explicit sortDirection with default sortField', () => {
+    expect(buildProcessedOrderBy(undefined, 'asc')).toEqual({ processedAt: 'asc' })
+    expect(buildProcessedOrderBy(null, 'asc')).toEqual({ processedAt: 'asc' })
+  })
+
+  it('maps createdAt directly', () => {
+    expect(buildProcessedOrderBy('createdAt', 'asc')).toEqual({ createdAt: 'asc' })
+    expect(buildProcessedOrderBy('createdAt', 'desc')).toEqual({ createdAt: 'desc' })
+  })
+
+  it('maps status directly', () => {
+    expect(buildProcessedOrderBy('status', 'asc')).toEqual({ status: 'asc' })
+  })
+
+  it('maps game.title into a nested Prisma order clause', () => {
+    expect(buildProcessedOrderBy('game.title', 'asc')).toEqual({ game: { title: 'asc' } })
+  })
+
+  it('maps author.name into a nested Prisma order clause', () => {
+    expect(buildProcessedOrderBy('author.name', 'desc')).toEqual({ author: { name: 'desc' } })
+  })
+
+  it('maps game.system.name into a doubly-nested clause', () => {
+    expect(buildProcessedOrderBy('game.system.name', 'asc')).toEqual({
+      game: { system: { name: 'asc' } },
+    })
+  })
+
+  it('maps emulator.name into a nested Prisma order clause', () => {
+    expect(buildProcessedOrderBy('emulator.name', 'desc')).toEqual({
+      emulator: { name: 'desc' },
+    })
+  })
+
+  it('maps device into a composite brand+model sort array', () => {
+    expect(buildProcessedOrderBy('device', 'asc')).toEqual([
+      { device: { brand: { name: 'asc' } } },
+      { device: { modelName: 'asc' } },
+    ])
+  })
+
+  it('maps processedAt explicitly (no change from default)', () => {
+    expect(buildProcessedOrderBy('processedAt', 'asc')).toEqual({ processedAt: 'asc' })
+  })
+})
diff --git a/src/server/api/utils/listingHelpers.ts b/src/server/api/utils/listingHelpers.ts
new file mode 100644
index 000000000..2ce5a232e
--- /dev/null
+++ b/src/server/api/utils/listingHelpers.ts
@@ -0,0 +1,41 @@
+import type { Prisma } from '@orm'
+
+export type ProcessedSortField =
+  | 'processedAt'
+  | 'createdAt'
+  | 'status'
+  | 'game.title'
+  | 'game.system.name'
+  | 'device'
+  | 'emulator.name'
+  | 'author.name'
+
+export type ProcessedSortDirection = 'asc' | 'desc'
+
+export function buildProcessedOrderBy(
+  sortField: ProcessedSortField | null | undefined,
+  sortDirection: ProcessedSortDirection | null | undefined,
+): Prisma.ListingOrderByWithRelationInput | Prisma.ListingOrderByWithRelationInput[] {
+  const direction: Prisma.SortOrder = sortDirection ?? 'desc'
+
+  switch (sortField) {
+    case 'createdAt':
+      return { createdAt: direction }
+    case 'status':
+      return { status: direction }
+    case 'game.title':
+      return { game: { title: direction } }
+    case 'game.system.name':
+      return { game: { system: { name: direction } } }
+    case 'device':
+      return [{ device: { brand: { name: direction } } }, { device: { modelName: direction } }]
+    case 'emulator.name':
+      return { emulator: { name: direction } }
+    case 'author.name':
+      return { author: { name: direction } }
+    case 'processedAt':
+    case null:
+    case undefined:
+      return { processedAt: direction }
+  }
+}
diff --git a/src/server/repositories/listings.repository.ts b/src/server/repositories/listings.repository.ts
index 114bf378a..073da543e 100644
--- a/src/server/repositories/listings.repository.ts
+++ b/src/server/repositories/listings.repository.ts
@@ -2,6 +2,7 @@ import { PAGINATION } from '@/data/constants'
 import { AppError, ResourceError } from '@/lib/errors'
 import { canUserAutoApprove } from '@/lib/trust/service'
 import { validateCustomFields } from '@/server/api/routers/listings/validation'
+import { computeVoteCounts } from '@/server/utils/moderator-info'
 import { paginate, calculateOffset } from '@/server/utils/pagination'
 import {
   buildNsfwFilter,
@@ -737,4 +738,58 @@ export class ListingsRepository extends BaseRepository {
       orderBy: { createdAt: 'desc' },
     })
   }
+
+  private static readonly moderatorInfoUserSelect = {
+    id: true,
+    name: true,
+  } as const
+
+  private static readonly moderatorInfoVoteSelect = {
+    id: true,
+    value: true,
+    createdAt: true,
+    nullifiedAt: true,
+    user: { select: { id: true, name: true, trustScore: true } },
+  } as const
+
+  async getModeratorInfo(listingId: string) {
+    const listing = await this.handleDatabaseOperation(
+      () =>
+        this.prisma.listing.findUnique({
+          where: { id: listingId },
+          select: {
+            status: true,
+            processedAt: true,
+            processedNotes: true,
+            processedByUser: { select: ListingsRepository.moderatorInfoUserSelect },
+          },
+        }),
+      'Listing',
+    )
+
+    if (!listing) throw ResourceError.listing.notFound()
+
+    const votes = await this.handleDatabaseOperation(
+      () =>
+        this.prisma.vote.findMany({
+          where: { listingId },
+          orderBy: { createdAt: 'desc' },
+          select: ListingsRepository.moderatorInfoVoteSelect,
+        }),
+      'Vote',
+    )
+
+    const voteCounts = computeVoteCounts(votes)
+
+    return {
+      approval: {
+        status: listing.status,
+        processedAt: listing.processedAt,
+        processedNotes: listing.processedNotes,
+        processedBy: listing.processedByUser,
+      },
+      votes,
+      voteCounts,
+    }
+  }
 }
diff --git a/src/server/repositories/pc-listings.repository.ts b/src/server/repositories/pc-listings.repository.ts
index 9b5d8aa4d..a6790136f 100644
--- a/src/server/repositories/pc-listings.repository.ts
+++ b/src/server/repositories/pc-listings.repository.ts
@@ -6,6 +6,7 @@
 import { PAGINATION } from '@/data/constants'
 import { AppError, ResourceError } from '@/lib/errors'
 import { canUserAutoApprove } from '@/lib/trust/service'
+import { computeVoteCounts } from '@/server/utils/moderator-info'
 import { paginate, calculateOffset } from '@/server/utils/pagination'
 import { sanitizeInput } from '@/server/utils/security-validation'
 import { roleIncludesRole } from '@/utils/permission-system'
@@ -945,4 +946,58 @@ export class PcListingsRepository extends BaseRepository {
     })
     return !!verified
   }
+
+  private static readonly moderatorInfoUserSelect = {
+    id: true,
+    name: true,
+  } as const
+
+  private static readonly moderatorInfoVoteSelect = {
+    id: true,
+    value: true,
+    createdAt: true,
+    nullifiedAt: true,
+    user: { select: { id: true, name: true, trustScore: true } },
+  } as const
+
+  async getModeratorInfo(pcListingId: string) {
+    const pcListing = await this.handleDatabaseOperation(
+      () =>
+        this.prisma.pcListing.findUnique({
+          where: { id: pcListingId },
+          select: {
+            status: true,
+            processedAt: true,
+            processedNotes: true,
+            processedByUser: { select: PcListingsRepository.moderatorInfoUserSelect },
+          },
+        }),
+      'PcListing',
+    )
+
+    if (!pcListing) throw ResourceError.pcListing.notFound()
+
+    const votes = await this.handleDatabaseOperation(
+      () =>
+        this.prisma.pcListingVote.findMany({
+          where: { pcListingId },
+          orderBy: { createdAt: 'desc' },
+          select: PcListingsRepository.moderatorInfoVoteSelect,
+        }),
+      'PcListingVote',
+    )
+
+    const voteCounts = computeVoteCounts(votes)
+
+    return {
+      approval: {
+        status: pcListing.status,
+        processedAt: pcListing.processedAt,
+        processedNotes: pcListing.processedNotes,
+        processedBy: pcListing.processedByUser,
+      },
+      votes,
+      voteCounts,
+    }
+  }
 }
diff --git a/src/server/services/audit.service.ts b/src/server/services/audit.service.ts
index 364b4b4fa..f8cd17c5e 100644
--- a/src/server/services/audit.service.ts
+++ b/src/server/services/audit.service.ts
@@ -35,7 +35,9 @@ type Params = {
   headers?: Headers | Record<string, string>
 }
 
-export async function logAudit(prisma: PrismaClient, params: Params): Promise<void> {
+type PrismaLike = PrismaClient | Prisma.TransactionClient
+
+export async function logAudit(prisma: PrismaLike, params: Params): Promise<void> {
   const { actorId, action, entityType, entityId, targetUserId, metadata, headers } = params
 
   const meta = extractRequestMeta(headers)
diff --git a/src/server/services/vote-nullification.service.test.ts b/src/server/services/vote-nullification.service.test.ts
index 9a469127e..1934eb946 100644
--- a/src/server/services/vote-nullification.service.test.ts
+++ b/src/server/services/vote-nullification.service.test.ts
@@ -10,9 +10,13 @@ vi.mock('@/server/services/audit.service', () => ({
   logAudit: vi.fn(),
 }))
 
-const mockApplyManualTrustAdjustment = vi.fn()
+const mockApplyManualAdjustment = vi.fn()
+const mockApplyBulkManualAdjustments = vi.fn()
 vi.mock('@/lib/trust/service', () => ({
-  applyManualTrustAdjustment: (...args: unknown[]) => mockApplyManualTrustAdjustment(...args),
+  TrustService: vi.fn().mockImplementation(() => ({
+    applyManualAdjustment: (...args: unknown[]) => mockApplyManualAdjustment(...args),
+    applyBulkManualAdjustments: (...args: unknown[]) => mockApplyBulkManualAdjustments(...args),
+  })),
 }))
 
 vi.mock('@/utils/wilson-score', () => ({
@@ -28,22 +32,22 @@ function createMockPrisma() {
     vote: {
       findMany: vi.fn().mockResolvedValue([]),
       updateMany: vi.fn().mockResolvedValue({ count: 0 }),
-      count: vi.fn().mockResolvedValue(0),
+      groupBy: vi.fn().mockResolvedValue([]),
     },
     pcListingVote: {
       findMany: vi.fn().mockResolvedValue([]),
       updateMany: vi.fn().mockResolvedValue({ count: 0 }),
-      count: vi.fn().mockResolvedValue(0),
+      groupBy: vi.fn().mockResolvedValue([]),
     },
     commentVote: {
       findMany: vi.fn().mockResolvedValue([]),
       updateMany: vi.fn().mockResolvedValue({ count: 0 }),
-      count: vi.fn().mockResolvedValue(0),
+      groupBy: vi.fn().mockResolvedValue([]),
     },
     pcListingCommentVote: {
       findMany: vi.fn().mockResolvedValue([]),
       updateMany: vi.fn().mockResolvedValue({ count: 0 }),
-      count: vi.fn().mockResolvedValue(0),
+      groupBy: vi.fn().mockResolvedValue([]),
     },
     listing: {
       update: vi.fn().mockResolvedValue({}),
@@ -146,7 +150,8 @@ describe('vote-nullification.service', () => {
 
   beforeEach(() => {
     prisma = createMockPrisma()
-    mockApplyManualTrustAdjustment.mockResolvedValue(undefined)
+    mockApplyManualAdjustment.mockResolvedValue(undefined)
+    mockApplyBulkManualAdjustments.mockResolvedValue(0)
   })
 
   describe('nullifyUserVotes', () => {
@@ -170,7 +175,7 @@ describe('vote-nullification.service', () => {
 
       expect(prisma.vote.updateMany).not.toHaveBeenCalled()
       expect(prisma.pcListingVote.updateMany).not.toHaveBeenCalled()
-      expect(mockApplyManualTrustAdjustment).not.toHaveBeenCalled()
+      expect(mockApplyBulkManualAdjustments).not.toHaveBeenCalled()
     })
 
     it('nullifies handheld votes and sets nullifiedAt', async () => {
@@ -179,7 +184,6 @@ describe('vote-nullification.service', () => {
         makeHandheldVote({ id: 'hv-2', value: false, authorId: AUTHOR_B }),
       ]
       prisma.vote.findMany.mockResolvedValue(votes)
-      prisma.vote.count.mockResolvedValue(0)
 
       const result = await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -198,7 +202,6 @@ describe('vote-nullification.service', () => {
     it('nullifies PC votes', async () => {
       const votes = [makePcVote({ id: 'pv-1' }), makePcVote({ id: 'pv-2' })]
       prisma.pcListingVote.findMany.mockResolvedValue(votes)
-      prisma.pcListingVote.count.mockResolvedValue(0)
 
       const result = await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -235,7 +238,6 @@ describe('vote-nullification.service', () => {
 
     it('skips comment votes when includeCommentVotes is false', async () => {
       prisma.vote.findMany.mockResolvedValue([makeHandheldVote()])
-      prisma.vote.count.mockResolvedValue(0)
 
       const result = await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -255,10 +257,6 @@ describe('vote-nullification.service', () => {
         makeHandheldVote({ id: 'hv-1', listingId: 'listing-1', value: true }),
         makeHandheldVote({ id: 'hv-2', listingId: 'listing-1', value: false }),
       ])
-      // After nullification: 0 active votes remain
-      prisma.vote.count
-        .mockResolvedValueOnce(0) // upvotes for listing-1
-        .mockResolvedValueOnce(0) // downvotes for listing-1
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -282,10 +280,6 @@ describe('vote-nullification.service', () => {
       prisma.pcListingVote.findMany.mockResolvedValue([
         makePcVote({ id: 'pv-1', pcListingId: 'pc-listing-1', value: true }),
       ])
-      prisma.pcListingVote.count
-        .mockResolvedValueOnce(0) // upvotes
-        .mockResolvedValueOnce(0) // downvotes
-
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
         adminUserId: ADMIN_ID,
@@ -308,9 +302,10 @@ describe('vote-nullification.service', () => {
       prisma.commentVote.findMany.mockResolvedValue([
         makeCommentVote({ id: 'cv-1', commentId: 'comment-1' }),
       ])
-      prisma.commentVote.count
-        .mockResolvedValueOnce(3) // remaining upvotes
-        .mockResolvedValueOnce(1) // remaining downvotes
+      prisma.commentVote.groupBy.mockResolvedValue([
+        { commentId: 'comment-1', value: true, _count: { _all: 3 } },
+        { commentId: 'comment-1', value: false, _count: { _all: 1 } },
+      ])
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -329,9 +324,10 @@ describe('vote-nullification.service', () => {
       prisma.pcListingCommentVote.findMany.mockResolvedValue([
         makePcCommentVote({ id: 'pcv-1', commentId: 'pc-comment-1' }),
       ])
-      prisma.pcListingCommentVote.count
-        .mockResolvedValueOnce(5) // remaining upvotes
-        .mockResolvedValueOnce(2) // remaining downvotes
+      prisma.pcListingCommentVote.groupBy.mockResolvedValue([
+        { commentId: 'pc-comment-1', value: true, _count: { _all: 5 } },
+        { commentId: 'pc-comment-1', value: false, _count: { _all: 2 } },
+      ])
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -351,7 +347,6 @@ describe('vote-nullification.service', () => {
         makeHandheldVote({ id: 'hv-1', value: true }),
         makeHandheldVote({ id: 'hv-2', value: false, authorId: AUTHOR_B }),
       ])
-      prisma.vote.count.mockResolvedValue(0)
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -361,11 +356,11 @@ describe('vote-nullification.service', () => {
       })
 
       // Voter should get -1 per handheld vote (total -2)
-      const voterCall = mockApplyManualTrustAdjustment.mock.calls.find(
-        (call: unknown[]) => (call[0] as { userId: string }).userId === USER_ID,
-      )
-      expect(voterCall).toBeDefined()
-      expect((voterCall![0] as { adjustment: number }).adjustment).toBe(-2)
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      expect(adjustments.get(USER_ID)).toBe(-2)
     })
 
     it('reverses author trust: -2 for upvote, +1 for downvote', async () => {
@@ -373,7 +368,6 @@ describe('vote-nullification.service', () => {
         makeHandheldVote({ id: 'hv-1', value: true, authorId: AUTHOR_A }),
         makeHandheldVote({ id: 'hv-2', value: false, authorId: AUTHOR_A }),
       ])
-      prisma.vote.count.mockResolvedValue(0)
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -383,18 +377,17 @@ describe('vote-nullification.service', () => {
       })
 
       // Author A: upvote reversed (-2) + downvote reversed (+1) = -1
-      const authorCall = mockApplyManualTrustAdjustment.mock.calls.find(
-        (call: unknown[]) => (call[0] as { userId: string }).userId === AUTHOR_A,
-      )
-      expect(authorCall).toBeDefined()
-      expect((authorCall![0] as { adjustment: number }).adjustment).toBe(-1)
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      expect(adjustments.get(AUTHOR_A)).toBe(-1)
     })
 
     it('skips author trust reversal for self-votes', async () => {
       prisma.vote.findMany.mockResolvedValue([
         makeHandheldVote({ id: 'hv-1', value: true, authorId: USER_ID }),
       ])
-      prisma.vote.count.mockResolvedValue(0)
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -404,18 +397,19 @@ describe('vote-nullification.service', () => {
       })
 
       // Only the voter trust should be adjusted (not author, since it's a self-vote)
-      expect(mockApplyManualTrustAdjustment).toHaveBeenCalledTimes(1)
-      expect(mockApplyManualTrustAdjustment).toHaveBeenCalledWith(
-        expect.objectContaining({ userId: USER_ID, adjustment: -1 }),
-      )
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      expect(adjustments.get(USER_ID)).toBe(-1)
+      expect(adjustments.size).toBe(1)
     })
 
-    it('does NOT reverse trust for PC votes', async () => {
+    it('reverses trust for PC votes (same as handheld)', async () => {
       prisma.pcListingVote.findMany.mockResolvedValue([
-        makePcVote({ id: 'pv-1', value: true }),
-        makePcVote({ id: 'pv-2', value: false }),
+        makePcVote({ id: 'pv-1', value: true, authorId: AUTHOR_B }),
+        makePcVote({ id: 'pv-2', value: false, authorId: AUTHOR_B }),
       ])
-      prisma.pcListingVote.count.mockResolvedValue(0)
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -424,7 +418,14 @@ describe('vote-nullification.service', () => {
         includeCommentVotes: false,
       })
 
-      expect(mockApplyManualTrustAdjustment).not.toHaveBeenCalled()
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      // Voter: -1 per vote = -2 total
+      expect(adjustments.get(USER_ID)).toBe(-2)
+      // Author B: upvote reversed (-2) + downvote reversed (+1) = -1
+      expect(adjustments.get(AUTHOR_B)).toBe(-1)
     })
 
     it('reverses comment author trust for handheld comment upvotes/downvotes', async () => {
@@ -432,7 +433,6 @@ describe('vote-nullification.service', () => {
         makeCommentVote({ id: 'cv-1', value: true, commentUserId: AUTHOR_A }),
         makeCommentVote({ id: 'cv-2', value: false, commentUserId: AUTHOR_A }),
       ])
-      prisma.commentVote.count.mockResolvedValue(0)
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -442,18 +442,17 @@ describe('vote-nullification.service', () => {
       })
 
       // Author A comment trust: upvote reversed (-2) + downvote reversed (+1) = -1
-      const authorCall = mockApplyManualTrustAdjustment.mock.calls.find(
-        (call: unknown[]) => (call[0] as { userId: string }).userId === AUTHOR_A,
-      )
-      expect(authorCall).toBeDefined()
-      expect((authorCall![0] as { adjustment: number }).adjustment).toBe(-1)
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      expect(adjustments.get(AUTHOR_A)).toBe(-1)
     })
 
     it('skips comment author trust for self-votes on own comments', async () => {
       prisma.commentVote.findMany.mockResolvedValue([
         makeCommentVote({ id: 'cv-1', value: true, commentUserId: USER_ID }),
       ])
-      prisma.commentVote.count.mockResolvedValue(0)
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -462,25 +461,26 @@ describe('vote-nullification.service', () => {
         includeCommentVotes: true,
       })
 
-      // No trust adjustment for comment self-votes
-      expect(mockApplyManualTrustAdjustment).not.toHaveBeenCalled()
+      // No trust adjustment for comment self-votes — map is empty
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      expect(adjustments.size).toBe(0)
     })
 
-    it('continues when trust adjustment fails and logs error', async () => {
+    it('propagates trust adjustment errors for transactional rollback', async () => {
       prisma.vote.findMany.mockResolvedValue([makeHandheldVote({ id: 'hv-1', value: true })])
-      prisma.vote.count.mockResolvedValue(0)
-      mockApplyManualTrustAdjustment.mockRejectedValue(new Error('Trust service down'))
-
-      const result = await nullifyUserVotes(prisma as unknown as PrismaClient, {
-        userId: USER_ID,
-        adminUserId: ADMIN_ID,
-        reason: 'Spam',
-        includeCommentVotes: false,
-      })
-
-      // Should still return counts despite trust failure
-      expect(result.handheldVotesNullified).toBe(1)
-      expect(result.trustAdjustments).toBe(0)
+      mockApplyBulkManualAdjustments.mockRejectedValue(new Error('Trust service down'))
+
+      await expect(
+        nullifyUserVotes(prisma as unknown as PrismaClient, {
+          userId: USER_ID,
+          adminUserId: ADMIN_ID,
+          reason: 'Spam',
+          includeCommentVotes: false,
+        }),
+      ).rejects.toThrow('Trust service down')
     })
 
     it('batches nullification for more than 100 votes', async () => {
@@ -488,7 +488,6 @@ describe('vote-nullification.service', () => {
         makeHandheldVote({ id: `hv-${i}`, listingId: `listing-${i % 5}` }),
       )
       prisma.vote.findMany.mockResolvedValue(votes)
-      prisma.vote.count.mockResolvedValue(0)
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -511,7 +510,6 @@ describe('vote-nullification.service', () => {
         makeHandheldVote({ id: 'hv-2', listingId: 'listing-1' }),
         makeHandheldVote({ id: 'hv-3', listingId: 'listing-2' }),
       ])
-      prisma.vote.count.mockResolvedValue(0)
 
       const result = await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -528,8 +526,6 @@ describe('vote-nullification.service', () => {
       const { logAudit } = await import('@/server/services/audit.service')
       prisma.vote.findMany.mockResolvedValue([makeHandheldVote()])
       prisma.pcListingVote.findMany.mockResolvedValue([makePcVote()])
-      prisma.vote.count.mockResolvedValue(0)
-      prisma.pcListingVote.count.mockResolvedValue(0)
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -559,10 +555,6 @@ describe('vote-nullification.service', () => {
       prisma.pcListingCommentVote.findMany.mockResolvedValue([
         makePcCommentVote({ id: 'pcv-1', value: false }),
       ])
-      prisma.vote.count.mockResolvedValue(0)
-      prisma.pcListingVote.count.mockResolvedValue(0)
-      prisma.commentVote.count.mockResolvedValue(0)
-      prisma.pcListingCommentVote.count.mockResolvedValue(0)
 
       const result = await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -606,7 +598,6 @@ describe('vote-nullification.service', () => {
         { ...makeHandheldVote({ id: 'hv-2' }), nullifiedAt: new Date() },
       ]
       prisma.vote.findMany.mockResolvedValue(nullifiedVotes)
-      prisma.vote.count.mockResolvedValue(0)
 
       const result = await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -624,7 +615,6 @@ describe('vote-nullification.service', () => {
     it('clears nullifiedAt on PC votes', async () => {
       const nullifiedVotes = [{ ...makePcVote({ id: 'pv-1' }), nullifiedAt: new Date() }]
       prisma.pcListingVote.findMany.mockResolvedValue(nullifiedVotes)
-      prisma.pcListingVote.count.mockResolvedValue(0)
 
       const result = await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -646,8 +636,6 @@ describe('vote-nullification.service', () => {
       prisma.pcListingCommentVote.findMany.mockResolvedValue([
         { ...makePcCommentVote({ id: 'pcv-1' }), nullifiedAt: new Date() },
       ])
-      prisma.commentVote.count.mockResolvedValue(0)
-      prisma.pcListingCommentVote.count.mockResolvedValue(0)
 
       const result = await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -666,9 +654,10 @@ describe('vote-nullification.service', () => {
           nullifiedAt: new Date(),
         },
       ])
-      prisma.vote.count
-        .mockResolvedValueOnce(5) // upvotes after restore
-        .mockResolvedValueOnce(2) // downvotes after restore
+      prisma.vote.groupBy.mockResolvedValue([
+        { listingId: 'listing-1', value: true, _count: { _all: 5 } },
+        { listingId: 'listing-1', value: false, _count: { _all: 2 } },
+      ])
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -691,9 +680,10 @@ describe('vote-nullification.service', () => {
       prisma.commentVote.findMany.mockResolvedValue([
         { ...makeCommentVote({ id: 'cv-1', commentId: 'comment-1' }), nullifiedAt: new Date() },
       ])
-      prisma.commentVote.count
-        .mockResolvedValueOnce(4) // upvotes
-        .mockResolvedValueOnce(1) // downvotes
+      prisma.commentVote.groupBy.mockResolvedValue([
+        { commentId: 'comment-1', value: true, _count: { _all: 4 } },
+        { commentId: 'comment-1', value: false, _count: { _all: 1 } },
+      ])
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -715,7 +705,6 @@ describe('vote-nullification.service', () => {
           nullifiedAt: new Date(),
         },
       ])
-      prisma.vote.count.mockResolvedValue(0)
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -724,11 +713,11 @@ describe('vote-nullification.service', () => {
       })
 
       // Voter should get +1 per handheld vote (total +2)
-      const voterCall = mockApplyManualTrustAdjustment.mock.calls.find(
-        (call: unknown[]) => (call[0] as { userId: string }).userId === USER_ID,
-      )
-      expect(voterCall).toBeDefined()
-      expect((voterCall![0] as { adjustment: number }).adjustment).toBe(2)
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      expect(adjustments.get(USER_ID)).toBe(2)
     })
 
     it('re-applies author trust: +2 for upvotes, -1 for downvotes', async () => {
@@ -742,7 +731,6 @@ describe('vote-nullification.service', () => {
           nullifiedAt: new Date(),
         },
       ])
-      prisma.vote.count.mockResolvedValue(0)
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -751,11 +739,11 @@ describe('vote-nullification.service', () => {
       })
 
       // Author A: upvote re-applied (+2) + downvote re-applied (-1) = +1
-      const authorCall = mockApplyManualTrustAdjustment.mock.calls.find(
-        (call: unknown[]) => (call[0] as { userId: string }).userId === AUTHOR_A,
-      )
-      expect(authorCall).toBeDefined()
-      expect((authorCall![0] as { adjustment: number }).adjustment).toBe(1)
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      expect(adjustments.get(AUTHOR_A)).toBe(1)
     })
 
     it('skips author trust re-application for self-votes', async () => {
@@ -765,7 +753,6 @@ describe('vote-nullification.service', () => {
           nullifiedAt: new Date(),
         },
       ])
-      prisma.vote.count.mockResolvedValue(0)
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -773,17 +760,22 @@ describe('vote-nullification.service', () => {
         reason: 'Wrongful ban',
       })
 
-      expect(mockApplyManualTrustAdjustment).toHaveBeenCalledTimes(1)
-      expect(mockApplyManualTrustAdjustment).toHaveBeenCalledWith(
-        expect.objectContaining({ userId: USER_ID, adjustment: 1 }),
-      )
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      expect(adjustments.get(USER_ID)).toBe(1)
+      expect(adjustments.size).toBe(1)
     })
 
-    it('does NOT re-apply trust for PC votes', async () => {
+    it('re-applies trust for PC votes (same as handheld)', async () => {
       prisma.pcListingVote.findMany.mockResolvedValue([
-        { ...makePcVote({ id: 'pv-1' }), nullifiedAt: new Date() },
+        { ...makePcVote({ id: 'pv-1', value: true, authorId: AUTHOR_B }), nullifiedAt: new Date() },
+        {
+          ...makePcVote({ id: 'pv-2', value: false, authorId: AUTHOR_B }),
+          nullifiedAt: new Date(),
+        },
       ])
-      prisma.pcListingVote.count.mockResolvedValue(0)
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -791,7 +783,14 @@ describe('vote-nullification.service', () => {
         reason: 'Wrongful ban',
       })
 
-      expect(mockApplyManualTrustAdjustment).not.toHaveBeenCalled()
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
+      // Voter: +1 per vote = +2 total
+      expect(adjustments.get(USER_ID)).toBe(2)
+      // Author B: upvote re-applied (+2) + downvote re-applied (-1) = +1
+      expect(adjustments.get(AUTHOR_B)).toBe(1)
     })
 
     it('batches restoration for more than 100 votes', async () => {
@@ -800,7 +799,6 @@ describe('vote-nullification.service', () => {
         nullifiedAt: new Date(),
       }))
       prisma.vote.findMany.mockResolvedValue(votes)
-      prisma.vote.count.mockResolvedValue(0)
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -821,8 +819,6 @@ describe('vote-nullification.service', () => {
       prisma.pcListingVote.findMany.mockResolvedValue([
         { ...makePcVote(), nullifiedAt: new Date() },
       ])
-      prisma.vote.count.mockResolvedValue(0)
-      prisma.pcListingVote.count.mockResolvedValue(0)
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -855,7 +851,6 @@ describe('vote-nullification.service', () => {
           nullifiedAt: new Date(),
         },
       ])
-      prisma.commentVote.count.mockResolvedValue(0)
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -863,19 +858,14 @@ describe('vote-nullification.service', () => {
         reason: 'Wrongful ban',
       })
 
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const { adjustments } = mockApplyBulkManualAdjustments.mock.calls[0][0] as {
+        adjustments: Map<string, number>
+      }
       // Author A: upvote re-applied (+2)
-      const authorACall = mockApplyManualTrustAdjustment.mock.calls.find(
-        (call: unknown[]) => (call[0] as { userId: string }).userId === AUTHOR_A,
-      )
-      expect(authorACall).toBeDefined()
-      expect((authorACall![0] as { adjustment: number }).adjustment).toBe(2)
-
+      expect(adjustments.get(AUTHOR_A)).toBe(2)
       // Author B: downvote re-applied (-1)
-      const authorBCall = mockApplyManualTrustAdjustment.mock.calls.find(
-        (call: unknown[]) => (call[0] as { userId: string }).userId === AUTHOR_B,
-      )
-      expect(authorBCall).toBeDefined()
-      expect((authorBCall![0] as { adjustment: number }).adjustment).toBe(-1)
+      expect(adjustments.get(AUTHOR_B)).toBe(-1)
     })
   })
 
@@ -890,8 +880,6 @@ describe('vote-nullification.service', () => {
       // Phase 1: Nullify
       prisma.vote.findMany.mockResolvedValue(votes)
       prisma.commentVote.findMany.mockResolvedValue(commentVotes)
-      prisma.vote.count.mockResolvedValue(0)
-      prisma.commentVote.count.mockResolvedValue(0)
 
       const nullifyResult = await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -904,14 +892,15 @@ describe('vote-nullification.service', () => {
       expect(nullifyResult.commentVotesNullified).toBe(1)
 
       // Capture nullification trust adjustments
-      const nullifyAdjustments = new Map<string, number>()
-      for (const call of mockApplyManualTrustAdjustment.mock.calls) {
-        const { userId, adjustment } = call[0] as { userId: string; adjustment: number }
-        nullifyAdjustments.set(userId, (nullifyAdjustments.get(userId) ?? 0) + adjustment)
-      }
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const nullifyAdjustments = mockApplyBulkManualAdjustments.mock.calls[0][0].adjustments as Map<
+        string,
+        number
+      >
 
       // Phase 2: Restore
-      mockApplyManualTrustAdjustment.mockClear()
+      mockApplyBulkManualAdjustments.mockClear()
+      mockApplyBulkManualAdjustments.mockResolvedValue(0)
       prisma = createMockPrisma()
 
       const nullifiedVotes = votes.map((v) => ({ ...v, nullifiedAt: new Date() }))
@@ -919,8 +908,6 @@ describe('vote-nullification.service', () => {
 
       prisma.vote.findMany.mockResolvedValue(nullifiedVotes)
       prisma.commentVote.findMany.mockResolvedValue(nullifiedCommentVotes)
-      prisma.vote.count.mockResolvedValue(0)
-      prisma.commentVote.count.mockResolvedValue(0)
 
       const restoreResult = await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -932,11 +919,11 @@ describe('vote-nullification.service', () => {
       expect(restoreResult.commentVotesRestored).toBe(1)
 
       // Capture restoration trust adjustments
-      const restoreAdjustments = new Map<string, number>()
-      for (const call of mockApplyManualTrustAdjustment.mock.calls) {
-        const { userId, adjustment } = call[0] as { userId: string; adjustment: number }
-        restoreAdjustments.set(userId, (restoreAdjustments.get(userId) ?? 0) + adjustment)
-      }
+      expect(mockApplyBulkManualAdjustments).toHaveBeenCalledTimes(1)
+      const restoreAdjustments = mockApplyBulkManualAdjustments.mock.calls[0][0].adjustments as Map<
+        string,
+        number
+      >
 
       // Net trust adjustment for each user should be zero
       for (const [uid, nullifyAdj] of nullifyAdjustments) {
@@ -950,7 +937,6 @@ describe('vote-nullification.service', () => {
 
       // Phase 1: Nullify
       prisma.vote.findMany.mockResolvedValue([vote])
-      prisma.vote.count.mockResolvedValue(0)
 
       await nullifyUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
@@ -968,9 +954,9 @@ describe('vote-nullification.service', () => {
       // Phase 2: Restore
       prisma = createMockPrisma()
       prisma.vote.findMany.mockResolvedValue([{ ...vote, nullifiedAt: new Date() }])
-      prisma.vote.count
-        .mockResolvedValueOnce(1) // upvotes after restore
-        .mockResolvedValueOnce(0) // downvotes after restore
+      prisma.vote.groupBy.mockResolvedValue([
+        { listingId: 'listing-1', value: true, _count: { _all: 1 } },
+      ])
 
       await restoreUserVotes(prisma as unknown as PrismaClient, {
         userId: USER_ID,
diff --git a/src/server/services/vote-nullification.service.ts b/src/server/services/vote-nullification.service.ts
index 2f31713c5..d91c7e72e 100644
--- a/src/server/services/vote-nullification.service.ts
+++ b/src/server/services/vote-nullification.service.ts
@@ -1,10 +1,11 @@
-import { logger } from '@/lib/logger'
 import { TRUST_ACTIONS } from '@/lib/trust/config'
-import { applyManualTrustAdjustment } from '@/lib/trust/service'
+import { TrustService } from '@/lib/trust/service'
 import { type ListingType } from '@/schemas/common'
 import { logAudit } from '@/server/services/audit.service'
 import { calculateWilsonScore } from '@/utils/wilson-score'
-import { AuditAction, AuditEntityType, TrustAction, type PrismaClient } from '@orm'
+import { AuditAction, AuditEntityType, TrustAction, type Prisma, type PrismaClient } from '@orm'
+
+type PrismaClientOrTransaction = PrismaClient | Prisma.TransactionClient
 
 interface NullifyParams {
   userId: string
@@ -43,86 +44,162 @@ interface RestoreResult {
 
 const BATCH_SIZE = 100
 
+interface VoteIdentifier {
+  id: string
+}
+
+interface NullifiableDelegate {
+  updateMany: (args: {
+    where: { id: { in: string[] } }
+    data: { nullifiedAt: Date | null }
+  }) => Promise<{ count: number }>
+}
+
+/**
+ * Updates `nullifiedAt` on a set of votes in serial batches of `BATCH_SIZE`.
+ * Batching is serial within one delegate (avoids exhausting the connection pool);
+ * the caller can run multiple `batchUpdateNullifiedAt` invocations in parallel
+ * across different delegates (different tables) via `Promise.all`.
+ */
+async function batchUpdateNullifiedAt<T extends VoteIdentifier>(
+  delegate: NullifiableDelegate,
+  votes: readonly T[],
+  nullifiedAt: Date | null,
+): Promise<void> {
+  for (let i = 0; i < votes.length; i += BATCH_SIZE) {
+    const batch = votes.slice(i, i + BATCH_SIZE)
+    await delegate.updateMany({
+      where: { id: { in: batch.map((v) => v.id) } },
+      data: { nullifiedAt },
+    })
+  }
+}
+
+interface VoteScoreEntry {
+  up: number
+  down: number
+}
+
 async function recalculateListingScores(
-  prisma: PrismaClient,
+  prisma: PrismaClientOrTransaction,
   listingIds: Set<string>,
   type: ListingType,
 ): Promise<number> {
-  let count = 0
-  for (const listingId of listingIds) {
-    if (type === 'handheld') {
-      const [upvotes, downvotes] = await Promise.all([
-        prisma.vote.count({ where: { listingId, value: true, nullifiedAt: null } }),
-        prisma.vote.count({ where: { listingId, value: false, nullifiedAt: null } }),
-      ])
+  if (listingIds.size === 0) return 0
+
+  const ids = [...listingIds]
+  const scoreMap = new Map<string, VoteScoreEntry>()
+  for (const id of ids) scoreMap.set(id, { up: 0, down: 0 })
+
+  if (type === 'handheld') {
+    const counts = await prisma.vote.groupBy({
+      by: ['listingId', 'value'],
+      where: { listingId: { in: ids }, nullifiedAt: null },
+      _count: { _all: true },
+    })
+    for (const row of counts) {
+      const entry = scoreMap.get(row.listingId)
+      if (entry) {
+        if (row.value) entry.up = row._count._all
+        else entry.down = row._count._all
+      }
+    }
+    for (const [id, { up, down }] of scoreMap) {
       await prisma.listing.update({
-        where: { id: listingId },
+        where: { id },
         data: {
-          upvoteCount: upvotes,
-          downvoteCount: downvotes,
-          voteCount: upvotes + downvotes,
-          successRate: calculateWilsonScore(upvotes, downvotes),
+          upvoteCount: up,
+          downvoteCount: down,
+          voteCount: up + down,
+          successRate: calculateWilsonScore(up, down),
         },
       })
-    } else {
-      const [upvotes, downvotes] = await Promise.all([
-        prisma.pcListingVote.count({
-          where: { pcListingId: listingId, value: true, nullifiedAt: null },
-        }),
-        prisma.pcListingVote.count({
-          where: { pcListingId: listingId, value: false, nullifiedAt: null },
-        }),
-      ])
+    }
+  } else {
+    const counts = await prisma.pcListingVote.groupBy({
+      by: ['pcListingId', 'value'],
+      where: { pcListingId: { in: ids }, nullifiedAt: null },
+      _count: { _all: true },
+    })
+    for (const row of counts) {
+      const entry = scoreMap.get(row.pcListingId)
+      if (entry) {
+        if (row.value) entry.up = row._count._all
+        else entry.down = row._count._all
+      }
+    }
+    for (const [id, { up, down }] of scoreMap) {
       await prisma.pcListing.update({
-        where: { id: listingId },
+        where: { id },
         data: {
-          upvoteCount: upvotes,
-          downvoteCount: downvotes,
-          voteCount: upvotes + downvotes,
-          successRate: calculateWilsonScore(upvotes, downvotes),
+          upvoteCount: up,
+          downvoteCount: down,
+          voteCount: up + down,
+          successRate: calculateWilsonScore(up, down),
         },
       })
     }
-    count++
   }
-  return count
+
+  return listingIds.size
 }
 
 async function recalculateCommentScores(
-  prisma: PrismaClient,
+  prisma: PrismaClientOrTransaction,
   commentIds: Set<string>,
   type: ListingType,
 ): Promise<number> {
-  let count = 0
-  for (const commentId of commentIds) {
-    if (type === 'handheld') {
-      const [upvotes, downvotes] = await Promise.all([
-        prisma.commentVote.count({ where: { commentId, value: true, nullifiedAt: null } }),
-        prisma.commentVote.count({ where: { commentId, value: false, nullifiedAt: null } }),
-      ])
+  if (commentIds.size === 0) return 0
+
+  const ids = [...commentIds]
+  const scoreMap = new Map<string, VoteScoreEntry>()
+  for (const id of ids) scoreMap.set(id, { up: 0, down: 0 })
+
+  if (type === 'handheld') {
+    const counts = await prisma.commentVote.groupBy({
+      by: ['commentId', 'value'],
+      where: { commentId: { in: ids }, nullifiedAt: null },
+      _count: { _all: true },
+    })
+    for (const row of counts) {
+      const entry = scoreMap.get(row.commentId)
+      if (entry) {
+        if (row.value) entry.up = row._count._all
+        else entry.down = row._count._all
+      }
+    }
+    for (const [id, { up, down }] of scoreMap) {
       await prisma.comment.update({
-        where: { id: commentId },
-        data: { score: upvotes - downvotes },
+        where: { id },
+        data: { score: up - down },
       })
-    } else {
-      const [upvotes, downvotes] = await Promise.all([
-        prisma.pcListingCommentVote.count({ where: { commentId, value: true, nullifiedAt: null } }),
-        prisma.pcListingCommentVote.count({
-          where: { commentId, value: false, nullifiedAt: null },
-        }),
-      ])
+    }
+  } else {
+    const counts = await prisma.pcListingCommentVote.groupBy({
+      by: ['commentId', 'value'],
+      where: { commentId: { in: ids }, nullifiedAt: null },
+      _count: { _all: true },
+    })
+    for (const row of counts) {
+      const entry = scoreMap.get(row.commentId)
+      if (entry) {
+        if (row.value) entry.up = row._count._all
+        else entry.down = row._count._all
+      }
+    }
+    for (const [id, { up, down }] of scoreMap) {
       await prisma.pcListingComment.update({
-        where: { id: commentId },
-        data: { score: upvotes - downvotes },
+        where: { id },
+        data: { score: up - down },
       })
     }
-    count++
   }
-  return count
+
+  return commentIds.size
 }
 
 export async function nullifyUserVotes(
-  prisma: PrismaClient,
+  prisma: PrismaClientOrTransaction,
   params: NullifyParams,
 ): Promise<NullifyResult> {
   const { userId, adminUserId, reason, includeCommentVotes, headers } = params
@@ -175,40 +252,16 @@ export async function nullifyUserVotes(
 
   const now = new Date()
 
-  // 2. Batch nullify votes
-  for (let i = 0; i < handheldVotes.length; i += BATCH_SIZE) {
-    const batch = handheldVotes.slice(i, i + BATCH_SIZE)
-    await prisma.vote.updateMany({
-      where: { id: { in: batch.map((v) => v.id) } },
-      data: { nullifiedAt: now },
-    })
-  }
-
-  for (let i = 0; i < pcVotes.length; i += BATCH_SIZE) {
-    const batch = pcVotes.slice(i, i + BATCH_SIZE)
-    await prisma.pcListingVote.updateMany({
-      where: { id: { in: batch.map((v) => v.id) } },
-      data: { nullifiedAt: now },
-    })
-  }
-
-  for (let i = 0; i < commentVotes.length; i += BATCH_SIZE) {
-    const batch = commentVotes.slice(i, i + BATCH_SIZE)
-    await prisma.commentVote.updateMany({
-      where: { id: { in: batch.map((v) => v.id) } },
-      data: { nullifiedAt: now },
-    })
-  }
-
-  for (let i = 0; i < pcCommentVotes.length; i += BATCH_SIZE) {
-    const batch = pcCommentVotes.slice(i, i + BATCH_SIZE)
-    await prisma.pcListingCommentVote.updateMany({
-      where: { id: { in: batch.map((v) => v.id) } },
-      data: { nullifiedAt: now },
-    })
-  }
+  // 2. Batch nullify votes — across different vote types in parallel,
+  //    serially batched within each type to keep connection-pool pressure bounded.
+  await Promise.all([
+    batchUpdateNullifiedAt(prisma.vote, handheldVotes, now),
+    batchUpdateNullifiedAt(prisma.pcListingVote, pcVotes, now),
+    batchUpdateNullifiedAt(prisma.commentVote, commentVotes, now),
+    batchUpdateNullifiedAt(prisma.pcListingCommentVote, pcCommentVotes, now),
+  ])
 
-  // 3. Recalculate listing scores
+  // 3. Recalculate listing scores — parallel across distinct tables.
   const handheldListingIds = new Set(handheldVotes.map((v) => v.listingId))
   const pcListingIds = new Set(pcVotes.map((v) => v.pcListingId))
   const handheldCommentIds = new Set(commentVotes.map((v) => v.commentId))
@@ -229,69 +282,56 @@ export async function nullifyUserVotes(
     trustAdjustments.set(targetUserId, (trustAdjustments.get(targetUserId) ?? 0) + amount)
   }
 
-  // Handheld listing votes: reverse trust for voter and authors
+  // Reverse listing vote trust for both handheld and PC
   for (const vote of handheldVotes) {
-    // Reverse voter trust: voter got +1 for either UPVOTE or DOWNVOTE
-    addAdjustment(userId, -TRUST_ACTIONS[TrustAction.UPVOTE].weight)
-
-    // Reverse author trust (skip self-votes)
+    const voterAction = vote.value ? TrustAction.UPVOTE : TrustAction.DOWNVOTE
+    addAdjustment(userId, -TRUST_ACTIONS[voterAction].weight)
     if (vote.listing.authorId && vote.listing.authorId !== userId) {
-      if (vote.value) {
-        // Was upvote: author got +2 (LISTING_RECEIVED_UPVOTE), reverse it
-        addAdjustment(
-          vote.listing.authorId,
-          -TRUST_ACTIONS[TrustAction.LISTING_RECEIVED_UPVOTE].weight,
-        )
-      } else {
-        // Was downvote: author got -1 (LISTING_RECEIVED_DOWNVOTE), reverse it
-        addAdjustment(
-          vote.listing.authorId,
-          -TRUST_ACTIONS[TrustAction.LISTING_RECEIVED_DOWNVOTE].weight,
-        )
-      }
+      const action = vote.value
+        ? TrustAction.LISTING_RECEIVED_UPVOTE
+        : TrustAction.LISTING_RECEIVED_DOWNVOTE
+      addAdjustment(vote.listing.authorId, -TRUST_ACTIONS[action].weight)
+    }
+  }
+
+  for (const vote of pcVotes) {
+    const voterAction = vote.value ? TrustAction.UPVOTE : TrustAction.DOWNVOTE
+    addAdjustment(userId, -TRUST_ACTIONS[voterAction].weight)
+    if (vote.pcListing.authorId && vote.pcListing.authorId !== userId) {
+      const action = vote.value
+        ? TrustAction.LISTING_RECEIVED_UPVOTE
+        : TrustAction.LISTING_RECEIVED_DOWNVOTE
+      addAdjustment(vote.pcListing.authorId, -TRUST_ACTIONS[action].weight)
     }
   }
 
-  // Handheld comment votes: reverse author trust (skip self-votes)
+  // Reverse comment vote trust for both handheld and PC
   for (const vote of commentVotes) {
     if (vote.comment.userId !== userId) {
-      if (vote.value) {
-        addAdjustment(
-          vote.comment.userId,
-          -TRUST_ACTIONS[TrustAction.COMMENT_RECEIVED_UPVOTE].weight,
-        )
-      } else {
-        addAdjustment(
-          vote.comment.userId,
-          -TRUST_ACTIONS[TrustAction.COMMENT_RECEIVED_DOWNVOTE].weight,
-        )
-      }
+      const action = vote.value
+        ? TrustAction.COMMENT_RECEIVED_UPVOTE
+        : TrustAction.COMMENT_RECEIVED_DOWNVOTE
+      addAdjustment(vote.comment.userId, -TRUST_ACTIONS[action].weight)
     }
   }
 
-  // PC votes: no trust reversal (trust was never applied for PC votes)
-  // PC comment votes: no trust reversal (trust was never applied)
-
-  // Apply trust adjustments
-  let trustAdjustmentCount = 0
-  for (const [targetUserId, adjustment] of trustAdjustments) {
-    if (adjustment === 0) continue
-    try {
-      await applyManualTrustAdjustment({
-        userId: targetUserId,
-        adjustment,
-        reason: `Vote nullification: ${reason}`,
-        adminUserId,
-      })
-      trustAdjustmentCount++
-    } catch (err) {
-      logger.error(
-        `[vote-nullification] Failed to apply trust adjustment for user ${targetUserId}:`,
-        err,
-      )
+  for (const vote of pcCommentVotes) {
+    if (vote.comment.userId !== userId) {
+      const action = vote.value
+        ? TrustAction.COMMENT_RECEIVED_UPVOTE
+        : TrustAction.COMMENT_RECEIVED_DOWNVOTE
+      addAdjustment(vote.comment.userId, -TRUST_ACTIONS[action].weight)
     }
   }
 
+  // Apply trust adjustments using TrustService for transaction participation
+  const trustService = new TrustService(prisma)
+  const trustAdjustmentCount = await trustService.applyBulkManualAdjustments({
+    adjustments: trustAdjustments,
+    reason: `Vote nullification: ${reason}`,
+    adminUserId,
+  })
+
   // 5. Audit log
   void logAudit(prisma, {
     actorId: adminUserId,
@@ -323,7 +363,7 @@ export async function nullifyUserVotes(
 }
 
 export async function restoreUserVotes(
-  prisma: PrismaClient,
+  prisma: PrismaClientOrTransaction,
   params: RestoreParams,
 ): Promise<RestoreResult> {
   const { userId, adminUserId, reason, headers } = params
@@ -370,40 +410,15 @@ export async function restoreUserVotes(
     }
   }
 
-  // 2. Clear nullifiedAt
-  for (let i = 0; i < handheldVotes.length; i += BATCH_SIZE) {
-    const batch = handheldVotes.slice(i, i + BATCH_SIZE)
-    await prisma.vote.updateMany({
-      where: { id: { in: batch.map((v) => v.id) } },
-      data: { nullifiedAt: null },
-    })
-  }
-
-  for (let i = 0; i < pcVotes.length; i += BATCH_SIZE) {
-    const batch = pcVotes.slice(i, i + BATCH_SIZE)
-    await prisma.pcListingVote.updateMany({
-      where: { id: { in: batch.map((v) => v.id) } },
-      data: { nullifiedAt: null },
-    })
-  }
-
-  for (let i = 0; i < commentVotes.length; i += BATCH_SIZE) {
-    const batch = commentVotes.slice(i, i + BATCH_SIZE)
-    await prisma.commentVote.updateMany({
-      where: { id: { in: batch.map((v) => v.id) } },
-      data: { nullifiedAt: null },
-    })
-  }
-
-  for (let i = 0; i < pcCommentVotes.length; i += BATCH_SIZE) {
-    const batch = pcCommentVotes.slice(i, i + BATCH_SIZE)
-    await prisma.pcListingCommentVote.updateMany({
-      where: { id: { in: batch.map((v) => v.id) } },
-      data: { nullifiedAt: null },
-    })
-  }
+  // 2. Clear nullifiedAt — parallel across vote types, serial batches within each type.
+  await Promise.all([
+    batchUpdateNullifiedAt(prisma.vote, handheldVotes, null),
+    batchUpdateNullifiedAt(prisma.pcListingVote, pcVotes, null),
+    batchUpdateNullifiedAt(prisma.commentVote, commentVotes, null),
+    batchUpdateNullifiedAt(prisma.pcListingCommentVote, pcCommentVotes, null),
+  ])
 
-  // 3. Recalculate scores
+  // 3. Recalculate scores — parallel across distinct tables.
   const handheldListingIds = new Set(handheldVotes.map((v) => v.listingId))
   const pcListingIds = new Set(pcVotes.map((v) => v.pcListingId))
   const handheldCommentIds = new Set(commentVotes.map((v) => v.commentId))
@@ -423,61 +438,55 @@ export async function restoreUserVotes(
     trustAdjustments.set(targetUserId, (trustAdjustments.get(targetUserId) ?? 0) + amount)
   }
 
+  // Re-apply listing vote trust for both handheld and PC
   for (const vote of handheldVotes) {
-    // Re-apply voter trust
-    addAdjustment(userId, TRUST_ACTIONS[TrustAction.UPVOTE].weight)
-
-    // Re-apply author trust (skip self-votes)
+    const voterAction = vote.value ? TrustAction.UPVOTE : TrustAction.DOWNVOTE
+    addAdjustment(userId, TRUST_ACTIONS[voterAction].weight)
     if (vote.listing.authorId && vote.listing.authorId !== userId) {
-      if (vote.value) {
-        addAdjustment(
-          vote.listing.authorId,
-          TRUST_ACTIONS[TrustAction.LISTING_RECEIVED_UPVOTE].weight,
-        )
-      } else {
-        addAdjustment(
-          vote.listing.authorId,
-          TRUST_ACTIONS[TrustAction.LISTING_RECEIVED_DOWNVOTE].weight,
-        )
-      }
+      const action = vote.value
+        ? TrustAction.LISTING_RECEIVED_UPVOTE
+        : TrustAction.LISTING_RECEIVED_DOWNVOTE
+      addAdjustment(vote.listing.authorId, TRUST_ACTIONS[action].weight)
+    }
+  }
+
+  for (const vote of pcVotes) {
+    const voterAction = vote.value ? TrustAction.UPVOTE : TrustAction.DOWNVOTE
+    addAdjustment(userId, TRUST_ACTIONS[voterAction].weight)
+    if (vote.pcListing.authorId && vote.pcListing.authorId !== userId) {
+      const action = vote.value
+        ? TrustAction.LISTING_RECEIVED_UPVOTE
+        : TrustAction.LISTING_RECEIVED_DOWNVOTE
+      addAdjustment(vote.pcListing.authorId, TRUST_ACTIONS[action].weight)
     }
   }
 
+  // Re-apply comment vote trust for both handheld and PC
   for (const vote of commentVotes) {
     if (vote.comment.userId !== userId) {
-      if (vote.value) {
-        addAdjustment(
-          vote.comment.userId,
-          TRUST_ACTIONS[TrustAction.COMMENT_RECEIVED_UPVOTE].weight,
-        )
-      } else {
-        addAdjustment(
-          vote.comment.userId,
-          TRUST_ACTIONS[TrustAction.COMMENT_RECEIVED_DOWNVOTE].weight,
-        )
-      }
+      const action = vote.value
+        ? TrustAction.COMMENT_RECEIVED_UPVOTE
+        : TrustAction.COMMENT_RECEIVED_DOWNVOTE
+      addAdjustment(vote.comment.userId, TRUST_ACTIONS[action].weight)
     }
   }
 
-  let trustAdjustmentCount = 0
-  for (const [targetUserId, adjustment] of trustAdjustments) {
-    if (adjustment === 0) continue
-    try {
-      await applyManualTrustAdjustment({
-        userId: targetUserId,
-        adjustment,
-        reason: `Vote restoration: ${reason}`,
-        adminUserId,
-      })
-      trustAdjustmentCount++
-    } catch (err) {
-      logger.error(
-        `[vote-restoration] Failed to apply trust adjustment for user ${targetUserId}:`,
-        err,
-      )
+  for (const vote of pcCommentVotes) {
+    if (vote.comment.userId !== userId) {
+      const action = vote.value
+        ? TrustAction.COMMENT_RECEIVED_UPVOTE
+        : TrustAction.COMMENT_RECEIVED_DOWNVOTE
+      addAdjustment(vote.comment.userId, TRUST_ACTIONS[action].weight)
     }
   }
 
+  const trustService = new TrustService(prisma)
+  const trustAdjustmentCount = await trustService.applyBulkManualAdjustments({
+    adjustments: trustAdjustments,
+    reason: `Vote restoration: ${reason}`,
+    adminUserId,
+  })
+
   // 5. Audit log
   void logAudit(prisma, {
     actorId: adminUserId,
diff --git a/src/server/utils/emulator-config/gamenative/gamenative.converter.test.ts b/src/server/utils/emulator-config/gamenative/gamenative.converter.test.ts
index 96e54c87c..0cc0463c8 100644
--- a/src/server/utils/emulator-config/gamenative/gamenative.converter.test.ts
+++ b/src/server/utils/emulator-config/gamenative/gamenative.converter.test.ts
@@ -18,10 +18,10 @@ describe('GameNative Converter', () => {
 
       expect(config).toMatchObject({
         name: '',
-        screenSize: '854x480',
+        screenSize: '1280x720',
         graphicsDriver: 'vortek',
         dxwrapper: 'dxvk',
-        audioDriver: 'alsa',
+        audioDriver: 'pulseaudio',
         startupSelection: 1,
         box64Version: '0.3.6',
         box86Version: '0.3.2',
@@ -30,6 +30,12 @@ describe('GameNative Converter', () => {
         wow64Mode: true,
         showFPS: false,
         csmt: true,
+        containerVariant: 'glibc',
+        emulator: 'FEXCore',
+        fexcoreVersion: '2603',
+        fexcorePreset: 'INTERMEDIATE',
+        useSteamInput: false,
+        dinputMapperType: 1,
       })
     })
 
@@ -38,8 +44,8 @@ describe('GameNative Converter', () => {
         { input: '1920x1080 (16:9)', expected: '1920x1080' },
         { input: '2560x1440', expected: '2560x1440' },
         { input: '854x480 (16:9)', expected: '854x480' },
-        { input: 'invalid', expected: '854x480' }, // Default fallback
-        { input: '', expected: '854x480' }, // Empty fallback
+        { input: 'invalid', expected: '1280x720' },
+        { input: '', expected: '1280x720' },
       ]
 
       testCases.forEach(({ input, expected }) => {
@@ -63,7 +69,6 @@ describe('GameNative Converter', () => {
     })
 
     it('should handle environment variables properly', () => {
-      // Test with empty value - should use default
       const configEmpty = convertToGameNativeConfig({
         listingId: 'test',
         gameId: 'game',
@@ -80,9 +85,8 @@ describe('GameNative Converter', () => {
       })
 
       expect(configEmpty.envVars).toContain('ZINK_DESCRIPTORS=lazy')
-      expect(configEmpty.envVars).toContain('MESA_VK_WSI_PRESENT_MODE=mailbox')
+      expect(configEmpty.envVars).toContain('PULSE_LATENCY_MSEC=144')
 
-      // Test with custom value
       const configCustom = convertToGameNativeConfig({
         listingId: 'test',
         gameId: 'game',
@@ -104,11 +108,11 @@ describe('GameNative Converter', () => {
     it('should map graphics driver names correctly', () => {
       const testCases = [
         { input: 'Vortek (Universal)', expected: 'vortek' },
-        { input: 'Mesa Turnip', expected: 'turnip' },
-        { input: 'VirGL', expected: 'virgl' },
-        { input: 'Freedreno', expected: 'vortek' }, // Not a valid driver, defaults to vortek
-        { input: 'Custom Driver', expected: 'vortek' }, // Unknown defaults to vortek
-        { input: '', expected: 'vortek' }, // Default when empty
+        { input: 'Turnip (Adreno)', expected: 'turnip' },
+        { input: 'VirGL (Universal)', expected: 'virgl' },
+        { input: 'Adreno (Adreno)', expected: 'adreno' },
+        { input: 'Custom Driver', expected: 'vortek' },
+        { input: '', expected: 'vortek' },
       ]
 
       testCases.forEach(({ input, expected }) => {
@@ -120,7 +124,7 @@ describe('GameNative Converter', () => {
               customFieldDefinition: {
                 name: 'graphics_driver',
                 label: 'Graphics Driver',
-                type: 'TEXT',
+                type: 'SELECT',
               },
               value: input,
             },
@@ -138,7 +142,7 @@ describe('GameNative Converter', () => {
         { input: 'VKD3D', expected: 'vkd3d' },
         { input: 'CNC DDraw', expected: 'cnc-ddraw' },
         { input: 'Other', expected: 'dxvk' },
-        { input: 'Unknown', expected: 'dxvk' }, // Default fallback
+        { input: 'Unknown', expected: 'dxvk' },
       ]
 
       testCases.forEach(({ input, expected }) => {
@@ -163,10 +167,12 @@ describe('GameNative Converter', () => {
 
     it('should map audio driver options correctly', () => {
       const testCases = [
+        { input: 'alsa', expected: 'alsa' },
+        { input: 'pulse', expected: 'pulseaudio' },
+        { input: 'other', expected: 'pulseaudio' },
         { input: 'ALSA', expected: 'alsa' },
-        { input: 'PulseAudio', expected: 'pulse' }, // Correct value is 'pulse'
-        { input: 'Other', expected: 'alsa' },
-        { input: 'Unknown', expected: 'alsa' }, // Default fallback
+        { input: 'PulseAudio', expected: 'pulseaudio' },
+        { input: 'Unknown', expected: 'pulseaudio' },
       ]
 
       testCases.forEach(({ input, expected }) => {
@@ -195,7 +201,7 @@ describe('GameNative Converter', () => {
         { input: 'Essential (Load only essential services)', expected: 1 },
         { input: 'Aggressive (Stop services on startup)', expected: 2 },
         { input: 'Other', expected: 1 },
-        { input: 'Unknown', expected: 1 }, // Default fallback
+        { input: 'Unknown', expected: 1 },
       ]
 
       testCases.forEach(({ input, expected }) => {
@@ -218,8 +224,7 @@ describe('GameNative Converter', () => {
       })
     })
 
-    it('should handle Box64 and Box86 versions with defaults', () => {
-      // Test with provided values
+    it('should handle Box64 versions with defaults', () => {
       const configWithValues = convertToGameNativeConfig({
         listingId: 'test',
         gameId: 'game',
@@ -228,25 +233,15 @@ describe('GameNative Converter', () => {
             customFieldDefinition: {
               name: 'box64_version',
               label: 'Box64 Version',
-              type: 'TEXT',
-            },
-            value: '0.3.6',
-          },
-          {
-            customFieldDefinition: {
-              name: 'box86_version',
-              label: 'Box86 Version',
-              type: 'TEXT',
+              type: 'SELECT',
             },
-            value: '0.3.4',
+            value: '0.3.8',
           },
         ],
       })
 
-      expect(configWithValues.box64Version).toBe('0.3.6') // Invalid version defaults to 0.3.6
-      expect(configWithValues.box86Version).toBe('0.3.2') // Invalid version defaults to 0.3.2
+      expect(configWithValues.box64Version).toBe('0.3.8')
 
-      // Test with empty values - should use defaults
       const configWithEmpty = convertToGameNativeConfig({
         listingId: 'test',
         gameId: 'game',
@@ -255,52 +250,58 @@ describe('GameNative Converter', () => {
             customFieldDefinition: {
               name: 'box64_version',
               label: 'Box64 Version',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: '',
           },
-          {
-            customFieldDefinition: {
-              name: 'box86_version',
-              label: 'Box86 Version',
-              type: 'TEXT',
-            },
-            value: null,
-          },
         ],
       })
 
       expect(configWithEmpty.box64Version).toBe('0.3.6')
-      expect(configWithEmpty.box86Version).toBe('0.3.2')
     })
 
-    it('should map Box64 and Box86 presets correctly', () => {
-      const presetOptions = [
-        { input: 'Stability', expected: 'STABILITY' },
-        { input: 'Compatibility', expected: 'COMPATIBILITY' },
-        { input: 'Intermediate', expected: 'INTERMEDIATE' },
-        { input: 'Performance', expected: 'PERFORMANCE' },
-        { input: 'Other/Custom', expected: 'COMPATIBILITY' },
-        { input: 'Unknown', expected: 'COMPATIBILITY' }, // Default fallback
-      ]
+    it('should accept bionic-specific Box64 versions', () => {
+      const bionicVersions = ['0.3.2', '0.3.7', '0.4.0']
 
-      presetOptions.forEach(({ input, expected }) => {
+      bionicVersions.forEach((version) => {
         const config = convertToGameNativeConfig({
           listingId: 'test',
           gameId: 'game',
           customFieldValues: [
             {
               customFieldDefinition: {
-                name: 'box64_preset',
-                label: 'Box64 Preset',
+                name: 'box64_version',
+                label: 'Box64 Version',
                 type: 'SELECT',
               },
-              value: input,
+              value: version,
             },
+          ],
+        })
+
+        expect(config.box64Version).toBe(version)
+      })
+    })
+
+    it('should map Box64 presets correctly (lowercase input)', () => {
+      const presetOptions = [
+        { input: 'stability', expected: 'STABILITY' },
+        { input: 'compatibility', expected: 'COMPATIBILITY' },
+        { input: 'intermediate', expected: 'INTERMEDIATE' },
+        { input: 'performance', expected: 'PERFORMANCE' },
+        { input: 'denuvo', expected: 'DENUVO' },
+        { input: 'other/custom', expected: 'COMPATIBILITY' },
+      ]
+
+      presetOptions.forEach(({ input, expected }) => {
+        const config = convertToGameNativeConfig({
+          listingId: 'test',
+          gameId: 'game',
+          customFieldValues: [
             {
               customFieldDefinition: {
-                name: 'box86_preset',
-                label: 'Box86 Preset',
+                name: 'box64_preset',
+                label: 'Box64 Preset',
                 type: 'SELECT',
               },
               value: input,
@@ -309,7 +310,6 @@ describe('GameNative Converter', () => {
         })
 
         expect(config.box64Preset).toBe(expected)
-        expect(config.box86Preset).toBe(expected)
       })
     })
 
@@ -330,65 +330,225 @@ describe('GameNative Converter', () => {
       })
 
       expect(config.execArgs).toBe('-skipmovies -nologo')
+    })
 
-      // Test with empty value
-      const configEmpty = convertToGameNativeConfig({
+    it('should merge dxvk_version into dxwrapperConfig', () => {
+      const config = convertToGameNativeConfig({
         listingId: 'test',
         gameId: 'game',
         customFieldValues: [
           {
             customFieldDefinition: {
-              name: 'exec_arguments',
-              label: 'Exec Arguments',
+              name: 'dxvk_version',
+              label: 'DXVK Version',
+              type: 'SELECT',
+            },
+            value: 'async-1.10.3',
+          },
+        ],
+      })
+
+      expect(config.dxwrapperConfig).toContain('version=async-1.10.3')
+    })
+
+    it('should merge max_device_memory into graphicsDriverConfig', () => {
+      const config = convertToGameNativeConfig({
+        listingId: 'test',
+        gameId: 'game',
+        customFieldValues: [
+          {
+            customFieldDefinition: {
+              name: 'max_device_memory',
+              label: 'Max Device Memory',
               type: 'TEXT',
             },
-            value: '',
+            value: '4096',
           },
         ],
       })
 
-      expect(configEmpty.execArgs).toBe('')
+      expect(config.graphicsDriverConfig).toContain('maxDeviceMemory=4096')
     })
 
-    it('should handle DX wrapper config properly', () => {
+    it('should merge use_adrenotools_turnip into graphicsDriverConfig', () => {
       const config = convertToGameNativeConfig({
         listingId: 'test',
         gameId: 'game',
         customFieldValues: [
           {
             customFieldDefinition: {
-              name: 'dx_wrapper_config',
-              label: 'DX Wrapper Config',
-              type: 'TEXTAREA',
+              name: 'use_adrenotools_turnip',
+              label: 'Use Adrenotools Turnip',
+              type: 'BOOLEAN',
             },
-            value: '2.6.1-gplasync',
+            value: true,
           },
         ],
       })
 
-      expect(config.dxwrapperConfig).toBe('2.6.1-gplasync')
+      expect(config.graphicsDriverConfig).toContain('adrenotoolsTurnip=1')
     })
 
-    it('should convert all fields from the complete example listing', () => {
-      // Using exact data from notes/GameNativeListingByIdExample.ts
-      const input: GameNativeConfigInput = {
-        listingId: 'ea4107c5-371b-4030-b42c-4469f251fe8b',
-        gameId: 'f097b273-ad3d-4b2b-b6a4-d76856aafabf',
+    it('should write adrenotoolsTurnip=0 when disabled', () => {
+      const config = convertToGameNativeConfig({
+        listingId: 'test',
+        gameId: 'game',
         customFieldValues: [
           {
             customFieldDefinition: {
-              name: 'audio_driver',
-              label: 'Audio Driver',
-              type: 'SELECT',
+              name: 'use_adrenotools_turnip',
+              label: 'Use Adrenotools Turnip',
+              type: 'BOOLEAN',
             },
-            value: 'ALSA',
+            value: false,
           },
+        ],
+      })
+
+      expect(config.graphicsDriverConfig).toContain('adrenotoolsTurnip=0')
+    })
+
+    it('should combine max_device_memory and use_adrenotools_turnip in graphicsDriverConfig', () => {
+      const config = convertToGameNativeConfig({
+        listingId: 'test',
+        gameId: 'game',
+        customFieldValues: [
           {
             customFieldDefinition: {
-              name: 'average_fps',
-              label: 'Average FPS',
+              name: 'max_device_memory',
+              label: 'Max Device Memory',
               type: 'TEXT',
             },
+            value: '4096',
+          },
+          {
+            customFieldDefinition: {
+              name: 'use_adrenotools_turnip',
+              label: 'Use Adrenotools Turnip',
+              type: 'BOOLEAN',
+            },
+            value: true,
+          },
+        ],
+      })
+
+      expect(config.graphicsDriverConfig).toContain('maxDeviceMemory=4096')
+      expect(config.graphicsDriverConfig).toContain('adrenotoolsTurnip=1')
+    })
+
+    it('should map container_variant correctly', () => {
+      const config = convertToGameNativeConfig({
+        listingId: 'test',
+        gameId: 'game',
+        customFieldValues: [
+          {
+            customFieldDefinition: {
+              name: 'container_variant',
+              label: 'Container Variant',
+              type: 'SELECT',
+            },
+            value: 'bionic',
+          },
+        ],
+      })
+
+      expect(config.containerVariant).toBe('bionic')
+    })
+
+    it('should map steam_type with ultra_light conversion', () => {
+      const config = convertToGameNativeConfig({
+        listingId: 'test',
+        gameId: 'game',
+        customFieldValues: [
+          {
+            customFieldDefinition: {
+              name: 'steam_type',
+              label: 'Steam Type',
+              type: 'SELECT',
+            },
+            value: 'ultra_light',
+          },
+        ],
+      })
+
+      expect(config.steamType).toBe('ultralight')
+    })
+
+    it('should map emulator from 64_bit_emulator field', () => {
+      const config = convertToGameNativeConfig({
+        listingId: 'test',
+        gameId: 'game',
+        customFieldValues: [
+          {
+            customFieldDefinition: {
+              name: '64_bit_emulator',
+              label: '64-bit Emulator',
+              type: 'SELECT',
+            },
+            value: 'box',
+          },
+        ],
+      })
+
+      expect(config.emulator).toBe('Box64')
+    })
+
+    it('should map fex_core_preset correctly (lowercase input)', () => {
+      const config = convertToGameNativeConfig({
+        listingId: 'test',
+        gameId: 'game',
+        customFieldValues: [
+          {
+            customFieldDefinition: {
+              name: 'fex_core_preset',
+              label: 'FEXCore Preset',
+              type: 'SELECT',
+            },
+            value: 'performance',
+          },
+        ],
+      })
+
+      expect(config.fexcorePreset).toBe('PERFORMANCE')
+    })
+
+    it('should map direct_input_mapper_type string values to numbers', () => {
+      const testCases = [
+        { input: 'standard', expected: 1 },
+        { input: 'xinput_mapper', expected: 2 },
+      ]
+
+      testCases.forEach(({ input, expected }) => {
+        const config = convertToGameNativeConfig({
+          listingId: 'test',
+          gameId: 'game',
+          customFieldValues: [
+            {
+              customFieldDefinition: {
+                name: 'direct_input_mapper_type',
+                label: 'DirectInput Mapper Type',
+                type: 'SELECT',
+              },
+              value: input,
+            },
+          ],
+        })
+
+        expect(config.dinputMapperType).toBe(expected)
+      })
+    })
+
+    it('should convert all fields from a complete listing', () => {
+      const input: GameNativeConfigInput = {
+        listingId: 'ea4107c5-371b-4030-b42c-4469f251fe8b',
+        gameId: 'f097b273-ad3d-4b2b-b6a4-d76856aafabf',
+        customFieldValues: [
+          {
+            customFieldDefinition: { name: 'audio_driver', label: 'Audio Driver', type: 'SELECT' },
+            value: 'alsa',
+          },
+          {
+            customFieldDefinition: { name: 'average_fps', label: 'Average FPS', type: 'TEXT' },
             value: '120',
           },
           {
@@ -397,22 +557,18 @@ describe('GameNative Converter', () => {
               label: 'Box64 Preset',
               type: 'SELECT',
             },
-            value: 'Performance',
+            value: 'performance',
           },
           {
             customFieldDefinition: {
               name: 'box64_version',
               label: 'Box64 Version',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: '0.3.6',
           },
           {
-            customFieldDefinition: {
-              name: 'dx_wrapper',
-              label: 'DX Wrapper',
-              type: 'SELECT',
-            },
+            customFieldDefinition: { name: 'dx_wrapper', label: 'DX Wrapper', type: 'SELECT' },
             value: 'DXVK',
           },
           {
@@ -421,7 +577,7 @@ describe('GameNative Converter', () => {
               label: 'DX Wrapper Config',
               type: 'TEXTAREA',
             },
-            value: '2.6.1-gplasync',
+            value: 'async=1',
           },
           {
             customFieldDefinition: {
@@ -447,35 +603,19 @@ describe('GameNative Converter', () => {
             },
             value: '-skipmovies',
           },
-          {
-            customFieldDefinition: {
-              name: 'game_version',
-              label: 'Game Version',
-              type: 'TEXT',
-            },
-            value: '',
-          },
           {
             customFieldDefinition: {
               name: 'graphics_driver',
               label: 'Graphics Driver',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: 'Vortek (Universal)',
           },
-          {
-            customFieldDefinition: {
-              name: 'media_url',
-              label: 'Screenshots, Blog Post, etc',
-              type: 'URL',
-            },
-            value: '',
-          },
           {
             customFieldDefinition: {
               name: 'resolution',
               label: 'Resolution (Screen Size)',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: '1920x1080 (16:9)',
           },
@@ -489,38 +629,51 @@ describe('GameNative Converter', () => {
           },
           {
             customFieldDefinition: {
-              name: 'youtube',
-              label: 'YouTube',
-              type: 'URL',
+              name: 'container_variant',
+              label: 'Container Variant',
+              type: 'SELECT',
+            },
+            value: 'bionic',
+          },
+          {
+            customFieldDefinition: {
+              name: '64_bit_emulator',
+              label: '64-bit Emulator',
+              type: 'SELECT',
             },
-            value: '',
+            value: 'fex',
+          },
+          {
+            customFieldDefinition: {
+              name: 'fex_core_preset',
+              label: 'FEXCore Preset',
+              type: 'SELECT',
+            },
+            value: 'intermediate',
           },
         ],
       }
 
       const config = convertToGameNativeConfig(input)
 
-      // Verify all mapped fields
       expect(config.audioDriver).toBe('alsa')
       expect(config.box64Preset).toBe('PERFORMANCE')
       expect(config.box64Version).toBe('0.3.6')
       expect(config.dxwrapper).toBe('dxvk')
-      expect(config.dxwrapperConfig).toBe('2.6.1-gplasync')
-      expect(config.envVars).toContain('ZINK_DESCRIPTORS=lazy') // Default since empty
+      expect(config.dxwrapperConfig).toBe('async=1')
+      expect(config.envVars).toContain('ZINK_DESCRIPTORS=lazy')
       expect(config.execArgs).toBe('-skipmovies')
       expect(config.graphicsDriver).toBe('vortek')
       expect(config.screenSize).toBe('1920x1080')
       expect(config.startupSelection).toBe(2)
+      expect(config.containerVariant).toBe('bionic')
+      expect(config.emulator).toBe('FEXCore')
+      expect(config.fexcorePreset).toBe('INTERMEDIATE')
 
-      // Verify default values for missing fields
-      expect(config.box86Version).toBe('0.3.2')
       expect(config.box86Preset).toBe('COMPATIBILITY')
       expect(config.wow64Mode).toBe(true)
       expect(config.showFPS).toBe(false)
       expect(config.launchRealSteam).toBe(false)
-      expect(config.cpuList).toBe('0,1,2,3,4,5,6,7')
-      expect(config.csmt).toBe(true)
-      expect(config.sdlControllerAPI).toBe(true)
       expect(config.enableXInput).toBe(true)
       expect(config.enableDInput).toBe(true)
     })
@@ -539,35 +692,19 @@ describe('GameNative Converter', () => {
             value: 'v0.3.0',
           },
           {
-            customFieldDefinition: {
-              name: 'game_version',
-              label: 'Game Version',
-              type: 'TEXT',
-            },
+            customFieldDefinition: { name: 'game_version', label: 'Game Version', type: 'TEXT' },
             value: '1.0.0',
           },
           {
-            customFieldDefinition: {
-              name: 'average_fps',
-              label: 'Average FPS',
-              type: 'TEXT',
-            },
+            customFieldDefinition: { name: 'average_fps', label: 'Average FPS', type: 'TEXT' },
             value: '60',
           },
           {
-            customFieldDefinition: {
-              name: 'media_url',
-              label: 'Media URL',
-              type: 'URL',
-            },
+            customFieldDefinition: { name: 'media_url', label: 'Media URL', type: 'URL' },
             value: 'https://example.com',
           },
           {
-            customFieldDefinition: {
-              name: 'youtube',
-              label: 'YouTube',
-              type: 'URL',
-            },
+            customFieldDefinition: { name: 'youtube', label: 'YouTube', type: 'URL' },
             value: 'https://youtube.com/watch?v=123',
           },
         ],
@@ -575,10 +712,8 @@ describe('GameNative Converter', () => {
 
       const config = convertToGameNativeConfig(input)
 
-      // These fields should not affect the config
-      // Config should still have default values
       expect(config.name).toBe('')
-      expect(config.screenSize).toBe('854x480')
+      expect(config.screenSize).toBe('1280x720')
       expect(config.graphicsDriver).toBe('vortek')
       expect(config.dxwrapper).toBe('dxvk')
     })
@@ -600,7 +735,7 @@ describe('GameNative Converter', () => {
             customFieldDefinition: {
               name: 'graphics_driver',
               label: 'Graphics Driver',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: null,
           },
@@ -617,9 +752,9 @@ describe('GameNative Converter', () => {
 
       const config = convertToGameNativeConfig(input)
 
-      expect(config.screenSize).toBe('854x480') // Default
-      expect(config.graphicsDriver).toBe('vortek') // Default
-      expect(config.execArgs).toBe('') // Empty string default
+      expect(config.screenSize).toBe('1280x720')
+      expect(config.graphicsDriver).toBe('vortek')
+      expect(config.execArgs).toBe('')
     })
   })
 
@@ -633,7 +768,7 @@ describe('GameNative Converter', () => {
             customFieldDefinition: {
               name: 'resolution',
               label: 'Resolution',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: '1920x1080',
           },
@@ -641,7 +776,7 @@ describe('GameNative Converter', () => {
             customFieldDefinition: {
               name: 'graphics_driver',
               label: 'Graphics Driver',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: 'vortek',
           },
@@ -655,10 +790,9 @@ describe('GameNative Converter', () => {
         screenSize: '1920x1080',
         graphicsDriver: 'vortek',
         dxwrapper: 'dxvk',
-        audioDriver: 'alsa',
+        audioDriver: 'pulseaudio',
       })
 
-      // Check formatting (should be pretty-printed)
       expect(serialized).toContain('\n')
       expect(serialized).toContain('  ')
     })
@@ -674,18 +808,16 @@ describe('GameNative Converter', () => {
       const parsed = JSON.parse(serialized)
 
       expect(parsed).toBeDefined()
-      expect(parsed.screenSize).toBe('854x480')
+      expect(parsed.screenSize).toBe('1280x720')
     })
   })
 
-  describe('Edge cases and special scenarios', () => {
+  describe('Edge cases', () => {
     it('should handle mixed case graphics driver names', () => {
       const testCases = [
         { input: 'VORTEK', expected: 'vortek' },
         { input: 'Turnip', expected: 'turnip' },
         { input: 'VIRGL', expected: 'virgl' },
-        { input: 'FREEDRENO', expected: 'vortek' }, // Not valid, defaults to vortek
-        { input: 'MeSa', expected: 'vortek' }, // Not valid, defaults to vortek
       ]
 
       testCases.forEach(({ input, expected }) => {
@@ -697,7 +829,7 @@ describe('GameNative Converter', () => {
               customFieldDefinition: {
                 name: 'graphics_driver',
                 label: 'Graphics Driver',
-                type: 'TEXT',
+                type: 'SELECT',
               },
               value: input,
             },
@@ -713,7 +845,7 @@ describe('GameNative Converter', () => {
         { input: 'Resolution: 1920x1080', expected: '1920x1080' },
         { input: '1920x1080 pixels', expected: '1920x1080' },
         { input: 'Use 2560x1440 for best quality', expected: '2560x1440' },
-        { input: 'No resolution here', expected: '854x480' }, // Default
+        { input: 'No resolution here', expected: '1280x720' },
       ]
 
       testCases.forEach(({ input, expected }) => {
@@ -752,7 +884,7 @@ describe('GameNative Converter', () => {
         ],
       })
 
-      expect(config.envVars).toBe('VAR1=value1   VAR2=value2') // Trimmed but internal spacing preserved
+      expect(config.envVars).toBe('VAR1=value1   VAR2=value2')
     })
 
     it('should handle multiple fields updating simultaneously', () => {
@@ -764,7 +896,7 @@ describe('GameNative Converter', () => {
             customFieldDefinition: {
               name: 'resolution',
               label: 'Resolution',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: '2560x1440',
           },
@@ -772,7 +904,7 @@ describe('GameNative Converter', () => {
             customFieldDefinition: {
               name: 'graphics_driver',
               label: 'Graphics Driver',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: 'turnip',
           },
@@ -790,7 +922,7 @@ describe('GameNative Converter', () => {
               label: 'Audio Driver',
               type: 'SELECT',
             },
-            value: 'PulseAudio',
+            value: 'pulse',
           },
           {
             customFieldDefinition: {
@@ -804,7 +936,7 @@ describe('GameNative Converter', () => {
             customFieldDefinition: {
               name: 'box64_version',
               label: 'Box64 Version',
-              type: 'TEXT',
+              type: 'SELECT',
             },
             value: '0.3.6',
           },
@@ -814,7 +946,7 @@ describe('GameNative Converter', () => {
               label: 'Box64 Preset',
               type: 'SELECT',
             },
-            value: 'Stability',
+            value: 'stability',
           },
         ],
       })
@@ -822,7 +954,7 @@ describe('GameNative Converter', () => {
       expect(config.screenSize).toBe('2560x1440')
       expect(config.graphicsDriver).toBe('turnip')
       expect(config.dxwrapper).toBe('vkd3d')
-      expect(config.audioDriver).toBe('pulse')
+      expect(config.audioDriver).toBe('pulseaudio')
       expect(config.startupSelection).toBe(0)
       expect(config.box64Version).toBe('0.3.6')
       expect(config.box64Preset).toBe('STABILITY')
diff --git a/src/server/utils/emulator-config/gamenative/gamenative.converter.ts b/src/server/utils/emulator-config/gamenative/gamenative.converter.ts
index bed404fa8..eff096d4a 100644
--- a/src/server/utils/emulator-config/gamenative/gamenative.converter.ts
+++ b/src/server/utils/emulator-config/gamenative/gamenative.converter.ts
@@ -10,19 +10,24 @@ import {
   AUDIO_DRIVER_MAPPING,
   STARTUP_SELECTION_MAPPING,
   BOX64_PRESET_MAPPING,
-  BOX86_PRESET_MAPPING,
+  EMULATOR_MAPPING,
+  CONTAINER_VARIANT_MAPPING,
+  STEAM_TYPE_MAPPING,
+  FEXCORE_PRESET_MAPPING,
 } from './gamenative.defaults'
-import {
-  type DxvkVersion,
-  type ContainerConfig,
-  type ScreenSize,
-  type GraphicsDriver,
-  type DxWrapper,
-  type AudioDriver,
-  type StartupSelection,
-  type Box86Version,
-  type Box64Version,
-  type Box86_64Preset,
+import type {
+  ContainerConfig,
+  ScreenSize,
+  GraphicsDriver,
+  DxWrapper,
+  AudioDriver,
+  StartupSelection,
+  Box64Version,
+  Box86_64Preset,
+  ContainerVariant,
+  SteamType,
+  FEXCorePreset,
+  DinputMapperType,
 } from './gamenative.types'
 import type { Prisma } from '@orm'
 
@@ -42,9 +47,16 @@ export interface GameNativeConfigInput {
   customFieldValues: CustomFieldValue[]
 }
 
-// Export the properly typed config
 export type GameNativeConfig = Required<ContainerConfig>
 
+/**
+ * Appends a key=value pair to a comma-separated config string
+ */
+function appendToConfigString(existing: string, key: string, value: string): string {
+  if (!existing) return `${key}=${value}`
+  return `${existing},${key}=${value}`
+}
+
 /**
  * Maps custom field names to GameNative config keys and transforms values
  */
@@ -56,280 +68,145 @@ const FIELD_MAPPINGS: Record<
     defaultIfEmpty?: unknown
   }
 > = {
-  // Resolution mapping
   resolution: {
     key: 'screenSize',
     transform: (value): ScreenSize => {
       const resStr = String(value)
-      // Extract resolution from formats like "1920x1080 (16:9)" or just "1920x1080"
       const match = resStr.match(/(\d+x\d+)/)
       return match ? match[1] : GameNativeDefaults.getDefaultScreenSize()
     },
   },
 
-  // Environment variables - complex string needs special handling
   env_variables: {
     key: 'envVars',
     transform: (value) => {
-      // If empty or not provided, use default environment variables
       return !value || String(value).trim() === ''
         ? GameNativeDefaults.getDefaultEnvVars()
         : String(value).trim()
     },
   },
 
-  // Graphics driver - handles both new SELECT values and legacy TEXT values
   graphics_driver: {
     key: 'graphicsDriver',
     transform: (value): GraphicsDriver => GameNativeDefaults.detectGraphicsDriver(String(value)),
     defaultIfEmpty: GameNativeDefaults.getDefaultGraphicsDriver(),
   },
 
-  // DX Wrapper
   dx_wrapper: {
     key: 'dxwrapper',
     transform: (value): DxWrapper =>
       DX_WRAPPER_MAPPING[String(value)] ?? GameNativeDefaults.getDefaultDxWrapper(),
   },
 
-  // DX Wrapper Config
   dx_wrapper_config: {
     key: 'dxwrapperConfig',
     transform: (value) => String(value || ''),
     defaultIfEmpty: '',
   },
 
-  // DXVK version
-  dxvk_version: {
-    key: 'dxvkVersion',
-    transform: (value): DxvkVersion => {
-      const version = String(value || GameNativeDefaults.getDefaultDxvkVersion()).trim()
-      return GameNativeDefaults.isValidDxvkVersion(version)
-        ? (version as DxvkVersion)
-        : GameNativeDefaults.getDefaultDxvkVersion()
-    },
-  },
-
-  // Audio driver
   audio_driver: {
     key: 'audioDriver',
     transform: (value): AudioDriver =>
       AUDIO_DRIVER_MAPPING[String(value)] ?? GameNativeDefaults.getDefaultAudioDriver(),
   },
 
-  // Execution arguments
   exec_arguments: {
     key: 'execArgs',
     transform: (value) => String(value || ''),
     defaultIfEmpty: '',
   },
 
-  // Startup selection
   startup_selection: {
     key: 'startupSelection',
     transform: (value): StartupSelection =>
       STARTUP_SELECTION_MAPPING[String(value)] ?? GameNativeDefaults.getDefaultStartupSelection(),
   },
 
-  // Box64 version
   box64_version: {
     key: 'box64Version',
     transform: (value): Box64Version => {
       const version = String(value || GameNativeDefaults.getDefaultBox64Version()).trim()
-      // Validate against known versions using centralized validation
-      return GameNativeDefaults.isValidBox64Version(version)
-        ? (version as Box64Version)
-        : GameNativeDefaults.getDefaultBox64Version()
+      if (GameNativeDefaults.isValidBox64Version(version)) return version
+      return GameNativeDefaults.getDefaultBox64Version()
     },
     defaultIfEmpty: GameNativeDefaults.getDefaultBox64Version(),
   },
 
-  // Box86 version (not in example but in target config)
-  box86_version: {
-    key: 'box86Version',
-    transform: (value): Box86Version => {
-      const version = String(value || GameNativeDefaults.getDefaultBox86Version()).trim()
-      // Validate against known versions using centralized validation
-      return GameNativeDefaults.isValidBox86Version(version)
-        ? (version as Box86Version)
-        : GameNativeDefaults.getDefaultBox86Version()
-    },
-    defaultIfEmpty: GameNativeDefaults.getDefaultBox86Version(),
-  },
-
-  // Box64 preset
   box64_preset: {
     key: 'box64Preset',
     transform: (value): Box86_64Preset =>
       BOX64_PRESET_MAPPING[String(value)] ?? GameNativeDefaults.getDefaultBoxPreset(),
   },
 
-  // Box86 preset (not in example but needed)
-  box86_preset: {
-    key: 'box86Preset',
-    transform: (value): Box86_64Preset =>
-      BOX86_PRESET_MAPPING[String(value)] ?? GameNativeDefaults.getDefaultBoxPreset(),
+  container_variant: {
+    key: 'containerVariant',
+    transform: (value): ContainerVariant =>
+      CONTAINER_VARIANT_MAPPING[String(value)] ?? GameNativeDefaults.getDefaultContainerVariant(),
   },
 
-  // Windows Components - Critical missing field
-  // Convert individual boolean fields to wincomponents string
-  use_native_direct3d: { key: 'wincomponents', transform: () => undefined }, // Handled specially
-  use_native_directsound: { key: 'wincomponents', transform: () => undefined }, // Handled specially
-  use_native_directmusic: { key: 'wincomponents', transform: () => undefined }, // Handled specially
-  use_native_directshow: { key: 'wincomponents', transform: () => undefined }, // Handled specially
-  use_native_directplay: { key: 'wincomponents', transform: () => undefined }, // Handled specially
-  use_native_vcrun2010: { key: 'wincomponents', transform: () => undefined }, // Handled specially
-  use_native_wmdecoder: { key: 'wincomponents', transform: () => undefined }, // Handled specially
-
-  // Video Memory Size
-  video_memory_size: {
-    key: 'videoMemorySize',
-    transform: (value) => {
-      const sizeStr = String(value || GameNativeDefaults.getDefaultVideoMemorySize()).trim()
-      // Validate against known sizes using centralized validation
-      return GameNativeDefaults.isValidVideoMemorySize(sizeStr)
-        ? sizeStr
-        : GameNativeDefaults.getDefaultVideoMemorySize()
-    },
-    defaultIfEmpty: GameNativeDefaults.getDefaultVideoMemorySize(),
+  wine_version: {
+    key: 'wineVersion',
+    transform: (value) => String(value || ''),
+    defaultIfEmpty: '',
   },
 
-  // CPU Core Affinity
-  cpu_list: {
-    key: 'cpuList',
-    transform: (value) => String(value || GameNativeDefaults.getDefaultCpuList()),
-    defaultIfEmpty: GameNativeDefaults.getDefaultCpuList(),
+  steam_type: {
+    key: 'steamType',
+    transform: (value): SteamType =>
+      STEAM_TYPE_MAPPING[String(value)] ?? GameNativeDefaults.getDefaultSteamType(),
   },
 
-  cpu_list_wow64: {
-    key: 'cpuListWoW64',
-    transform: (value) => String(value || GameNativeDefaults.getDefaultCpuList()),
-    defaultIfEmpty: GameNativeDefaults.getDefaultCpuList(),
+  dynamic_driver_version: {
+    key: 'graphicsDriverVersion',
+    transform: (value) => String(value || ''),
+    defaultIfEmpty: '',
   },
 
-  // WoW64 Mode
-  wow64_mode: {
-    key: 'wow64Mode',
-    transform: (value) => Boolean(value ?? true),
-    defaultIfEmpty: true,
+  fex_core_version: {
+    key: 'fexcoreVersion',
+    transform: (value) => String(value || ''),
+    defaultIfEmpty: '',
   },
 
-  // Show FPS Overlay
-  show_fps: {
-    key: 'showFPS',
-    transform: (value) => Boolean(value ?? false),
-    defaultIfEmpty: false,
+  fex_core_preset: {
+    key: 'fexcorePreset',
+    transform: (value): FEXCorePreset =>
+      FEXCORE_PRESET_MAPPING[String(value)] ?? GameNativeDefaults.getDefaultFexcorePreset(),
   },
 
-  // Input/Controller Settings
-  sdl_controller_api: {
-    key: 'sdlControllerAPI',
-    transform: (value) => Boolean(value ?? true),
-    defaultIfEmpty: true,
+  use_steam_input: {
+    key: 'useSteamInput',
+    transform: (value) => Boolean(value ?? false),
+    defaultIfEmpty: false,
   },
 
-  enable_xinput: {
+  enable_x_input_api: {
     key: 'enableXInput',
     transform: (value) => Boolean(value ?? true),
     defaultIfEmpty: true,
   },
 
-  enable_dinput: {
+  enable_direct_input_api: {
     key: 'enableDInput',
     transform: (value) => Boolean(value ?? true),
     defaultIfEmpty: true,
   },
 
-  dinput_mapper_type: {
+  direct_input_mapper_type: {
     key: 'dinputMapperType',
-    transform: (value) => {
+    transform: (value): DinputMapperType => {
+      const str = String(value)
+      if (str === 'xinput_mapper') return 2
+      if (str === 'standard') return 1
       const num = Number(value)
-      // 0: Standard, 1: XInput
-      return num === 0 || num === 1 ? num : 1
+      if (num === 2) return 2
+      return 1
     },
     defaultIfEmpty: 1,
   },
-
-  disable_mouse_input: {
-    key: 'disableMouseInput',
-    transform: (value) => Boolean(value ?? false),
-    defaultIfEmpty: false,
-  },
-
-  // Graphics/Rendering Settings
-  csmt: {
-    key: 'csmt',
-    transform: (value) => Boolean(value ?? true),
-    defaultIfEmpty: true,
-  },
-
-  video_pci_device_id: {
-    key: 'videoPciDeviceID',
-    transform: (value) => {
-      const num = Number(value)
-      return isNaN(num) ? GameNativeDefaults.getDefaultVideoPciDeviceId() : num
-    },
-    defaultIfEmpty: GameNativeDefaults.getDefaultVideoPciDeviceId(),
-  },
-
-  offscreen_rendering_mode: {
-    key: 'offScreenRenderingMode',
-    transform: (value) => {
-      const mode = String(
-        value || GameNativeDefaults.getDefaultOffscreenRenderingMode(),
-      ).toLowerCase()
-      return GameNativeDefaults.isValidOffscreenRenderingMode(mode)
-        ? mode
-        : GameNativeDefaults.getDefaultOffscreenRenderingMode()
-    },
-    defaultIfEmpty: GameNativeDefaults.getDefaultOffscreenRenderingMode(),
-  },
-
-  strict_shader_math: {
-    key: 'strictShaderMath',
-    transform: (value) => Boolean(value ?? true),
-    defaultIfEmpty: true,
-  },
-
-  mouse_warp_override: {
-    key: 'mouseWarpOverride',
-    transform: (value) => {
-      const mode = String(value || GameNativeDefaults.getDefaultMouseWarpOverride()).toLowerCase()
-      return GameNativeDefaults.isValidMouseWarpOverride(mode)
-        ? mode
-        : GameNativeDefaults.getDefaultMouseWarpOverride()
-    },
-    defaultIfEmpty: GameNativeDefaults.getDefaultMouseWarpOverride(),
-  },
-
-  shader_backend: {
-    key: 'shaderBackend',
-    transform: (value) => String(value || GameNativeDefaults.getDefaultShaderBackend()),
-    defaultIfEmpty: GameNativeDefaults.getDefaultShaderBackend(),
-  },
-
-  use_glsl: {
-    key: 'useGLSL',
-    transform: (value) => {
-      const val = String(value || GameNativeDefaults.getDefaultUseGlsl())
-      return GameNativeDefaults.isValidUseGlsl(val) ? val : GameNativeDefaults.getDefaultUseGlsl()
-    },
-    defaultIfEmpty: GameNativeDefaults.getDefaultUseGlsl(),
-  },
-
-  // Graphics Driver Version - dynamic field
-  graphics_driver_version: {
-    key: 'graphicsDriverVersion',
-    transform: (value) => String(value || ''),
-    defaultIfEmpty: '',
-  },
 }
 
-/**
- * Get default GameNative configuration
- */
 function getDefaultConfig(): GameNativeConfig {
-  // Use the typed default config from the types file
   return { ...DEFAULT_CONFIG }
 }
 
@@ -339,73 +216,73 @@ function getDefaultConfig(): GameNativeConfig {
 export function convertToGameNativeConfig(input: GameNativeConfigInput): GameNativeConfig {
   const config = getDefaultConfig()
 
-  // Process each custom field value
+  const fieldValuesByName = new Map<string, unknown>()
+  for (const fieldValue of input.customFieldValues) {
+    fieldValuesByName.set(fieldValue.customFieldDefinition.name, fieldValue.value)
+  }
+
   for (const fieldValue of input.customFieldValues) {
     const fieldName = fieldValue.customFieldDefinition.name
     const mapping = FIELD_MAPPINGS[fieldName]
 
-    if (mapping) {
-      const value = fieldValue.value
+    if (!mapping) continue
 
-      // Skip if empty and no defaultIfEmpty is specified
-      if ((value === '' || value === null || value === undefined) && !mapping.defaultIfEmpty) {
-        continue
-      }
+    const value = fieldValue.value
 
-      // Use defaultIfEmpty if value is empty
-      const actualValue =
-        value === '' || value === null || value === undefined ? mapping.defaultIfEmpty : value
+    if ((value === '' || value === null || value === undefined) && !mapping.defaultIfEmpty) {
+      continue
+    }
 
-      // Transform and assign value
-      const transformedValue = mapping.transform ? mapping.transform(actualValue) : actualValue
+    const actualValue =
+      value === '' || value === null || value === undefined ? mapping.defaultIfEmpty : value
 
-      // Skip Windows Components fields - they're handled specially below
-      if (mapping.key === 'wincomponents') {
-        continue
-      }
+    const transformedValue = mapping.transform ? mapping.transform(actualValue) : actualValue
 
-      // Assign value to the correct key
-      // We need to use Object.assign or explicit property access
-      Object.assign(config, { [mapping.key]: transformedValue })
-    }
+    Object.assign(config, { [mapping.key]: transformedValue })
   }
 
-  // Special handling for Windows Components - combine multiple boolean fields
-  const winComponentFields = {
-    use_native_direct3d: 'direct3d',
-    use_native_directsound: 'directsound',
-    use_native_directmusic: 'directmusic',
-    use_native_directshow: 'directshow',
-    use_native_directplay: 'directplay',
-    use_native_vcrun2010: 'vcrun2010',
-    use_native_wmdecoder: 'wmdecoder',
+  // Merge dxvk_version into dxwrapperConfig as version=X
+  const dxvkVersion = fieldValuesByName.get('dxvk_version')
+  if (dxvkVersion && String(dxvkVersion).trim()) {
+    config.dxwrapperConfig = appendToConfigString(
+      config.dxwrapperConfig,
+      'version',
+      String(dxvkVersion).trim(),
+    )
   }
 
-  const winComponentsMap = new Map<string, boolean>()
-
-  // Set defaults using centralized configuration
-  for (const [component, value] of Object.entries(
-    GameNativeDefaults.getDefaultWindowsComponents(),
-  )) {
-    winComponentsMap.set(component, value)
+  // Merge max_device_memory into graphicsDriverConfig as maxDeviceMemory=N
+  const maxDeviceMemory = fieldValuesByName.get('max_device_memory')
+  if (maxDeviceMemory && String(maxDeviceMemory).trim()) {
+    config.graphicsDriverConfig = appendToConfigString(
+      config.graphicsDriverConfig,
+      'maxDeviceMemory',
+      String(maxDeviceMemory).trim(),
+    )
   }
 
-  // Override with user values if present
-  for (const fieldValue of input.customFieldValues) {
-    const fieldName = fieldValue.customFieldDefinition.name
-    const componentName = winComponentFields[fieldName as keyof typeof winComponentFields]
-
-    if (componentName) {
-      winComponentsMap.set(componentName, Boolean(fieldValue.value))
-    }
+  // Handle 32_bit_emulator / 64_bit_emulator → emulator field
+  const emulator32 = fieldValuesByName.get('32_bit_emulator')
+  const emulator64 = fieldValuesByName.get('64_bit_emulator')
+  const emulatorValue = emulator64 ?? emulator32
+  if (emulatorValue && String(emulatorValue).trim()) {
+    config.emulator =
+      EMULATOR_MAPPING[String(emulatorValue)] ?? GameNativeDefaults.getDefaultEmulator()
   }
 
-  // Build wincomponents string
-  const winComponentsParts: string[] = []
-  for (const [component, useNative] of winComponentsMap) {
-    winComponentsParts.push(`${component}=${useNative ? '1' : '0'}`)
+  // Handle use_adrenotools_turnip → graphicsDriverConfig.adrenotoolsTurnip
+  const useAdrenotoolsTurnip = fieldValuesByName.get('use_adrenotools_turnip')
+  if (useAdrenotoolsTurnip !== undefined && useAdrenotoolsTurnip !== null) {
+    const isEnabled =
+      useAdrenotoolsTurnip === true ||
+      useAdrenotoolsTurnip === 'true' ||
+      useAdrenotoolsTurnip === '1'
+    config.graphicsDriverConfig = appendToConfigString(
+      config.graphicsDriverConfig,
+      'adrenotoolsTurnip',
+      isEnabled ? '1' : '0',
+    )
   }
-  config.wincomponents = winComponentsParts.join(',')
 
   return config
 }
diff --git a/src/server/utils/emulator-config/gamenative/gamenative.defaults.ts b/src/server/utils/emulator-config/gamenative/gamenative.defaults.ts
index 2334d5ba9..facbebee0 100644
--- a/src/server/utils/emulator-config/gamenative/gamenative.defaults.ts
+++ b/src/server/utils/emulator-config/gamenative/gamenative.defaults.ts
@@ -1,8 +1,8 @@
 /**
  * GameNative Configuration Defaults
  *
- * Centralized default values for GameNative emulator configurations
- * to avoid hardcoding values throughout transform functions
+ * Source of truth: ContainerData.kt defaults, Container.java constants,
+ * DefaultVersion.java, arrays.xml
  */
 
 import type {
@@ -15,82 +15,102 @@ import type {
   Box86_64Preset,
   ScreenSize,
   DxvkVersion,
+  VKD3DVersion,
   ContainerConfig,
+  Emulator,
+  ContainerVariant,
+  SteamType,
+  FEXCorePreset,
+  FEXCoreVersion,
+  ExternalDisplayMode,
+  SharpnessEffect,
 } from './gamenative.types'
 
-// Default environment variables for GameNative
 export const DEFAULT_ENV_VARS =
-  'ZINK_DESCRIPTORS=lazy ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true WINEESYNC=1 MESA_VK_WSI_PRESENT_MODE=mailbox TU_DEBUG=noconform DXVK_FRAME_RATE=60'
+  'WRAPPER_MAX_IMAGE_COUNT=0 ZINK_DESCRIPTORS=lazy ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true WINEESYNC=1 MESA_VK_WSI_PRESENT_MODE=mailbox TU_DEBUG=noconform DXVK_FRAME_RATE=60 PULSE_LATENCY_MSEC=144'
 
-// Default resolution for GameNative
-export const DEFAULT_SCREEN_SIZE: ScreenSize = '854x480'
+export const DEFAULT_SCREEN_SIZE: ScreenSize = '1280x720'
 
-// Default graphics driver
 export const DEFAULT_GRAPHICS_DRIVER: GraphicsDriver = 'vortek'
 
-// Graphics driver mapping for both new SELECT values and legacy TEXT values
 export const GRAPHICS_DRIVER_MAPPING: Record<string, GraphicsDriver> = {
-  // New SELECT values (exact match)
   'VirGL (Universal)': 'virgl',
   'Turnip (Adreno)': 'turnip',
   'Vortek (Universal)': 'vortek',
-
-  // Legacy TEXT values (for backward compatibility)
+  'Adreno (Adreno)': 'adreno',
+  'SD 8 Elite (SD 8 Elite)': 'sd-8-elite',
+  Wrapper: 'wrapper',
+  'Wrapper-v2': 'wrapper-v2',
+  'Wrapper-leegao': 'wrapper-leegao',
+  'Wrapper-legacy': 'wrapper-legacy',
   virgl: 'virgl',
   turnip: 'turnip',
   vortek: 'vortek',
+  adreno: 'adreno',
+  'sd-8-elite': 'sd-8-elite',
+  wrapper: 'wrapper',
+  'wrapper-v2': 'wrapper-v2',
+  'wrapper-leegao': 'wrapper-leegao',
+  'wrapper-legacy': 'wrapper-legacy',
   VirGL: 'virgl',
   Turnip: 'turnip',
   Vortek: 'vortek',
   VIRGL: 'virgl',
   TURNIP: 'turnip',
   VORTEK: 'vortek',
-
-  // Partial matches for legacy TEXT entries
-  adreno: 'turnip', // Common legacy entry
 }
 
-// Default DX wrapper
 export const DEFAULT_DX_WRAPPER: DxWrapper = 'dxvk'
 
-// Default DXVK version
-export const DEFAULT_DXVK_VERSION = '2.6.1-gplasync'
+export const DEFAULT_DXVK_VERSION: DxvkVersion = '2.6.1-gplasync'
 
-// Default audio driver
-export const DEFAULT_AUDIO_DRIVER: AudioDriver = 'alsa'
+export const DEFAULT_VKD3D_VERSION: VKD3DVersion = '2.14.1'
+
+export const DEFAULT_AUDIO_DRIVER: AudioDriver = 'pulseaudio'
 
-// Default startup selection (Essential services)
 export const DEFAULT_STARTUP_SELECTION: StartupSelection = 1
 
-// Default Box versions
 export const DEFAULT_BOX64_VERSION: Box64Version = '0.3.6'
 export const DEFAULT_BOX86_VERSION: Box86Version = '0.3.2'
 
-// Default Box presets (Compatibility for best results)
 export const DEFAULT_BOX_PRESET: Box86_64Preset = 'COMPATIBILITY'
 
-// Default video memory size in MB
+export const DEFAULT_EMULATOR: Emulator = 'FEXCore'
+
+export const DEFAULT_CONTAINER_VARIANT: ContainerVariant = 'glibc'
+
+export const DEFAULT_STEAM_TYPE: SteamType = 'normal'
+
+export const DEFAULT_FEXCORE_VERSION: FEXCoreVersion = '2603'
+export const DEFAULT_FEXCORE_PRESET: FEXCorePreset = 'INTERMEDIATE'
+export const DEFAULT_FEXCORE_TSO_MODE = 'Fast'
+export const DEFAULT_FEXCORE_X87_MODE = 'Fast'
+export const DEFAULT_FEXCORE_MULTIBLOCK = 'Disabled'
+
+export const DEFAULT_WINE_VERSION = 'wine-9.2-x86_64'
+
+export const DEFAULT_EXTERNAL_DISPLAY_MODE: ExternalDisplayMode = 'off'
+
+export const DEFAULT_SHARPNESS_EFFECT: SharpnessEffect = 'None'
+export const DEFAULT_SHARPNESS_LEVEL = 100
+export const DEFAULT_SHARPNESS_DENOISE = 100
+
 export const DEFAULT_VIDEO_MEMORY_SIZE = '2048'
 
-// Default CPU affinity (all 8 cores)
 export const DEFAULT_CPU_LIST = '0,1,2,3,4,5,6,7'
 
-// Default PCI device ID (NVIDIA GeForce GTX 480)
 export const DEFAULT_VIDEO_PCI_DEVICE_ID = 1728
 
-// Default offscreen rendering mode
 export const DEFAULT_OFFSCREEN_RENDERING_MODE = 'fbo'
 
-// Default mouse warp override
 export const DEFAULT_MOUSE_WARP_OVERRIDE = 'disable'
 
-// Default shader backend
 export const DEFAULT_SHADER_BACKEND = 'glsl'
 
-// Default GLSL usage
 export const DEFAULT_USE_GLSL = 'enabled'
 
-// Valid video memory sizes (in MB)
+export const DEFAULT_RENDERER = 'gl'
+
 export const VALID_VIDEO_MEMORY_SIZES = [
   '32',
   '64',
@@ -106,16 +126,12 @@ export const VALID_VIDEO_MEMORY_SIZES = [
   '12288',
 ]
 
-// Valid offscreen rendering modes
 export const VALID_OFFSCREEN_RENDERING_MODES = ['fbo', 'backbuffer']
 
-// Valid mouse warp override modes
 export const VALID_MOUSE_WARP_OVERRIDE_MODES = ['disable', 'enable', 'force']
 
-// Valid GLSL values
 export const VALID_USE_GLSL_VALUES = ['enabled', 'disabled']
 
-// Windows Components defaults
 export const DEFAULT_WINDOWS_COMPONENTS = {
   direct3d: true,
   directsound: true,
@@ -124,9 +140,9 @@ export const DEFAULT_WINDOWS_COMPONENTS = {
   directplay: false,
   vcrun2010: true,
   wmdecoder: true,
+  opengl: false,
 }
 
-// Mapping objects for user-friendly values to internal values
 export const DX_WRAPPER_MAPPING: Record<string, DxWrapper> = {
   WineD3D: 'wined3d',
   DXVK: 'dxvk',
@@ -136,9 +152,13 @@ export const DX_WRAPPER_MAPPING: Record<string, DxWrapper> = {
 }
 
 export const AUDIO_DRIVER_MAPPING: Record<string, AudioDriver> = {
+  alsa: 'alsa',
+  pulse: 'pulseaudio',
+  other: 'pulseaudio',
+  // Legacy/display label variants for backward compatibility
   ALSA: 'alsa',
-  PulseAudio: 'pulse', // Correct value is 'pulse' not 'pulseaudio'
-  Other: 'alsa',
+  PulseAudio: 'pulseaudio',
+  Other: 'pulseaudio',
 }
 
 export const STARTUP_SELECTION_MAPPING: Record<string, StartupSelection> = {
@@ -149,98 +169,221 @@ export const STARTUP_SELECTION_MAPPING: Record<string, StartupSelection> = {
 }
 
 export const BOX64_PRESET_MAPPING: Record<string, Box86_64Preset> = {
+  stability: 'STABILITY',
+  compatibility: 'COMPATIBILITY',
+  intermediate: 'INTERMEDIATE',
+  performance: 'PERFORMANCE',
+  denuvo: 'DENUVO',
+  unity: 'UNITY',
+  unity_mono_bleeding_edge: 'UNITY_MONO_BLEEDING_EDGE',
+  other: 'COMPATIBILITY',
+  'other/custom': 'COMPATIBILITY',
   Stability: 'STABILITY',
   Compatibility: 'COMPATIBILITY',
   Intermediate: 'INTERMEDIATE',
   Performance: 'PERFORMANCE',
+  Denuvo: 'DENUVO',
+  Unity: 'UNITY',
+  'Unity Mono Bleeding Edge': 'UNITY_MONO_BLEEDING_EDGE',
   'Other/Custom': 'COMPATIBILITY',
 }
 
 export const BOX86_PRESET_MAPPING: Record<string, Box86_64Preset> = {
+  stability: 'STABILITY',
+  compatibility: 'COMPATIBILITY',
+  intermediate: 'INTERMEDIATE',
+  performance: 'PERFORMANCE',
+  denuvo: 'DENUVO',
+  unity: 'UNITY',
+  unity_mono_bleeding_edge: 'UNITY_MONO_BLEEDING_EDGE',
+  'other/custom': 'COMPATIBILITY',
   Stability: 'STABILITY',
   Compatibility: 'COMPATIBILITY',
   Intermediate: 'INTERMEDIATE',
   Performance: 'PERFORMANCE',
+  Denuvo: 'DENUVO',
+  Unity: 'UNITY',
+  'Unity Mono Bleeding Edge': 'UNITY_MONO_BLEEDING_EDGE',
   'Other/Custom': 'COMPATIBILITY',
 }
 
+export const EMULATOR_MAPPING: Record<string, Emulator> = {
+  FEXCore: 'FEXCore',
+  Box64: 'Box64',
+  fex: 'FEXCore',
+  box: 'Box64',
+  Other: 'FEXCore',
+}
+
+export const CONTAINER_VARIANT_MAPPING: Record<string, ContainerVariant> = {
+  glibc: 'glibc',
+  bionic: 'bionic',
+  Other: 'glibc',
+}
+
+export const STEAM_TYPE_MAPPING: Record<string, SteamType> = {
+  normal: 'normal',
+  light: 'light',
+  ultralight: 'ultralight',
+  ultra_light: 'ultralight',
+  Normal: 'normal',
+  Light: 'light',
+  Ultralight: 'ultralight',
+  Other: 'normal',
+}
+
+export const FEXCORE_PRESET_MAPPING: Record<string, FEXCorePreset> = {
+  stability: 'STABILITY',
+  compatibility: 'COMPATIBILITY',
+  intermediate: 'INTERMEDIATE',
+  performance: 'PERFORMANCE',
+  extreme: 'EXTREME',
+  denuvo: 'DENUVO',
+  other: 'INTERMEDIATE',
+  'other/custom': 'INTERMEDIATE',
+  Stability: 'STABILITY',
+  Compatibility: 'COMPATIBILITY',
+  Intermediate: 'INTERMEDIATE',
+  Performance: 'PERFORMANCE',
+  Extreme: 'EXTREME',
+  Denuvo: 'DENUVO',
+  'Other/Custom': 'INTERMEDIATE',
+}
+
+export const EXTERNAL_DISPLAY_MODE_MAPPING: Record<string, ExternalDisplayMode> = {
+  off: 'off',
+  touchpad: 'touchpad',
+  keyboard: 'keyboard',
+  hybrid: 'hybrid',
+  Off: 'off',
+  Touchpad: 'touchpad',
+  Keyboard: 'keyboard',
+  Hybrid: 'hybrid',
+  Other: 'off',
+}
+
+export const SHARPNESS_EFFECT_MAPPING: Record<string, SharpnessEffect> = {
+  None: 'None',
+  CAS: 'CAS',
+  DLS: 'DLS',
+  Other: 'None',
+}
+
 const VALID_DXVK_VERSIONS: DxvkVersion[] = [
-  '2.6.1-gplasync',
+  'async-1.10.3',
+  '2.7.1',
   '1.10.3',
+  '1.10.1',
   '1.10.9-sarek',
+  '1.11.1-sarek',
   '1.9.2',
   '2.3.1',
   '2.4-gplasync',
-  'async-1.10.3',
+  '2.4.1',
+  '2.4.1-gplasync',
+  '2.6.1-gplasync',
+  '2.6-arm64ec',
 ]
 
-/**
- * Default configuration values (from GameNative)
- */
+const VALID_VKD3D_VERSIONS: VKD3DVersion[] = ['2.6', '2.12', '2.13', '2.14.1', '3.0b']
+
+const VALID_BOX64_ALL_VERSIONS: Box64Version[] = [
+  '0.3.2',
+  '0.3.4',
+  '0.3.6',
+  '0.3.7',
+  '0.3.8',
+  '0.4.0',
+]
+
+const VALID_FEXCORE_VERSIONS: FEXCoreVersion[] = ['2507', '2508', '2511', '2512', '2601', '2603']
+
 export const DEFAULT_CONFIG: Required<ContainerConfig> = {
   name: '',
-  screenSize: '854x480',
+  screenSize: '1280x720',
   envVars:
-    'ZINK_DESCRIPTORS=lazy ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true WINEESYNC=1 MESA_VK_WSI_PRESENT_MODE=mailbox TU_DEBUG=noconform',
+    'WRAPPER_MAX_IMAGE_COUNT=0 ZINK_DESCRIPTORS=lazy ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true WINEESYNC=1 MESA_VK_WSI_PRESENT_MODE=mailbox TU_DEBUG=noconform DXVK_FRAME_RATE=60 PULSE_LATENCY_MSEC=144',
   graphicsDriver: 'vortek',
   graphicsDriverVersion: '',
+  graphicsDriverConfig: '',
   dxwrapper: 'dxvk',
-  dxvkVersion: '2.6.1-gplasync',
   dxwrapperConfig: '',
-  audioDriver: 'alsa',
+  audioDriver: 'pulseaudio',
   wincomponents:
-    'direct3d=1,directsound=1,directmusic=0,directshow=0,directplay=0,vcrun2010=1,wmdecoder=1',
+    'direct3d=1,directsound=1,directmusic=0,directshow=0,directplay=0,vcrun2010=1,wmdecoder=1,opengl=0',
+  drives: '',
   execArgs: '',
   executablePath: '',
+  installPath: '',
   showFPS: false,
   launchRealSteam: false,
-  cpuList: '0,1,2,3,4,5,6,7', // Dynamic: matches Runtime.getRuntime().availableProcessors()
-  cpuListWoW64: '0,1,2,3,4,5,6,7', // Same as cpuList
+  allowSteamUpdates: false,
+  steamType: 'normal',
+  cpuList: '0,1,2,3,4,5,6,7',
+  cpuListWoW64: '0,1,2,3,4,5,6,7',
   wow64Mode: true,
-  startupSelection: 1, // STARTUP_SELECTION_ESSENTIAL
+  startupSelection: 1,
   box86Version: '0.3.2',
   box64Version: '0.3.6',
   box86Preset: 'COMPATIBILITY',
   box64Preset: 'COMPATIBILITY',
-  desktopTheme: 'LIGHT,IMAGE,#0277bd,854x480', // TODO: check if `854x480` is correct and needed
-  sdlControllerAPI: true,
-  enableXInput: true,
-  enableDInput: true,
-  dinputMapperType: 1,
-  disableMouseInput: false,
+  desktopTheme: 'LIGHT,IMAGE,#0277bd',
+  containerVariant: 'glibc',
+  wineVersion: 'wine-9.2-x86_64',
+  emulator: 'FEXCore',
+  fexcoreVersion: '2603',
+  fexcoreTSOMode: 'Fast',
+  fexcoreX87Mode: 'Fast',
+  fexcoreMultiBlock: 'Disabled',
+  fexcorePreset: 'INTERMEDIATE',
+  renderer: 'gl',
   csmt: true,
   videoPciDeviceID: 1728,
   offScreenRenderingMode: 'fbo',
   strictShaderMath: true,
+  useDRI3: true,
   videoMemorySize: '2048',
   mouseWarpOverride: 'disable',
   shaderBackend: 'glsl',
   useGLSL: 'enabled',
+  sdlControllerAPI: true,
+  useSteamInput: false,
+  enableXInput: true,
+  enableDInput: true,
+  dinputMapperType: 1,
+  disableMouseInput: false,
+  touchscreenMode: false,
+  shooterMode: true,
+  gestureConfig: '',
+  externalDisplayMode: 'off',
+  externalDisplaySwap: false,
+  language: 'english',
+  forceDlc: false,
+  steamOfflineMode: false,
+  useLegacyDRM: false,
+  unpackFiles: false,
+  portraitMode: false,
+  sharpnessEffect: 'None',
+  sharpnessLevel: 100,
+  sharpnessDenoise: 100,
 }
 
-// Helper functions for validation
 export const GameNativeDefaults = {
-  // Environment variables
   getDefaultEnvVars: (): string => DEFAULT_ENV_VARS,
-
-  // Screen size
   getDefaultScreenSize: (): ScreenSize => DEFAULT_SCREEN_SIZE,
-
-  // Graphics
   getDefaultGraphicsDriver: (): GraphicsDriver => DEFAULT_GRAPHICS_DRIVER,
   getDefaultDxWrapper: (): DxWrapper => DEFAULT_DX_WRAPPER,
   getDefaultDxvkVersion: (): DxvkVersion => DEFAULT_DXVK_VERSION,
-
-  // Audio
   getDefaultAudioDriver: (): AudioDriver => DEFAULT_AUDIO_DRIVER,
-
-  // System
   getDefaultStartupSelection: (): StartupSelection => DEFAULT_STARTUP_SELECTION,
   getDefaultBox64Version: (): Box64Version => DEFAULT_BOX64_VERSION,
   getDefaultBox86Version: (): Box86Version => DEFAULT_BOX86_VERSION,
   getDefaultBoxPreset: (): Box86_64Preset => DEFAULT_BOX_PRESET,
-
-  // Video/Graphics settings
+  getDefaultEmulator: (): Emulator => DEFAULT_EMULATOR,
+  getDefaultContainerVariant: (): ContainerVariant => DEFAULT_CONTAINER_VARIANT,
+  getDefaultSteamType: (): SteamType => DEFAULT_STEAM_TYPE,
+  getDefaultFexcoreVersion: (): FEXCoreVersion => DEFAULT_FEXCORE_VERSION,
+  getDefaultFexcorePreset: (): FEXCorePreset => DEFAULT_FEXCORE_PRESET,
   getDefaultVideoMemorySize: (): string => DEFAULT_VIDEO_MEMORY_SIZE,
   getDefaultCpuList: (): string => DEFAULT_CPU_LIST,
   getDefaultVideoPciDeviceId: (): number => DEFAULT_VIDEO_PCI_DEVICE_ID,
@@ -248,47 +391,53 @@ export const GameNativeDefaults = {
   getDefaultMouseWarpOverride: (): string => DEFAULT_MOUSE_WARP_OVERRIDE,
   getDefaultShaderBackend: (): string => DEFAULT_SHADER_BACKEND,
   getDefaultUseGlsl: (): string => DEFAULT_USE_GLSL,
-
-  // Windows components
   getDefaultWindowsComponents: () => ({ ...DEFAULT_WINDOWS_COMPONENTS }),
 
-  // Validation functions
   isValidVideoMemorySize: (size: string): boolean => VALID_VIDEO_MEMORY_SIZES.includes(size),
   isValidOffscreenRenderingMode: (mode: string): boolean =>
     VALID_OFFSCREEN_RENDERING_MODES.includes(mode.toLowerCase()),
   isValidMouseWarpOverride: (mode: string): boolean =>
     VALID_MOUSE_WARP_OVERRIDE_MODES.includes(mode.toLowerCase()),
   isValidUseGlsl: (value: string): boolean => VALID_USE_GLSL_VALUES.includes(value.toLowerCase()),
-  isValidBox64Version: (version: string): boolean => version === '0.3.6' || version === '0.3.4',
-  isValidBox86Version: (version: string): boolean => version === '0.3.2' || version === '0.3.7',
-  isValidDxvkVersion: (version: string): boolean =>
-    VALID_DXVK_VERSIONS.includes(version as DxvkVersion),
+  isValidBox64Version: (version: string): version is Box64Version =>
+    VALID_BOX64_ALL_VERSIONS.some((v) => v === version),
+  isValidBox86Version: (version: string): version is Box86Version =>
+    version === '0.3.2' || version === '0.3.7',
+  isValidDxvkVersion: (version: string): version is DxvkVersion =>
+    VALID_DXVK_VERSIONS.some((v) => v === version),
+  isValidVkd3dVersion: (version: string): version is VKD3DVersion =>
+    VALID_VKD3D_VERSIONS.some((v) => v === version),
+  isValidFexcoreVersion: (version: string): version is FEXCoreVersion =>
+    VALID_FEXCORE_VERSIONS.some((v) => v === version),
+  isValidEmulator: (value: string): value is Emulator => value === 'FEXCore' || value === 'Box64',
+  isValidContainerVariant: (value: string): value is ContainerVariant =>
+    value === 'glibc' || value === 'bionic',
 
-  // Graphics driver detection with smart fallback for legacy TEXT values
   detectGraphicsDriver: (value: string): GraphicsDriver => {
     if (!value) return DEFAULT_GRAPHICS_DRIVER
 
     const cleanValue = value.trim()
 
-    // First try exact match for new SELECT values (these are reliable)
     if (GRAPHICS_DRIVER_MAPPING[cleanValue]) return GRAPHICS_DRIVER_MAPPING[cleanValue]
 
-    // For legacy TEXT values, normalize by trimming whitespace and converting to lowercase
     const normalizedValue = cleanValue.toLowerCase()
 
-    // Try exact match after normalization
     for (const [key, driver] of Object.entries(GRAPHICS_DRIVER_MAPPING)) {
       if (key.toLowerCase() === normalizedValue) return driver
     }
 
-    // Try substring matching for legacy TEXT entries (handles messy user input)
-    if (normalizedValue.includes('turnip') || normalizedValue.includes('adreno')) {
-      return 'turnip'
+    if (normalizedValue.includes('sd-8-elite') || normalizedValue.includes('sd 8 elite')) {
+      return 'sd-8-elite'
     }
+    if (normalizedValue.includes('wrapper-leegao')) return 'wrapper-leegao'
+    if (normalizedValue.includes('wrapper-legacy')) return 'wrapper-legacy'
+    if (normalizedValue.includes('wrapper-v2')) return 'wrapper-v2'
+    if (normalizedValue.includes('wrapper')) return 'wrapper'
+    if (normalizedValue.includes('turnip')) return 'turnip'
+    if (normalizedValue.includes('adreno')) return 'adreno'
     if (normalizedValue.includes('virgl')) return 'virgl'
     if (normalizedValue.includes('vortek')) return 'vortek'
 
-    // If we can't determine, fall back to default
     return DEFAULT_GRAPHICS_DRIVER
   },
 }
diff --git a/src/server/utils/emulator-config/gamenative/gamenative.types.ts b/src/server/utils/emulator-config/gamenative/gamenative.types.ts
index 1f810c6dd..175c670bb 100644
--- a/src/server/utils/emulator-config/gamenative/gamenative.types.ts
+++ b/src/server/utils/emulator-config/gamenative/gamenative.types.ts
@@ -1,15 +1,14 @@
 /**
  * GameNative Container Configuration Type Definitions
  *
- * Contains all type definitions for GameNative container configuration files.
+ * Source of truth: ContainerData.kt, Container.java, DefaultVersion.java, arrays.xml
  */
 
-// Screen resolution options
 export type ScreenSize =
   | '640x480' // 4:3
   | '800x600' // 4:3
   | '854x480' // 16:9 (default)
-  | '960x544' // 16:9
+  | '960x540' // 16:9
   | '1024x768' // 4:3
   | '1280x720' // 16:9
   | '1280x800' // 16:10
@@ -20,242 +19,234 @@ export type ScreenSize =
   | '1920x1080' // 16:9
   | string // Custom format: "WIDTHxHEIGHT"
 
-// Graphics driver options
 export type GraphicsDriver =
-  | 'vortek' // Universal (default) - uses Zink backend
-  | 'turnip' // Adreno GPUs - uses Zink + Turnip Vulkan
-  | 'virgl' // Universal - uses VirGL
+  | 'vortek'
+  | 'turnip'
+  | 'virgl'
+  | 'adreno'
+  | 'sd-8-elite'
+  | 'wrapper'
+  | 'wrapper-v2'
+  | 'wrapper-leegao'
+  | 'wrapper-legacy'
 
-// Graphics driver versions by driver type
 export type GraphicsDriverVersions = {
-  turnip: '25.1.0' | '25.2.0' | '25.0.0' | '24.1.0'
+  turnip: '25.1.0' | '25.2.0' | '25.3.0' | '25.0.0' | '24.1.0'
   virgl: '23.1.9'
-  vortek: '2.0'
-  zink: '22.2.5' // Used internally by vortek/turnip
+  vortek: '2.1'
+  zink: '22.2.5'
+  adreno: '819.2' | '805'
+  sd8elite: '800.51' | '2-842.6'
 }
 
-// DirectX wrapper options
-export type DxWrapper =
-  | 'dxvk' // DXVK (default)
-  | 'vkd3d' // VKD3D
-  | 'wined3d' // WineD3D
-  | 'cnc-ddraw' // CNC DDraw
+export type DxWrapper = 'dxvk' | 'vkd3d' | 'wined3d' | 'cnc-ddraw'
 
-// DXVK version options
 export type DxvkVersion =
-  | '2.6.1-gplasync'
+  | 'async-1.10.3'
+  | '2.7.1'
   | '1.10.3'
+  | '1.10.1'
   | '1.10.9-sarek'
+  | '1.11.1-sarek'
   | '1.9.2'
   | '2.3.1'
   | '2.4-gplasync'
-  | 'async-1.10.3'
+  | '2.4.1'
+  | '2.4.1-gplasync'
+  | '2.6.1-gplasync'
+  | '2.6-arm64ec'
+
+export type VKD3DVersion = '2.6' | '2.12' | '2.13' | '2.14.1' | '3.0b'
 
-// Audio driver options
-export type AudioDriver =
-  | 'alsa' // ALSA (default)
-  | 'pulse' // PulseAudio
+export type AudioDriver = 'alsa' | 'pulseaudio'
 
-// Video memory size options
 export type VideoMemorySize =
-  | '32' // 32 MB
-  | '64' // 64 MB
-  | '128' // 128 MB
-  | '256' // 256 MB
-  | '512' // 512 MB
-  | '1024' // 1024 MB
-  | '2048' // 2048 MB (default)
-  | '4096' // 4096 MB
-  | '6144' // 6144 MB
-  | '8192' // 8192 MB
-  | '10240' // 10240 MB
-  | '12288' // 12288 MB
+  | '32'
+  | '64'
+  | '128'
+  | '256'
+  | '512'
+  | '1024'
+  | '2048'
+  | '4096'
+  | '6144'
+  | '8192'
+  | '10240'
+  | '12288'
 
-// Box86/64 performance presets
 export type Box86_64Preset =
   | 'STABILITY'
-  | 'COMPATIBILITY' // Default
+  | 'COMPATIBILITY'
   | 'INTERMEDIATE'
   | 'PERFORMANCE'
-  | string // Custom presets start with "CUSTOM-"
+  | 'DENUVO'
+  | 'UNITY'
+  | 'UNITY_MONO_BLEEDING_EDGE'
+  | 'CUSTOM'
+  | string
 
-// Box86/64 version options
 export type Box86Version = '0.3.2' | '0.3.7'
-export type Box64Version = '0.3.6' | '0.3.4'
+export type Box64Version = '0.3.2' | '0.3.4' | '0.3.6' | '0.3.7' | '0.3.8' | '0.4.0'
+
+export type FEXCorePreset =
+  | 'STABILITY'
+  | 'COMPATIBILITY'
+  | 'INTERMEDIATE'
+  | 'PERFORMANCE'
+  | 'EXTREME'
+  | 'DENUVO'
+  | 'CUSTOM'
+  | string
 
-// Startup selection modes
-export type StartupSelection =
-  | 0 // Normal (Load all services)
-  | 1 // Essential (Load only essential services) - Default
-  | 2 // Aggressive (Stop services on startup)
+export type FEXCoreVersion = '2507' | '2508' | '2511' | '2512' | '2601' | '2603'
+
+export type FEXCoreTSOMode = 'Fast' | 'Slow'
+export type FEXCoreX87Mode = 'Fast' | 'Slow'
+export type FEXCoreMultiBlock = 'Enabled' | 'Disabled'
+
+export type Emulator = 'FEXCore' | 'Box64'
+
+export type ContainerVariant = 'glibc' | 'bionic'
+
+export type SteamType = 'normal' | 'light' | 'ultralight'
+
+export type ExternalDisplayMode = 'off' | 'touchpad' | 'keyboard' | 'hybrid'
+
+export type SharpnessEffect = 'None' | 'CAS' | 'DLS'
+
+export type StartupSelection = 0 | 1 | 2
 
-// Wine desktop theme options
 export type DesktopTheme = 'LIGHT' | 'DARK'
 
-// Wine desktop background type
-export type DesktopBackgroundType =
-  | 'IMAGE' // Default
-  | 'COLOR'
+export type DesktopBackgroundType = 'IMAGE' | 'COLOR'
 
-// Off-screen rendering modes
-export type OffScreenRenderingMode =
-  | 'fbo' // FBO (default)
-  | 'backbuffer' // Backbuffer
+export type OffScreenRenderingMode = 'fbo' | 'backbuffer'
 
-// Mouse warp override options
-export type MouseWarpOverride =
-  | 'disable' // Disable (default)
-  | 'enable' // Enable
-  | 'force' // Force
+export type MouseWarpOverride = 'disable' | 'enable' | 'force'
 
-// DirectInput mapper type
-export type DinputMapperType =
-  | 0 // Standard (Old Gamepads)
-  | 1 // XInput (default)
+export type DinputMapperType = 1 | 2
 
-// Shader backend options
 export type ShaderBackend = 'glsl'
 
-// Wine component configuration
 export type WinComponents = string
-
-// Environment variables
 export type EnvVars = string
-
-// CPU list format
 export type CpuList = string
 
-/**
- * Complete container configuration interface
- */
+export type VulkanVersion = '1.1' | '1.2' | '1.3'
+export type BcnEmulation = 'none' | 'partial' | 'full' | 'auto'
+export type BcnEmulationType = 'software' | 'compute'
+export type PresentMode = 'mailbox' | 'fifo' | 'immediate' | 'relaxed'
+export type ResourceType = 'auto' | 'dmabuf' | 'ahb' | 'opaque'
+
+export interface DxWrapperConfig {
+  version?: DxvkVersion
+  framerate?: number
+  maxDeviceMemory?: number
+  async?: '0' | '1'
+  asyncCache?: '0' | '1'
+  vkd3dVersion?: VKD3DVersion
+  vkd3dLevel?: string
+  ddrawrapper?: 'none' | string
+  csmt?: number
+  gpuName?: string
+  videoMemorySize?: string
+  strict_shader_math?: '0' | '1'
+  OffscreenRenderingMode?: OffScreenRenderingMode
+  renderer?: string
+}
+
+export interface GraphicsDriverConfig {
+  vulkanVersion?: VulkanVersion
+  version?: string
+  blacklistedExtensions?: string
+  maxDeviceMemory?: number
+  presentMode?: PresentMode
+  syncFrame?: '0' | '1'
+  disablePresentWait?: '0' | '1'
+  resourceType?: ResourceType
+  bcnEmulation?: BcnEmulation
+  bcnEmulationType?: BcnEmulationType
+  bcnEmulationCache?: '0' | '1'
+  gpuName?: string
+  adrenotoolsTurnip?: '0' | '1'
+}
+
 export interface ContainerConfig {
-  /** Container name */
   name?: string
-
-  /** Screen resolution */
   screenSize?: ScreenSize
-
-  /** Environment variables */
   envVars?: EnvVars
-
-  /** Graphics driver */
   graphicsDriver?: GraphicsDriver
-
-  /** Graphics driver version */
   graphicsDriverVersion?: string
-
-  /** DirectX wrapper */
+  graphicsDriverConfig?: string
   dxwrapper?: DxWrapper
-
-  /** DirectX wrapper configuration */
   dxwrapperConfig?: string
-
-  /** DXVK version */
-  dxvkVersion?: DxvkVersion
-
-  /** Audio driver */
   audioDriver?: AudioDriver
-
-  /** Windows components configuration */
   wincomponents?: WinComponents
-
-  /** Execution arguments */
+  drives?: string
   execArgs?: string
-
-  /** Executable path */
   executablePath?: string
-
-  /** Show FPS overlay */
+  installPath?: string
   showFPS?: boolean
-
-  /** Launch real Steam client */
   launchRealSteam?: boolean
-
-  /** CPU core list */
+  allowSteamUpdates?: boolean
+  steamType?: SteamType
   cpuList?: CpuList
-
-  /** CPU core list for WoW64 */
   cpuListWoW64?: CpuList
-
-  /** Enable WoW64 mode */
   wow64Mode?: boolean
-
-  /** Startup selection mode */
   startupSelection?: StartupSelection
-
-  /** Box86 version */
   box86Version?: Box86Version
-
-  /** Box64 version */
   box64Version?: Box64Version
-
-  /** Box86 performance preset */
   box86Preset?: Box86_64Preset
-
-  /** Box64 performance preset */
   box64Preset?: Box86_64Preset
-
-  /**
-   * Desktop theme configuration
-   * Format: "THEME,BACKGROUND_TYPE,COLOR"
-   *
-   * Examples:
-   * - "LIGHT,IMAGE,#0277bd" (default)
-   * - "DARK,COLOR,#000000"
-   * - "LIGHT,COLOR,#ffffff"
-   */
   desktopTheme?: string
-
-  /** Enable SDL controller API */
-  sdlControllerAPI?: boolean
-
-  /** Enable XInput support */
-  enableXInput?: boolean
-
-  /** Enable DirectInput support */
-  enableDInput?: boolean
-
-  /** DirectInput mapper type */
-  dinputMapperType?: DinputMapperType
-
-  /** Disable mouse input */
-  disableMouseInput?: boolean
-
-  /** Enable Command Stream Multithreading */
+  containerVariant?: ContainerVariant
+  wineVersion?: string
+  emulator?: Emulator
+  fexcoreVersion?: FEXCoreVersion | string
+  fexcoreTSOMode?: FEXCoreTSOMode
+  fexcoreX87Mode?: FEXCoreX87Mode
+  fexcoreMultiBlock?: FEXCoreMultiBlock
+  fexcorePreset?: FEXCorePreset
+  renderer?: string
   csmt?: boolean
-
-  /**
-   * Video PCI device ID for GPU emulation
-   * Default: 1728 (NVIDIA GeForce GTX 480)
-   */
   videoPciDeviceID?: number
-
-  /** Off-screen rendering mode */
   offScreenRenderingMode?: OffScreenRenderingMode
-
-  /** Enable strict shader math */
   strictShaderMath?: boolean
-
-  /** Video memory size in MB */
+  useDRI3?: boolean
   videoMemorySize?: VideoMemorySize
-
-  /** Mouse warp override setting */
   mouseWarpOverride?: MouseWarpOverride
-
-  /** Shader backend */
   shaderBackend?: ShaderBackend
-
-  /** Use GLSL shaders */
   useGLSL?: 'enabled' | 'disabled'
+  sdlControllerAPI?: boolean
+  useSteamInput?: boolean
+  enableXInput?: boolean
+  enableDInput?: boolean
+  dinputMapperType?: DinputMapperType
+  disableMouseInput?: boolean
+  touchscreenMode?: boolean
+  shooterMode?: boolean
+  gestureConfig?: string
+  externalDisplayMode?: ExternalDisplayMode
+  externalDisplaySwap?: boolean
+  language?: string
+  forceDlc?: boolean
+  steamOfflineMode?: boolean
+  useLegacyDRM?: boolean
+  unpackFiles?: boolean
+  portraitMode?: boolean
+  sharpnessEffect?: SharpnessEffect
+  sharpnessLevel?: number
+  sharpnessDenoise?: number
 }
 
-/**
- * Helper type for graphics driver version based on selected driver
- */
 export type GraphicsDriverVersionFor<T extends GraphicsDriver> = T extends 'turnip'
   ? GraphicsDriverVersions['turnip']
   : T extends 'virgl'
     ? GraphicsDriverVersions['virgl']
     : T extends 'vortek'
       ? GraphicsDriverVersions['vortek']
-      : string
+      : T extends 'adreno'
+        ? GraphicsDriverVersions['adreno']
+        : T extends 'sd-8-elite'
+          ? GraphicsDriverVersions['sd8elite']
+          : string
diff --git a/src/server/utils/moderator-info.test.ts b/src/server/utils/moderator-info.test.ts
new file mode 100644
index 000000000..fc32b36e9
--- /dev/null
+++ b/src/server/utils/moderator-info.test.ts
@@ -0,0 +1,51 @@
+import { describe, expect, it } from 'vitest'
+import { computeVoteCounts, type VoteForCounting } from './moderator-info'
+
+function vote(value: boolean, nullifiedAt: Date | null = null): VoteForCounting {
+  return { value, nullifiedAt }
+}
+
+describe('computeVoteCounts', () => {
+  it('returns zeroes for an empty array', () => {
+    expect(computeVoteCounts([])).toEqual({ up: 0, down: 0, nullified: 0 })
+  })
+
+  it('counts only active upvotes in `up`', () => {
+    const result = computeVoteCounts([vote(true), vote(true), vote(true)])
+    expect(result).toEqual({ up: 3, down: 0, nullified: 0 })
+  })
+
+  it('counts only active downvotes in `down`', () => {
+    const result = computeVoteCounts([vote(false), vote(false)])
+    expect(result).toEqual({ up: 0, down: 2, nullified: 0 })
+  })
+
+  it('counts nullified votes separately, regardless of value', () => {
+    const result = computeVoteCounts([
+      vote(true, new Date('2026-01-01')),
+      vote(false, new Date('2026-01-02')),
+      vote(true, new Date('2026-01-03')),
+    ])
+    expect(result).toEqual({ up: 0, down: 0, nullified: 3 })
+  })
+
+  it('returns the correct mix when active and nullified votes are interleaved', () => {
+    const result = computeVoteCounts([
+      vote(true), // up
+      vote(true, new Date()), // nullified
+      vote(false), // down
+      vote(false, new Date()), // nullified
+      vote(true), // up
+      vote(true), // up
+      vote(false), // down
+    ])
+    expect(result).toEqual({ up: 3, down: 2, nullified: 2 })
+  })
+
+  it('does NOT double-count: a nullified upvote contributes only to `nullified`', () => {
+    const result = computeVoteCounts([vote(true, new Date('2026-01-01'))])
+    expect(result.up).toBe(0)
+    expect(result.down).toBe(0)
+    expect(result.nullified).toBe(1)
+  })
+})
diff --git a/src/server/utils/moderator-info.ts b/src/server/utils/moderator-info.ts
new file mode 100644
index 000000000..c1ec794d5
--- /dev/null
+++ b/src/server/utils/moderator-info.ts
@@ -0,0 +1,27 @@
+export interface VoteCounts {
+  up: number
+  down: number
+  nullified: number
+}
+
+// A nullified vote counts only in `nullified`, not in up/down.
+export interface VoteForCounting {
+  value: boolean
+  nullifiedAt: Date | null
+}
+
+export function computeVoteCounts(votes: readonly VoteForCounting[]): VoteCounts {
+  let up = 0
+  let down = 0
+  let nullified = 0
+  for (const vote of votes) {
+    if (vote.nullifiedAt !== null) {
+      nullified += 1
+    } else if (vote.value) {
+      up += 1
+    } else {
+      down += 1
+    }
+  }
+  return { up, down, nullified }
+}
diff --git a/src/server/utils/spam-check.test.ts b/src/server/utils/spam-check.test.ts
new file mode 100644
index 000000000..850a8cceb
--- /dev/null
+++ b/src/server/utils/spam-check.test.ts
@@ -0,0 +1,107 @@
+import { TRPCError } from '@trpc/server'
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import analytics from '@/lib/analytics'
+import { type PrismaClient } from '@orm'
+import { checkSpamContent } from './spam-check'
+import { SpamDetectionService } from './spamDetection'
+
+vi.mock('@/lib/analytics', () => ({
+  default: {
+    contentQuality: {
+      spamDetected: vi.fn(),
+    },
+  },
+}))
+
+const mockPrisma = {} as unknown as PrismaClient
+
+afterEach(() => vi.restoreAllMocks())
+
+describe('checkSpamContent', () => {
+  const USER_ID = 'user-123'
+
+  it('returns without side effects when content is not spam', async () => {
+    const detectSpy = vi.spyOn(SpamDetectionService.prototype, 'detectSpam').mockResolvedValue({
+      isSpam: false,
+      confidence: 0.1,
+      method: 'content_analysis',
+    })
+
+    await expect(
+      checkSpamContent({
+        prisma: mockPrisma,
+        userId: USER_ID,
+        content: 'Clean content',
+        entityType: 'listing',
+      }),
+    ).resolves.toBeUndefined()
+
+    expect(detectSpy).toHaveBeenCalledOnce()
+    expect(analytics.contentQuality.spamDetected).not.toHaveBeenCalled()
+  })
+
+  it('emits analytics and throws AppError.badRequest with reason when spam detected', async () => {
+    vi.spyOn(SpamDetectionService.prototype, 'detectSpam').mockResolvedValue({
+      isSpam: true,
+      confidence: 0.95,
+      method: 'rate_limiting',
+      reason: 'Exceeded rate limit: 4 listings in 5 minutes',
+    })
+
+    await expect(
+      checkSpamContent({
+        prisma: mockPrisma,
+        userId: USER_ID,
+        content: 'Flagged content',
+        entityType: 'listing',
+      }),
+    ).rejects.toThrow(/Spam detected: Exceeded rate limit/)
+
+    expect(analytics.contentQuality.spamDetected).toHaveBeenCalledTimes(1)
+    expect(analytics.contentQuality.spamDetected).toHaveBeenCalledWith({
+      entityType: 'listing',
+      entityId: USER_ID,
+      confidence: 0.95,
+      method: 'rate_limiting',
+    })
+  })
+
+  it('falls back to the community-guidelines message when reason is missing', async () => {
+    vi.spyOn(SpamDetectionService.prototype, 'detectSpam').mockResolvedValue({
+      isSpam: true,
+      confidence: 0.8,
+      method: 'pattern_matching',
+    })
+
+    await expect(
+      checkSpamContent({
+        prisma: mockPrisma,
+        userId: USER_ID,
+        content: 'Looks spammy but no explicit reason',
+        entityType: 'comment',
+      }),
+    ).rejects.toThrow(/community guidelines/i)
+  })
+
+  it('throws a BAD_REQUEST TRPCError', async () => {
+    vi.spyOn(SpamDetectionService.prototype, 'detectSpam').mockResolvedValue({
+      isSpam: true,
+      confidence: 0.9,
+      method: 'duplicate_detection',
+      reason: 'Duplicate of recent submission',
+    })
+
+    try {
+      await checkSpamContent({
+        prisma: mockPrisma,
+        userId: USER_ID,
+        content: 'duplicate',
+        entityType: 'comment',
+      })
+      throw new Error('Expected checkSpamContent to throw')
+    } catch (error) {
+      expect(error).toBeInstanceOf(TRPCError)
+      expect((error as TRPCError).code).toBe('BAD_REQUEST')
+    }
+  })
+})
diff --git a/src/server/utils/spam-check.ts b/src/server/utils/spam-check.ts
new file mode 100644
index 000000000..7890aefb1
--- /dev/null
+++ b/src/server/utils/spam-check.ts
@@ -0,0 +1,35 @@
+import analytics from '@/lib/analytics'
+import { AppError } from '@/lib/errors'
+import { SpamDetectionService } from '@/server/utils/spamDetection'
+import { type PrismaClient } from '@orm'
+
+type SpamEntityType = 'listing' | 'comment'
+
+interface CheckSpamContentParams {
+  prisma: PrismaClient
+  userId: string
+  content: string
+  entityType: SpamEntityType
+}
+
+export async function checkSpamContent(params: CheckSpamContentParams): Promise<void> {
+  const detector = new SpamDetectionService(params.prisma)
+  const result = await detector.detectSpam({
+    userId: params.userId,
+    content: params.content,
+    entityType: params.entityType,
+  })
+
+  if (!result.isSpam) return
+
+  analytics.contentQuality.spamDetected({
+    entityType: params.entityType,
+    entityId: params.userId,
+    confidence: result.confidence,
+    method: result.method,
+  })
+
+  throw AppError.badRequest(
+    `Spam detected: ${result.reason || 'Your content appears to be spam. Please review our community guidelines.'}`,
+  )
+}
diff --git a/src/server/utils/spamDetection.ts b/src/server/utils/spamDetection.ts
index 6c776ac55..7d7b3a817 100644
--- a/src/server/utils/spamDetection.ts
+++ b/src/server/utils/spamDetection.ts
@@ -43,7 +43,7 @@ const DEFAULT_CONFIG: Required<SpamDetectionConfig> = {
 }
 
 /**
- * Comprehensive spam detection service
+ * Spam detection service
  * Checks content for spam using multiple detection methods
  */
 export class SpamDetectionService {
diff --git a/src/server/utils/vote-trust-effects.test.ts b/src/server/utils/vote-trust-effects.test.ts
index dd5722073..825c55fb0 100644
--- a/src/server/utils/vote-trust-effects.test.ts
+++ b/src/server/utils/vote-trust-effects.test.ts
@@ -1,283 +1,646 @@
 import { describe, expect, it, beforeEach, vi } from 'vitest'
 import { TrustAction } from '@orm'
-import { handleVoteTrustEffects } from './vote-trust-effects'
+import { handleCommentVoteTrustEffects, handleListingVoteTrustEffects } from './vote-trust-effects'
 
-const mockApplyTrustAction = vi.fn()
-const mockReverseTrustAction = vi.fn()
+const mockLogAction = vi.fn()
+const mockReverseLogAction = vi.fn()
 
 vi.mock('@/lib/trust/service', () => ({
-  applyTrustAction: (...args: unknown[]) => mockApplyTrustAction(...args),
-  reverseTrustAction: (...args: unknown[]) => mockReverseTrustAction(...args),
+  TrustService: vi.fn().mockImplementation(() => ({
+    logAction: mockLogAction,
+    reverseLogAction: mockReverseLogAction,
+  })),
 }))
 
 const USER_ID = 'voter-1'
 const AUTHOR_ID = 'author-1'
 const LISTING_ID = 'listing-1'
+const mockTx = {} as Parameters<typeof handleListingVoteTrustEffects>[0]['tx']
 
-describe('handleVoteTrustEffects', () => {
-  beforeEach(() => {
-    mockApplyTrustAction.mockResolvedValue(undefined)
-    mockReverseTrustAction.mockResolvedValue(undefined)
-  })
+beforeEach(() => {
+  vi.clearAllMocks()
+  mockLogAction.mockResolvedValue(undefined)
+  mockReverseLogAction.mockResolvedValue(undefined)
+})
 
-  describe('vote created', () => {
-    it('applies UPVOTE trust for voter on upvote', async () => {
-      await handleVoteTrustEffects({
+describe('handleListingVoteTrustEffects', () => {
+  describe('created action', () => {
+    it('applies UPVOTE for voter and LISTING_RECEIVED_UPVOTE for author on upvote', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'created',
         currentValue: true,
         previousValue: null,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'handheld',
         authorId: AUTHOR_ID,
       })
 
-      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+      expect(mockLogAction).toHaveBeenCalledTimes(2)
+      expect(mockLogAction).toHaveBeenNthCalledWith(1, {
         userId: USER_ID,
         action: TrustAction.UPVOTE,
-        context: { listingId: LISTING_ID },
+        metadata: { listingId: LISTING_ID },
       })
-    })
-
-    it('applies DOWNVOTE trust for voter on downvote', async () => {
-      await handleVoteTrustEffects({
-        action: 'created',
-        currentValue: false,
-        previousValue: null,
-        userId: USER_ID,
-        listingId: LISTING_ID,
-        authorId: AUTHOR_ID,
-      })
-
-      expect(mockApplyTrustAction).toHaveBeenCalledWith({
-        userId: USER_ID,
-        action: TrustAction.DOWNVOTE,
-        context: { listingId: LISTING_ID },
-      })
-    })
-
-    it('applies LISTING_RECEIVED_UPVOTE trust for author on upvote', async () => {
-      await handleVoteTrustEffects({
-        action: 'created',
-        currentValue: true,
-        previousValue: null,
-        userId: USER_ID,
-        listingId: LISTING_ID,
-        authorId: AUTHOR_ID,
-      })
-
-      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+      expect(mockLogAction).toHaveBeenNthCalledWith(2, {
         userId: AUTHOR_ID,
         action: TrustAction.LISTING_RECEIVED_UPVOTE,
-        context: { listingId: LISTING_ID, voterId: USER_ID },
+        metadata: { listingId: LISTING_ID, voterId: USER_ID },
       })
+      expect(mockReverseLogAction).not.toHaveBeenCalled()
     })
 
-    it('applies LISTING_RECEIVED_DOWNVOTE trust for author on downvote', async () => {
-      await handleVoteTrustEffects({
+    it('applies DOWNVOTE for voter and LISTING_RECEIVED_DOWNVOTE for author on downvote', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'created',
         currentValue: false,
         previousValue: null,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'handheld',
         authorId: AUTHOR_ID,
       })
 
-      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: USER_ID,
+        action: TrustAction.DOWNVOTE,
+        metadata: { listingId: LISTING_ID },
+      })
+      expect(mockLogAction).toHaveBeenCalledWith({
         userId: AUTHOR_ID,
         action: TrustAction.LISTING_RECEIVED_DOWNVOTE,
-        context: { listingId: LISTING_ID, voterId: USER_ID },
+        metadata: { listingId: LISTING_ID, voterId: USER_ID },
       })
     })
 
     it('skips author trust on self-vote', async () => {
-      await handleVoteTrustEffects({
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'created',
         currentValue: true,
         previousValue: null,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'handheld',
         authorId: USER_ID,
       })
 
-      expect(mockApplyTrustAction).toHaveBeenCalledTimes(1)
-      expect(mockApplyTrustAction).toHaveBeenCalledWith(
+      expect(mockLogAction).toHaveBeenCalledTimes(1)
+      expect(mockLogAction).toHaveBeenCalledWith(
         expect.objectContaining({ userId: USER_ID, action: TrustAction.UPVOTE }),
       )
     })
 
     it('skips author trust when authorId is null', async () => {
-      await handleVoteTrustEffects({
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'created',
         currentValue: true,
         previousValue: null,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'handheld',
         authorId: null,
       })
 
-      expect(mockApplyTrustAction).toHaveBeenCalledTimes(1)
+      expect(mockLogAction).toHaveBeenCalledTimes(1)
     })
 
-    it('does not call reverseTrustAction', async () => {
-      await handleVoteTrustEffects({
+    it('uses pcListingId context key for PC listings', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'created',
         currentValue: true,
         previousValue: null,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'pc',
         authorId: AUTHOR_ID,
       })
 
-      expect(mockReverseTrustAction).not.toHaveBeenCalled()
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: USER_ID,
+        action: TrustAction.UPVOTE,
+        metadata: { pcListingId: LISTING_ID },
+      })
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: AUTHOR_ID,
+        action: TrustAction.LISTING_RECEIVED_UPVOTE,
+        metadata: { pcListingId: LISTING_ID, voterId: USER_ID },
+      })
     })
   })
 
-  describe('vote updated', () => {
-    it('applies trust for the new vote value', async () => {
-      await handleVoteTrustEffects({
+  describe('updated action (vote change — reverse then apply)', () => {
+    it('reverses previous upvote and applies downvote when changing from up to down', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'updated',
         currentValue: false,
         previousValue: true,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'handheld',
         authorId: AUTHOR_ID,
       })
 
-      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+      // Expect 2 reversals (voter + author) then 2 applications (voter + author)
+      expect(mockReverseLogAction).toHaveBeenCalledTimes(2)
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
+        userId: USER_ID,
+        originalAction: TrustAction.UPVOTE,
+        metadata: { listingId: LISTING_ID },
+      })
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
+        userId: AUTHOR_ID,
+        originalAction: TrustAction.LISTING_RECEIVED_UPVOTE,
+        metadata: { listingId: LISTING_ID, voterId: USER_ID },
+      })
+
+      expect(mockLogAction).toHaveBeenCalledTimes(2)
+      expect(mockLogAction).toHaveBeenCalledWith({
         userId: USER_ID,
         action: TrustAction.DOWNVOTE,
-        context: { listingId: LISTING_ID },
+        metadata: { listingId: LISTING_ID },
       })
-      expect(mockApplyTrustAction).toHaveBeenCalledWith({
+      expect(mockLogAction).toHaveBeenCalledWith({
         userId: AUTHOR_ID,
         action: TrustAction.LISTING_RECEIVED_DOWNVOTE,
-        context: { listingId: LISTING_ID, voterId: USER_ID },
+        metadata: { listingId: LISTING_ID, voterId: USER_ID },
       })
     })
-  })
 
-  describe('vote deleted (toggled off)', () => {
-    it('reverses UPVOTE trust for voter when upvote was toggled off', async () => {
-      await handleVoteTrustEffects({
-        action: 'deleted',
+    it('reverses previous downvote and applies upvote when changing from down to up', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
+        action: 'updated',
         currentValue: true,
-        previousValue: true,
+        previousValue: false,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'handheld',
         authorId: AUTHOR_ID,
       })
 
-      expect(mockReverseTrustAction).toHaveBeenCalledWith({
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
         userId: USER_ID,
-        originalAction: TrustAction.UPVOTE,
-        context: { listingId: LISTING_ID },
+        originalAction: TrustAction.DOWNVOTE,
+        metadata: { listingId: LISTING_ID },
+      })
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
+        userId: AUTHOR_ID,
+        originalAction: TrustAction.LISTING_RECEIVED_DOWNVOTE,
+        metadata: { listingId: LISTING_ID, voterId: USER_ID },
+      })
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: USER_ID,
+        action: TrustAction.UPVOTE,
+        metadata: { listingId: LISTING_ID },
+      })
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: AUTHOR_ID,
+        action: TrustAction.LISTING_RECEIVED_UPVOTE,
+        metadata: { listingId: LISTING_ID, voterId: USER_ID },
       })
     })
 
-    it('reverses DOWNVOTE trust for voter when downvote was toggled off', async () => {
-      await handleVoteTrustEffects({
-        action: 'deleted',
+    it('does nothing when previousValue is null (safety)', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
+        action: 'updated',
         currentValue: false,
-        previousValue: false,
+        previousValue: null,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'handheld',
         authorId: AUTHOR_ID,
       })
 
-      expect(mockReverseTrustAction).toHaveBeenCalledWith({
+      expect(mockReverseLogAction).not.toHaveBeenCalled()
+      expect(mockLogAction).not.toHaveBeenCalled()
+    })
+
+    it('skips author reversals/applications on self-vote', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
+        action: 'updated',
+        currentValue: false,
+        previousValue: true,
         userId: USER_ID,
-        originalAction: TrustAction.DOWNVOTE,
-        context: { listingId: LISTING_ID },
+        listingId: LISTING_ID,
+        listingType: 'handheld',
+        authorId: USER_ID,
+      })
+
+      // Only voter-side trust calls, one reverse and one apply
+      expect(mockReverseLogAction).toHaveBeenCalledTimes(1)
+      expect(mockLogAction).toHaveBeenCalledTimes(1)
+    })
+
+    it('uses pcListingId for PC listings on update', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
+        action: 'updated',
+        currentValue: false,
+        previousValue: true,
+        userId: USER_ID,
+        listingId: LISTING_ID,
+        listingType: 'pc',
+        authorId: AUTHOR_ID,
       })
+
+      expect(mockReverseLogAction).toHaveBeenCalledWith(
+        expect.objectContaining({ metadata: { pcListingId: LISTING_ID } }),
+      )
+      expect(mockLogAction).toHaveBeenCalledWith(
+        expect.objectContaining({ metadata: { pcListingId: LISTING_ID } }),
+      )
     })
+  })
 
-    it('reverses LISTING_RECEIVED_UPVOTE trust for author when upvote was toggled off', async () => {
-      await handleVoteTrustEffects({
+  describe('deleted action (vote toggled off)', () => {
+    it('reverses UPVOTE and LISTING_RECEIVED_UPVOTE when an upvote is toggled off', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'deleted',
         currentValue: true,
         previousValue: true,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'handheld',
         authorId: AUTHOR_ID,
       })
 
-      expect(mockReverseTrustAction).toHaveBeenCalledWith({
+      expect(mockReverseLogAction).toHaveBeenCalledTimes(2)
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
+        userId: USER_ID,
+        originalAction: TrustAction.UPVOTE,
+        metadata: { listingId: LISTING_ID },
+      })
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
         userId: AUTHOR_ID,
         originalAction: TrustAction.LISTING_RECEIVED_UPVOTE,
-        context: { listingId: LISTING_ID, voterId: USER_ID },
+        metadata: { listingId: LISTING_ID, voterId: USER_ID },
       })
+      expect(mockLogAction).not.toHaveBeenCalled()
     })
 
-    it('reverses LISTING_RECEIVED_DOWNVOTE trust for author when downvote was toggled off', async () => {
-      await handleVoteTrustEffects({
+    it('reverses DOWNVOTE and LISTING_RECEIVED_DOWNVOTE when a downvote is toggled off', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'deleted',
         currentValue: false,
         previousValue: false,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'handheld',
         authorId: AUTHOR_ID,
       })
 
-      expect(mockReverseTrustAction).toHaveBeenCalledWith({
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
+        userId: USER_ID,
+        originalAction: TrustAction.DOWNVOTE,
+        metadata: { listingId: LISTING_ID },
+      })
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
         userId: AUTHOR_ID,
         originalAction: TrustAction.LISTING_RECEIVED_DOWNVOTE,
-        context: { listingId: LISTING_ID, voterId: USER_ID },
+        metadata: { listingId: LISTING_ID, voterId: USER_ID },
       })
     })
 
-    it('skips author trust reversal on self-vote toggle-off', async () => {
-      await handleVoteTrustEffects({
+    it('does nothing when previousValue is null (safety)', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'deleted',
         currentValue: true,
-        previousValue: true,
+        previousValue: null,
         userId: USER_ID,
         listingId: LISTING_ID,
-        authorId: USER_ID,
+        listingType: 'handheld',
+        authorId: AUTHOR_ID,
       })
 
-      expect(mockReverseTrustAction).toHaveBeenCalledTimes(1)
-      expect(mockReverseTrustAction).toHaveBeenCalledWith(
-        expect.objectContaining({ userId: USER_ID, originalAction: TrustAction.UPVOTE }),
-      )
+      expect(mockReverseLogAction).not.toHaveBeenCalled()
+      expect(mockLogAction).not.toHaveBeenCalled()
     })
 
-    it('skips author trust reversal when authorId is null', async () => {
-      await handleVoteTrustEffects({
+    it('skips author reversal on self-vote toggle-off', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'deleted',
         currentValue: true,
         previousValue: true,
         userId: USER_ID,
         listingId: LISTING_ID,
-        authorId: null,
+        listingType: 'handheld',
+        authorId: USER_ID,
       })
 
-      expect(mockReverseTrustAction).toHaveBeenCalledTimes(1)
+      expect(mockReverseLogAction).toHaveBeenCalledTimes(1)
+      expect(mockReverseLogAction).toHaveBeenCalledWith(
+        expect.objectContaining({ userId: USER_ID, originalAction: TrustAction.UPVOTE }),
+      )
     })
 
-    it('does not reverse trust when previousValue is null', async () => {
-      await handleVoteTrustEffects({
+    it('uses pcListingId for PC listings on delete', async () => {
+      await handleListingVoteTrustEffects({
+        tx: mockTx,
         action: 'deleted',
         currentValue: true,
-        previousValue: null,
+        previousValue: true,
         userId: USER_ID,
         listingId: LISTING_ID,
+        listingType: 'pc',
         authorId: AUTHOR_ID,
       })
 
-      expect(mockReverseTrustAction).not.toHaveBeenCalled()
+      expect(mockReverseLogAction).toHaveBeenCalledWith(
+        expect.objectContaining({
+          originalAction: TrustAction.UPVOTE,
+          metadata: { pcListingId: LISTING_ID },
+        }),
+      )
+      expect(mockReverseLogAction).toHaveBeenCalledWith(
+        expect.objectContaining({
+          originalAction: TrustAction.LISTING_RECEIVED_UPVOTE,
+          metadata: { pcListingId: LISTING_ID, voterId: USER_ID },
+        }),
+      )
     })
+  })
+})
 
-    it('does not call applyTrustAction', async () => {
-      await handleVoteTrustEffects({
-        action: 'deleted',
-        currentValue: true,
+describe('handleCommentVoteTrustEffects', () => {
+  const COMMENT_AUTHOR_ID = 'comment-author-1'
+  const VOTER_ID = 'voter-1'
+  const COMMENT_ID = 'comment-1'
+  const PARENT_ENTITY_ID = 'parent-entity-1'
+  const commentMockTx = {} as Parameters<typeof handleCommentVoteTrustEffects>[0]['tx']
+
+  function callWithDefaults(
+    overrides: Partial<Parameters<typeof handleCommentVoteTrustEffects>[0]>,
+  ) {
+    return handleCommentVoteTrustEffects({
+      tx: commentMockTx,
+      trustAction: 'upvote',
+      newValue: true,
+      previousValue: null,
+      commentAuthorId: COMMENT_AUTHOR_ID,
+      voterId: VOTER_ID,
+      commentId: COMMENT_ID,
+      parentEntityId: PARENT_ENTITY_ID,
+      listingType: 'handheld',
+      updatedScore: 1,
+      scoreChange: 1,
+      ...overrides,
+    })
+  }
+
+  describe('self-vote skipping', () => {
+    it('does nothing when commentAuthorId equals voterId', async () => {
+      await callWithDefaults({
+        commentAuthorId: 'same-user',
+        voterId: 'same-user',
+      })
+
+      expect(mockLogAction).not.toHaveBeenCalled()
+      expect(mockReverseLogAction).not.toHaveBeenCalled()
+    })
+  })
+
+  describe('upvote action', () => {
+    it('logs COMMENT_RECEIVED_UPVOTE for handheld with listingId', async () => {
+      await callWithDefaults({ trustAction: 'upvote', listingType: 'handheld' })
+
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        action: TrustAction.COMMENT_RECEIVED_UPVOTE,
+        targetUserId: VOTER_ID,
+        metadata: { commentId: COMMENT_ID, voterId: VOTER_ID, listingId: PARENT_ENTITY_ID },
+      })
+    })
+
+    it('logs COMMENT_RECEIVED_UPVOTE for PC with pcListingId', async () => {
+      await callWithDefaults({ trustAction: 'upvote', listingType: 'pc' })
+
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        action: TrustAction.COMMENT_RECEIVED_UPVOTE,
+        targetUserId: VOTER_ID,
+        metadata: { commentId: COMMENT_ID, voterId: VOTER_ID, pcListingId: PARENT_ENTITY_ID },
+      })
+    })
+  })
+
+  describe('downvote action', () => {
+    it('logs COMMENT_RECEIVED_DOWNVOTE for handheld', async () => {
+      await callWithDefaults({
+        trustAction: 'downvote',
+        newValue: false,
+        listingType: 'handheld',
+      })
+
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
+        targetUserId: VOTER_ID,
+        metadata: { commentId: COMMENT_ID, voterId: VOTER_ID, listingId: PARENT_ENTITY_ID },
+      })
+    })
+  })
+
+  describe('change action (uses reverseLogAction for the reversal)', () => {
+    it('properly reverses previous downvote then applies upvote when changing to upvote', async () => {
+      await callWithDefaults({
+        trustAction: 'change',
+        newValue: true,
+        previousValue: false,
+      })
+
+      // Must use reverseLogAction (not logAction) to actually reverse weight
+      expect(mockReverseLogAction).toHaveBeenCalledTimes(1)
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        originalAction: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
+        targetUserId: VOTER_ID,
+        metadata: { commentId: COMMENT_ID, voterId: VOTER_ID, listingId: PARENT_ENTITY_ID },
+      })
+
+      // Then apply new upvote
+      expect(mockLogAction).toHaveBeenCalledTimes(1)
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        action: TrustAction.COMMENT_RECEIVED_UPVOTE,
+        targetUserId: VOTER_ID,
+        metadata: { commentId: COMMENT_ID, voterId: VOTER_ID, listingId: PARENT_ENTITY_ID },
+      })
+    })
+
+    it('properly reverses previous upvote then applies downvote when changing to downvote', async () => {
+      await callWithDefaults({
+        trustAction: 'change',
+        newValue: false,
         previousValue: true,
-        userId: USER_ID,
-        listingId: LISTING_ID,
-        authorId: AUTHOR_ID,
       })
 
-      expect(mockApplyTrustAction).not.toHaveBeenCalled()
+      expect(mockReverseLogAction).toHaveBeenCalledTimes(1)
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        originalAction: TrustAction.COMMENT_RECEIVED_UPVOTE,
+        targetUserId: VOTER_ID,
+        metadata: expect.any(Object),
+      })
+
+      expect(mockLogAction).toHaveBeenCalledTimes(1)
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
+        targetUserId: VOTER_ID,
+        metadata: expect.any(Object),
+      })
+    })
+
+    it('uses pcListingId for PC listing vote changes', async () => {
+      await callWithDefaults({
+        trustAction: 'change',
+        newValue: true,
+        previousValue: false,
+        listingType: 'pc',
+      })
+
+      expect(mockReverseLogAction).toHaveBeenCalledWith(
+        expect.objectContaining({
+          metadata: expect.objectContaining({ pcListingId: PARENT_ENTITY_ID }),
+        }),
+      )
+      expect(mockLogAction).toHaveBeenCalledWith(
+        expect.objectContaining({
+          metadata: expect.objectContaining({ pcListingId: PARENT_ENTITY_ID }),
+        }),
+      )
+    })
+  })
+
+  describe('remove action (uses reverseLogAction)', () => {
+    it('reverses COMMENT_RECEIVED_UPVOTE when previousValue was upvote', async () => {
+      await callWithDefaults({
+        trustAction: 'remove',
+        previousValue: true,
+      })
+
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        originalAction: TrustAction.COMMENT_RECEIVED_UPVOTE,
+        targetUserId: VOTER_ID,
+        metadata: { commentId: COMMENT_ID, voterId: VOTER_ID, listingId: PARENT_ENTITY_ID },
+      })
+      // remove doesn't need to log a new action
+      expect(mockLogAction).not.toHaveBeenCalled()
+    })
+
+    it('reverses COMMENT_RECEIVED_DOWNVOTE when previousValue was downvote', async () => {
+      await callWithDefaults({
+        trustAction: 'remove',
+        previousValue: false,
+      })
+
+      expect(mockReverseLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        originalAction: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
+        targetUserId: VOTER_ID,
+        metadata: expect.any(Object),
+      })
+    })
+
+    it('does nothing when previousValue is null (safety)', async () => {
+      await callWithDefaults({
+        trustAction: 'remove',
+        previousValue: null,
+      })
+
+      expect(mockReverseLogAction).not.toHaveBeenCalled()
+      expect(mockLogAction).not.toHaveBeenCalled()
+    })
+  })
+
+  describe('helpful comment threshold', () => {
+    it('logs HELPFUL_COMMENT when score crosses 5 from below', async () => {
+      await callWithDefaults({
+        trustAction: 'upvote',
+        updatedScore: 5,
+        scoreChange: 1,
+      })
+
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        action: TrustAction.HELPFUL_COMMENT,
+        metadata: {
+          commentId: COMMENT_ID,
+          listingId: PARENT_ENTITY_ID,
+          score: 5,
+          threshold: 5,
+        },
+      })
+    })
+
+    it('does NOT log HELPFUL_COMMENT when score was already at threshold', async () => {
+      await callWithDefaults({
+        trustAction: 'upvote',
+        updatedScore: 6,
+        scoreChange: 1,
+      })
+
+      expect(mockLogAction).not.toHaveBeenCalledWith(
+        expect.objectContaining({ action: TrustAction.HELPFUL_COMMENT }),
+      )
+    })
+
+    it('does NOT log HELPFUL_COMMENT when score is still below threshold', async () => {
+      await callWithDefaults({
+        trustAction: 'upvote',
+        updatedScore: 3,
+        scoreChange: 1,
+      })
+
+      expect(mockLogAction).not.toHaveBeenCalledWith(
+        expect.objectContaining({ action: TrustAction.HELPFUL_COMMENT }),
+      )
+    })
+
+    it('uses pcListingId in HELPFUL_COMMENT metadata for PC listings', async () => {
+      await callWithDefaults({
+        trustAction: 'upvote',
+        listingType: 'pc',
+        updatedScore: 5,
+        scoreChange: 1,
+      })
+
+      expect(mockLogAction).toHaveBeenCalledWith({
+        userId: COMMENT_AUTHOR_ID,
+        action: TrustAction.HELPFUL_COMMENT,
+        metadata: {
+          commentId: COMMENT_ID,
+          pcListingId: PARENT_ENTITY_ID,
+          score: 5,
+          threshold: 5,
+        },
+      })
+    })
+
+    it('does NOT log HELPFUL_COMMENT when score drops below threshold', async () => {
+      await callWithDefaults({
+        trustAction: 'downvote',
+        newValue: false,
+        updatedScore: 4,
+        scoreChange: -1,
+      })
+
+      expect(mockLogAction).not.toHaveBeenCalledWith(
+        expect.objectContaining({ action: TrustAction.HELPFUL_COMMENT }),
+      )
     })
   })
 })
diff --git a/src/server/utils/vote-trust-effects.ts b/src/server/utils/vote-trust-effects.ts
index 44180ac83..55bd1f54c 100644
--- a/src/server/utils/vote-trust-effects.ts
+++ b/src/server/utils/vote-trust-effects.ts
@@ -1,53 +1,202 @@
-import { applyTrustAction, reverseTrustAction } from '@/lib/trust/service'
-import { TrustAction } from '@orm'
+import { TrustService } from '@/lib/trust/service'
+import { type ListingType } from '@/schemas/common'
+import { TrustAction, type PrismaClient, type Prisma } from '@orm'
 
 type VoteAction = 'created' | 'updated' | 'deleted'
 
-interface VoteTrustEffectsParams {
+type PrismaTransactionClient = Omit<
+  PrismaClient,
+  '$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends'
+>
+
+interface ListingVoteTrustParams {
+  tx: PrismaTransactionClient | Prisma.TransactionClient
   action: VoteAction
   currentValue: boolean
   previousValue: boolean | null
   userId: string
   listingId: string
+  listingType: ListingType
   authorId: string | null
 }
 
-export async function handleVoteTrustEffects(params: VoteTrustEffectsParams): Promise<void> {
-  const { action, currentValue, previousValue, userId, listingId, authorId } = params
+/**
+ * Applies or reverses trust effects when a listing vote is created, updated, or deleted.
+ * Works for both handheld and PC listings. Must be called within a transaction — pass `tx`.
+ */
+export async function handleListingVoteTrustEffects(params: ListingVoteTrustParams): Promise<void> {
+  const { tx, action, currentValue, previousValue, userId, listingId, listingType, authorId } =
+    params
+
+  const contextKey = listingType === 'handheld' ? 'listingId' : 'pcListingId'
+  const context = { [contextKey]: listingId }
+  const trustService = new TrustService(tx)
 
-  if (action === 'created' || action === 'updated') {
-    await applyTrustAction({
+  const applyVoterTrust = (value: boolean) =>
+    trustService.logAction({
       userId,
-      action: currentValue ? TrustAction.UPVOTE : TrustAction.DOWNVOTE,
-      context: { listingId },
+      action: value ? TrustAction.UPVOTE : TrustAction.DOWNVOTE,
+      metadata: context,
     })
 
-    if (authorId && authorId !== userId) {
-      await applyTrustAction({
-        userId: authorId,
-        action: currentValue
-          ? TrustAction.LISTING_RECEIVED_UPVOTE
-          : TrustAction.LISTING_RECEIVED_DOWNVOTE,
-        context: { listingId, voterId: userId },
-      })
-    }
+  const reverseVoterTrust = (value: boolean) =>
+    trustService.reverseLogAction({
+      userId,
+      originalAction: value ? TrustAction.UPVOTE : TrustAction.DOWNVOTE,
+      metadata: context,
+    })
+
+  const applyAuthorTrust = (value: boolean) => {
+    if (!authorId || authorId === userId) return Promise.resolve()
+    return trustService.logAction({
+      userId: authorId,
+      action: value ? TrustAction.LISTING_RECEIVED_UPVOTE : TrustAction.LISTING_RECEIVED_DOWNVOTE,
+      metadata: { ...context, voterId: userId },
+    })
   }
 
-  if (action === 'deleted' && previousValue !== null) {
-    await reverseTrustAction({
-      userId,
-      originalAction: previousValue ? TrustAction.UPVOTE : TrustAction.DOWNVOTE,
-      context: { listingId },
+  const reverseAuthorTrust = (value: boolean) => {
+    if (!authorId || authorId === userId) return Promise.resolve()
+    return trustService.reverseLogAction({
+      userId: authorId,
+      originalAction: value
+        ? TrustAction.LISTING_RECEIVED_UPVOTE
+        : TrustAction.LISTING_RECEIVED_DOWNVOTE,
+      metadata: { ...context, voterId: userId },
     })
+  }
+
+  if (action === 'created') {
+    await applyVoterTrust(currentValue)
+    await applyAuthorTrust(currentValue)
+    return
+  }
+
+  if (action === 'updated' && previousValue !== null) {
+    await reverseVoterTrust(previousValue)
+    await reverseAuthorTrust(previousValue)
+    await applyVoterTrust(currentValue)
+    await applyAuthorTrust(currentValue)
+    return
+  }
+
+  if (action === 'deleted' && previousValue !== null) {
+    await reverseVoterTrust(previousValue)
+    await reverseAuthorTrust(previousValue)
+  }
+}
+
+type CommentVoteTrustAction = 'upvote' | 'downvote' | 'change' | 'remove'
+
+interface CommentVoteTrustParams {
+  tx: PrismaTransactionClient | Prisma.TransactionClient
+  trustAction: CommentVoteTrustAction
+  newValue: boolean
+  previousValue: boolean | null
+  commentAuthorId: string
+  voterId: string
+  commentId: string
+  parentEntityId: string
+  listingType: ListingType
+  updatedScore: number
+  scoreChange: number
+}
+
+const HELPFUL_THRESHOLD = 5
+
+/**
+ * Applies trust effects when a comment vote is created, changed, or removed.
+ * Works for both handheld and PC listing comments.
+ */
+export async function handleCommentVoteTrustEffects(params: CommentVoteTrustParams): Promise<void> {
+  const {
+    tx,
+    trustAction,
+    newValue,
+    previousValue,
+    commentAuthorId,
+    voterId,
+    commentId,
+    parentEntityId,
+    listingType,
+    updatedScore,
+    scoreChange,
+  } = params
+
+  if (commentAuthorId === voterId) return
+
+  const trustService = new TrustService(tx)
+  const parentKey = listingType === 'handheld' ? 'listingId' : 'pcListingId'
+  const baseMeta = { commentId, voterId, [parentKey]: parentEntityId }
 
-    if (authorId && authorId !== userId) {
-      await reverseTrustAction({
-        userId: authorId,
-        originalAction: previousValue
-          ? TrustAction.LISTING_RECEIVED_UPVOTE
-          : TrustAction.LISTING_RECEIVED_DOWNVOTE,
-        context: { listingId, voterId: userId },
+  if (trustAction === 'upvote') {
+    await trustService.logAction({
+      userId: commentAuthorId,
+      action: TrustAction.COMMENT_RECEIVED_UPVOTE,
+      targetUserId: voterId,
+      metadata: baseMeta,
+    })
+  } else if (trustAction === 'downvote') {
+    await trustService.logAction({
+      userId: commentAuthorId,
+      action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
+      targetUserId: voterId,
+      metadata: baseMeta,
+    })
+  } else if (trustAction === 'change') {
+    if (newValue) {
+      // Changed from downvote to upvote: reverse downvote, apply upvote
+      await trustService.reverseLogAction({
+        userId: commentAuthorId,
+        originalAction: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
+        targetUserId: voterId,
+        metadata: baseMeta,
+      })
+      await trustService.logAction({
+        userId: commentAuthorId,
+        action: TrustAction.COMMENT_RECEIVED_UPVOTE,
+        targetUserId: voterId,
+        metadata: baseMeta,
+      })
+    } else {
+      // Changed from upvote to downvote: reverse upvote, apply downvote
+      await trustService.reverseLogAction({
+        userId: commentAuthorId,
+        originalAction: TrustAction.COMMENT_RECEIVED_UPVOTE,
+        targetUserId: voterId,
+        metadata: baseMeta,
+      })
+      await trustService.logAction({
+        userId: commentAuthorId,
+        action: TrustAction.COMMENT_RECEIVED_DOWNVOTE,
+        targetUserId: voterId,
+        metadata: baseMeta,
       })
     }
+  } else if (trustAction === 'remove' && previousValue !== null) {
+    const originalAction = previousValue
+      ? TrustAction.COMMENT_RECEIVED_UPVOTE
+      : TrustAction.COMMENT_RECEIVED_DOWNVOTE
+    await trustService.reverseLogAction({
+      userId: commentAuthorId,
+      originalAction,
+      targetUserId: voterId,
+      metadata: baseMeta,
+    })
+  }
+
+  // Helpful comment bonus when crossing the threshold
+  const previousScore = updatedScore - scoreChange
+  if (previousScore < HELPFUL_THRESHOLD && updatedScore >= HELPFUL_THRESHOLD) {
+    await trustService.logAction({
+      userId: commentAuthorId,
+      action: TrustAction.HELPFUL_COMMENT,
+      metadata: {
+        commentId,
+        [parentKey]: parentEntityId,
+        score: updatedScore,
+        threshold: HELPFUL_THRESHOLD,
+      },
+    })
   }
 }
diff --git a/src/shared/emulator-config/gamenative/index.ts b/src/shared/emulator-config/gamenative/index.ts
new file mode 100644
index 000000000..b8ceae358
--- /dev/null
+++ b/src/shared/emulator-config/gamenative/index.ts
@@ -0,0 +1,13 @@
+import { parseGameNativeConfigFromJson } from './parser'
+import { registerEmulatorConfigMapper } from '../index'
+import type { EmulatorConfigMapper } from '../types'
+
+const gamenativeMapper: EmulatorConfigMapper = {
+  slug: 'gamenative',
+  fileTypes: ['json'],
+  parse: parseGameNativeConfigFromJson,
+}
+
+registerEmulatorConfigMapper(gamenativeMapper)
+
+export default gamenativeMapper
diff --git a/src/shared/emulator-config/gamenative/mapping.ts b/src/shared/emulator-config/gamenative/mapping.ts
new file mode 100644
index 000000000..9a9dfbf06
--- /dev/null
+++ b/src/shared/emulator-config/gamenative/mapping.ts
@@ -0,0 +1,207 @@
+import {
+  GRAPHICS_DRIVER_MAPPING,
+  DX_WRAPPER_MAPPING,
+  STARTUP_SELECTION_MAPPING,
+  BOX64_PRESET_MAPPING,
+  FEXCORE_PRESET_MAPPING,
+} from '@/server/utils/emulator-config/gamenative/gamenative.defaults'
+
+function createReverseLookup<T extends Record<string, string | number | boolean>>(
+  mapping: T,
+): Record<string, string> {
+  const reverse: Record<string, string> = {}
+  for (const [customValue, configValue] of Object.entries(mapping)) {
+    const key = String(configValue)
+    if (!(key in reverse)) {
+      reverse[key] = customValue
+    }
+  }
+  return reverse
+}
+
+const GRAPHICS_DRIVER_REVERSE = createReverseLookup(GRAPHICS_DRIVER_MAPPING)
+const DX_WRAPPER_REVERSE = createReverseLookup(DX_WRAPPER_MAPPING)
+const STARTUP_SELECTION_REVERSE = createReverseLookup(STARTUP_SELECTION_MAPPING)
+
+export interface GameNativeFieldMapping {
+  jsonPath: string | string[]
+  fromConfig?: (rawValue: unknown, fullConfig?: Record<string, unknown>) => unknown
+}
+
+/**
+ * Parse a value from a comma-separated key=value config string
+ */
+export function parseConfigString(configStr: string, key: string): string | undefined {
+  if (!configStr) return undefined
+  for (const part of configStr.split(',')) {
+    const eqIndex = part.indexOf('=')
+    if (eqIndex === -1) continue
+    const k = part.slice(0, eqIndex).trim()
+    const v = part.slice(eqIndex + 1).trim()
+    if (k === key) return v
+  }
+  return undefined
+}
+
+export const GAMENATIVE_IMPORT_MAPPINGS: Record<string, GameNativeFieldMapping> = {
+  resolution: {
+    jsonPath: 'screenSize',
+  },
+
+  env_variables: {
+    jsonPath: 'envVars',
+  },
+
+  graphics_driver: {
+    jsonPath: 'graphicsDriver',
+    fromConfig: (value) => GRAPHICS_DRIVER_REVERSE[String(value)] ?? String(value),
+  },
+
+  dx_wrapper: {
+    jsonPath: 'dxwrapper',
+    fromConfig: (value) => DX_WRAPPER_REVERSE[String(value)] ?? String(value),
+  },
+
+  dx_wrapper_config: {
+    jsonPath: 'dxwrapperConfig',
+  },
+
+  dxvk_version: {
+    jsonPath: ['dxwrapperConfig', 'dxvkVersion'],
+    fromConfig: (value, fullConfig) => {
+      const configStr = String(value || '')
+      const fromConfigStr = parseConfigString(configStr, 'version')
+      if (fromConfigStr) return fromConfigStr
+      // Fallback: check top-level dxvkVersion for old configs
+      if (fullConfig && typeof fullConfig['dxvkVersion'] === 'string') {
+        return fullConfig['dxvkVersion']
+      }
+      return configStr.includes('=') ? undefined : configStr || undefined
+    },
+  },
+
+  audio_driver: {
+    jsonPath: 'audioDriver',
+    fromConfig: (value) => {
+      const str = String(value)
+      if (str === 'pulseaudio' || str === 'pulse') return 'pulse'
+      if (str === 'alsa') return 'alsa'
+      return str
+    },
+  },
+
+  exec_arguments: {
+    jsonPath: 'execArgs',
+  },
+
+  startup_selection: {
+    jsonPath: 'startupSelection',
+    fromConfig: (value) => STARTUP_SELECTION_REVERSE[String(value)] ?? String(value),
+  },
+
+  box64_version: {
+    jsonPath: 'box64Version',
+  },
+
+  box64_preset: {
+    jsonPath: 'box64Preset',
+    fromConfig: (value) => {
+      const upper = String(value)
+      // Reverse: uppercase → lowercase for custom field
+      const reversed = createReverseLookup(BOX64_PRESET_MAPPING)
+      return reversed[upper] ?? upper.toLowerCase()
+    },
+  },
+
+  container_variant: {
+    jsonPath: 'containerVariant',
+  },
+
+  wine_version: {
+    jsonPath: 'wineVersion',
+  },
+
+  steam_type: {
+    jsonPath: 'steamType',
+    fromConfig: (value) => {
+      const str = String(value)
+      if (str === 'ultralight') return 'ultra_light'
+      return str
+    },
+  },
+
+  dynamic_driver_version: {
+    jsonPath: 'graphicsDriverVersion',
+  },
+
+  max_device_memory: {
+    jsonPath: ['graphicsDriverConfig', 'dxwrapperConfig'],
+    fromConfig: (value) => {
+      const configStr = String(value || '')
+      return parseConfigString(configStr, 'maxDeviceMemory')
+    },
+  },
+
+  use_adrenotools_turnip: {
+    jsonPath: 'graphicsDriverConfig',
+    fromConfig: (value) => {
+      const configStr = String(value || '')
+      return parseConfigString(configStr, 'adrenotoolsTurnip') === '1'
+    },
+  },
+
+  fex_core_version: {
+    jsonPath: 'fexcoreVersion',
+  },
+
+  '32_bit_emulator': {
+    jsonPath: 'emulator',
+    fromConfig: (value) => {
+      const str = String(value)
+      if (str === 'FEXCore') return 'fex'
+      if (str === 'Box64') return 'box'
+      return str
+    },
+  },
+
+  '64_bit_emulator': {
+    jsonPath: 'emulator',
+    fromConfig: (value) => {
+      const str = String(value)
+      if (str === 'FEXCore') return 'fex'
+      if (str === 'Box64') return 'box'
+      return str
+    },
+  },
+
+  fex_core_preset: {
+    jsonPath: 'fexcorePreset',
+    fromConfig: (value) => {
+      const upper = String(value)
+      const reversed = createReverseLookup(FEXCORE_PRESET_MAPPING)
+      return reversed[upper] ?? upper.toLowerCase()
+    },
+  },
+
+  use_steam_input: {
+    jsonPath: 'useSteamInput',
+  },
+
+  enable_x_input_api: {
+    jsonPath: 'enableXInput',
+  },
+
+  enable_direct_input_api: {
+    jsonPath: 'enableDInput',
+  },
+
+  direct_input_mapper_type: {
+    jsonPath: 'dinputMapperType',
+    fromConfig: (value) => {
+      const num = Number(value)
+      if (num === 1) return 'standard'
+      if (num === 2) return 'xinput_mapper'
+      return String(value)
+    },
+  },
+}
diff --git a/src/shared/emulator-config/gamenative/parser.test.ts b/src/shared/emulator-config/gamenative/parser.test.ts
new file mode 100644
index 000000000..cff4a433c
--- /dev/null
+++ b/src/shared/emulator-config/gamenative/parser.test.ts
@@ -0,0 +1,627 @@
+import { describe, expect, it } from 'vitest'
+import { CustomFieldType } from '@orm'
+import { parseGameNativeConfigFromJson } from './parser'
+import type { CustomFieldImportDefinition } from '../types'
+
+const baseFields: CustomFieldImportDefinition[] = [
+  {
+    id: 'resolution',
+    name: 'resolution',
+    label: 'Resolution',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: '854x480', label: '854x480' },
+      { value: '1280x720', label: '1280x720' },
+      { value: '1920x1080', label: '1920x1080' },
+    ],
+  },
+  {
+    id: 'graphics_driver',
+    name: 'graphics_driver',
+    label: 'Graphics Driver',
+    type: CustomFieldType.SELECT,
+    isRequired: true,
+    options: [
+      { value: 'VirGL (Universal)', label: 'VirGL (Universal)' },
+      { value: 'Turnip (Adreno)', label: 'Turnip (Adreno)' },
+      { value: 'Vortek (Universal)', label: 'Vortek (Universal)' },
+    ],
+  },
+  {
+    id: 'dx_wrapper',
+    name: 'dx_wrapper',
+    label: 'DX Wrapper',
+    type: CustomFieldType.SELECT,
+    isRequired: true,
+    options: [
+      { value: 'WineD3D', label: 'WineD3D' },
+      { value: 'DXVK', label: 'DXVK' },
+      { value: 'VKD3D', label: 'VKD3D' },
+    ],
+  },
+  {
+    id: 'dxvk_version',
+    name: 'dxvk_version',
+    label: 'DXVK Version',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: '2.6.1-gplasync', label: '2.6.1-gplasync' },
+      { value: 'async-1.10.3', label: 'async-1.10.3' },
+    ],
+  },
+  {
+    id: 'audio_driver',
+    name: 'audio_driver',
+    label: 'Audio Driver',
+    type: CustomFieldType.SELECT,
+    isRequired: true,
+    options: [
+      { value: 'alsa', label: 'ALSA' },
+      { value: 'pulse', label: 'PulseAudio' },
+    ],
+  },
+  {
+    id: 'startup_selection',
+    name: 'startup_selection',
+    label: 'Startup Selection',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: 'Normal (Load all services)', label: 'Normal (Load all services)' },
+      { value: 'Essential (Load only essential services)', label: 'Essential' },
+      { value: 'Aggressive (Stop services on startup)', label: 'Aggressive' },
+    ],
+  },
+  {
+    id: 'box64_version',
+    name: 'box64_version',
+    label: 'Box64 Version',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: '0.3.6', label: '0.3.6' },
+      { value: '0.3.8', label: '0.3.8' },
+    ],
+  },
+  {
+    id: 'box64_preset',
+    name: 'box64_preset',
+    label: 'Box64 Preset',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: 'stability', label: 'Stability' },
+      { value: 'compatibility', label: 'Compatibility' },
+      { value: 'intermediate', label: 'Intermediate' },
+      { value: 'performance', label: 'Performance' },
+    ],
+  },
+  {
+    id: 'env_variables',
+    name: 'env_variables',
+    label: 'Environment Variables',
+    type: CustomFieldType.TEXTAREA,
+    isRequired: false,
+  },
+  {
+    id: 'exec_arguments',
+    name: 'exec_arguments',
+    label: 'Execution Arguments',
+    type: CustomFieldType.TEXT,
+    isRequired: false,
+  },
+  {
+    id: 'container_variant',
+    name: 'container_variant',
+    label: 'Container Variant',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: 'glibc', label: 'Glibc' },
+      { value: 'bionic', label: 'Bionic' },
+    ],
+  },
+  {
+    id: 'wine_version',
+    name: 'wine_version',
+    label: 'Wine Version',
+    type: CustomFieldType.TEXT,
+    isRequired: false,
+  },
+  {
+    id: 'steam_type',
+    name: 'steam_type',
+    label: 'Steam Type',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: 'normal', label: 'Normal' },
+      { value: 'light', label: 'Light' },
+      { value: 'ultra_light', label: 'Ultra Light' },
+    ],
+  },
+  {
+    id: 'dynamic_driver_version',
+    name: 'dynamic_driver_version',
+    label: 'Graphics Driver Version',
+    type: CustomFieldType.TEXT,
+    isRequired: false,
+  },
+  {
+    id: 'fex_core_version',
+    name: 'fex_core_version',
+    label: 'FEXCore Version',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [{ value: '2603', label: '2603' }],
+  },
+  {
+    id: '32_bit_emulator',
+    name: '32_bit_emulator',
+    label: '32-bit Emulator',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: 'fex', label: 'FEXCore' },
+      { value: 'box', label: 'Box64' },
+    ],
+  },
+  {
+    id: '64_bit_emulator',
+    name: '64_bit_emulator',
+    label: '64-bit Emulator',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: 'fex', label: 'FEXCore' },
+      { value: 'box', label: 'Box64' },
+    ],
+  },
+  {
+    id: 'fex_core_preset',
+    name: 'fex_core_preset',
+    label: 'FEXCore Preset',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: 'stability', label: 'Stability' },
+      { value: 'intermediate', label: 'Intermediate' },
+      { value: 'performance', label: 'Performance' },
+    ],
+  },
+  {
+    id: 'use_steam_input',
+    name: 'use_steam_input',
+    label: 'Use Steam Input',
+    type: CustomFieldType.BOOLEAN,
+    isRequired: false,
+  },
+  {
+    id: 'enable_x_input_api',
+    name: 'enable_x_input_api',
+    label: 'Enable XInput API',
+    type: CustomFieldType.BOOLEAN,
+    isRequired: false,
+  },
+  {
+    id: 'enable_direct_input_api',
+    name: 'enable_direct_input_api',
+    label: 'Enable DirectInput API',
+    type: CustomFieldType.BOOLEAN,
+    isRequired: false,
+  },
+  {
+    id: 'direct_input_mapper_type',
+    name: 'direct_input_mapper_type',
+    label: 'DirectInput Mapper Type',
+    type: CustomFieldType.SELECT,
+    isRequired: false,
+    options: [
+      { value: 'standard', label: 'Standard' },
+      { value: 'xinput_mapper', label: 'XInput Mapper' },
+    ],
+  },
+  {
+    id: 'use_adrenotools_turnip',
+    name: 'use_adrenotools_turnip',
+    label: 'Use Adrenotools Turnip',
+    type: CustomFieldType.BOOLEAN,
+    isRequired: false,
+  },
+  {
+    id: 'max_device_memory',
+    name: 'max_device_memory',
+    label: 'Max Device Memory',
+    type: CustomFieldType.TEXT,
+    isRequired: false,
+  },
+]
+
+const SAMPLE_CONFIG = {
+  screenSize: '1920x1080',
+  graphicsDriver: 'turnip',
+  graphicsDriverVersion: '25.2.0',
+  graphicsDriverConfig: 'adrenotoolsTurnip=1,maxDeviceMemory=4096',
+  dxwrapper: 'dxvk',
+  dxwrapperConfig: 'version=async-1.10.3,maxDeviceMemory=0',
+  audioDriver: 'pulseaudio',
+  startupSelection: 1,
+  box64Version: '0.3.6',
+  box64Preset: 'COMPATIBILITY',
+  envVars: 'ZINK_DESCRIPTORS=lazy MESA_SHADER_CACHE_DISABLE=false',
+  execArgs: '-windowed',
+  containerVariant: 'glibc',
+  wineVersion: 'wine-9.2-x86_64',
+  steamType: 'ultralight',
+  emulator: 'FEXCore',
+  fexcoreVersion: '2603',
+  fexcorePreset: 'INTERMEDIATE',
+  useSteamInput: false,
+  enableXInput: true,
+  enableDInput: true,
+  dinputMapperType: 1,
+}
+
+describe('parseGameNativeConfigFromJson', () => {
+  it('parses a full config and maps all known fields', () => {
+    const raw = JSON.stringify(SAMPLE_CONFIG)
+    const result = parseGameNativeConfigFromJson(raw, baseFields)
+
+    expect(result.warnings).toHaveLength(0)
+
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+
+    expect(valueMap.get('resolution')).toBe('1920x1080')
+    expect(valueMap.get('graphics_driver')).toBe('Turnip (Adreno)')
+    expect(valueMap.get('dx_wrapper')).toBe('DXVK')
+    expect(valueMap.get('dxvk_version')).toBe('async-1.10.3')
+    expect(valueMap.get('audio_driver')).toBe('pulse')
+    expect(valueMap.get('startup_selection')).toBe('Essential (Load only essential services)')
+    expect(valueMap.get('box64_version')).toBe('0.3.6')
+    expect(valueMap.get('box64_preset')).toBe('compatibility')
+    expect(valueMap.get('env_variables')).toBe(
+      'ZINK_DESCRIPTORS=lazy MESA_SHADER_CACHE_DISABLE=false',
+    )
+    expect(valueMap.get('exec_arguments')).toBe('-windowed')
+    expect(valueMap.get('container_variant')).toBe('glibc')
+    expect(valueMap.get('wine_version')).toBe('wine-9.2-x86_64')
+    expect(valueMap.get('steam_type')).toBe('ultra_light')
+    expect(valueMap.get('dynamic_driver_version')).toBe('25.2.0')
+    expect(valueMap.get('fex_core_version')).toBe('2603')
+    expect(valueMap.get('32_bit_emulator')).toBe('fex')
+    expect(valueMap.get('64_bit_emulator')).toBe('fex')
+    expect(valueMap.get('fex_core_preset')).toBe('intermediate')
+    expect(valueMap.get('use_steam_input')).toBe(false)
+    expect(valueMap.get('enable_x_input_api')).toBe(true)
+    expect(valueMap.get('enable_direct_input_api')).toBe(true)
+    expect(valueMap.get('direct_input_mapper_type')).toBe('standard')
+    expect(valueMap.get('use_adrenotools_turnip')).toBe(true)
+    expect(valueMap.get('max_device_memory')).toBe('4096')
+  })
+
+  it('extracts dxvk_version from dxwrapperConfig string', () => {
+    const raw = JSON.stringify({ dxwrapperConfig: 'version=2.6.1-gplasync,async=1' })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'dxvk_version',
+        name: 'dxvk_version',
+        label: 'DXVK Version',
+        type: CustomFieldType.SELECT,
+        isRequired: false,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+    expect(valueMap.get('dxvk_version')).toBe('2.6.1-gplasync')
+  })
+
+  it('falls back to top-level dxvkVersion for old configs', () => {
+    const raw = JSON.stringify({ dxvkVersion: '1.10.3', dxwrapperConfig: 'async=1' })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'dxvk_version',
+        name: 'dxvk_version',
+        label: 'DXVK Version',
+        type: CustomFieldType.SELECT,
+        isRequired: false,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+    expect(valueMap.get('dxvk_version')).toBe('1.10.3')
+  })
+
+  it('handles audio driver backward compatibility with "pulse"', () => {
+    const raw = JSON.stringify({ audioDriver: 'pulse' })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'audio_driver',
+        name: 'audio_driver',
+        label: 'Audio Driver',
+        type: CustomFieldType.SELECT,
+        isRequired: false,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+    expect(valueMap.get('audio_driver')).toBe('pulse')
+  })
+
+  it('handles minimal config with missing optional fields', () => {
+    const minimalConfig = {
+      graphicsDriver: 'vortek',
+      dxwrapper: 'wined3d',
+      audioDriver: 'alsa',
+    }
+    const raw = JSON.stringify(minimalConfig)
+    const result = parseGameNativeConfigFromJson(raw, baseFields)
+
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+
+    expect(valueMap.get('graphics_driver')).toBe('Vortek (Universal)')
+    expect(valueMap.get('dx_wrapper')).toBe('WineD3D')
+    expect(valueMap.get('audio_driver')).toBe('alsa')
+    expect(valueMap.has('resolution')).toBe(false)
+  })
+
+  it('returns warning for invalid JSON', () => {
+    const result = parseGameNativeConfigFromJson('not valid json{', baseFields)
+
+    expect(result.values).toHaveLength(0)
+    expect(result.warnings).toContain('Failed to parse JSON configuration file.')
+  })
+
+  it('marks required fields as missing when not in JSON and no default', () => {
+    const raw = JSON.stringify({})
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'graphics_driver',
+        name: 'graphics_driver',
+        label: 'Graphics Driver',
+        type: CustomFieldType.SELECT,
+        isRequired: true,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+
+    expect(result.missing).toContain('Graphics Driver')
+  })
+
+  it('uses defaultValue for unmapped fields', () => {
+    const raw = JSON.stringify(SAMPLE_CONFIG)
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'unknown_field',
+        name: 'unknown_field',
+        label: 'Unknown Field',
+        type: CustomFieldType.TEXT,
+        isRequired: false,
+        defaultValue: 'fallback',
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+
+    expect(valueMap.get('unknown_field')).toBe('fallback')
+  })
+
+  it('uses defaultValue when mapped JSON key is absent', () => {
+    const raw = JSON.stringify({})
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'resolution',
+        name: 'resolution',
+        label: 'Resolution',
+        type: CustomFieldType.SELECT,
+        isRequired: false,
+        defaultValue: '854x480',
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+
+    expect(valueMap.get('resolution')).toBe('854x480')
+  })
+
+  it('reverse-maps startup_selection numeric values', () => {
+    const configs = [
+      { startupSelection: 0, expected: 'Normal (Load all services)' },
+      { startupSelection: 1, expected: 'Essential (Load only essential services)' },
+      { startupSelection: 2, expected: 'Aggressive (Stop services on startup)' },
+    ]
+
+    for (const { startupSelection, expected } of configs) {
+      const raw = JSON.stringify({ startupSelection })
+      const fields: CustomFieldImportDefinition[] = [
+        {
+          id: 'startup_selection',
+          name: 'startup_selection',
+          label: 'Startup Selection',
+          type: CustomFieldType.SELECT,
+          isRequired: false,
+        },
+      ]
+      const result = parseGameNativeConfigFromJson(raw, fields)
+      const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+      expect(valueMap.get('startup_selection')).toBe(expected)
+    }
+  })
+
+  it('reverse-maps box64 presets from uppercase to lowercase', () => {
+    const presets = [
+      { preset: 'STABILITY', expected: 'stability' },
+      { preset: 'COMPATIBILITY', expected: 'compatibility' },
+      { preset: 'INTERMEDIATE', expected: 'intermediate' },
+      { preset: 'PERFORMANCE', expected: 'performance' },
+    ]
+
+    for (const { preset, expected } of presets) {
+      const raw = JSON.stringify({ box64Preset: preset })
+      const fields: CustomFieldImportDefinition[] = [
+        {
+          id: 'box64_preset',
+          name: 'box64_preset',
+          label: 'Box64 Preset',
+          type: CustomFieldType.SELECT,
+          isRequired: false,
+        },
+      ]
+      const result = parseGameNativeConfigFromJson(raw, fields)
+      const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+      expect(valueMap.get('box64_preset')).toBe(expected)
+    }
+  })
+
+  it('handles all graphics driver reverse mappings', () => {
+    const drivers = [
+      { config: 'virgl', expected: 'VirGL (Universal)' },
+      { config: 'turnip', expected: 'Turnip (Adreno)' },
+      { config: 'vortek', expected: 'Vortek (Universal)' },
+    ]
+
+    for (const { config, expected } of drivers) {
+      const raw = JSON.stringify({ graphicsDriver: config })
+      const fields: CustomFieldImportDefinition[] = [
+        {
+          id: 'graphics_driver',
+          name: 'graphics_driver',
+          label: 'Graphics Driver',
+          type: CustomFieldType.SELECT,
+          isRequired: true,
+        },
+      ]
+      const result = parseGameNativeConfigFromJson(raw, fields)
+      const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+      expect(valueMap.get('graphics_driver')).toBe(expected)
+    }
+  })
+
+  it('passes through unknown graphics driver values', () => {
+    const raw = JSON.stringify({ graphicsDriver: 'future_driver' })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'graphics_driver',
+        name: 'graphics_driver',
+        label: 'Graphics Driver',
+        type: CustomFieldType.SELECT,
+        isRequired: true,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+    expect(valueMap.get('graphics_driver')).toBe('future_driver')
+  })
+
+  it('reverse-maps dinputMapperType numbers to strings', () => {
+    const raw = JSON.stringify({ dinputMapperType: 2 })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'direct_input_mapper_type',
+        name: 'direct_input_mapper_type',
+        label: 'DirectInput Mapper Type',
+        type: CustomFieldType.SELECT,
+        isRequired: false,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+    expect(valueMap.get('direct_input_mapper_type')).toBe('xinput_mapper')
+  })
+
+  it('reverse-maps emulator field to fex/box values', () => {
+    const raw = JSON.stringify({ emulator: 'Box64' })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: '64_bit_emulator',
+        name: '64_bit_emulator',
+        label: '64-bit Emulator',
+        type: CustomFieldType.SELECT,
+        isRequired: false,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+    expect(valueMap.get('64_bit_emulator')).toBe('box')
+  })
+
+  it('detects use_adrenotools_turnip from graphicsDriverConfig', () => {
+    const configEnabled = JSON.stringify({
+      graphicsDriverConfig: 'adrenotoolsTurnip=1,vulkanVersion=1.3',
+    })
+    const configDisabled = JSON.stringify({
+      graphicsDriverConfig: 'adrenotoolsTurnip=0,vulkanVersion=1.3',
+    })
+    const configMissing = JSON.stringify({ graphicsDriverConfig: 'vulkanVersion=1.3' })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'use_adrenotools_turnip',
+        name: 'use_adrenotools_turnip',
+        label: 'Use Adrenotools Turnip',
+        type: CustomFieldType.BOOLEAN,
+        isRequired: false,
+      },
+    ]
+
+    const resultEnabled = parseGameNativeConfigFromJson(configEnabled, fields)
+    const mapEnabled = new Map(resultEnabled.values.map((v) => [v.id, v.value]))
+    expect(mapEnabled.get('use_adrenotools_turnip')).toBe(true)
+
+    const resultDisabled = parseGameNativeConfigFromJson(configDisabled, fields)
+    const mapDisabled = new Map(resultDisabled.values.map((v) => [v.id, v.value]))
+    expect(mapDisabled.get('use_adrenotools_turnip')).toBe(false)
+
+    const resultMissing = parseGameNativeConfigFromJson(configMissing, fields)
+    const mapMissing = new Map(resultMissing.values.map((v) => [v.id, v.value]))
+    expect(mapMissing.get('use_adrenotools_turnip')).toBe(false)
+  })
+
+  it('parses max_device_memory from graphicsDriverConfig', () => {
+    const raw = JSON.stringify({ graphicsDriverConfig: 'vulkanVersion=1.3,maxDeviceMemory=8192' })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'max_device_memory',
+        name: 'max_device_memory',
+        label: 'Max Device Memory',
+        type: CustomFieldType.TEXT,
+        isRequired: false,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+    expect(valueMap.get('max_device_memory')).toBe('8192')
+  })
+
+  it('reverse-maps FEXCore presets from uppercase to lowercase', () => {
+    const raw = JSON.stringify({ fexcorePreset: 'PERFORMANCE' })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'fex_core_preset',
+        name: 'fex_core_preset',
+        label: 'FEXCore Preset',
+        type: CustomFieldType.SELECT,
+        isRequired: false,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+    expect(valueMap.get('fex_core_preset')).toBe('performance')
+  })
+
+  it('reverse-maps steam_type ultralight to ultra_light', () => {
+    const raw = JSON.stringify({ steamType: 'ultralight' })
+    const fields: CustomFieldImportDefinition[] = [
+      {
+        id: 'steam_type',
+        name: 'steam_type',
+        label: 'Steam Type',
+        type: CustomFieldType.SELECT,
+        isRequired: false,
+      },
+    ]
+    const result = parseGameNativeConfigFromJson(raw, fields)
+    const valueMap = new Map(result.values.map((v) => [v.id, v.value]))
+    expect(valueMap.get('steam_type')).toBe('ultra_light')
+  })
+})
diff --git a/src/shared/emulator-config/gamenative/parser.ts b/src/shared/emulator-config/gamenative/parser.ts
new file mode 100644
index 000000000..93ad3a381
--- /dev/null
+++ b/src/shared/emulator-config/gamenative/parser.ts
@@ -0,0 +1,74 @@
+import { GAMENATIVE_IMPORT_MAPPINGS } from './mapping'
+import type { CustomFieldImportDefinition, EmulatorConfigImportResult } from '../types'
+
+function getNestedValue(obj: Record<string, unknown>, path: string): unknown {
+  const keys = path.split('.')
+  let current: unknown = obj
+  for (const key of keys) {
+    if (current === null || current === undefined || typeof current !== 'object') {
+      return undefined
+    }
+    current = (current as Record<string, unknown>)[key]
+  }
+  return current
+}
+
+export function parseGameNativeConfigFromJson(
+  raw: string,
+  customFields: CustomFieldImportDefinition[],
+): EmulatorConfigImportResult {
+  const values: { id: string; value: unknown }[] = []
+  const missing: string[] = []
+  const warnings: string[] = []
+
+  let config: Record<string, unknown>
+  try {
+    config = JSON.parse(raw) as Record<string, unknown>
+  } catch {
+    warnings.push('Failed to parse JSON configuration file.')
+    return { values, missing, warnings }
+  }
+
+  if (typeof config !== 'object' || config === null) {
+    warnings.push('Configuration file does not contain a valid JSON object.')
+    return { values, missing, warnings }
+  }
+
+  for (const field of customFields) {
+    const mapping = GAMENATIVE_IMPORT_MAPPINGS[field.name]
+    if (!mapping) {
+      if (field.defaultValue !== undefined && field.defaultValue !== null) {
+        values.push({ id: field.id, value: field.defaultValue })
+      } else if (field.isRequired) {
+        missing.push(field.label)
+      }
+      continue
+    }
+
+    const paths = Array.isArray(mapping.jsonPath) ? mapping.jsonPath : [mapping.jsonPath]
+    let rawValue: unknown
+    let found = false
+
+    for (const path of paths) {
+      rawValue = getNestedValue(config, path)
+      if (rawValue !== undefined) {
+        found = true
+        break
+      }
+    }
+
+    if (!found) {
+      if (field.defaultValue !== undefined && field.defaultValue !== null) {
+        values.push({ id: field.id, value: field.defaultValue })
+      } else if (field.isRequired) {
+        missing.push(field.label)
+      }
+      continue
+    }
+
+    const value = mapping.fromConfig ? mapping.fromConfig(rawValue, config) : rawValue
+    values.push({ id: field.id, value })
+  }
+
+  return { values, missing, warnings }
+}
diff --git a/src/test/setup.ts b/src/test/setup.ts
index 2cf42d0f6..63409e145 100644
--- a/src/test/setup.ts
+++ b/src/test/setup.ts
@@ -17,13 +17,23 @@ vi.mock('@orm/sql', () => ({
 
 // Mock the entire Prisma client module to prevent database initialization
 vi.mock('@orm', () => ({
-  // Prisma namespace for raw SQL tagged templates
+  // Prisma namespace — includes raw SQL tagged templates and the
+  // runtime-enum-like objects that router modules reference at module top
+  // (e.g. `const mode = Prisma.QueryMode.insensitive`).
   Prisma: {
     sql: (strings: TemplateStringsArray, ...values: unknown[]) => ({
       strings: Array.from(strings),
       values,
       text: strings.join('?'),
     }),
+    QueryMode: {
+      default: 'default',
+      insensitive: 'insensitive',
+    },
+    SortOrder: {
+      asc: 'asc',
+      desc: 'desc',
+    },
   },
   PrismaClient: vi.fn().mockImplementation(() => ({
     $transaction: vi.fn(),
diff --git a/src/utils/user-bans.test.ts b/src/utils/user-bans.test.ts
new file mode 100644
index 000000000..3f4e2da58
--- /dev/null
+++ b/src/utils/user-bans.test.ts
@@ -0,0 +1,40 @@
+import { describe, expect, it } from 'vitest'
+import { hasActiveBans } from './user-bans'
+
+describe('hasActiveBans', () => {
+  it('returns false for null', () => {
+    expect(hasActiveBans(null)).toBe(false)
+  })
+
+  it('returns false for undefined', () => {
+    expect(hasActiveBans(undefined)).toBe(false)
+  })
+
+  it('returns false for a primitive', () => {
+    expect(hasActiveBans('user-id')).toBe(false)
+    expect(hasActiveBans(42)).toBe(false)
+  })
+
+  it('returns false when `userBans` is not a property', () => {
+    expect(hasActiveBans({})).toBe(false)
+    expect(hasActiveBans({ id: 'x', name: 'y' })).toBe(false)
+  })
+
+  it('returns false when `userBans` is null', () => {
+    expect(hasActiveBans({ userBans: null })).toBe(false)
+  })
+
+  it('returns false when `userBans` is not an array', () => {
+    expect(hasActiveBans({ userBans: 'not-an-array' })).toBe(false)
+    expect(hasActiveBans({ userBans: { id: 'x' } })).toBe(false)
+  })
+
+  it('returns false when `userBans` is an empty array', () => {
+    expect(hasActiveBans({ userBans: [] })).toBe(false)
+  })
+
+  it('returns true when `userBans` contains at least one entry', () => {
+    expect(hasActiveBans({ userBans: [{ id: 'ban-1' }] })).toBe(true)
+    expect(hasActiveBans({ userBans: [{ id: 'a' }, { id: 'b' }] })).toBe(true)
+  })
+})
diff --git a/src/utils/user-bans.ts b/src/utils/user-bans.ts
new file mode 100644
index 000000000..0788f05d1
--- /dev/null
+++ b/src/utils/user-bans.ts
@@ -0,0 +1,10 @@
+interface AuthorWithPossibleBans {
+  userBans?: { id: string }[] | null
+}
+
+export function hasActiveBans(author: unknown): author is AuthorWithPossibleBans {
+  if (!author || typeof author !== 'object') return false
+  if (!('userBans' in author)) return false
+  const bans = (author as AuthorWithPossibleBans).userBans
+  return Array.isArray(bans) && bans.length > 0
+}
diff --git a/tests/accessibility.spec.ts b/tests/accessibility.spec.ts
index d40b63e3e..06411553f 100644
--- a/tests/accessibility.spec.ts
+++ b/tests/accessibility.spec.ts
@@ -8,24 +8,16 @@ test.describe('Accessibility Tests', () => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Check for h1
-    const h1Elements = page.locator('h1')
-    const h1Count = await h1Elements.count()
+    const h1Count = await page.locator('h1').count()
     expect(h1Count).toBeGreaterThanOrEqual(1)
-    expect(h1Count).toBeLessThanOrEqual(1) // Should only have one h1
+    expect(h1Count).toBeLessThanOrEqual(1)
 
-    // Check heading hierarchy
-    const headings = page.locator('h1, h2, h3, h4, h5, h6')
-    const headingLevels = await headings.evaluateAll((elements) =>
-      elements.map((el) => parseInt(el.tagName.substring(1))),
-    )
+    const headingLevels = await page
+      .locator('h1, h2, h3, h4, h5, h6')
+      .evaluateAll((elements) => elements.map((el) => parseInt(el.tagName.substring(1))))
 
-    // Verify no skipped heading levels
     for (let i = 1; i < headingLevels.length; i++) {
-      const diff = headingLevels[i] - headingLevels[i - 1]
-      // Heading levels should not skip more than 1 level
-      // TODO: Fix heading hierarchy in the app
-      expect(diff).toBeLessThanOrEqual(2)
+      expect(headingLevels[i] - headingLevels[i - 1]).toBeLessThanOrEqual(2)
     }
   })
 
@@ -33,57 +25,46 @@ test.describe('Accessibility Tests', () => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Check buttons have accessible names
-    const buttons = page.locator('button')
-    const buttonCount = await buttons.count()
-
-    for (let i = 0; i < Math.min(buttonCount, 5); i++) {
-      // Check first 5 buttons
-      const button = buttons.nth(i)
-      const accessibleName =
-        (await button.getAttribute('aria-label')) ||
-        (await button.textContent()) ||
-        (await button.getAttribute('title'))
+    const buttonData = await page.locator('button').evaluateAll((elements) =>
+      elements.slice(0, 5).map((el) => ({
+        ariaLabel: el.getAttribute('aria-label'),
+        text: el.textContent,
+        title: el.getAttribute('title'),
+      })),
+    )
 
-      expect(accessibleName).toBeTruthy()
-      expect(accessibleName!.trim().length).toBeGreaterThan(0)
+    for (const btn of buttonData) {
+      const name = btn.ariaLabel || btn.text || btn.title
+      expect(name).toBeTruthy()
+      expect(name!.trim().length).toBeGreaterThan(0)
     }
 
-    // Check links have accessible text
-    const links = page.locator('a')
-    const linkCount = await links.count()
-
-    for (let i = 0; i < Math.min(linkCount, 5); i++) {
-      const link = links.nth(i)
-      const linkText = (await link.textContent()) || (await link.getAttribute('aria-label'))
+    const linkData = await page.locator('a').evaluateAll((elements) =>
+      elements.slice(0, 5).map((el) => ({
+        text: el.textContent,
+        ariaLabel: el.getAttribute('aria-label'),
+      })),
+    )
 
-      // Should not have generic link text
-      expect(linkText?.toLowerCase()).not.toMatch(/^(click here|here|link)$/)
+    for (const link of linkData) {
+      const text = link.text || link.ariaLabel
+      expect(text?.toLowerCase()).not.toMatch(/^(click here|here|link)$/)
     }
   })
 
   test('should have alt text for all images', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
-
-    // Wait for content to load
     await listingsPage.verifyPageLoaded()
 
-    // Check all images
-    const images = page.locator('img')
-    const imageCount = await images.count()
-
-    for (let i = 0; i < imageCount; i++) {
-      const img = images.nth(i)
-      const altText = await img.getAttribute('alt')
+    const altTexts = await page
+      .locator('img')
+      .evaluateAll((elements) => elements.map((el) => el.getAttribute('alt')))
 
-      // Every image should have alt text
+    for (const altText of altTexts) {
       expect(altText).toBeDefined()
-
-      // Decorative images should have empty alt=""
-      // Content images should have descriptive alt
       if (altText !== '') {
-        expect(altText!.length).toBeGreaterThan(3) // Not just "img" or "pic"
+        expect(altText!.length).toBeGreaterThan(3)
       }
     }
   })
@@ -92,28 +73,18 @@ test.describe('Accessibility Tests', () => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Tab through interactive elements
-    const tabSequence = []
-
+    const tabData = []
     for (let i = 0; i < 10; i++) {
       await page.keyboard.press('Tab')
-
-      const focusedElement = page.locator(':focus')
-      const tagName = await focusedElement.evaluate((el) => el.tagName.toLowerCase())
-      const text = await focusedElement.textContent().catch(() => '')
-
-      tabSequence.push({ tagName, text })
-
-      // Focused element should be visible
-      await expect(focusedElement).toBeVisible()
+      const [tagName, text] = await page.evaluate(() => {
+        const focused = document.activeElement
+        return [focused?.tagName.toLowerCase() ?? '', focused?.textContent ?? '']
+      })
+      tabData.push({ tagName, text })
     }
 
-    // Should have focused on various interactive elements
     const interactiveTags = ['a', 'button', 'input', 'select', 'textarea']
-    const hasInteractiveElements = tabSequence.some((item) =>
-      interactiveTags.includes(item.tagName),
-    )
-
+    const hasInteractiveElements = tabData.some((item) => interactiveTags.includes(item.tagName))
     expect(hasInteractiveElements).toBe(true)
   })
 
@@ -121,41 +92,30 @@ test.describe('Accessibility Tests', () => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Check text elements for potential contrast issues
-    const textElements = page
+    const styles = await page
       .locator('p, span, div, h1, h2, h3, h4, h5, h6')
       .filter({ hasText: /\S+/ })
-    const sampleSize = Math.min(await textElements.count(), 10)
-
-    for (let i = 0; i < sampleSize; i++) {
-      const element = textElements.nth(i)
-
-      const styles = await element.evaluate((el) => {
-        const computed = window.getComputedStyle(el)
-        return {
-          color: computed.color,
-          backgroundColor: computed.backgroundColor,
-          fontSize: computed.fontSize,
-          fontWeight: computed.fontWeight,
-        }
-      })
-
-      // Skip transparent elements as they inherit parent background
-      if (
-        styles.backgroundColor === 'rgba(0, 0, 0, 0)' ||
-        styles.backgroundColor === 'transparent'
-      ) {
+      .evaluateAll((elements) =>
+        elements.slice(0, 10).map((el) => {
+          const computed = window.getComputedStyle(el)
+          return {
+            color: computed.color,
+            backgroundColor: computed.backgroundColor,
+            fontSize: computed.fontSize,
+          }
+        }),
+      )
+
+    for (const s of styles) {
+      if (s.backgroundColor === 'rgba(0, 0, 0, 0)' || s.backgroundColor === 'transparent') {
         continue
       }
 
-      // Basic check: text should not be same color as background
-      if (styles.color !== 'rgba(0, 0, 0, 0)' && styles.color !== 'transparent') {
-        expect(styles.color).not.toBe(styles.backgroundColor)
+      if (s.color !== 'rgba(0, 0, 0, 0)' && s.color !== 'transparent') {
+        expect(s.color).not.toBe(s.backgroundColor)
       }
 
-      // Text should be readable size
-      const fontSize = parseInt(styles.fontSize)
-      expect(fontSize).toBeGreaterThanOrEqual(12)
+      expect(parseInt(s.fontSize)).toBeGreaterThanOrEqual(12)
     }
   })
 
@@ -163,11 +123,9 @@ test.describe('Accessibility Tests', () => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Focus on first link
     const firstLink = page.locator('a').first()
     await firstLink.focus()
 
-    // Check if focus is visible
     const focusStyles = await firstLink.evaluate((el) => {
       const computed = window.getComputedStyle(el)
       return {
@@ -179,7 +137,6 @@ test.describe('Accessibility Tests', () => {
       }
     })
 
-    // Should have some visual focus indicator
     const hasFocusIndicator =
       (focusStyles.outline && focusStyles.outline !== 'none') ||
       (focusStyles.outlineWidth && focusStyles.outlineWidth !== '0px') ||
@@ -193,31 +150,24 @@ test.describe('Accessibility Tests', () => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    // Check all form inputs
-    const inputs = page.locator('input, select, textarea')
-    const inputCount = await inputs.count()
-
-    for (let i = 0; i < inputCount; i++) {
-      const input = inputs.nth(i)
-      const inputId = await input.getAttribute('id')
-      const ariaLabel = await input.getAttribute('aria-label')
-      const ariaLabelledBy = await input.getAttribute('aria-labelledby')
-      const placeholder = await input.getAttribute('placeholder')
-
-      // Check for associated label
-      let hasLabel = false
-
-      if (inputId) {
-        const label = page.locator(`label[for="${inputId}"]`)
-        hasLabel = (await label.count()) > 0
-      }
-
-      // Input should have some form of label
-      const hasAccessibleName = hasLabel || ariaLabel || ariaLabelledBy
-
-      // Placeholder alone is not sufficient (except for search inputs which are commonly understood)
-      // TODO: Add proper labels to form inputs
-      expect(hasAccessibleName || !!placeholder).toBe(true)
+    const inputAccessibility = await page
+      .locator('input, select, textarea')
+      .evaluateAll((elements) =>
+        elements.map((el) => {
+          const id = el.getAttribute('id')
+          const hasLabel = id ? !!document.querySelector(`label[for="${CSS.escape(id)}"]`) : false
+          return {
+            hasLabel,
+            ariaLabel: el.getAttribute('aria-label'),
+            ariaLabelledBy: el.getAttribute('aria-labelledby'),
+            placeholder: el.getAttribute('placeholder'),
+          }
+        }),
+      )
+
+    for (const input of inputAccessibility) {
+      const hasAccessibleName = input.hasLabel || input.ariaLabel || input.ariaLabelledBy
+      expect(hasAccessibleName || !!input.placeholder).toBe(true)
     }
   })
 
@@ -225,13 +175,10 @@ test.describe('Accessibility Tests', () => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Navigate to trigger potential announcements
     await homePage.navigateToGames()
 
-    // Page title should update - wait for navigation to complete
     await page.waitForLoadState('domcontentloaded')
     const title = await page.title()
-    // Title should be "Games | EmuReady" or similar
     expect(title.toLowerCase()).toMatch(/games|emuready/)
   })
 
@@ -239,25 +186,23 @@ test.describe('Accessibility Tests', () => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Look for skip links (often hidden until focused)
-    const skipLinks = page.locator('a').filter({ hasText: /skip to (content|main|navigation)/i })
-
-    if ((await skipLinks.count()) > 0) {
-      const firstSkipLink = skipLinks.first()
-
-      // Focus to make it visible
-      await firstSkipLink.focus()
-
-      // Should become visible when focused
-      await expect(firstSkipLink).toBeVisible()
-
-      // Should have proper href
-      const href = await firstSkipLink.getAttribute('href')
-      expect(href).toMatch(/^#\w+/)
+    const skipLinkData = await page
+      .locator('a')
+      .filter({ hasText: /skip to (content|main|navigation)/i })
+      .evaluateAll((elements) =>
+        elements.map((el) => ({
+          href: el.getAttribute('href'),
+          text: el.textContent,
+        })),
+      )
+
+    if (skipLinkData.length > 0) {
+      expect(skipLinkData[0].href).toMatch(/^#\w+/)
     } else {
-      // Check if main content has id for direct navigation
-      const mainContent = page.locator('main, [role="main"]')
-      const hasMainLandmark = (await mainContent.count()) > 0
+      const hasMainLandmark = await page
+        .locator('main, [role="main"]')
+        .count()
+        .then((c) => c > 0)
       expect(hasMainLandmark).toBe(true)
     }
   })
@@ -266,79 +211,37 @@ test.describe('Accessibility Tests', () => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Check html lang attribute
     const htmlLang = await page.locator('html').getAttribute('lang')
     expect(htmlLang).toBeTruthy()
-    expect(htmlLang).toMatch(/^[a-z]{2}(-[A-Z]{2})?$/) // e.g., "en" or "en-US"
+    expect(htmlLang).toMatch(/^[a-z]{2}(-[A-Z]{2})?$/)
   })
 
-  test('should handle focus trap in modals', async ({ page }) => {
-    const homePage = new HomePage(page)
-    await homePage.goto()
-
-    // Look for modal triggers
-    const modalTriggers = page.locator('button').filter({ hasText: /sign in|sign up/i })
-    const triggerCount = await modalTriggers.count()
-    test.skip(triggerCount === 0, 'No modal triggers found on page')
-
-    const trigger = modalTriggers.first()
-    await trigger.click()
-
-    // Check for standard ARIA dialog (our own modals)
-    const standardModal = page.locator('[role="dialog"], [aria-modal="true"], .modal')
-    const hasStandardModal = await standardModal
-      .first()
-      .waitFor({ state: 'visible', timeout: 5000 })
-      .then(() => true)
-      .catch(() => false)
-
-    if (hasStandardModal) {
-      // Tab should stay within modal
-      await page.keyboard.press('Tab')
-      await page.keyboard.press('Tab')
-
-      const focusedElement = page.locator(':focus')
-      const isInModal = await focusedElement.evaluate((el, modalSelector) => {
-        const modal = document.querySelector(modalSelector)
-        return modal ? modal.contains(el) : false
-      }, '[role="dialog"], [aria-modal="true"], .modal')
-
-      expect(isInModal).toBe(true)
-
-      // Escape should close modal
-      await page.keyboard.press('Escape')
-
-      await standardModal.first().waitFor({ state: 'hidden', timeout: 5000 })
-      const modalStillVisible = await standardModal.first().isVisible()
-      expect(modalStillVisible).toBe(false)
-    } else {
-      // Clerk's auth modal (third-party, no role="dialog") — skip focus trap test
-      // Clerk manages its own focus trapping and accessibility
-      test.skip(true, 'Only third-party Clerk modal available — focus trap is managed by Clerk')
-    }
+  test('should handle focus trap in modals', async () => {
+    test.skip(true, 'Focus trap verification depends on Clerk third-party modal behavior')
   })
 
   test('should have proper table accessibility', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    // Check for tables
     const tables = page.locator('table')
     const tableCount = await tables.count()
-    test.skip(tableCount === 0, 'No tables found on listings page')
-
-    const table = tables.first()
+    expect(tableCount).toBeGreaterThan(0)
 
-    // Should have proper headers
-    const headers = table.locator('th')
-    const headerCount = await headers.count()
-    expect(headerCount).toBeGreaterThan(0)
-
-    // Headers should have scope (but it's not always required)
-    const firstHeader = headers.first()
-    const scope = await firstHeader.getAttribute('scope')
-    if (scope) {
-      expect(scope).toMatch(/^(col|row)$/)
+    const headerData = await tables
+      .first()
+      .locator('th')
+      .evaluateAll((elements) =>
+        elements.map((el) => ({
+          scope: el.getAttribute('scope'),
+          text: el.textContent,
+        })),
+      )
+
+    expect(headerData.length).toBeGreaterThan(0)
+
+    if (headerData[0].scope) {
+      expect(headerData[0].scope).toMatch(/^(col|row)$/)
     }
   })
 })
@@ -348,39 +251,28 @@ test.describe('Screen Reader Tests', () => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Check for landmark regions
-    const landmarks = {
-      header: page.locator('header, [role="banner"]'),
-      nav: page.locator('nav, [role="navigation"]'),
-      main: page.locator('main, [role="main"]'),
-      footer: page.locator('footer, [role="contentinfo"]'),
-    }
+    const nav = page.locator('nav, [role="navigation"]')
+    const main = page.locator('main, [role="main"]')
 
-    // At minimum, we should have nav and main
-    const hasNav = (await landmarks.nav.count()) > 0
-    const hasMain = (await landmarks.main.count()) > 0
-    expect(hasNav || hasMain).toBe(true)
+    await expect(nav.first()).toBeVisible()
+    await expect(main.first()).toBeVisible()
   })
 
   test('should provide context for icon buttons', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Find buttons that might only have icons
-    const buttons = page.locator('button')
-    const buttonCount = await buttons.count()
-
-    for (let i = 0; i < Math.min(buttonCount, 10); i++) {
-      const button = buttons.nth(i)
-      const text = await button.textContent()
-
-      // If button has no visible text (likely icon-only)
-      if (!text || text.trim().length === 0) {
-        const ariaLabel = await button.getAttribute('aria-label')
-        const title = await button.getAttribute('title')
+    const buttonData = await page.locator('button').evaluateAll((elements) =>
+      elements.slice(0, 10).map((el) => ({
+        text: el.textContent?.trim() ?? '',
+        ariaLabel: el.getAttribute('aria-label'),
+        title: el.getAttribute('title'),
+      })),
+    )
 
-        // Should have aria-label or title
-        expect(ariaLabel || title).toBeTruthy()
+    for (const btn of buttonData) {
+      if (btn.text.length === 0) {
+        expect(btn.ariaLabel || btn.title).toBeTruthy()
       }
     }
   })
diff --git a/tests/admin-dashboard.spec.ts b/tests/admin-dashboard.spec.ts
index eb7be2dde..6d8feca23 100644
--- a/tests/admin-dashboard.spec.ts
+++ b/tests/admin-dashboard.spec.ts
@@ -1,134 +1,55 @@
 import { test, expect } from '@playwright/test'
 
-test.describe('Admin Dashboard Tests - Requires Admin Role', () => {
-  test.use({ storageState: 'tests/.auth/admin.json' })
+test.describe('Admin Dashboard', () => {
+  test.use({ storageState: 'tests/.auth/super_admin.json' })
+
   test.beforeEach(async ({ page }) => {
     await page.goto('/admin', { waitUntil: 'domcontentloaded' })
     await expect(page).toHaveURL(/\/admin/)
-
-    // Wait for dashboard content to render
-    await page
-      .locator('[data-testid="admin-nav"]')
-      .or(page.locator('nav').filter({ hasText: /systems|games/i }))
-      .first()
-      .waitFor({ state: 'visible', timeout: 15000 })
+    await page.locator('[data-testid="admin-nav"]').waitFor({ state: 'visible', timeout: 15000 })
   })
 
-  test('should display admin navigation menu with all required items', async ({ page }) => {
-    // Admin navigation should be visible - specifically the QuickNavigation component
+  test('should display admin navigation with required menu items', async ({ page }) => {
     const adminNav = page.locator('[data-testid="admin-nav"]')
     await expect(adminNav).toBeVisible()
 
-    // All admin menu items must be present (based on admin role permissions)
-    const requiredMenuItems = ['Games', 'Systems', 'Devices', 'Emulators']
-
-    for (const item of requiredMenuItems) {
-      // Look for links specifically within the admin nav, use first() to avoid duplicates
+    for (const item of ['Games', 'Systems', 'Devices', 'Emulators']) {
       const menuLink = adminNav
         .locator('a')
         .filter({ hasText: new RegExp(item, 'i') })
         .first()
-      await expect(menuLink).toBeVisible({ timeout: 5000 })
+      await expect(menuLink).toBeVisible()
     }
   })
 
-  test('should display dashboard statistics with valid data', async ({ page }) => {
-    // PlatformStats component renders stat sections with headings and numeric values
+  test('should display dashboard statistics', async ({ page }) => {
     const mainContent = page.locator('main').first()
     await expect(mainContent).toBeVisible()
 
-    // Wait for dashboard data to load (stats or activity cards)
-    await page
-      .locator('text=/\\d+/')
-      .first()
-      .waitFor({ state: 'visible', timeout: 15000 })
-      .catch(() => {})
-
-    // Dashboard must have numeric stat values rendered
     const statValues = mainContent.locator('text=/\\d+/')
+    await expect(statValues.first()).toBeVisible()
+
     const statCount = await statValues.count()
-    test.skip(statCount === 0, 'Dashboard statistics have not loaded')
     expect(statCount).toBeGreaterThan(0)
   })
 
-  test('should show recent activity feed with entries', async ({ page }) => {
-    // Activity cards are rendered with time range buttons (24h, 48h, 7d)
+  test('should show time range filter buttons', async ({ page }) => {
     const timeRangeButtons = page.locator('button').filter({ hasText: /24h|48h|7d/i })
-    const buttonCount = await timeRangeButtons.count()
-
-    if (buttonCount > 0) {
-      // Activity section is present with time range controls
-      await expect(timeRangeButtons.first()).toBeVisible()
-
-      // Clicking a time range button should keep the dashboard functional
-      await timeRangeButtons.first().click()
-      await page.waitForLoadState('domcontentloaded')
+    await expect(timeRangeButtons.first()).toBeVisible()
 
-      // Dashboard main content must remain visible after interaction
-      await expect(page.locator('main').first()).toBeVisible()
-    } else {
-      // If no time range buttons, PlatformStats or QuickNavigation must be present
-      const quickNav = page.locator('[data-testid="admin-nav"]')
-      await expect(quickNav).toBeVisible()
+    for (const range of ['24h', '48h', '7d']) {
+      const rangeButton = page
+        .locator('button')
+        .filter({ hasText: new RegExp(range, 'i') })
+        .first()
+      await expect(rangeButton).toBeVisible()
     }
   })
 
-  test('should have functional quick actions', async ({ page }) => {
-    // Quick Navigation component serves as quick actions
-    const quickNav = page.locator('[data-testid="admin-nav"]')
-    await expect(quickNav).toBeVisible()
-
-    // Check that navigation links are present in the QuickNavigation
-    const navLinks = quickNav.locator('a')
-    const linkCount = await navLinks.count()
-
-    // Must have at least 2 navigation links for quick access
-    expect(linkCount).toBeGreaterThanOrEqual(2)
-  })
-
-  test('should display activity cards on dashboard', async ({ page }) => {
-    // Wait for dashboard content to load
-    await page
-      .locator('button')
-      .filter({ hasText: /24h|48h|7d/i })
-      .first()
-      .or(page.locator('[data-testid="admin-nav"]'))
-      .waitFor({ state: 'visible', timeout: 15000 })
-      .catch(() => {})
-
-    // Activity cards contain time range buttons and data sections
-    const timeRangeButtons = page.locator('button').filter({ hasText: /24h|48h|7d/i })
-    const buttonCount = await timeRangeButtons.count()
-
-    // Dashboard must have time range buttons (from activity cards) or QuickNavigation
-    const quickNav = page.locator('[data-testid="admin-nav"]')
-    const hasQuickNav = await quickNav.isVisible()
-
-    expect(buttonCount > 0 || hasQuickNav).toBe(true)
-  })
-
-  test('should have time range filters on activity cards', async ({ page }) => {
-    // Wait for dashboard content to load
-    await page
-      .locator('button')
-      .filter({ hasText: /24h|48h|7d/i })
-      .first()
-      .waitFor({ state: 'visible', timeout: 15000 })
-      .catch(() => {})
-
-    // Activity cards have time range filter buttons: 24h, 48h, 7d
+  test('should switch time ranges on activity cards', async ({ page }) => {
     const timeRangeButtons = page.locator('button').filter({ hasText: /24h|48h|7d/i })
-    const buttonCount = await timeRangeButtons.count()
-
-    test.skip(buttonCount === 0, 'No time range buttons found on dashboard')
-
-    // Verify all expected time ranges are present
-    for (const range of ['24h', '48h', '7d']) {
-      const rangeButton = page.locator('button').filter({ hasText: new RegExp(range, 'i') })
-      await expect(rangeButton.first()).toBeVisible()
-    }
+    await expect(timeRangeButtons.first()).toBeVisible()
 
-    // Click each time range button and verify the page remains stable
     for (const range of ['24h', '48h', '7d']) {
       const rangeButton = page
         .locator('button')
@@ -140,121 +61,61 @@ test.describe('Admin Dashboard Tests - Requires Admin Role', () => {
     }
   })
 
-  test('should have responsive admin layout', async ({ page }) => {
-    // Test desktop view
-    await page.setViewportSize({ width: 1280, height: 800 })
-
-    // Wait for dashboard content to load
-    await page
-      .locator('[data-testid="admin-nav"]')
-      .waitFor({ state: 'visible', timeout: 15000 })
-      .catch(() => {})
-
-    // QuickNavigation should be visible on desktop
+  test('should have functional quick action nav links', async ({ page }) => {
     const quickNav = page.locator('[data-testid="admin-nav"]')
-    await expect(quickNav).toBeVisible({ timeout: 5000 })
-
-    // Dashboard main content must be visible
-    await expect(page.locator('main').first()).toBeVisible()
-
-    // Test mobile view
-    await page.setViewportSize({ width: 375, height: 667 })
-
-    // QuickNavigation should still be accessible on mobile
     await expect(quickNav).toBeVisible()
 
-    // Check if the QuickNavigation toggle button is present
-    const toggleButton = quickNav.locator('button').first()
-    if (await toggleButton.isVisible()) {
-      // Toggle the navigation
-      await toggleButton.click()
-      await page.waitForLoadState('domcontentloaded')
-    }
-
-    // Navigation items should be accessible when expanded
     const navLinks = quickNav.locator('a')
     const linkCount = await navLinks.count()
-
-    // If no links visible, try expanding the navigation first
-    if (linkCount === 0) {
-      const expandButton = quickNav.locator('button').first()
-      if (await expandButton.isVisible()) {
-        await expandButton.click()
-        await page.waitForLoadState('domcontentloaded')
-      }
-      // Check again after expanding
-      const expandedLinks = quickNav.locator('a')
-      expect(await expandedLinks.count()).toBeGreaterThan(0)
-    } else {
-      expect(linkCount).toBeGreaterThan(0)
-    }
+    expect(linkCount).toBeGreaterThanOrEqual(2)
   })
 
-  test('should have working admin search functionality', async ({ page }) => {
-    // The admin dashboard doesn't have global search
-    // Instead, verify that navigation links work to access different admin sections
+  test('should navigate to games admin page via nav link', async ({ page }) => {
     const quickNav = page.locator('[data-testid="admin-nav"]')
-    await expect(quickNav).toBeVisible()
-
-    // Find and click a navigation link (e.g., Games)
     const gamesLink = quickNav.locator('a').filter({ hasText: /games/i }).first()
     await expect(gamesLink).toBeVisible()
+
     await gamesLink.click()
+    await expect(page).toHaveURL(/\/admin\/games/)
+  })
 
-    // Should navigate to games admin page
-    await expect(page).toHaveURL(/\/admin\/games/, { timeout: 5000 })
+  test('should display responsive layout at mobile viewport', async ({ page }) => {
+    await page.setViewportSize({ width: 375, height: 667 })
 
-    // Navigate back to dashboard
-    await page.goto('/admin', { waitUntil: 'domcontentloaded' })
+    const quickNav = page.locator('[data-testid="admin-nav"]')
+    await expect(quickNav).toBeVisible()
+    await expect(page.locator('main').first()).toBeVisible()
   })
 
-  test('should have logout functionality', async ({ page }) => {
-    // User menu button should be present for authenticated users
+  test('should show user menu button', async ({ page }) => {
     const userMenuButton = page.getByRole('button', { name: /open user menu/i })
-    await expect(userMenuButton).toBeVisible({ timeout: 5000 })
+    await expect(userMenuButton).toBeVisible()
   })
 })
 
-test.describe('Admin Dashboard Data Visualizations - Requires Admin Role', () => {
-  test.use({ storageState: 'tests/.auth/admin.json' })
+test.describe('Admin Dashboard Data Visualizations', () => {
+  test.use({ storageState: 'tests/.auth/super_admin.json' })
+
   test.beforeEach(async ({ page }) => {
     await page.goto('/admin', { waitUntil: 'domcontentloaded' })
     await expect(page).toHaveURL(/\/admin/)
-
-    // Wait for dashboard content to render
-    await page
-      .locator('[data-testid="admin-nav"]')
-      .or(page.locator('nav').filter({ hasText: /systems|games/i }))
-      .first()
-      .waitFor({ state: 'visible', timeout: 15000 })
+    await page.locator('[data-testid="admin-nav"]').waitFor({ state: 'visible', timeout: 15000 })
   })
 
-  test('should display interactive data charts', async ({ page }) => {
-    // Wait for numeric data to appear
-    await page
-      .locator('text=/\\d+/')
-      .first()
-      .waitFor({ state: 'visible', timeout: 15000 })
-      .catch(() => {})
+  test('should display numeric data on dashboard', async ({ page }) => {
+    const numbers = page.locator('text=/\\d+/')
+    await expect(numbers.first()).toBeVisible()
 
-    // Dashboard should have numeric data indicators (stats, counts, etc.)
-    const numbersOnPage = await page.locator('text=/\\d+/').count()
-    expect(numbersOnPage).toBeGreaterThan(0)
+    const count = await numbers.count()
+    expect(count).toBeGreaterThan(0)
   })
 
-  test('should support data refresh functionality', async ({ page }) => {
-    // ActivityCard refresh button uses aria-label="Refresh"
-    const refreshButton = page.locator('button[aria-label="Refresh"]')
-    const hasRefresh = await refreshButton
-      .first()
-      .isVisible({ timeout: 5000 })
-      .catch(() => false)
-    test.skip(!hasRefresh, 'No refresh button available on dashboard')
+  test('should have a refresh button on activity cards', async ({ page }) => {
+    const refreshButton = page.locator('button[aria-label="Refresh"]').first()
+    await expect(refreshButton).toBeVisible()
 
-    await refreshButton.first().click()
+    await refreshButton.click()
     await page.waitForLoadState('domcontentloaded')
-
-    // Dashboard should remain functional after refresh
     await expect(page.locator('main').first()).toBeVisible()
   })
 })
diff --git a/tests/admin-permissions.spec.ts b/tests/admin-permissions.spec.ts
index 7a74b8ec7..e7eab6ac2 100644
--- a/tests/admin-permissions.spec.ts
+++ b/tests/admin-permissions.spec.ts
@@ -1,126 +1,85 @@
 import { test, expect } from '@playwright/test'
 
 test.describe('Admin Permissions Tests - Requires Admin Role', () => {
-  test.use({ storageState: 'tests/.auth/admin.json' })
+  test.use({ storageState: 'tests/.auth/super_admin.json' })
   test.beforeEach(async ({ page }) => {
     await page.goto('/admin/permissions', { waitUntil: 'domcontentloaded' })
     await expect(page).toHaveURL(/\/admin\/permissions/)
+
+    await expect(page.locator('table').first()).toBeVisible()
   })
 
   test('should display permissions management page with table', async ({ page }) => {
-    const mainContent = page.locator('main').first()
-    await expect(mainContent).toBeVisible()
+    const table = page.locator('table').first()
+    await expect(table).toBeVisible()
 
-    // Wait for page content to finish loading
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
+    const headers = table.locator('thead th')
+    const headerTexts = await headers.allTextContents()
+    const headerString = headerTexts.join(' ').toLowerCase()
 
-    const table = page.locator('table').first()
-    const hasTable = await table.isVisible({ timeout: 10000 }).catch(() => false)
-
-    if (hasTable) {
-      const headers = table.locator('thead th')
-      const headerTexts = await headers.allTextContents()
-      const headerString = headerTexts.join(' ').toLowerCase()
-
-      expect(headerString).toMatch(/label|permission|key/i)
-
-      const rows = table.locator('tbody tr')
-      const rowCount = await rows.count()
-      if (rowCount > 0) {
-        const firstRow = rows.first()
-        await expect(firstRow).toBeVisible()
-      }
-    } else {
-      // Wait for meaningful content to load before checking text
-      await page
-        .waitForFunction(
-          (el) => el !== null && (el.textContent ?? '').trim().length > 10,
-          await mainContent.elementHandle(),
-          { timeout: 10000 },
-        )
-        .catch(() => {})
-      const pageText = await mainContent.textContent()
-      expect(pageText).toMatch(/permission/i)
-    }
+    expect(headerString).toMatch(/label|permission|key/i)
+
+    const rows = table.locator('tbody tr')
+    const rowCount = await rows.count()
+    expect(rowCount).toBeGreaterThan(0)
+
+    await expect(rows.first()).toBeVisible()
   })
 
   test('should edit role permissions', async ({ page }) => {
-    // EditButton uses title="Edit Permission"
     const editButtons = page.locator('button[title="Edit Permission"]')
-    const hasEdit = (await editButtons.count()) > 0
-    test.skip(!hasEdit, 'No edit permission buttons available')
+    await expect(editButtons.first()).toBeVisible()
+    expect(await editButtons.count()).toBeGreaterThan(0)
 
     await editButtons.first().click()
 
     const permissionsModal = page.locator('[role="dialog"]')
-    const hasModal = await permissionsModal.isVisible({ timeout: 3000 }).catch(() => false)
-    test.skip(!hasModal, 'Permissions editor dialog did not appear')
+    await expect(permissionsModal).toBeVisible()
 
-    // Modal should have form fields (key, label, description, category)
     const formFields = permissionsModal.locator('input, textarea, select')
     const fieldCount = await formFields.count()
     expect(fieldCount).toBeGreaterThan(0)
 
-    // Close the modal
     const cancelButton = permissionsModal.locator('button').filter({ hasText: /cancel/i })
-    if (await cancelButton.isVisible({ timeout: 1000 }).catch(() => false)) {
-      await cancelButton.click()
-    } else {
-      await page.keyboard.press('Escape')
-    }
+    await expect(cancelButton).toBeVisible()
+    await cancelButton.click()
   })
 
   test('should manage individual permissions', async ({ page }) => {
-    // The permissions table IS the permissions list
     const table = page.locator('table').first()
-    const hasTable = await table.isVisible({ timeout: 5000 }).catch(() => false)
-    test.skip(!hasTable, 'No permissions table visible')
+    await expect(table).toBeVisible()
 
     const rows = table.locator('tbody tr')
     const rowCount = await rows.count()
-    test.skip(rowCount === 0, 'No permission rows found')
+    expect(rowCount).toBeGreaterThan(0)
 
     const firstRow = rows.first()
     await expect(firstRow).toBeVisible()
 
-    // Each row should have cells with permission data
     const cells = firstRow.locator('td')
     const cellCount = await cells.count()
     expect(cellCount).toBeGreaterThan(0)
 
-    // First cell should contain the permission key or label
     const firstCell = cells.first()
     const cellText = await firstCell.textContent()
     expect(cellText?.trim().length).toBeGreaterThan(0)
   })
 
   test('should create custom permissions', async ({ page }) => {
-    // "Add Permission" button at top of page
     const addButton = page.locator('button').filter({ hasText: /add permission/i })
-    const hasAdd = await addButton.isVisible({ timeout: 3000 }).catch(() => false)
-    test.skip(!hasAdd, 'No Add Permission button available')
+    await expect(addButton).toBeVisible()
 
     await addButton.click()
 
-    // Dialog opens for creating a new permission
     const dialog = page.locator('[role="dialog"]')
-    const hasDialog = await dialog.isVisible({ timeout: 3000 }).catch(() => false)
-    test.skip(!hasDialog, 'Permission creation dialog did not appear')
+    await expect(dialog).toBeVisible()
 
-    // Verify the dialog has input fields (key, label, description, category)
     const formFields = dialog.locator('input, textarea, select')
     const fieldCount = await formFields.count()
     expect(fieldCount).toBeGreaterThan(0)
 
-    // Close the dialog without creating
     const cancelButton = dialog.locator('button').filter({ hasText: /cancel/i })
-    if (await cancelButton.isVisible({ timeout: 1000 }).catch(() => false)) {
-      await cancelButton.click()
-    } else {
-      await page.keyboard.press('Escape')
-    }
+    await expect(cancelButton).toBeVisible()
+    await cancelButton.click()
   })
 })
diff --git a/tests/admin-reports.spec.ts b/tests/admin-reports.spec.ts
index 184c27ba7..bcf75ba60 100644
--- a/tests/admin-reports.spec.ts
+++ b/tests/admin-reports.spec.ts
@@ -1,182 +1,111 @@
 import { test, expect } from '@playwright/test'
 
 test.describe('Admin Reports Management Tests - Requires Admin Role', () => {
-  test.use({ storageState: 'tests/.auth/admin.json' })
+  test.use({ storageState: 'tests/.auth/super_admin.json' })
   test.beforeEach(async ({ page }) => {
     await page.goto('/admin/reports', { waitUntil: 'domcontentloaded' })
     await expect(page).toHaveURL(/\/admin\/reports/)
+
+    await expect(page.locator('table').first()).toBeVisible()
   })
 
   test('should display reports dashboard with required elements', async ({ page }) => {
-    const mainContent = page.locator('main').first()
-    await expect(mainContent).toBeVisible()
+    const reportsTable = page.locator('table').first()
+    await expect(reportsTable).toBeVisible()
 
-    // Wait for page content to finish loading
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
+    const headers = reportsTable.locator('thead th')
+    const headerTexts = await headers.allTextContents()
+    const headerString = headerTexts.join(' ').toLowerCase()
 
-    const reportsTable = page.locator('table').first()
-    const hasTable = await reportsTable.isVisible({ timeout: 10000 }).catch(() => false)
-
-    if (hasTable) {
-      const headers = reportsTable.locator('thead th')
-      const headerTexts = await headers.allTextContents()
-      const headerString = headerTexts.join(' ').toLowerCase()
-
-      expect(headerString).toMatch(/reason/i)
-      expect(headerString).toMatch(/status/i)
-
-      const reportRows = reportsTable.locator('tbody tr')
-      const rowCount = await reportRows.count()
-      expect(typeof rowCount).toBe('number')
-    } else {
-      // Wait for meaningful content to load before checking text
-      await page
-        .waitForFunction(
-          (el) => el !== null && (el.textContent ?? '').trim().length > 10,
-          await mainContent.elementHandle(),
-          { timeout: 10000 },
-        )
-        .catch(() => {})
-      const pageText = await mainContent.textContent()
-      expect(pageText).toMatch(/report/i)
-    }
+    expect(headerString).toMatch(/reason/i)
+    expect(headerString).toMatch(/status/i)
+
+    const reportRows = reportsTable.locator('tbody tr')
+    const rowCount = await reportRows.count()
+    expect(rowCount).toBeGreaterThanOrEqual(0)
   })
 
   test('should filter reports by status', async ({ page }) => {
-    // Reports page uses a <select> dropdown for status filtering
-    const statusDropdown = page.locator('select').first()
-    const hasDropdown = await statusDropdown.isVisible({ timeout: 5000 }).catch(() => false)
-    test.skip(!hasDropdown, 'No status filter dropdown available')
+    const statusDropdownButton = page.getByRole('button', { name: /all statuses/i })
+    await expect(statusDropdownButton).toBeVisible()
+
+    await statusDropdownButton.click()
 
-    // Select "Pending" from the dropdown
-    const options = await statusDropdown.locator('option').allTextContents()
-    const hasPendingOption = options.some((opt) => /pending/i.test(opt))
-    test.skip(!hasPendingOption, 'No Pending option in status dropdown')
+    const pendingOption = page.getByText('Pending').last()
+    await expect(pendingOption).toBeVisible()
+    await pendingOption.click()
 
-    await statusDropdown.selectOption({ label: 'Pending' })
     await page.waitForLoadState('domcontentloaded')
 
-    // Verify the table updated — should still have table or show no results
     const table = page.locator('table').first()
-    const hasTable = await table.isVisible({ timeout: 5000 }).catch(() => false)
-    expect(hasTable).toBe(true)
+    await expect(table).toBeVisible()
   })
 
   test('should display report details', async ({ page }) => {
-    // ViewButton uses title="View Report Details"
     const viewButtons = page.locator('button[title="View Report Details"]')
-    const hasViewButtons = (await viewButtons.count()) > 0
-    test.skip(!hasViewButtons, 'No View Report Details buttons available')
+    await expect(viewButtons.first()).toBeVisible()
+    expect(await viewButtons.count()).toBeGreaterThan(0)
 
     await viewButtons.first().click()
 
-    // ReportDetailsModal opens as a dialog
     const reportModal = page.locator('[role="dialog"]')
-    const hasModal = await reportModal.isVisible({ timeout: 3000 }).catch(() => false)
-    test.skip(!hasModal, 'Report details modal did not appear')
+    await expect(reportModal).toBeVisible()
 
-    // Modal should have content (report details)
     const modalContent = await reportModal.textContent()
     expect(modalContent).toBeTruthy()
-    expect(modalContent!.length).toBeGreaterThan(0)
+    expect(modalContent?.length).toBeGreaterThan(0)
 
-    // Close button in footer
     const closeButton = reportModal.locator('button').filter({ hasText: /close/i })
-    if (await closeButton.isVisible({ timeout: 2000 }).catch(() => false)) {
-      await closeButton.click()
-    } else {
-      await page.keyboard.press('Escape')
-    }
+    await expect(closeButton).toBeVisible()
+    await closeButton.click()
   })
 
   test('should handle report status updates', async ({ page }) => {
-    // "Update Status" button opens ReportStatusModal
     const statusButton = page.locator('button[title="Update Status"]').first()
-    const hasStatusButton = await statusButton.isVisible({ timeout: 5000 }).catch(() => false)
-    test.skip(!hasStatusButton, 'No Update Status button available')
+    await expect(statusButton).toBeVisible()
 
     await statusButton.click()
 
-    // ReportStatusModal opens as a dialog with status select and notes
     const dialog = page.locator('[role="dialog"]')
-    const hasDialog = await dialog.isVisible({ timeout: 3000 }).catch(() => false)
-    test.skip(!hasDialog, 'Report status modal did not appear')
+    await expect(dialog).toBeVisible()
 
-    // Should have a status select and review notes textarea
     const statusSelect = dialog.locator('select')
-    const hasStatusSelect = await statusSelect.isVisible({ timeout: 2000 }).catch(() => false)
+    await expect(statusSelect).toBeVisible()
 
-    if (hasStatusSelect) {
-      const options = await statusSelect.locator('option').allTextContents()
-      expect(options.length).toBeGreaterThan(0)
-    }
+    const options = await statusSelect.locator('option').allTextContents()
+    expect(options.length).toBeGreaterThan(0)
 
-    // Close without saving
     const cancelButton = dialog.locator('button').filter({ hasText: /cancel/i })
-    const closeButton = dialog.locator('button').filter({ hasText: /close/i })
-
-    if (await cancelButton.isVisible({ timeout: 1000 }).catch(() => false)) {
-      await cancelButton.click()
-    } else if (await closeButton.isVisible({ timeout: 1000 }).catch(() => false)) {
-      await closeButton.click()
-    } else {
-      await page.keyboard.press('Escape')
-    }
+    await expect(cancelButton).toBeVisible()
+    await cancelButton.click()
   })
 
   test('should display report statistics', async ({ page }) => {
-    // AdminStatsDisplay renders stat cards at top of page
     const statsLabels = [/total reports/i, /pending/i, /under review/i, /resolved/i, /dismissed/i]
 
-    let visibleStats = 0
+    // These labels may appear multiple times on the page (stats, table status
+    // column, action buttons). Use .first() to match the stats label.
     for (const label of statsLabels) {
-      const stat = page.getByText(label)
-      if (await stat.isVisible({ timeout: 2000 }).catch(() => false)) {
-        visibleStats++
-      }
+      await expect(page.getByText(label).first()).toBeVisible()
     }
-    test.skip(visibleStats === 0, 'No report statistics section visible')
-
-    // Verify numeric values exist alongside stats
-    const mainContent = page.locator('main').first()
-    const numbersOnPage = await mainContent.locator('text=/\\d+/').count()
-    expect(numbersOnPage).toBeGreaterThan(0)
   })
 
   test('should link to reported content', async ({ page }) => {
-    // Open ReportDetailsModal first to find the listing link
     const viewButton = page.locator('button[title="View Report Details"]').first()
-    const hasViewButton = await viewButton.isVisible({ timeout: 5000 }).catch(() => false)
-    test.skip(!hasViewButton, 'No view button available')
+    await expect(viewButton).toBeVisible()
 
     await viewButton.click()
 
     const dialog = page.locator('[role="dialog"]')
-    const hasDialog = await dialog.isVisible({ timeout: 3000 }).catch(() => false)
-    test.skip(!hasDialog, 'Report details modal did not appear')
-
-    // ReportDetailsModal has a "View Listing" button/link in the footer
-    const contentLink = dialog.locator('a, button').filter({ hasText: /view listing/i })
-    const hasContentLink = await contentLink.isVisible({ timeout: 3000 }).catch(() => false)
-    test.skip(!hasContentLink, 'No content link found in report details')
-
-    const tagName = await contentLink.evaluate((el) => el.tagName.toLowerCase())
-    if (tagName === 'a') {
-      const href = await contentLink.getAttribute('href')
-      expect(href).toBeTruthy()
-    } else {
-      await expect(contentLink).toBeVisible()
-    }
+    await expect(dialog).toBeVisible()
 
-    // Close the dialog
-    const closeButton = dialog.locator('button').filter({ hasText: /close/i })
-    if (await closeButton.isVisible({ timeout: 1000 }).catch(() => false)) {
-      await closeButton.click()
-    } else {
-      await page.keyboard.press('Escape')
-    }
+    // "View Listing" is a button that opens the listing in a new tab via
+    // window.open — not an anchor tag.
+    const viewListingButton = dialog.getByRole('button', { name: /view listing/i })
+    await expect(viewListingButton).toBeVisible()
+
+    const closeButton = dialog.getByRole('button', { name: /^close$/i })
+    await expect(closeButton).toBeVisible()
+    await closeButton.click()
   })
 })
diff --git a/tests/admin-users.spec.ts b/tests/admin-users.spec.ts
index 2381274ed..d93f3d4fb 100644
--- a/tests/admin-users.spec.ts
+++ b/tests/admin-users.spec.ts
@@ -1,21 +1,18 @@
 import { test, expect } from '@playwright/test'
 
 test.describe('Admin User Management Tests - Requires Admin Role', () => {
-  test.use({ storageState: 'tests/.auth/admin.json' })
+  test.use({ storageState: 'tests/.auth/super_admin.json' })
   test.beforeEach(async ({ page }) => {
     await page.goto('/admin/users', { waitUntil: 'domcontentloaded' })
     await expect(page).toHaveURL(/\/admin\/users/)
+
+    await expect(page.locator('tbody tr').first()).toBeVisible()
   })
 
   test('should display users list with required columns', async ({ page }) => {
-    // Users table must be present - wait for data to load
-    const usersTable = page.locator('table, [data-testid="users-table"]')
-    await usersTable.waitFor({ state: 'visible', timeout: 15000 })
-
-    // Wait for at least one data row to appear
-    await page.locator('tbody tr').first().waitFor({ state: 'visible', timeout: 15000 })
+    const usersTable = page.locator('table').first()
+    await expect(usersTable).toBeVisible()
 
-    // Verify all required columns are present
     const headers = usersTable.locator('thead th')
     const headerTexts = await headers.allTextContents()
 
@@ -27,184 +24,102 @@ test.describe('Admin User Management Tests - Requires Admin Role', () => {
       expect(hasHeader).toBe(true)
     }
 
-    // Must have at least one user row
     const userRows = usersTable.locator('tbody tr')
     const rowCount = await userRows.count()
     expect(rowCount).toBeGreaterThan(0)
 
-    // Verify first user row has valid email
     const firstRow = userRows.first()
     const email = await firstRow.locator('td').filter({ hasText: /@/ }).textContent()
     expect(email).toMatch(/^.+@.+\..+$/)
   })
 
-  test('should support user search and filtering', async ({ page }) => {
-    // Search input must be present
+  test('should support user search', async ({ page }) => {
     const searchInput = page.getByPlaceholder(/search/i)
     await expect(searchInput).toBeVisible()
 
     await searchInput.fill('test')
     await page.keyboard.press('Enter')
 
-    // Wait for search results to load
-    await page
-      .locator('tbody tr')
-      .first()
-      .or(page.getByText(/no users found|no results/i))
-      .waitFor({ state: 'visible', timeout: 10000 })
-      .catch(() => {})
-
-    // Table should update - verify it still shows content or no results message
-    const hasResults = (await page.locator('tbody tr').count()) > 0
-    const noResultsMessage = page.getByText(/no users found|no results/i)
-
-    // Either show results or show no results message
-    if (!hasResults) {
-      await expect(noResultsMessage).toBeVisible()
-    } else {
-      expect(hasResults).toBe(true)
-    }
-
-    // Users page does not have a role filter dropdown - only search
-    const roleFilter = page.locator('select[name*="role"], [data-testid="role-filter"]')
-    if (await roleFilter.isVisible({ timeout: 2000 }).catch(() => false)) {
-      await roleFilter.selectOption({ index: 1 })
-      await page.waitForLoadState('domcontentloaded')
-    }
+    await expect(
+      page
+        .locator('tbody tr')
+        .first()
+        .or(page.getByText(/no users found|no results/i)),
+    ).toBeVisible()
   })
 
   test('should handle user role changes', async ({ page }) => {
     const roleButtons = page.locator('button[title="Change Role"]')
-    const roleButtonCount = await roleButtons.count()
-    test.skip(roleButtonCount === 0, 'No Change Role buttons found in the current table view')
+    await expect(roleButtons.first()).toBeVisible()
+    expect(await roleButtons.count()).toBeGreaterThan(0)
 
     await roleButtons.first().click()
 
     const dialog = page.locator('[role="dialog"]')
-    const hasDialog = await dialog.isVisible({ timeout: 3000 }).catch(() => false)
-    expect(hasDialog).toBe(true)
-
-    // Cancel the change
-    const cancelButton = dialog.locator('button').filter({ hasText: /cancel|close/i })
-    if (await cancelButton.isVisible({ timeout: 2000 }).catch(() => false)) {
-      await cancelButton.click()
-    } else {
-      await page.keyboard.press('Escape')
-    }
+    await expect(dialog).toBeVisible()
+
+    const cancelButton = dialog.locator('button').filter({ hasText: /cancel/i })
+    await expect(cancelButton).toBeVisible()
+    await cancelButton.click()
   })
 
   test('should display user details modal', async ({ page }) => {
-    // Ensure table is loaded before trying to click buttons
-    await page.locator('tbody tr').first().waitFor({ state: 'visible', timeout: 15000 })
-
     const viewButton = page.locator('button[title="View User Details"]').first()
-    const nameButton = page.locator('tbody button').first()
-
-    const hasViewButton = await viewButton.isVisible({ timeout: 3000 }).catch(() => false)
-    const clickTarget = hasViewButton ? viewButton : nameButton
-
-    await clickTarget.click()
+    await expect(viewButton).toBeVisible()
 
-    await page.waitForLoadState('domcontentloaded')
+    await viewButton.click()
 
-    // Wait for either dialog or URL change
     const modal = page.locator('[role="dialog"]')
-    const hasModal = await modal.isVisible({ timeout: 5000 }).catch(() => false)
-    const hasUserIdParam = page.url().includes('userId=')
-
-    // Either a modal appeared or the URL changed to include userId param
-    // If neither happened, the click target may not trigger a modal (skip gracefully)
-    test.skip(!hasModal && !hasUserIdParam, 'User details modal did not appear after clicking')
-
-    if (hasModal) {
-      const closeButton = modal.locator('button').filter({ hasText: /close/i }).first()
-      if (await closeButton.isVisible({ timeout: 2000 }).catch(() => false)) {
-        await closeButton.click()
-      } else {
-        await page.keyboard.press('Escape')
-      }
-    }
+    await expect(modal).toBeVisible()
+
+    const closeButton = modal.locator('button').filter({ hasText: /close/i }).first()
+    await expect(closeButton).toBeVisible()
+    await closeButton.click()
   })
 
-  test('should handle user suspension/banning', async ({ page }) => {
-    // BanButton uses title="Ban User"
+  test('should handle user banning navigation', async ({ page }) => {
     const banButtons = page.locator('button[title="Ban User"]')
-    const hasBan = (await banButtons.count()) > 0
-    test.skip(!hasBan, 'No Ban User buttons available')
+    await expect(banButtons.first()).toBeVisible()
+    expect(await banButtons.count()).toBeGreaterThan(0)
 
     await banButtons.first().click()
 
-    const confirmDialog = page.locator('[role="dialog"]')
-    const hasConfirm = await confirmDialog.isVisible({ timeout: 3000 }).catch(() => false)
-    expect(hasConfirm).toBe(true)
-
-    // Check for reason input
-    const reasonInput = confirmDialog.locator('textarea, input[type="text"]')
-    if (await reasonInput.isVisible().catch(() => false)) {
-      await reasonInput.fill('Test suspension reason')
-    }
+    await expect(page).toHaveURL(/\/admin\/user-bans/)
 
-    // Cancel for safety
-    const cancelButton = confirmDialog.locator('button').filter({ hasText: /cancel/i })
-    await expect(cancelButton).toBeVisible()
-    await cancelButton.click()
+    await page.goBack()
+    await expect(page).toHaveURL(/\/admin\/users/)
   })
 
   test('should display user activity history', async ({ page }) => {
-    // Open UserDetailsModal via the View User Details button
     const viewButton = page.locator('button[title="View User Details"]').first()
-    const hasView = await viewButton.isVisible({ timeout: 5000 }).catch(() => false)
-    test.skip(!hasView, 'No View User Details button available')
+    await expect(viewButton).toBeVisible()
 
     await viewButton.click()
 
-    // UserDetailsModal opens as a dialog or updates URL with userId param
     const modal = page.locator('[role="dialog"]')
-    const hasModal = await modal.isVisible({ timeout: 5000 }).catch(() => false)
-    test.skip(!hasModal, 'User details modal did not appear')
+    await expect(modal).toBeVisible()
+    // Wait for modal content to finish loading
+    await expect(modal.getByText(/loading/i)).toBeHidden()
 
-    // UserDetailsModal has tabs: Listings, Votes, Trust Actions, Reports
     const tabButtons = modal.locator('button').filter({ hasText: /listings|votes|trust/i })
-    const hasTab = (await tabButtons.count()) > 0
-    test.skip(!hasTab, 'No activity tabs available in user details modal')
+    await expect(tabButtons.first()).toBeVisible()
 
-    // Click the first available tab
     await tabButtons.first().click()
 
-    // Verify tab content loaded (modal should still be visible with content)
     await expect(modal).toBeVisible()
-    const modalContent = await modal.textContent()
-    expect(modalContent!.length).toBeGreaterThan(0)
 
-    // Close modal
     const closeButton = modal.locator('button').filter({ hasText: /close/i }).first()
-    if (await closeButton.isVisible({ timeout: 2000 }).catch(() => false)) {
-      await closeButton.click()
-    } else {
-      await page.keyboard.press('Escape')
-    }
+    await expect(closeButton).toBeVisible()
+    await closeButton.click()
   })
 
   test('should display user statistics', async ({ page }) => {
-    // AdminStatsDisplay renders stat cards at top of users page
-    // Stats include: Total Users, Users, Authors, Developers, Moderators, Admins, Super Admins, Banned
-    const statsLabels = [/total users/i, /users/i, /authors/i, /banned/i]
+    const statsLabels = [/total users/i, /authors/i]
 
-    let visibleStats = 0
     for (const label of statsLabels) {
-      const stat = page.getByText(label)
-      if (
-        await stat
-          .first()
-          .isVisible({ timeout: 2000 })
-          .catch(() => false)
-      ) {
-        visibleStats++
-      }
+      await expect(page.getByText(label).first()).toBeVisible()
     }
-    test.skip(visibleStats === 0, 'No user statistics section visible')
 
-    // Verify numeric values exist alongside stats
     const mainContent = page.locator('main').first()
     const numbersOnPage = await mainContent.locator('text=/\\d+/').count()
     expect(numbersOnPage).toBeGreaterThan(0)
diff --git a/tests/android-downloads.spec.ts b/tests/android-downloads.spec.ts
index 27ee3c90a..683e76064 100644
--- a/tests/android-downloads.spec.ts
+++ b/tests/android-downloads.spec.ts
@@ -2,14 +2,12 @@ import { test, expect } from '@playwright/test'
 
 test('Profile downloads tab loads (if feature-flag enabled)', async ({ page }) => {
   await page.goto('/profile?tab=downloads')
-  // Page should render without crashing; unauthenticated users see a friendly screen
   await expect(page).toHaveTitle(/My Profile|EmuReady/i)
   const downloadsTab = page.getByRole('tab', { name: /downloads/i })
   if ((await downloadsTab.count()) === 0) {
     test.skip(true, 'Downloads tab feature flag disabled in this environment')
   } else {
     await downloadsTab.click()
-    // The section title should be visible when enabled
     await expect(page.getByText(/Latest Build/i)).toBeVisible()
   }
 })
diff --git a/tests/auth.spec.ts b/tests/auth.spec.ts
index 96c8a5ca3..e097cabd3 100644
--- a/tests/auth.spec.ts
+++ b/tests/auth.spec.ts
@@ -1,338 +1,156 @@
+import { setupClerkTestingToken } from '@clerk/testing/playwright'
 import { test, expect } from '@playwright/test'
 import { AuthPage } from './pages/AuthPage'
 import { GamesPage } from './pages/GamesPage'
 import { HomePage } from './pages/HomePage'
 import { ListingsPage } from './pages/ListingsPage'
 
-test.describe('Modern Authentication Flow Tests', () => {
-  test('should show authentication buttons when not logged in', async ({ page, browserName }) => {
+test.describe('Unauthenticated User - Auth Buttons', () => {
+  test('should show sign in and sign up buttons on homepage', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
     const authPage = new AuthPage(page)
-
-    // Webkit sometimes has different rendering behavior
-    if (browserName === 'webkit') {
-      // For webkit, just verify we're on the home page and not authenticated
-      const isAuthenticated = await homePage.isAuthenticated()
-      expect(isAuthenticated).toBe(false)
-
-      // Try to find auth buttons with more flexible selectors
-      const signInVisible = await page
-        .getByText(/sign in/i)
-        .first()
-        .isVisible({ timeout: 5000 })
-        .catch(() => false)
-      const signUpVisible = await page
-        .getByText(/sign up/i)
-        .first()
-        .isVisible({ timeout: 5000 })
-        .catch(() => false)
-
-      // At least one auth option should be available
-      expect(signInVisible || signUpVisible).toBe(true)
-    } else {
-      await authPage.verifyUserNotAuthenticated()
-    }
+    await authPage.verifyUserNotAuthenticated()
+    await expect(authPage.signInButton).toBeVisible()
+    await expect(authPage.signUpButton).toBeVisible()
   })
 
-  test('should open sign in modal when sign in button is clicked', async ({ page }) => {
+  test('should show auth buttons consistently across pages', async ({ page }) => {
     const homePage = new HomePage(page)
-    await homePage.goto()
-
+    const gamesPage = new GamesPage(page)
+    const listingsPage = new ListingsPage(page)
     const authPage = new AuthPage(page)
-    const isMobile = page.viewportSize()?.width ? page.viewportSize()!.width < 768 : false
 
-    try {
-      if (isMobile) {
-        // On mobile, might need to open menu first
-        const mobileMenuVisible = await homePage.mobileMenuButton
-          .isVisible({ timeout: 1000 })
-          .catch(() => false)
-        if (mobileMenuVisible) {
-          await homePage.openMobileMenu()
-        }
-      }
+    await homePage.goto()
+    await expect(authPage.signInButton).toBeVisible()
+    await expect(authPage.signUpButton).toBeVisible()
+
+    await gamesPage.goto()
+    await expect(authPage.signInButton).toBeVisible()
+    await expect(authPage.signUpButton).toBeVisible()
 
-      await authPage.openSignInModal()
-      await authPage.verifySignInModalVisible()
-    } catch {
-      // If Clerk modal doesn't appear, verify auth elements are still present
-      if (isMobile) {
-        const isAuthenticated = await authPage.isAuthenticated()
-        expect(isAuthenticated).toBe(false)
-      } else {
-        await expect(authPage.signInButton).toBeVisible()
-      }
-    }
+    await listingsPage.goto()
+    await expect(authPage.signInButton).toBeVisible()
+    await expect(authPage.signUpButton).toBeVisible()
   })
+})
 
-  test('should open sign up modal when sign up button is clicked', async ({ page }) => {
+test.describe('Unauthenticated User - Clerk Redirects', () => {
+  // The Navbar's SignInButton/SignUpButton omit `mode`, so Clerk's default
+  // redirect mode applies — clicking navigates to the Clerk-hosted Account
+  // Portal. setupClerkTestingToken bypasses Clerk's bot protection.
+  test('should redirect to Clerk sign-in page when sign in button is clicked', async ({ page }) => {
+    await setupClerkTestingToken({ page })
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    const authPage = new AuthPage(page)
-    const isMobile = page.viewportSize()?.width ? page.viewportSize()!.width < 768 : false
-
-    try {
-      if (isMobile) {
-        // On mobile, might need to open menu first
-        const mobileMenuVisible = await homePage.mobileMenuButton
-          .isVisible({ timeout: 1000 })
-          .catch(() => false)
-        if (mobileMenuVisible) {
-          await homePage.openMobileMenu()
-        }
-      }
-
-      await authPage.openSignUpModal()
-      await authPage.verifySignUpModalVisible()
-    } catch {
-      // If Clerk modal doesn't appear, verify auth elements are still present
-      if (isMobile) {
-        const isAuthenticated = await authPage.isAuthenticated()
-        expect(isAuthenticated).toBe(false)
-      } else {
-        await expect(authPage.signUpButton).toBeVisible()
-      }
-    }
+    await page
+      .getByRole('button', { name: /^sign in$/i })
+      .first()
+      .click()
+    await expect(page).toHaveURL(/clerk\.accounts\.dev.*sign-in|\/sign-in/)
   })
 
-  test('should show authentication state correctly across pages', async ({ page }) => {
+  test('should redirect to Clerk sign-up page when sign up button is clicked', async ({ page }) => {
+    await setupClerkTestingToken({ page })
     const homePage = new HomePage(page)
-    const gamesPage = new GamesPage(page)
-    const listingsPage = new ListingsPage(page)
-    const authPage = new AuthPage(page)
-
-    // Start at home page and verify not authenticated
     await homePage.goto()
-    await authPage.verifyUserNotAuthenticated()
-
-    // Navigate to games page
-    await gamesPage.goto()
-    await authPage.verifyUserNotAuthenticated()
-
-    // Navigate to listings page
-    await listingsPage.goto()
-    await authPage.verifyUserNotAuthenticated()
 
-    // Authentication state should be consistent across all pages
-    await expect(authPage.signInButton).toBeVisible()
-    await expect(authPage.signUpButton).toBeVisible()
+    await page
+      .getByRole('button', { name: /^sign up$/i })
+      .first()
+      .click()
+    await expect(page).toHaveURL(/clerk\.accounts\.dev.*sign-up|\/sign-up/)
   })
+})
 
-  test('should handle mobile authentication menu', async ({ page }) => {
-    // Set mobile viewport
-    await page.setViewportSize({ width: 375, height: 667 })
+test.describe('Unauthenticated User - Mobile', () => {
+  test.use({ viewport: { width: 375, height: 667 } })
 
+  test('should show auth buttons in mobile menu', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    const authPage = new AuthPage(page)
+    await homePage.openMobileMenu()
 
-    // Open mobile menu
-    try {
-      await homePage.openMobileMenu()
-
-      // Should see authentication options in mobile menu
-      await expect(authPage.signInButton).toBeVisible()
-      await expect(authPage.signUpButton).toBeVisible()
-    } catch {
-      // At minimum, auth buttons should be accessible on mobile
-      await expect(authPage.signInButton).toBeVisible()
-      await expect(authPage.signUpButton).toBeVisible()
-    }
+    const authPage = new AuthPage(page)
+    await expect(authPage.signInButton).toBeVisible()
+    await expect(authPage.signUpButton).toBeVisible()
   })
 })
 
-test.describe('Protected Routes Tests', () => {
+test.describe('Protected Routes', () => {
   test('should require authentication for add game page', async ({ page }) => {
     const authPage = new AuthPage(page)
-
-    // Try to access add game page directly
     await page.goto('/games/new')
-
-    // Should show authentication requirement or redirect
-    try {
-      await authPage.verifyAuthRequired()
-    } catch {
-      // If no explicit auth requirement, verify the page responded in some way
-      const hasForm = await page.locator('form').isVisible({ timeout: 2000 })
-      // Either a form is shown (client-side auth protection) or no form (server-side protection)
-      expect(typeof hasForm).toBe('boolean')
-    }
+    await authPage.verifyAuthRequired()
   })
 
   test('should require authentication for add listing page', async ({ page }) => {
     const authPage = new AuthPage(page)
-
-    // Try to access add listing page directly
     await page.goto('/listings/new')
-
-    // Should show authentication requirement or redirect
-    try {
-      await authPage.verifyAuthRequired()
-    } catch {
-      // If no explicit auth requirement, verify the page responded in some way
-      const hasForm = await page.locator('form').isVisible({ timeout: 2000 })
-      expect(typeof hasForm).toBe('boolean')
-    }
+    await authPage.verifyAuthRequired()
   })
 
   test('should require authentication for profile page', async ({ page }) => {
     const authPage = new AuthPage(page)
-
-    // Try to access profile page directly
     await page.goto('/profile')
+    await authPage.verifyAuthRequired()
+  })
+})
 
-    // Should show authentication requirement or redirect
-    try {
-      await authPage.verifyAuthRequired()
-    } catch {
-      // Verify the page loaded (either with auth wall or redirect)
-      const hasProfileContent = await page.locator('main').textContent()
-      expect(hasProfileContent).toBeDefined()
-    }
+test.describe('Authenticated User', () => {
+  test.use({ storageState: 'tests/.auth/user.json' })
+
+  test('should show user button instead of sign in/up buttons', async ({ page }) => {
+    const homePage = new HomePage(page)
+    await homePage.goto()
+
+    const authPage = new AuthPage(page)
+    await expect(authPage.userButton).toBeVisible()
   })
+})
 
-  test('should handle browser navigation with authentication', async ({ page }) => {
+test.describe('Navigation with Auth State', () => {
+  test('should remain unauthenticated after visiting protected route and navigating back', async ({
+    page,
+  }) => {
     const homePage = new HomePage(page)
     const gamesPage = new GamesPage(page)
     const authPage = new AuthPage(page)
 
-    // Start at home page
     await homePage.goto()
     await authPage.verifyUserNotAuthenticated()
 
-    // Navigate to games page
     await gamesPage.goto()
     await authPage.verifyUserNotAuthenticated()
 
-    // Try to go to protected route (may redirect via Clerk middleware)
     await page.goto('/games/new')
-
-    // Use browser back button -- Clerk redirects may push extra history entries,
-    // so we may need multiple goBack() calls to return to our page
     await page.goBack()
-    await page.waitForLoadState('domcontentloaded')
-
-    // If we landed on an external Clerk URL, go back again
-    const currentUrl = page.url()
-    if (!currentUrl.includes('localhost') && !currentUrl.includes('127.0.0.1')) {
-      await page.goBack()
-      await page.waitForLoadState('domcontentloaded')
-    }
-
-    // We should land on a page within our app
-    expect(page.url()).toMatch(/localhost|127\.0\.0\.1/)
-
-    // Authentication state should persist
+    await expect(page).toHaveURL(/localhost|127\.0\.0\.1/)
     await authPage.verifyUserNotAuthenticated()
   })
 })
 
-test.describe('Authentication Error Handling', () => {
-  test('should handle authentication timeouts gracefully', async ({ page }) => {
+test.describe('Auth Error Handling', () => {
+  test('should show error feedback for invalid credentials', async ({ page }) => {
+    await setupClerkTestingToken({ page })
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    const authPage = new AuthPage(page)
-    const isMobile = page.viewportSize()?.width ? page.viewportSize()!.width < 768 : false
+    await page
+      .getByRole('button', { name: /^sign in$/i })
+      .first()
+      .click()
+    await expect(page).toHaveURL(/sign-in/)
 
-    // Try to open sign in modal with short timeout
-    try {
-      if (isMobile) {
-        // On mobile, might need to open menu first
-        const mobileMenuVisible = await homePage.mobileMenuButton
-          .isVisible({ timeout: 1000 })
-          .catch(() => false)
-        if (mobileMenuVisible) {
-          await homePage.openMobileMenu()
-        }
-      }
+    // Clerk's hosted form uses input name="identifier" then "password" — these
+    // input names are part of Clerk's public API.
+    await page.locator('input[name="identifier"]').fill('invalid-e2e-test@example.com')
+    await page.getByRole('button', { name: /continue/i }).click()
 
-      await authPage.signInButton.click()
-
-      // Wait for modal with shorter timeout to test error handling
-      await authPage.clerkSignInModal.waitFor({
-        state: 'visible',
-        timeout: 3000,
-      })
-
-      // Modal opened successfully
-      await expect(authPage.clerkSignInModal).toBeVisible()
-    } catch {
-      // Verify page is still functional after timeout
-      await expect(authPage.signInButton).toBeVisible()
-      await expect(page.getByText(/emuready/i).first()).toBeVisible()
-    }
-  })
-
-  test('should handle network errors during authentication', async ({ page }) => {
-    const homePage = new HomePage(page)
-    await homePage.goto()
-
-    const authPage = new AuthPage(page)
-    const isMobile = page.viewportSize()?.width ? page.viewportSize()!.width < 768 : false
-
-    if (isMobile) {
-      // On mobile, might need to open menu first
-      const mobileMenuVisible = await homePage.mobileMenuButton
-        .isVisible({ timeout: 1000 })
-        .catch(() => false)
-      if (mobileMenuVisible) {
-        await homePage.openMobileMenu()
-      }
-    }
-
-    // Verify authentication elements are present
-    await expect(authPage.signInButton).toBeVisible()
-    await expect(authPage.signUpButton).toBeVisible()
-
-    // Click sign in button
-    await authPage.signInButton.click()
-
-    // Page should remain functional regardless of Clerk setup
-    await expect(page.getByText(/emuready/i).first()).toBeVisible()
-  })
-
-  test('should provide clear feedback for authentication failures', async ({ page }) => {
-    const homePage = new HomePage(page)
-    await homePage.goto()
-
-    const authPage = new AuthPage(page)
-    const isMobile = page.viewportSize()?.width ? page.viewportSize()!.width < 768 : false
-
-    try {
-      if (isMobile) {
-        // On mobile, might need to open menu first
-        const mobileMenuVisible = await homePage.mobileMenuButton
-          .isVisible({ timeout: 1000 })
-          .catch(() => false)
-        if (mobileMenuVisible) {
-          await homePage.openMobileMenu()
-        }
-      }
-
-      await authPage.openSignInModal()
-
-      // If modal opens, try to interact with it
-      if (await authPage.emailInput.isVisible({ timeout: 2000 })) {
-        // Try invalid credentials
-        await authPage.fillSignInForm('invalid@example.com', 'wrongpassword')
-        await authPage.submitSignIn()
-
-        // Check for error messages
-        const hasError = await page
-          .getByText(/error|invalid|incorrect/i)
-          .isVisible({ timeout: 2000 })
-        expect(hasError).toBe(true)
-      }
-    } catch {
-      // Authentication modal not available in test environment is acceptable
-    }
-
-    // Verify page remains functional after any authentication attempts
-    await expect(authPage.signInButton).toBeVisible()
+    // role="alert" is Clerk's stable error contract; internal classes are not.
+    await expect(page.getByRole('alert').first()).toBeVisible()
   })
 })
diff --git a/tests/badge-system.spec.ts b/tests/badge-system.spec.ts
index b4f6b40ab..0090d81c8 100644
--- a/tests/badge-system.spec.ts
+++ b/tests/badge-system.spec.ts
@@ -1,155 +1,66 @@
 import { test, expect } from '@playwright/test'
 
-/**
- * E2E tests for the badge system.
- *
- * Admin page: /admin/badges (heading: "Badge Management")
- * Stats: Total Badges, Active, Inactive, Total Assignments.
- * Badge CRUD via modal forms.
- * Badges display on user profiles as colored pills.
- */
-
 test.describe('Badge System', () => {
   test.describe('Admin Badge Management', () => {
-    test.use({ storageState: 'tests/.auth/admin.json' })
+    test.use({ storageState: 'tests/.auth/super_admin.json' })
 
-    test('should access badge management page', async ({ page }) => {
+    test('should display page heading', async ({ page }) => {
       await page.goto('/admin/badges', { waitUntil: 'domcontentloaded' })
 
-      // Page heading
       await expect(page.getByRole('heading', { name: /badge management/i })).toBeVisible()
-
-      // Stats display: Total Badges, Active, Inactive, Total Assignments
-      const stats = page.locator('text=/total badges|active|inactive|total assignments/i')
-      await expect(stats.first()).toBeVisible()
     })
 
     test('should display badge statistics', async ({ page }) => {
       await page.goto('/admin/badges', { waitUntil: 'domcontentloaded' })
 
-      // Wait for badge data to load
-      await page
-        .getByText(/loading/i)
-        .waitFor({ state: 'hidden', timeout: 15000 })
-        .catch(() => {})
-
-      // Wait for any content to appear
-      const statsText = page.getByText(/total badges/i)
-      const table = page.locator('table')
-      const emptyState = page.getByText(/no badges/i)
-      const heading = page.getByRole('heading', { name: /badge management/i })
-
-      await statsText
-        .or(table)
-        .or(emptyState)
-        .or(heading)
-        .first()
-        .waitFor({ state: 'visible', timeout: 10000 })
-
-      const hasStats = await statsText.isVisible().catch(() => false)
-      const hasTable = await table.isVisible().catch(() => false)
-      const hasEmptyState = await emptyState.isVisible().catch(() => false)
-      const hasHeading = await heading.isVisible().catch(() => false)
-
-      expect(hasStats || hasTable || hasEmptyState || hasHeading).toBe(true)
+      await expect(page.getByText(/total badges/i)).toBeVisible()
     })
 
-    test('should show search input and badge table', async ({ page }) => {
+    test('should show search input', async ({ page }) => {
       await page.goto('/admin/badges', { waitUntil: 'domcontentloaded' })
 
-      // Search input
       await expect(page.getByPlaceholder(/search badges/i)).toBeVisible()
-
-      // Wait for badge data to load
-      const table = page.locator('table')
-      const emptyState = page.getByText(/no badges/i)
-
-      await table
-        .or(emptyState)
-        .waitFor({ state: 'visible', timeout: 15000 })
-        .catch(() => {})
-
-      const hasTable = await table.isVisible().catch(() => false)
-      const hasEmpty = await emptyState.isVisible().catch(() => false)
-
-      expect(hasTable || hasEmpty).toBe(true)
     })
 
-    test('should display badge table columns', async ({ page }) => {
+    test('should show badge table or empty state', async ({ page }) => {
       await page.goto('/admin/badges', { waitUntil: 'domcontentloaded' })
 
-      const table = page.locator('table')
-      const hasTable = await table.isVisible().catch(() => false)
-
-      if (hasTable) {
-        // Expected columns: Badge, Description, Assignments, Status, Creator, Created, Actions
-        const headerText = await page.locator('thead').textContent()
-        expect(headerText?.toLowerCase()).toMatch(/badge/)
-        expect(headerText?.toLowerCase()).toMatch(/status/)
-      }
+      await expect(page.locator('table').or(page.getByText(/no badges/i))).toBeVisible()
     })
 
-    test('should open create badge modal', async ({ page }) => {
+    test('should display badge table with expected columns', async ({ page }) => {
       await page.goto('/admin/badges', { waitUntil: 'domcontentloaded' })
 
-      const createButton = page.getByRole('button', { name: /create badge/i })
-      const hasButton = await createButton.isVisible().catch(() => false)
-
-      if (hasButton) {
-        await createButton.click()
-
-        // Modal should open with "Create New Badge" heading
-        await expect(page.getByText(/create new badge/i)).toBeVisible()
-
-        // Form fields: Name, Description, Color, Icon, Active
-        const nameInput = page.getByPlaceholder(/enter badge name/i)
-        await expect(nameInput).toBeVisible()
-
-        const descriptionInput = page.getByPlaceholder(/enter badge description/i)
-        await expect(descriptionInput).toBeVisible()
+      const table = page.locator('table')
+      await expect(table).toBeVisible()
 
-        // Color picker
-        const colorInput = page.locator('input[type="color"]')
-        await expect(colorInput).toBeVisible()
-      }
+      const headerText = await page.locator('thead').textContent()
+      expect(headerText?.toLowerCase()).toMatch(/badge/)
+      expect(headerText?.toLowerCase()).toMatch(/status/)
     })
 
-    test('should filter badges by status', async ({ page }) => {
+    test('should open create badge modal', async ({ page }) => {
       await page.goto('/admin/badges', { waitUntil: 'domcontentloaded' })
 
-      const statusFilter = page.locator('select').filter({ hasText: /all status|active|inactive/i })
-      const hasFilter = await statusFilter.isVisible().catch(() => false)
+      const createButton = page.getByRole('button', { name: /create badge/i })
+      await expect(createButton).toBeVisible()
+      await createButton.click()
 
-      if (hasFilter) {
-        await statusFilter.selectOption({ label: 'Active' })
-        await expect(page.locator('table')).toBeVisible()
-      }
+      await expect(page.getByText(/create new badge/i)).toBeVisible()
+      await expect(page.getByPlaceholder(/enter badge name/i)).toBeVisible()
+      await expect(page.getByPlaceholder(/enter badge description/i)).toBeVisible()
+      await expect(page.locator('input[type="color"]')).toBeVisible()
     })
 
     test('should search badges by name', async ({ page }) => {
       await page.goto('/admin/badges', { waitUntil: 'domcontentloaded' })
 
-      // Wait for initial badge data to load before searching
-      const table = page.locator('table')
-      const emptyState = page.getByText(/no badges/i)
-      await table
-        .or(emptyState)
-        .waitFor({ state: 'visible', timeout: 15000 })
-        .catch(() => {})
+      await expect(page.locator('table').or(page.getByText(/no badges/i))).toBeVisible()
 
       const searchInput = page.getByPlaceholder(/search badges/i)
       await searchInput.fill('test')
 
-      // Wait for search results to load
-      await table
-        .or(emptyState)
-        .waitFor({ state: 'visible', timeout: 10000 })
-        .catch(() => {})
-
-      const hasTable = await table.isVisible().catch(() => false)
-      const hasNoResults = await emptyState.isVisible().catch(() => false)
-
-      expect(hasTable || hasNoResults).toBe(true)
+      await expect(page.locator('table').or(page.getByText(/no badges/i))).toBeVisible()
     })
   })
 })
diff --git a/tests/browsing.spec.ts b/tests/browsing.spec.ts
index e85af60eb..87a2f879e 100644
--- a/tests/browsing.spec.ts
+++ b/tests/browsing.spec.ts
@@ -3,408 +3,205 @@ import { GamesPage } from './pages/GamesPage'
 import { HomePage } from './pages/HomePage'
 import { ListingsPage } from './pages/ListingsPage'
 
-test.describe('Modern Browsing Tests', () => {
-  test('should display home page with latest content', async ({ page }) => {
+test.describe('Browsing Tests', () => {
+  test('should display home page with hero and latest reports', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Verify hero section
     await homePage.verifyHeroSectionVisible()
-
-    // Verify latest listings section
     await homePage.verifyLatestListingsVisible()
-
-    // Check if browse links work
-    const browseLink = page.getByRole('link', { name: /browse.*compatibility/i }).first()
-    if (await browseLink.isVisible({ timeout: 2000 }).catch(() => false)) {
-      await browseLink.click()
-      await expect(page).toHaveURL(/\/(listings|pc-listings)/)
-    }
   })
 
-  test('should display games library with content', async ({ page }) => {
+  test('should display games library with game items', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Verify page loads
     await gamesPage.verifyPageLoaded()
 
-    // Verify games are displayed
-    await gamesPage.verifyGamesVisible()
-
-    // Check that we have game content
-    const gameCount = await gamesPage.getGameCount()
-    test.skip(gameCount === 0, 'No games available for this test')
-
-    // Verify first game is clickable
     await expect(gamesPage.gameItems.first()).toBeVisible()
-
-    // Verify game headings exist
-    await gamesPage.verifyGameHeadingsVisible()
   })
 
   test('should display compatibility listings with content', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    // Verify page loads
     await listingsPage.verifyPageLoaded()
+    await expect(listingsPage.filtersHeading).toBeVisible()
 
-    // Verify filters section
-    await listingsPage.verifyFiltersHeadingVisible()
-
-    // Verify listings are displayed
-    await listingsPage.verifyListingsVisible()
-
-    // Check that we have listing content
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available for this test')
-
-    // Verify first listing is clickable
     await expect(listingsPage.listingItems.first()).toBeVisible()
   })
 
-  test('should navigate between different content areas', async ({ page }) => {
+  test('should navigate between home, games, and listings', async ({ page }) => {
     const homePage = new HomePage(page)
     const gamesPage = new GamesPage(page)
     const listingsPage = new ListingsPage(page)
 
-    // Start at home
     await homePage.goto()
     await homePage.verifyHeroSectionVisible()
 
-    // Navigate to games
     await homePage.waitForOverlaysToDisappear()
     await homePage.navigateToGames()
     await gamesPage.verifyPageLoaded()
 
-    // Navigate to handheld listings
     await gamesPage.waitForOverlaysToDisappear()
     await gamesPage.navigateToHandheld()
     await listingsPage.verifyPageLoaded()
 
-    // Navigate back to home via logo
     await listingsPage.waitForOverlaysToDisappear()
     await listingsPage.clickLogo()
     await homePage.verifyHeroSectionVisible()
-
-    // PC navigation is tested separately due to overlay timing issues
   })
 
-  test('should navigate to PC listings', async ({ page }) => {
-    const homePage = new HomePage(page)
-    await homePage.goto()
-
-    // Wait for page to fully load and dismiss any overlays
-    await homePage.waitForPageLoad()
-
-    // Navigate directly to PC listings
+  test('should load PC listings page', async ({ page }) => {
     await page.goto('/pc-listings')
     await page.waitForLoadState('domcontentloaded')
 
-    // Verify we're on PC listings page
     await expect(page).toHaveURL('/pc-listings')
-
-    // PC listings page might use icons or different layout on mobile
-    // Look for any PC-related content, including icons, buttons, or navigation
-    const hasPCContent =
-      (await page
-        .getByText(/pc|computer|cpu|gpu|memory|add pc listing/i)
-        .first()
-        .isVisible({ timeout: 5000 })
-        .catch(() => false)) ||
-      (await page
-        .locator('[data-testid*="pc"]')
-        .first()
-        .isVisible({ timeout: 5000 })
-        .catch(() => false)) ||
-      (await page
-        .locator('svg')
-        .first()
-        .isVisible({ timeout: 5000 })
-        .catch(() => false)) ||
-      (await page
-        .getByRole('link', { name: /add.*pc.*listing/i })
-        .first()
-        .isVisible({ timeout: 5000 })
-        .catch(() => false)) ||
-      (await page
-        .getByRole('button')
-        .first()
-        .isVisible({ timeout: 5000 })
-        .catch(() => false))
-
-    expect(hasPCContent).toBe(true)
+    await expect(page.getByRole('heading', { name: /pc/i }).first()).toBeVisible()
   })
 
-  test('should handle game detail navigation', async ({ page }) => {
+  test('should navigate to a game detail page', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
     await gamesPage.verifyPageLoaded()
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
-
-    const gameCount = await gamesPage.getGameCount()
-    test.skip(gameCount === 0, 'No games available to test navigation')
-
-    await gamesPage.cookieBanner.dismissIfPresent()
-
-    const firstGame = gamesPage.gameItems.first()
-    await firstGame.waitFor({ state: 'visible', timeout: 5000 })
-    const href = await firstGame.getAttribute('href')
-    await firstGame.click({ force: true })
-
-    try {
-      await expect(page).toHaveURL(/\/games\/[^/?]+/, { timeout: 5000 })
-    } catch {
-      // Client-side navigation may have been absorbed; use direct navigation
-      if (href) {
-        await page.goto(href)
-        await expect(page).toHaveURL(/\/games\/[^/?]+/, { timeout: 5000 })
-      }
-    }
-
-    await expect(page.locator('h1, h2').first()).toBeVisible({ timeout: 5000 })
+
+    await expect(gamesPage.gameItems.first()).toBeVisible()
+
+    await gamesPage.clickFirstGame()
+
+    await expect(page.locator('h1').first()).toBeVisible()
   })
 
-  test('should handle listing detail navigation', async ({ page }) => {
+  test('should navigate to a listing detail page', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
     await listingsPage.verifyPageLoaded()
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
-
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test navigation')
-
-    await listingsPage.cookieBanner.dismissIfPresent()
-
-    const firstListing = listingsPage.listingItems.first()
-    await firstListing.waitFor({ state: 'visible', timeout: 5000 })
-    const href = await firstListing.getAttribute('href')
-    await firstListing.click({ force: true })
-
-    try {
-      await expect(page).toHaveURL(/\/listings\/[^/?]+/, { timeout: 5000 })
-    } catch {
-      // Client-side navigation may have been absorbed; use direct navigation
-      if (href) {
-        await page.goto(href)
-        await expect(page).toHaveURL(/\/listings\/[^/?]+/, { timeout: 5000 })
-      }
-    }
-
-    await expect(page.locator('h1, h2').first()).toBeVisible({ timeout: 5000 })
+
+    await expect(listingsPage.listingItems.first()).toBeVisible()
+
+    await listingsPage.clickFirstListing()
+
+    await expect(page.locator('h1').first()).toBeVisible()
   })
 })
 
-test.describe('Modern Content Display Tests', () => {
-  test('should display consistent branding across pages', async ({ page }) => {
-    const pages = [
-      { path: '/', name: 'home' },
-      { path: '/games', name: 'games' },
-      { path: '/listings', name: 'listings' },
-      { path: '/pc-listings', name: 'pc-listings' },
-    ]
-
-    const isMobile = page.viewportSize()?.width ? page.viewportSize()!.width < 768 : false
-
-    for (const pageInfo of pages) {
-      await page.goto(pageInfo.path)
-      await page.waitForLoadState('domcontentloaded')
-
-      // Should see EmuReady branding - might be in logo or mobile menu
-      const brandingVisible =
-        (await page
-          .getByText(/emuready/i)
-          .first()
-          .isVisible({ timeout: 5000 })
-          .catch(() => false)) ||
-        (await page
-          .getByRole('link', { name: /emuready/i })
-          .first()
-          .isVisible({ timeout: 5000 })
-          .catch(() => false))
-      expect(brandingVisible).toBe(true)
-
-      // Navigation visibility depends on viewport
-      const homePage = new HomePage(page)
-      if (isMobile) {
-        // On mobile, check for mobile menu button instead of nav links
-        const mobileMenuVisible = await homePage.mobileMenuButton
-          .isVisible({ timeout: 2000 })
-          .catch(() => false)
-        const navLinksVisible = await homePage.homeLink
-          .isVisible({ timeout: 2000 })
-          .catch(() => false)
-        expect(mobileMenuVisible || navLinksVisible).toBe(true)
-      } else {
-        // Desktop should have visible nav links (Home, Handheld, PC)
-        await expect(homePage.homeLink).toBeVisible()
-        await expect(homePage.handheldLink).toBeVisible()
-        await expect(homePage.pcLink).toBeVisible()
-      }
-    }
+test.describe('Content Display Tests', () => {
+  test('should display EmuReady branding on home page', async ({ page }) => {
+    const homePage = new HomePage(page)
+    await homePage.goto()
+
+    await expect(homePage.logo).toBeVisible()
   })
 
-  test('should display footer information consistently', async ({ page }) => {
-    const pages = ['/', '/games', '/listings']
-
-    for (const pagePath of pages) {
-      await page.goto(pagePath)
-
-      // Check for footer elements - use more specific locators to avoid duplicates
-      try {
-        // Look for footer specifically
-        const footer = page.locator('footer').first()
-        await footer.waitFor({ state: 'visible', timeout: 5000 })
-
-        // Check for footer content within the footer element
-        const hasAboutLink = await footer
-          .getByRole('link', { name: /about/i })
-          .isVisible({ timeout: 2000 })
-        const hasCommunityLink = await footer.getByText(/community/i).isVisible({ timeout: 2000 })
-        const hasCopyright = await footer.getByText(/emuready/i).isVisible({ timeout: 2000 })
-
-        // At least some footer content should be present
-        expect(hasAboutLink || hasCommunityLink || hasCopyright).toBe(true)
-      } catch {
-        // Fallback: check if page has any footer-like content at the bottom
-        const hasCopyright = await page.getByText(/© \d{4} emuready/i).isVisible({ timeout: 2000 })
-        // Use nth() to avoid multiple element matches
-        const hasEmuReady = await page
-          .getByText(/emuready/i)
-          .nth(0)
-          .isVisible({ timeout: 1000 })
-        expect(hasCopyright || hasEmuReady).toBe(true)
-      }
-    }
+  test('should display EmuReady branding on games page', async ({ page }) => {
+    await page.goto('/games')
+    await page.waitForLoadState('domcontentloaded')
+
+    const homePage = new HomePage(page)
+    await expect(homePage.logo).toBeVisible()
   })
 
-  test('should handle empty states appropriately', async ({ page }) => {
-    const gamesPage = new GamesPage(page)
-    await gamesPage.goto()
+  test('should display EmuReady branding on listings page', async ({ page }) => {
+    await page.goto('/listings')
+    await page.waitForLoadState('domcontentloaded')
 
-    const gameCount = await gamesPage.getGameCount()
-    test.skip(gameCount > 0, 'Games page has content - empty state test not applicable')
+    const homePage = new HomePage(page)
+    await expect(homePage.logo).toBeVisible()
+  })
 
-    // Page should still be functional
-    await gamesPage.verifyPageLoaded()
+  test('should display footer on home page', async ({ page }) => {
+    await page.goto('/')
+
+    const footer = page.locator('footer').first()
+    await expect(footer).toBeVisible()
+    await expect(footer.getByText(/emuready/i).first()).toBeVisible()
+  })
+
+  test('should display footer on games page', async ({ page }) => {
+    await page.goto('/games')
+
+    const footer = page.locator('footer').first()
+    await expect(footer).toBeVisible()
+    await expect(footer.getByText(/emuready/i).first()).toBeVisible()
+  })
+
+  test('should display footer on listings page', async ({ page }) => {
+    await page.goto('/listings')
 
-    // Should have appropriate messaging
-    const bodyText = await page.textContent('body')
-    expect(bodyText).toBeTruthy()
-    expect(bodyText!.length).toBeGreaterThan(100)
+    const footer = page.locator('footer').first()
+    await expect(footer).toBeVisible()
+    await expect(footer.getByText(/emuready/i).first()).toBeVisible()
   })
 
-  test('should display page titles correctly', async ({ page }) => {
-    const pages = [
-      { path: '/', expectedTitle: /home/i },
-      { path: '/games', expectedTitle: /games.*emuready/i },
-      { path: '/listings', expectedTitle: /compatibility.*reports.*emuready/i },
-    ]
+  test('should show empty state when search returns no games', async ({ page }) => {
+    // A no-match search term exercises the same empty-state branch as a
+    // truly empty database, without needing to mock the tRPC query.
+    await page.goto('/games?search=zzqqxxnotarealgame42')
 
-    for (const pageInfo of pages) {
-      await page.goto(pageInfo.path)
+    await expect(page.getByText(/no games found matching your criteria/i)).toBeVisible()
+  })
+
+  test('should display correct page titles', async ({ page }) => {
+    await page.goto('/')
+    await expect(page).toHaveTitle(/home/i)
 
-      const title = await page.title()
-      expect(title).toMatch(pageInfo.expectedTitle)
-    }
+    await page.goto('/games')
+    await expect(page).toHaveTitle(/games.*emuready/i)
+
+    await page.goto('/listings')
+    await expect(page).toHaveTitle(/compatibility.*reports.*emuready/i)
   })
 })
 
-test.describe('Modern Responsive Design Tests', () => {
-  test('should work correctly on tablet viewport', async ({ page }) => {
-    // Set tablet viewport
-    await page.setViewportSize({ width: 768, height: 1024 })
+test.describe('Responsive Design - Tablet', () => {
+  test.use({ viewport: { width: 768, height: 1024 } })
 
+  test('should load and navigate on tablet viewport', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Verify page loads and is functional
     await homePage.verifyHeroSectionVisible()
     await homePage.verifyNavigationVisible()
 
-    // Test navigation on tablet
     await homePage.navigateToGames()
     await expect(page).toHaveURL('/games')
 
     const gamesPage = new GamesPage(page)
     await gamesPage.verifyPageLoaded()
   })
+})
 
-  test('should work correctly on mobile viewport', async ({ page }) => {
-    // Set mobile viewport
-    await page.setViewportSize({ width: 375, height: 667 })
+test.describe('Responsive Design - Mobile', () => {
+  test.use({ viewport: { width: 375, height: 667 } })
 
+  test('should load home page on mobile viewport', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
-    await homePage.waitForPageLoad()
 
-    // The page should have loaded with mobile viewport
     await expect(page).toHaveURL('/')
-
-    // Check for any visible elements that indicate the page loaded
-    const pageElements = [
-      homePage.mobileMenuButton,
-      homePage.homeLink,
-      homePage.gamesLink,
-      page.locator('nav').first(),
-      page.getByRole('button').first(),
-      page.getByRole('link').first(),
-      page.locator('main').first(),
-      page.locator('header').first(),
-      page.locator('div').first(),
-    ]
-
-    let foundVisibleElement = false
-    for (const element of pageElements) {
-      try {
-        const isVisible = await element.isVisible({ timeout: 1000 })
-        if (isVisible) {
-          foundVisibleElement = true
-          break
-        }
-      } catch {
-        // Element might not exist, continue checking
-      }
-    }
-
-    // At minimum, we should have some visible content
-    expect(foundVisibleElement).toBe(true)
-
-    // Try to interact with mobile navigation if available
-    const hasMobileMenu = await homePage.mobileMenuButton
-      .isVisible({ timeout: 2000 })
-      .catch(() => false)
-    if (hasMobileMenu) {
-      await homePage.openMobileMenu()
-    }
+    await expect(homePage.mobileMenuButton).toBeVisible()
   })
 
-  test('should handle viewport changes gracefully', async ({ page }) => {
+  test('should open mobile menu on mobile viewport', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Start with desktop
-    await page.setViewportSize({ width: 1200, height: 800 })
-    await homePage.verifyNavigationVisible()
+    await expect(homePage.mobileMenuButton).toBeVisible()
+    await homePage.openMobileMenu()
+    await expect(homePage.mobileMenu).toBeVisible()
+  })
+
+  test('should display desktop nav after resizing from mobile to desktop', async ({ page }) => {
+    const homePage = new HomePage(page)
+    await homePage.goto()
 
-    // Change to mobile
-    await page.setViewportSize({ width: 375, height: 667 })
     await expect(homePage.mobileMenuButton).toBeVisible()
 
-    // Change back to desktop
     await page.setViewportSize({ width: 1200, height: 800 })
     await homePage.verifyNavigationVisible()
 
-    // Page should remain functional throughout
     await expect(page.getByText(/emuready/i).first()).toBeVisible()
   })
 })
diff --git a/tests/commenting.spec.ts b/tests/commenting.spec.ts
index 77813b5a4..7b6c101b4 100644
--- a/tests/commenting.spec.ts
+++ b/tests/commenting.spec.ts
@@ -6,30 +6,26 @@ test.describe('Commenting System Tests', () => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test comments')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // GenericCommentThread renders "Comments ({count})" as an h3 heading
     const commentsHeading = page.getByRole('heading', { name: /comments/i })
-    await expect(commentsHeading).toBeVisible({ timeout: 10000 })
+    await expect(commentsHeading).toBeVisible()
   })
 
   test('should show comment count in heading', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test comments')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // Heading includes comment count in parentheses: "Comments (N)"
     const commentsHeading = page.getByRole('heading', { name: /comments\s*\(\d+\)/i })
-    await expect(commentsHeading).toBeVisible({ timeout: 10000 })
+    await expect(commentsHeading).toBeVisible()
   })
 
   test('should show sign-in prompt for unauthenticated users instead of comment form', async ({
@@ -38,41 +34,34 @@ test.describe('Commenting System Tests', () => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test comments')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // Wait for comments section to load before checking the prompt
     const commentsHeading = page.getByRole('heading', { name: /comments/i })
-    await expect(commentsHeading).toBeVisible({ timeout: 15000 })
+    await expect(commentsHeading).toBeVisible()
 
-    // Unauthenticated users see "Please sign in to leave a comment."
-    // The text is split across elements (sign in is inside a button), so match the container
     const signInPrompt = page
       .locator('p, div')
       .filter({ hasText: /sign in/i })
       .filter({ hasText: /to leave a comment/i })
       .first()
-    await expect(signInPrompt).toBeVisible({ timeout: 10000 })
+    await expect(signInPrompt).toBeVisible()
   })
 
   test('should not show comment textarea for unauthenticated users', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test comments')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // Wait for comments section to load
     const commentsHeading = page.getByRole('heading', { name: /comments/i })
-    await expect(commentsHeading).toBeVisible({ timeout: 10000 })
+    await expect(commentsHeading).toBeVisible()
 
-    // Comment textarea with placeholder "Write your comment..." should not be visible
     const commentTextarea = page.getByPlaceholder(/write your comment/i)
     await expect(commentTextarea).not.toBeVisible()
   })
@@ -81,40 +70,33 @@ test.describe('Commenting System Tests', () => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test comments')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
     const commentsHeading = page.getByRole('heading', { name: /comments/i })
-    await expect(commentsHeading).toBeVisible({ timeout: 10000 })
+    await expect(commentsHeading).toBeVisible()
 
-    // Either comments exist (rendered as cards with bg-white rounded-lg)
-    // or the empty state shows "No comments yet. Be the first to share your thoughts!"
     const commentCards = page.locator('[id^="comment-"]')
     const emptyState = page.getByText(/no comments yet/i)
 
-    const hasComments = (await commentCards.count()) > 0
-    const hasEmptyState = await emptyState.isVisible().catch(() => false)
-
-    expect(hasComments || hasEmptyState).toBe(true)
+    // Either comments or empty state must be visible
+    await expect(commentCards.first().or(emptyState)).toBeVisible()
   })
 
   test('should not show reply button for unauthenticated users', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test comments')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
     const commentsHeading = page.getByRole('heading', { name: /comments/i })
-    await expect(commentsHeading).toBeVisible({ timeout: 10000 })
+    await expect(commentsHeading).toBeVisible()
 
-    // Reply buttons are only rendered when user is authenticated
     const replyButtons = page.getByRole('button', { name: /reply/i })
     await expect(replyButtons).toHaveCount(0)
   })
diff --git a/tests/custom-fields.spec.ts b/tests/custom-fields.spec.ts
index 766a3fa03..096d43c01 100644
--- a/tests/custom-fields.spec.ts
+++ b/tests/custom-fields.spec.ts
@@ -1,14 +1,5 @@
 import { test, expect } from '@playwright/test'
 
-/**
- * E2E tests for the custom field templates admin page.
- *
- * Route: /admin/custom-field-templates
- * Heading: "Custom Field Templates"
- * Templates are managed via modal forms, listed as cards.
- * No data-testid attributes exist — uses roles, text, and structure.
- */
-
 test.describe('Custom Fields System', () => {
   test.describe('Admin Custom Field Template Management', () => {
     test.use({ storageState: 'tests/.auth/admin.json' })
@@ -17,35 +8,21 @@ test.describe('Custom Fields System', () => {
       await page.goto('/admin/custom-field-templates')
       await page.waitForLoadState('domcontentloaded')
 
-      // Page heading
       await expect(page.getByRole('heading', { name: /custom field templates/i })).toBeVisible()
-
-      // Stats display should show template counts
-      const stats = page.locator('text=/total templates|with fields|empty templates/i')
-      await expect(stats.first()).toBeVisible()
+      await expect(
+        page.locator('text=/total templates|with fields|empty templates/i').first(),
+      ).toBeVisible()
     })
 
     test('should open create template modal', async ({ page }) => {
       await page.goto('/admin/custom-field-templates')
       await page.waitForLoadState('domcontentloaded')
 
-      // Click "Create Template" button
-      const createButton = page.getByRole('button', { name: /create template/i })
-      await expect(createButton).toBeVisible()
-      await createButton.click()
+      await page.getByRole('button', { name: /create template/i }).click()
 
-      // Modal should open with "Create Template" heading
       await expect(page.getByRole('heading', { name: /create template/i, level: 2 })).toBeVisible()
-
-      // Template Name field
-      const nameInput = page.locator('#templateName')
-      await expect(nameInput).toBeVisible()
-
-      // Description field
-      const descriptionInput = page.locator('#templateDescription')
-      await expect(descriptionInput).toBeVisible()
-
-      // "Add Field" button in the form
+      await expect(page.locator('#templateName')).toBeVisible()
+      await expect(page.locator('#templateDescription')).toBeVisible()
       await expect(page.getByRole('button', { name: /add field/i })).toBeVisible()
     })
 
@@ -54,60 +31,39 @@ test.describe('Custom Fields System', () => {
       await page.waitForLoadState('domcontentloaded')
 
       await page.getByRole('button', { name: /create template/i }).click()
-
-      // Fill template name
       await page.locator('#templateName').fill('Test Template')
-
-      // Click "Add Field" to add a field definition
       await page.getByRole('button', { name: /add field/i }).click()
 
-      // Field 1 card should appear with field name and label inputs
       await expect(page.getByText('Field 1')).toBeVisible()
-
-      // Field type dropdown should be present
-      const fieldTypeSelect = page
-        .locator('select')
-        .filter({ hasText: /text|long text|url|yes\/no|dropdown|range/i })
-      await expect(fieldTypeSelect.first()).toBeVisible()
+      await expect(
+        page
+          .locator('select')
+          .filter({ hasText: /text|long text|url|yes\/no|dropdown|range/i })
+          .first(),
+      ).toBeVisible()
     })
 
-    test('should show template cards in the list', async ({ page }) => {
+    test('should show template cards or empty state in the list', async ({ page }) => {
       await page.goto('/admin/custom-field-templates')
       await page.waitForLoadState('domcontentloaded')
 
-      // Check for template cards or empty state
       const templateCards = page.locator('h3')
       const emptyState = page.getByText(/no custom field templates/i)
-
-      const hasTemplates = (await templateCards.count()) > 0
-      const hasEmptyState = await emptyState.isVisible().catch(() => false)
-
-      // Page must show either templates or empty state
-      expect(hasTemplates || hasEmptyState).toBe(true)
-
-      if (hasTemplates) {
-        // Each template card has action buttons (Edit, Delete, etc.)
-        const editButtons = page.getByRole('button', { name: /edit/i })
-        await expect(editButtons.first()).toBeVisible()
-      }
+      await expect(templateCards.first().or(emptyState)).toBeVisible()
     })
 
     test('should toggle field visibility on template card', async ({ page }) => {
       await page.goto('/admin/custom-field-templates')
       await page.waitForLoadState('domcontentloaded')
 
-      // Look for "Show Fields" button on a template card
       const showFieldsButton = page
         .getByRole('button', { name: /show fields|hide fields/i })
         .first()
-      const hasTemplates = await showFieldsButton.isVisible().catch(() => false)
+      await expect(showFieldsButton).toBeVisible()
 
-      if (hasTemplates) {
-        await showFieldsButton.click()
+      await showFieldsButton.click()
 
-        // After clicking, should see field details or "Hide Fields" text
-        await expect(page.getByText(/template fields:|hide fields/i).first()).toBeVisible()
-      }
+      await expect(page.getByText(/template fields:|hide fields/i).first()).toBeVisible()
     })
   })
 
@@ -118,19 +74,10 @@ test.describe('Custom Fields System', () => {
       await page.goto('/listings/new')
       await page.waitForLoadState('domcontentloaded')
 
-      // The form should have game, device, emulator, performance fields
       await expect(page.getByText(/game/i).first()).toBeVisible()
       await expect(page.getByText(/device/i).first()).toBeVisible()
       await expect(page.getByText(/emulator/i).first()).toBeVisible()
-
-      // Before emulator selection, custom fields section shows placeholder
-      const customFieldsPlaceholder = page.getByText(/select an emulator/i)
-      const isPlaceholderVisible = await customFieldsPlaceholder.isVisible().catch(() => false)
-
-      // Placeholder prompts user to select an emulator first
-      if (isPlaceholderVisible) {
-        await expect(customFieldsPlaceholder).toBeVisible()
-      }
+      await expect(page.getByText(/select an emulator/i)).toBeVisible()
     })
   })
 })
diff --git a/tests/data-setup.spec.ts b/tests/data-setup.spec.ts
new file mode 100644
index 000000000..f28dc5af9
--- /dev/null
+++ b/tests/data-setup.spec.ts
@@ -0,0 +1,37 @@
+import { test } from '@playwright/test'
+import {
+  createHandheldListing,
+  createPcListing,
+  createReport,
+  withContext,
+} from './helpers/data-factory'
+
+// Runs after auth setup, before all specs. Creates baseline data other tests
+// depend on. The test user has enough trust score that their listings are
+// auto-approved on creation — no explicit approve step needed. Pending
+// listings for approval tests come from the seeder. If any step fails, the
+// Playwright project-dependency system cascades skips to downstream specs.
+
+test.describe.serial('Test Data Setup', () => {
+  test.setTimeout(120000)
+
+  test('ensure approved handheld listing exists', async ({ browser }) => {
+    await withContext(browser, 'tests/.auth/user.json', async (page) => {
+      await createHandheldListing(page)
+    })
+  })
+
+  test('ensure approved PC listing exists', async ({ browser }) => {
+    await withContext(browser, 'tests/.auth/user.json', async (page) => {
+      await createPcListing(page)
+    })
+  })
+
+  test('ensure report exists for admin-reports tests', async ({ browser }) => {
+    // Use a different user than the listing author — the Report button is
+    // hidden when viewing your own listings.
+    await withContext(browser, 'tests/.auth/author.json', async (page) => {
+      await createReport(page)
+    })
+  })
+})
diff --git a/tests/error-handling.spec.ts b/tests/error-handling.spec.ts
index 862bcd604..2aa485944 100644
--- a/tests/error-handling.spec.ts
+++ b/tests/error-handling.spec.ts
@@ -4,287 +4,115 @@ import { HomePage } from './pages/HomePage'
 import { ListingsPage } from './pages/ListingsPage'
 
 test.describe('Error Handling Tests', () => {
-  test('should handle 404 pages gracefully', async ({ page }) => {
-    // Navigate to non-existent page
+  test('should display 404 page for non-existent routes', async ({ page }) => {
     await page.goto('/non-existent-page-12345')
 
-    // Should show 404 error page
-    const error404Text = page.getByText(/404|not found|page.*not.*exist/i)
-    const error404 = await error404Text.first().isVisible({ timeout: 5000 })
-    expect(error404).toBe(true)
-
-    // Should have navigation to go back home
-    const homeLink = page.getByRole('link', { name: /home|go back|return/i })
-    const hasHomeLink = await homeLink.isVisible({ timeout: 3000 }).catch(() => false)
-
-    if (hasHomeLink) {
-      await homeLink.click()
-      await expect(page).toHaveURL('/')
-    } else {
-      // Or should have logo link
-      const logo = page.getByRole('link', { name: /emuready/i }).first()
-      if (await logo.isVisible()) {
-        await logo.click()
-        await expect(page).toHaveURL('/')
-      }
-    }
+    await expect(page.getByText(/not found/i).first()).toBeVisible()
   })
 
-  test('should handle invalid game ID gracefully', async ({ page }) => {
-    // Try to access game with invalid ID
+  test('should display error for invalid game ID', async ({ page }) => {
     await page.goto('/games/invalid-game-id-xyz')
 
-    // Should either show error or redirect
-    const errorMessage = await page
-      .getByText(/not found|does not exist|invalid|error/i)
-      .isVisible({ timeout: 5000 })
-      .catch(() => false)
-
-    if (errorMessage) {
-      expect(errorMessage).toBe(true)
-    } else {
-      // Might redirect to games list
-      await page.waitForURL('/games', { timeout: 5000 }).catch(() => {})
-      expect(page.url()).toContain('/games')
-    }
+    await expect(page.getByText(/not found|does not exist|invalid|error/i).first()).toBeVisible()
   })
 
-  test('should handle network errors during data loading', async ({ page }) => {
-    await page.context().setOffline(true)
-
-    let navigationFailed = false
-    try {
-      await page.goto('/games', { timeout: 5000 })
-    } catch {
-      navigationFailed = true
-    } finally {
-      await page.context().setOffline(false)
-    }
-
-    // When offline, navigation should fail or the page should show an error
-    if (!navigationFailed) {
-      // If navigation somehow succeeded (cached response), page should still work
-      await expect(page.locator('body')).toBeVisible()
-    } else {
-      // Expected: navigation failed due to offline mode
-      expect(navigationFailed).toBe(true)
-    }
+  test('should show error UI when API requests fail', async ({ page }) => {
+    await page.route('**/api/trpc/**', (route) => route.abort('failed'))
+
+    await page.goto('/games', { waitUntil: 'domcontentloaded' })
+
+    await expect(page.locator('main')).toBeVisible()
   })
 
-  test('should handle API timeouts gracefully', async ({ page }) => {
-    // Intercept API calls to add delay
-    await page.route('**/api/**', async (route) => {
+  test('should render page shell even when API is slow', async ({ page }) => {
+    await page.route('**/api/trpc/**', async (route) => {
       await new Promise((resolve) => setTimeout(resolve, 3000))
-      await route.continue().catch(() => {})
+      await route.continue()
     })
 
     await page.goto('/listings', { waitUntil: 'domcontentloaded' })
 
-    // Page should be functional even while API calls are delayed
-    await expect(page.locator('main')).toBeVisible({ timeout: 5000 })
+    await expect(page.locator('main')).toBeVisible()
 
-    // Clear route handler
-    await page.unroute('**/api/**')
+    await page.unroute('**/api/trpc/**')
   })
 
-  test('should recover from failed form submissions', async ({ page }) => {
-    // Try to submit a form that will fail (search with network issues)
+  test('should keep search input functional after failed search API call', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Intercept search API calls to fail
-    await page.route('**/api/*search*', (route) => {
-      route.abort('failed')
-    })
+    await page.route('**/api/trpc/**', (route) => route.abort('failed'))
 
-    // Perform search
-    await gamesPage.searchGames('Mario')
+    await gamesPage.searchInput.fill('Mario')
+    await page.keyboard.press('Enter')
 
-    // Page should still be functional
     await expect(gamesPage.searchInput).toBeVisible()
     await expect(gamesPage.searchInput).toBeEnabled()
 
-    // Clear route handler
-    await page.unroute('**/api/*search*')
-
-    // Should be able to search again
-    await gamesPage.searchInput.clear()
-    await gamesPage.searchInput.fill('Zelda')
-    await page.keyboard.press('Enter')
-
-    // Search should work now
-    await page.waitForLoadState('domcontentloaded')
-    expect(page.url()).toBeDefined()
+    await page.unroute('**/api/trpc/**')
   })
 
-  test('should handle invalid filter combinations', async ({ page }) => {
+  test('should not crash with invalid filter query parameters', async ({ page }) => {
+    await page.goto(
+      '/listings?device=invalid-device-123&emulator=non-existent-emulator&performance=super-invalid',
+    )
+
+    // Page layout has nested <main> elements, so strict-mode selectors on
+    // main fail. The heading is a unique non-crash indicator.
     const listingsPage = new ListingsPage(page)
-    await listingsPage.goto()
-
-    // Directly manipulate URL with invalid filter params
-    const url = new URL(page.url())
-    url.searchParams.set('device', 'invalid-device-123')
-    url.searchParams.set('emulator', 'non-existent-emulator')
-    url.searchParams.set('performance', 'super-invalid')
-
-    await page.goto(url.toString())
-
-    // Should handle invalid filters gracefully
-    await page.waitForLoadState('domcontentloaded')
-
-    // Wait for page content to settle (loading to finish)
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
-
-    // Should either:
-    // 1. Ignore invalid filters and show content
-    // 2. Show no results
-    // 3. Show error message
-    // 4. Reset invalid filters from URL
-
-    const hasError = await page
-      .getByText(/invalid|error|no results|no listings/i)
-      .isVisible({ timeout: 3000 })
-      .catch(() => false)
-    const hasListings = (await listingsPage.getListingCount()) > 0
-    const hasTable = await page
-      .locator('table')
-      .isVisible({ timeout: 2000 })
-      .catch(() => false)
-    const filtersReset = !page.url().includes('invalid')
-
-    // One of these must be true -- the app handles invalid filters in some way
-    expect(hasError || hasListings || hasTable || filtersReset).toBe(true)
+    await expect(listingsPage.pageHeading).toBeVisible()
   })
 
-  test('should handle session expiration gracefully', async ({ page }) => {
-    const homePage = new HomePage(page)
-    await homePage.goto()
+  test('should not expose technical error details on 404 page', async ({ page }) => {
+    await page.goto('/this-page-does-not-exist-at-all')
 
-    // Clear all cookies to simulate session expiration
-    await page.context().clearCookies()
+    await expect(page.getByText(/not found/i).first()).toBeVisible()
 
-    // Try to perform an action that might require session
-    await homePage.navigateToGames()
+    await expect(page.getByText(/stack trace/i)).not.toBeVisible()
+    await expect(page.getByText(/database.*error/i)).not.toBeVisible()
+  })
 
-    // Should still work for public pages
-    await expect(page).toHaveURL('/games')
+  test('should still render page when images fail to load', async ({ page }) => {
+    await page.route('**/*.{png,jpg,jpeg,gif,webp}', (route) => route.abort('failed'))
 
-    // Page should load without errors
     const gamesPage = new GamesPage(page)
-    await gamesPage.verifyPageLoaded()
-  })
+    await gamesPage.goto()
 
-  test('should show user-friendly error messages', async ({ page }) => {
-    // Navigate to a non-existent page to trigger 404
-    await page.goto('/this-page-does-not-exist-at-all')
+    await gamesPage.verifyPageLoaded()
 
-    // Should not expose sensitive error details
-    const technicalErrors = [
-      page.getByText(/stack trace/i),
-      page.getByText(/error: .*\sat\s/i), // Stack trace pattern
-      page.getByText(/internal server error/i),
-      page.getByText(/database.*error/i),
-    ]
-
-    for (const techError of technicalErrors) {
-      const isVisible = await techError.isVisible({ timeout: 1000 }).catch(() => false)
-      expect(isVisible).toBe(false)
-    }
-
-    // Should show user-friendly message instead
-    const friendlyErrors = [
-      page.getByText(/something went wrong/i),
-      page.getByText(/page not found/i),
-      page.getByText(/oops/i),
-      page.getByText(/can't find.*page/i),
-    ]
-
-    let foundFriendlyError = false
-    for (const friendly of friendlyErrors) {
-      if (await friendly.isVisible({ timeout: 2000 }).catch(() => false)) {
-        foundFriendlyError = true
-        break
-      }
-    }
-
-    expect(foundFriendlyError).toBe(true)
+    await page.unroute('**/*.{png,jpg,jpeg,gif,webp}')
   })
 
-  test('should handle rapid navigation without breaking', async ({ page }) => {
+  test('should allow public page access after cookies are cleared', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Rapidly navigate between pages
-    await homePage.gamesLink.click({ timeout: 500 }).catch(() => {})
-    await homePage.handheldLink.click({ timeout: 500 }).catch(() => {})
-    await homePage.pcLink.click({ timeout: 500 }).catch(() => {})
-    await homePage.homeLink.click({ timeout: 500 }).catch(() => {})
-
-    // Page should be in a valid state
-    const currentUrl = page.url()
-    expect(currentUrl).toMatch(/^https?:\/\/[^\/]+\/(games|listings|pc-listings|)(\?.*)?$/)
-
-    // Page should be responsive
-    const logo = page.getByRole('link', { name: /emuready/i }).first()
-    await expect(logo).toBeVisible()
-  })
+    await page.context().clearCookies()
 
-  test('should handle missing images gracefully', async ({ page }) => {
-    // Intercept image requests and fail them
-    await page.route('**/*.{png,jpg,jpeg,gif,webp}', (route) => {
-      route.abort('failed')
-    })
+    await homePage.navigateToGames()
 
     const gamesPage = new GamesPage(page)
-    await gamesPage.goto()
-
-    // Page should still be functional
     await gamesPage.verifyPageLoaded()
-
-    // Should show alt text or placeholder
-    const images = page.locator('img')
-    const imageCount = await images.count()
-
-    if (imageCount > 0) {
-      // Check first image has meaningful alt text
-      const firstImage = images.first()
-      const altText = await firstImage.getAttribute('alt')
-
-      expect(altText).toBeTruthy()
-      expect(altText!.length).toBeGreaterThan(0)
-    }
-
-    // Clear route handler
-    await page.unroute('**/*.{png,jpg,jpeg,gif,webp}')
   })
 })
 
-test.describe('Browser Compatibility Error Tests', () => {
-  test('should handle browser back button after error', async ({ page }) => {
+test.describe('Browser Navigation Error Tests', () => {
+  test('should recover via back button after hitting a 404', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Navigate to invalid page
     await page.goto('/invalid-page-404')
 
-    // Should show error
-    await expect(page.getByText(/404|not found/i).first()).toBeVisible()
+    await expect(page.getByText(/not found/i).first()).toBeVisible()
 
-    // Browser back should work
     await page.goBack()
     await expect(page).toHaveURL('/')
 
-    // Page should be functional
     await homePage.verifyHeroSectionVisible()
   })
 
-  test('should maintain functionality after JavaScript errors', async ({ page }) => {
+  test('should remain functional even if console errors occur', async ({ page }) => {
     const jsErrors: string[] = []
-
-    // Listen for JavaScript errors
     page.on('pageerror', (error) => {
       jsErrors.push(error.message)
     })
@@ -292,10 +120,8 @@ test.describe('Browser Compatibility Error Tests', () => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Even if JS errors occur, basic functionality should work
     await gamesPage.verifyPageLoaded()
 
-    // Navigation should still work
     await gamesPage.navigateToHome()
     await expect(page).toHaveURL('/')
   })
diff --git a/tests/filtering.spec.ts b/tests/filtering.spec.ts
index 071db5064..a0bce2c91 100644
--- a/tests/filtering.spec.ts
+++ b/tests/filtering.spec.ts
@@ -1,369 +1,118 @@
 import { test, expect } from '@playwright/test'
 import { ListingsPage } from './pages/ListingsPage'
+import type { Locator, Page } from '@playwright/test'
+
+async function selectFirstFilterOption(page: Page, filterButton: Locator) {
+  await filterButton.click()
+  const firstOption = page.locator('label:has(input[type="checkbox"])').first()
+  await expect(firstOption).toBeVisible()
+  await firstOption.click()
+  // MultiSelect doesn't close on Escape and a lingering open dropdown blocks
+  // subsequent clicks, so toggle the filter button to dismiss it.
+  await filterButton.click()
+  await expect(firstOption).toBeHidden()
+}
 
 test.describe('Filtering Tests', () => {
-  test('should filter listings by device', async ({ page }) => {
+  test('should display filter controls on the listings page', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
     await listingsPage.verifyPageLoaded()
 
-    // Check if device filter is available
-    if (await listingsPage.deviceFilter.isVisible({ timeout: 3000 })) {
-      // Get initial count
-      const initialCount = await listingsPage.getListingCount()
-
-      // Apply device filter
-      await listingsPage.deviceFilter.click()
-      const deviceOptions = page.getByRole('option')
-      const optionCount = await deviceOptions.count()
-
-      if (optionCount > 1) {
-        // First option is usually "All"
-        // Click second option
-        const secondOption = deviceOptions.nth(1)
-        const deviceName = await secondOption.textContent()
-        await secondOption.click()
-
-        await expect(
-          listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-        ).toBeVisible()
-
-        const filteredCount = await listingsPage.getListingCount()
-        expect(filteredCount).toBeLessThanOrEqual(initialCount)
-
-        // Verify filter is applied
-        const selectedDevice = await listingsPage.deviceFilter.textContent()
-        expect(selectedDevice).toContain(deviceName || '')
-      }
-    }
-  })
-
-  test('should filter listings by emulator', async ({ page }) => {
-    const listingsPage = new ListingsPage(page)
-    await listingsPage.goto()
-
-    if (await listingsPage.emulatorFilter.isVisible({ timeout: 3000 })) {
-      await listingsPage.emulatorFilter.click()
-
-      const emulatorOptions = page.getByRole('option')
-      if ((await emulatorOptions.count()) > 1) {
-        const firstEmulator = emulatorOptions.nth(1)
-        const emulatorName = await firstEmulator.textContent()
-        await firstEmulator.click()
-
-        await expect(
-          listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-        ).toBeVisible()
-
-        // All visible listings should be for this emulator
-        const listings = await listingsPage.listingItems.all()
-        if (listings.length > 0) {
-          // Check first listing contains emulator name
-          const firstListingText = await listings[0].textContent()
-
-          // Verify the listing actually contains the emulator name
-          if (emulatorName && firstListingText) {
-            expect(firstListingText.toLowerCase()).toContain(emulatorName.toLowerCase())
-          }
-        }
-
-        // Verify filter is active
-        expect(await listingsPage.emulatorFilter.textContent()).toContain(emulatorName || '')
-      }
-    }
-  })
-
-  test('should filter listings by performance rating', async ({ page }) => {
-    const listingsPage = new ListingsPage(page)
-    await listingsPage.goto()
-
-    if (await listingsPage.performanceFilter.isVisible({ timeout: 3000 })) {
-      const initialCount = await listingsPage.getListingCount()
-
-      await listingsPage.performanceFilter.click()
-
-      // Look for performance options (Perfect, Excellent, Good, etc.)
-      const perfOptions = page
-        .getByRole('option')
-        .filter({ hasText: /perfect|excellent|good|playable/i })
-
-      if ((await perfOptions.count()) > 0) {
-        const firstOption = perfOptions.first()
-        const performanceLevel = await firstOption.textContent()
-        await firstOption.click()
-
-        await expect(
-          listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-        ).toBeVisible()
-
-        const filteredCount = await listingsPage.getListingCount()
-
-        // Verify the filter actually changed the results (unless all listings already matched)
-        if (initialCount > 0) {
-          expect(filteredCount).toBeLessThanOrEqual(initialCount)
-        }
-
-        // Verify filter is applied
-        expect(await listingsPage.performanceFilter.textContent()).toContain(performanceLevel || '')
-      }
-    }
+    await expect(listingsPage.deviceFilter).toBeVisible()
+    await expect(listingsPage.emulatorFilter).toBeVisible()
+    await expect(listingsPage.performanceFilter).toBeVisible()
+    await expect(listingsPage.systemFilter).toBeVisible()
   })
 
-  test('should apply multiple filters simultaneously', async ({ page }) => {
+  test('should show listings table with seed data', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await listingsPage.verifyPageLoaded()
 
-    let filtersApplied = 0
-
-    // Apply device filter
-    if (await listingsPage.deviceFilter.isVisible({ timeout: 2000 })) {
-      await listingsPage.deviceFilter.click()
-      const deviceOption = page.getByRole('option').nth(1)
-      if (await deviceOption.isVisible({ timeout: 1000 })) {
-        await deviceOption.click()
-        filtersApplied++
-        await expect(
-          listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-        ).toBeVisible()
-      }
-    }
-
-    // Apply emulator filter
-    if (await listingsPage.emulatorFilter.isVisible({ timeout: 2000 })) {
-      await listingsPage.emulatorFilter.click()
-      const emulatorOption = page.getByRole('option').nth(1)
-      if (await emulatorOption.isVisible({ timeout: 1000 })) {
-        await emulatorOption.click()
-        filtersApplied++
-        await expect(
-          listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-        ).toBeVisible()
-      }
-    }
-
-    if (filtersApplied > 1) {
-      // Results should be filtered by both criteria
-      const filteredCount = await listingsPage.getListingCount()
-      expect(typeof filteredCount).toBe('number')
-
-      // Clear filters button should be visible
-      if (await listingsPage.clearFiltersButton.isVisible({ timeout: 2000 })) {
-        await listingsPage.clearAllFilters()
-
-        // Count should increase after clearing
-        const clearedCount = await listingsPage.getListingCount()
-        expect(clearedCount).toBeGreaterThanOrEqual(filteredCount)
-      }
-    }
+    await expect(listingsPage.listingItems.first()).toBeVisible()
   })
 
-  test('should persist filters when navigating', async ({ page }) => {
+  test('should filter listings by device', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await listingsPage.verifyPageLoaded()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    // Apply a filter
-    if (await listingsPage.deviceFilter.isVisible({ timeout: 3000 })) {
-      await listingsPage.deviceFilter.click()
-      const option = page.getByRole('option').nth(1)
-      const filterValue = await option.textContent()
-      await option.click()
-      await expect(
-        listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-      ).toBeVisible()
-
-      // Navigate to a listing detail if available
-      if ((await listingsPage.getListingCount()) > 0) {
-        await listingsPage.clickFirstListing()
-
-        // Go back
-        await page.goBack()
-        await listingsPage.verifyPageLoaded()
+    await selectFirstFilterOption(page, listingsPage.deviceFilter)
 
-        // Filter should still be applied
-        const currentFilter = await listingsPage.deviceFilter.textContent()
-        expect(currentFilter).toContain(filterValue || '')
-      }
-    }
+    await expect(page).toHaveURL(/[?&]deviceIds=/)
+    await expect(listingsPage.listingItems.first().or(listingsPage.noListingsMessage)).toBeVisible()
   })
 
-  test('should update URL with filter parameters', async ({ page }) => {
+  test('should filter listings by emulator', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await listingsPage.verifyPageLoaded()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    const initialUrl = page.url()
-
-    // Apply any available filter
-    const filters = [
-      listingsPage.deviceFilter,
-      listingsPage.emulatorFilter,
-      listingsPage.performanceFilter,
-    ]
-
-    for (const filter of filters) {
-      if (await filter.isVisible({ timeout: 2000 })) {
-        await filter.click()
-        const option = page.getByRole('option').nth(1)
-        if (await option.isVisible({ timeout: 1000 })) {
-          await option.click()
-          await expect(
-            listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-          ).toBeVisible()
-
-          // URL should have changed
-          const newUrl = page.url()
-          expect(newUrl).not.toBe(initialUrl)
+    await selectFirstFilterOption(page, listingsPage.emulatorFilter)
 
-          // URL should contain filter parameter
-          expect(newUrl).toMatch(/[?&](device|emulator|performance)=/i)
-          break
-        }
-      }
-    }
+    await expect(page).toHaveURL(/[?&]emulatorIds=/)
+    await expect(listingsPage.listingItems.first().or(listingsPage.noListingsMessage)).toBeVisible()
   })
 
-  test('should show appropriate message when no results match filters', async ({ page }) => {
+  test('should filter listings by performance rating', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await listingsPage.verifyPageLoaded()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    // Try to apply very restrictive filters
-    let filtersApplied = 0
-
-    // Apply all available filters with random selections
-    const filters = [
-      listingsPage.deviceFilter,
-      listingsPage.emulatorFilter,
-      listingsPage.performanceFilter,
-    ]
-
-    for (const filter of filters) {
-      if (await filter.isVisible({ timeout: 2000 })) {
-        await filter.click()
-        const options = page.getByRole('option')
-        const count = await options.count()
-
-        if (count > 1) {
-          // Select last option (often most restrictive)
-          await options.nth(count - 1).click()
-          filtersApplied++
-          await expect(
-            listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-          ).toBeVisible()
-        }
-      }
-    }
-
-    if (filtersApplied > 0) {
-      const resultCount = await listingsPage.getListingCount()
-
-      if (resultCount === 0) {
-        // Should show no results message
-        const noResultsMessage = await listingsPage.noListingsMessage.isVisible({ timeout: 3000 })
-        expect(noResultsMessage).toBe(true)
-
-        // Clear filters should still be available
-        if (await listingsPage.clearFiltersButton.isVisible()) {
-          await listingsPage.clearAllFilters()
+    await selectFirstFilterOption(page, listingsPage.performanceFilter)
 
-          // Should show results again
-          const clearedCount = await listingsPage.getListingCount()
-          expect(clearedCount).toBeGreaterThan(0)
-        }
-      }
-    }
+    await expect(page).toHaveURL(/[?&]performanceIds=/)
+    await expect(listingsPage.listingItems.first().or(listingsPage.noListingsMessage)).toBeVisible()
   })
 
-  test('should handle filter combinations that produce no results', async ({ page }) => {
+  test('should apply multiple filters and clear them', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await listingsPage.verifyPageLoaded()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    // Apply conflicting filters if possible
-    if (
-      (await listingsPage.deviceFilter.isVisible()) &&
-      (await listingsPage.emulatorFilter.isVisible())
-    ) {
-      // Select a device
-      await listingsPage.deviceFilter.click()
-      await page.getByRole('option').nth(1).click()
-      await expect(
-        listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-      ).toBeVisible()
+    await selectFirstFilterOption(page, listingsPage.deviceFilter)
+    await expect(page).toHaveURL(/[?&]deviceIds=/)
 
-      // Select an emulator that might not support this device
-      await listingsPage.emulatorFilter.click()
-      const emulatorOptions = page.getByRole('option')
-      const lastOption = emulatorOptions.nth((await emulatorOptions.count()) - 1)
-      await lastOption.click()
-      await expect(
-        listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-      ).toBeVisible()
+    await selectFirstFilterOption(page, listingsPage.emulatorFilter)
+    await expect(page).toHaveURL(/[?&]emulatorIds=/)
 
-      const count = await listingsPage.getListingCount()
+    await expect(listingsPage.clearFiltersButton).toBeVisible()
+    await listingsPage.clearFiltersButton.click()
 
-      if (count === 0) {
-        // Should show helpful message
-        const noResults = await page
-          .getByText(/no listings found|try adjusting your filters/i)
-          .isVisible({ timeout: 2000 })
-        expect(noResults).toBe(true)
-      }
-    }
+    await expect(page).not.toHaveURL(/[?&]deviceIds=/)
+    await expect(page).not.toHaveURL(/[?&]emulatorIds=/)
+    await expect(listingsPage.listingItems.first()).toBeVisible()
   })
-})
 
-test.describe('Quick Filter Tests', () => {
-  test('should apply quick filters if available', async ({ page }) => {
+  test('should update URL with filter parameters when a filter is applied', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await listingsPage.verifyPageLoaded()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    // Look for quick filter buttons
-    const quickFilters = page
-      .getByRole('button')
-      .filter({ hasText: /latest|popular|trending|top rated|newest/i })
-
-    if ((await quickFilters.count()) > 0) {
-      const firstQuickFilter = quickFilters.first()
-
-      await firstQuickFilter.click()
-      await expect(
-        listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-      ).toBeVisible()
-
-      // Verify filter is active (button might change style)
-      const isActive =
-        (await firstQuickFilter.getAttribute('aria-pressed')) === 'true' ||
-        (await firstQuickFilter.getAttribute('data-state')) === 'active' ||
-        ((await firstQuickFilter.getAttribute('class')) || '').includes('active')
+    await selectFirstFilterOption(page, listingsPage.deviceFilter)
 
-      expect(isActive).toBe(true)
-    }
+    await expect(page).toHaveURL(/[?&]deviceIds=/)
   })
 
-  test('should toggle filter panel on mobile', async ({ page }) => {
-    // Set mobile viewport
-    await page.setViewportSize({ width: 375, height: 667 })
-
+  test('should persist filters after navigating back from a listing detail', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await listingsPage.verifyPageLoaded()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    // Look for filter toggle button on mobile
-    const filterToggle = page
-      .getByRole('button')
-      .filter({ hasText: /filter/i })
-      .or(page.getByLabel(/toggle filter/i))
-
-    if (await filterToggle.isVisible({ timeout: 3000 })) {
-      // Filters might be hidden initially on mobile
-      const filtersVisible = await listingsPage.deviceFilter.isVisible({
-        timeout: 1000,
-      })
+    await selectFirstFilterOption(page, listingsPage.deviceFilter)
+    await expect(page).toHaveURL(/[?&]deviceIds=/)
 
-      if (!filtersVisible) {
-        // Click to show filters
-        await filterToggle.click()
+    await listingsPage.clickFirstListing()
+    await page.goBack()
+    await listingsPage.verifyPageLoaded()
 
-        // Filters should now be visible
-        await expect(listingsPage.deviceFilter.or(listingsPage.emulatorFilter)).toBeVisible()
-      }
-    }
+    await expect(page).toHaveURL(/[?&]deviceIds=/)
   })
 })
diff --git a/tests/fixtures/test-fixtures.ts b/tests/fixtures/test-fixtures.ts
deleted file mode 100644
index d0b10e330..000000000
--- a/tests/fixtures/test-fixtures.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { test as base, type Page, type BrowserContext } from '@playwright/test'
-import { TEST_CONFIG } from '../helpers/test-config'
-
-/**
- * Custom test fixtures with optimized settings for production builds
- */
-export const test = base.extend({
-  // Override default page fixture with custom settings
-  page: async ({ page }, applyPage: (p: Page) => Promise<void>) => {
-    // Set default timeouts
-    page.setDefaultTimeout(TEST_CONFIG.timeouts.action)
-    page.setDefaultNavigationTimeout(TEST_CONFIG.timeouts.navigation)
-
-    // Add request interceptor to log slow API calls
-    page.on('requestfailed', (request) => {
-      console.error(`Request failed: ${request.url()} - ${request.failure()?.errorText}`)
-    })
-
-    // Log slow responses in dev mode
-    if (process.env.DEBUG) {
-      page.on('response', async (response) => {
-        const timing = response.request().timing()
-        if (timing.responseEnd && timing.responseEnd - timing.responseStart > 5000) {
-          console.warn(
-            `Slow response: ${response.url()} took ${Math.round(timing.responseEnd - timing.responseStart)}ms`,
-          )
-        }
-      })
-    }
-
-    await applyPage(page)
-  },
-
-  context: async ({ context }, applyContext: (ctx: BrowserContext) => Promise<void>) => {
-    await applyContext(context)
-  },
-})
-
-export { expect } from '@playwright/test'
diff --git a/tests/forms.spec.ts b/tests/forms.spec.ts
index e256205a8..0f0d1113b 100644
--- a/tests/forms.spec.ts
+++ b/tests/forms.spec.ts
@@ -1,364 +1,154 @@
 import { test, expect } from '@playwright/test'
-import { AuthPage } from './pages/AuthPage'
 import { GameFormPage } from './pages/GameFormPage'
 import { GamesPage } from './pages/GamesPage'
-import { ListingFormPage } from './pages/ListingFormPage'
 import { ListingsPage } from './pages/ListingsPage'
 
-test.describe('Game Form Tests', () => {
-  test('should require authentication to access add game form', async ({ page }) => {
-    const gameForm = new GameFormPage(page)
-
-    // Navigate to add game page
-    await gameForm.goto()
-    await page.waitForLoadState('domcontentloaded')
+test.describe('Game Form - Authentication Required', () => {
+  test('should redirect unauthenticated users away from game form', async ({ page }) => {
+    await page.goto('/games/new', { waitUntil: 'domcontentloaded' })
 
-    // Check various indicators that page is protected
-    const currentUrl = page.url()
-    const hasSignInButton = await page
+    const signInIndicator = page
       .getByRole('button', { name: /sign in/i })
-      .isVisible({ timeout: 2000 })
-      .catch(() => false)
-    const hasAuthText = await page
-      .getByText(/sign in|log in|authentication required|please sign/i)
-      .isVisible({ timeout: 2000 })
-      .catch(() => false)
-    const hasForm = await page
-      .locator('form')
-      .isVisible({ timeout: 2000 })
-      .catch(() => false)
-    const hasGameTitleInput = await page
-      .locator('input[name="title"], input[placeholder*="title"]')
-      .isVisible({ timeout: 2000 })
-      .catch(() => false)
-
-    // The page is protected if ANY of these conditions are true:
-    // 1. URL changed (redirect)
-    // 2. Sign-in button is shown
-    // 3. Auth message is shown
-    // 4. No form is visible
-    // 5. No game title input is visible
-    const isProtected =
-      !currentUrl.includes('/games/new') ||
-      hasSignInButton ||
-      hasAuthText ||
-      !hasForm ||
-      !hasGameTitleInput
-
-    if (!isProtected) {
-      // If none of the above, try to interact with the form
-      try {
-        await gameForm.fillRequiredFields('Test Game')
-        await gameForm.submitForm()
-        // If submission succeeds without auth, that's unexpected
-        const newUrl = page.url()
-        // Form should not submit successfully
-        expect(newUrl).toContain('/games/new')
-      } catch {
-        // Expected - form interaction failed
-      }
-    }
-
-    // Test passes if page is protected in any way
-    expect(isProtected).toBe(true)
+      .or(page.getByText(/sign in|log in|authentication required|please sign/i))
+    await expect(signInIndicator.first()).toBeVisible()
   })
+})
 
-  test('should show game form fields when accessible', async ({ page }) => {
-    const gameForm = new GameFormPage(page)
-    const authPage = new AuthPage(page)
+test.describe('Game Form - Authenticated Admin', () => {
+  test.use({ storageState: 'tests/.auth/super_admin.json' })
 
+  test('should display game form with title, system, and submit', async ({ page }) => {
+    const gameForm = new GameFormPage(page)
     await gameForm.goto()
 
-    // Check if form is accessible
-    const hasForm = await page.locator('form').isVisible({ timeout: 2000 })
-
-    if (hasForm) {
-      // Check for any input fields to verify we have a form
-      const hasInputs = await page
-        .locator('input, select, textarea')
-        .first()
-        .isVisible({ timeout: 3000 })
-      expect(hasInputs).toBe(true)
-
-      // Try to find specific form fields
-      const titleVisible = await gameForm.titleInput.isVisible({ timeout: 2000 }).catch(() => false)
-      const systemVisible = await gameForm.systemSelect
-        .isVisible({ timeout: 2000 })
-        .catch(() => false)
-      const submitVisible = await gameForm.submitButton
-        .isVisible({ timeout: 2000 })
-        .catch(() => false)
-
-      // At least some form fields should be present
-      expect(titleVisible || systemVisible || submitVisible).toBe(true)
-    } else {
-      // Form requires authentication
-      const hasAuthRequirement = await authPage.hasAuthRequirement()
-      expect(hasAuthRequirement).toBe(true)
-    }
+    await expect(gameForm.form).toBeVisible()
+    await expect(gameForm.titleInput).toBeVisible()
+    await expect(gameForm.systemSelect).toBeVisible()
+    await expect(gameForm.submitButton).toBeVisible()
   })
 
-  test('should validate required fields on game form', async ({ page }) => {
+  test('should show validation errors on empty submit', async ({ page }) => {
     const gameForm = new GameFormPage(page)
-
     await gameForm.goto()
 
-    // Check if form is accessible
-    const hasForm = await page.locator('form').isVisible({ timeout: 2000 })
-    test.skip(!hasForm, 'Game form not accessible - auth required')
-
-    // Try to submit empty form
+    await expect(gameForm.form).toBeVisible()
     await gameForm.submitForm()
 
-    // Should show validation errors or remain on form page
-    const hasErrors = await gameForm.hasValidationErrors()
-    if (hasErrors) {
-      await gameForm.verifyValidationError()
-    } else {
-      // Form might be submitted but rejected server-side
-      const currentUrl = page.url()
-      expect(currentUrl).toContain('/games/new')
-    }
+    await expect(gameForm.errorMessages.first()).toBeVisible()
   })
 
-  test('should navigate to add game form from games page', async ({ page }) => {
+  test('should navigate to game form from games page', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     const gameForm = new GameFormPage(page)
-    const authPage = new AuthPage(page)
 
     await gamesPage.goto()
 
-    // Wait for loading to finish
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
-
-    // Look for add game button
-    const hasAddButton = await gamesPage.addGameButton
-      .isVisible({ timeout: 5000 })
-      .catch(() => false)
-
-    test.skip(!hasAddButton, 'Add game button not visible - requires auth or conditionally shown')
-
+    await expect(gamesPage.addGameButton).toBeVisible()
     await gamesPage.clickAddGame()
 
-    // Should either show form or auth requirement
-    const hasForm = await gameForm.isFormVisible()
-    const hasAuthRequirement = await authPage.hasAuthRequirement()
-
-    expect(hasForm || hasAuthRequirement).toBe(true)
+    await expect(gameForm.form).toBeVisible()
+    await expect(gameForm.titleInput).toBeVisible()
   })
 })
 
-test.describe('Listing Form Tests', () => {
-  test('should require authentication to access add listing form', async ({ page }) => {
-    const listingForm = new ListingFormPage(page)
-    const authPage = new AuthPage(page)
-
-    await listingForm.goto()
-
-    // Wait for page to settle (may show loading spinner during redirect/auth check)
-    await page.waitForLoadState('networkidle').catch(() => {})
-
-    // Should either show auth requirement or have form without submission capability
-    const hasAuthRequirement = await authPage.hasAuthRequirement()
-    const hasForm = await page.locator('form').isVisible({ timeout: 2000 })
-
-    if (hasAuthRequirement) {
-      await authPage.verifyAuthRequired()
-    } else if (hasForm) {
-      // Form is accessible - try to submit without auth, should fail
-      await listingForm.fillRequiredFields()
-      await listingForm.submitForm()
-
-      // Should either show error or not submit
-      const hasErrors = await listingForm.hasValidationErrors()
-      const currentUrl = page.url()
-
-      expect(hasErrors || currentUrl.includes('/listings/new')).toBe(true)
-    } else {
-      // Page shows loading spinner or redirected - auth is protecting the page
-      const isOnFormPage = page.url().includes('/listings/new')
-      const hasLoadingSpinner = await page
-        .locator('main img')
-        .isVisible({ timeout: 1000 })
-        .catch(() => false)
-
-      // Either redirected away or still showing loading (auth check in progress)
-      expect(!isOnFormPage || hasLoadingSpinner).toBe(true)
-    }
-  })
-
-  test('should show listing form fields when accessible', async ({ page }) => {
-    const listingForm = new ListingFormPage(page)
-
-    await listingForm.goto()
-
-    // Wait for page to settle (may show loading spinner during redirect/auth check)
-    await page.waitForLoadState('networkidle').catch(() => {})
+test.describe('Listing Form - Authentication Required', () => {
+  test('should require authentication to access listing form', async ({ page }) => {
+    await page.goto('/listings/new', { waitUntil: 'domcontentloaded' })
 
-    // Check if form is accessible
-    const hasForm = await page
-      .locator('form')
-      .isVisible({ timeout: 3000 })
-      .catch(() => false)
-    test.skip(!hasForm, 'Listing form not accessible - auth required or page still loading')
-
-    // Verify form fields are present with timeout
-    await expect(listingForm.gameSelect).toBeVisible({ timeout: 5000 })
-    await expect(listingForm.deviceSelect).toBeVisible({ timeout: 2000 })
-    await expect(listingForm.submitButton).toBeVisible({ timeout: 2000 })
+    const signInIndicator = page
+      .getByText(/you need to be logged in|please sign in|sign in required/i)
+      .or(page.locator('main').getByRole('button', { name: /sign in/i }))
+    await expect(signInIndicator.first()).toBeVisible()
   })
+})
 
-  test('should validate required fields on listing form', async ({ page }) => {
-    const listingForm = new ListingFormPage(page)
-
-    await listingForm.goto()
-
-    // Check if form is accessible
-    const hasForm = await page.locator('form').isVisible({ timeout: 2000 })
-    test.skip(!hasForm, 'Listing form not accessible - auth required')
-
-    // Try to submit empty form
-    await listingForm.submitForm()
+test.describe('Listing Form - Authenticated User', () => {
+  test.use({ storageState: 'tests/.auth/user.json' })
 
-    // Should show validation errors or remain on form page
-    const hasErrors = await listingForm.hasValidationErrors()
-    if (hasErrors) {
-      await listingForm.verifyValidationError()
-    } else {
-      // Form might be submitted but rejected server-side
-      const currentUrl = page.url()
-      expect(currentUrl).toContain('/listings/new')
-    }
+  test.beforeEach(async ({ page }) => {
+    await page.goto('/listings/new', { waitUntil: 'domcontentloaded' })
+    await expect(
+      page.getByRole('heading', { name: /create.*handheld.*compatibility.*report/i }),
+    ).toBeVisible()
   })
 
-  test('should navigate to add listing form from listings page', async ({ page }) => {
-    const listingsPage = new ListingsPage(page)
-    const listingForm = new ListingFormPage(page)
-    const authPage = new AuthPage(page)
-
-    await listingsPage.goto()
-
-    // Wait for loading to finish
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
-
-    // Look for add listing button (may be named "Add Report" on the listings page)
-    const hasAddButton = await listingsPage.addListingButton
-      .isVisible({ timeout: 5000 })
-      .catch(() => false)
-    const hasCreateButton = await listingsPage.createListingButton
-      .isVisible({ timeout: 2000 })
-      .catch(() => false)
-    const hasAddReport = await page
-      .getByRole('link', { name: /add report/i })
-      .isVisible({ timeout: 2000 })
-      .catch(() => false)
-
-    test.skip(
-      !hasAddButton && !hasCreateButton && !hasAddReport,
-      'Add listing button not visible - requires auth or conditionally shown',
-    )
-
-    if (hasAddButton || hasCreateButton) {
-      await listingsPage.clickAddListing()
-    } else {
-      await page.getByRole('link', { name: /add report/i }).click()
-      await page.waitForLoadState('domcontentloaded')
-    }
-
-    // Should either show form or auth requirement
-    const hasForm = await listingForm.isFormVisible()
-    const hasAuthRequirement = await authPage.hasAuthRequirement()
-
-    expect(hasForm || hasAuthRequirement).toBe(true)
+  test('should display listing form fields', async ({ page }) => {
+    await expect(page.getByPlaceholder(/search for a game/i)).toBeVisible()
+    await expect(page.getByPlaceholder(/search for a device/i)).toBeVisible()
+    await expect(page.getByRole('button', { name: /create compatibility report/i })).toBeVisible()
   })
 
-  test('should show custom fields when device is selected', async ({ page }) => {
-    const listingForm = new ListingFormPage(page)
-
-    await listingForm.goto()
+  test('should show validation errors on empty submit', async ({ page }) => {
+    const submitBtn = page.getByRole('button', { name: /create compatibility report/i })
+    await submitBtn.click()
 
-    // Check if form is accessible
-    const hasForm = await page.locator('form').isVisible({ timeout: 2000 })
-    test.skip(!hasForm, 'Listing form not accessible - auth required')
-
-    // Select a device to trigger custom fields
-    await listingForm.selectDeviceByIndex(1)
+    const errorIndicator = page
+      .locator('.error, [role="alert"], .text-red-500, .text-destructive')
+      .first()
+    await expect(errorIndicator).toBeVisible()
+  })
 
-    // Wait for potential custom fields to load
-    await page.waitForLoadState('domcontentloaded')
+  test('should populate game autocomplete options on search', async ({ page }) => {
+    const gameInput = page.getByPlaceholder(/search for a game/i)
+    await gameInput.click()
+    await gameInput.fill('Mario')
 
-    // Check if custom fields appear
-    const customFieldCount = await listingForm.getCustomFieldCount()
-    test.skip(customFieldCount === 0, 'No custom fields configured for this device')
+    const listbox = page.locator('[role="listbox"]')
+    await expect(listbox).toBeVisible()
 
-    await listingForm.verifyCustomFieldsVisible()
+    const options = listbox.locator('[role="option"]')
+    await expect(options.first()).toBeVisible()
   })
-})
-
-test.describe('Form Navigation Tests', () => {
-  test('should handle form cancellation properly', async ({ page }) => {
-    const gameForm = new GameFormPage(page)
 
-    await gameForm.goto()
+  test('should show selected game after picking from autocomplete', async ({ page }) => {
+    const gameInput = page.getByPlaceholder(/search for a game/i)
+    await gameInput.click()
+    await gameInput.fill('Mario')
 
-    // Check if form is accessible
-    const hasForm = await page.locator('form').isVisible({ timeout: 2000 })
-    test.skip(!hasForm, 'Game form not accessible - auth required')
+    const listbox = page.locator('[role="listbox"]')
+    await expect(listbox).toBeVisible()
+    await listbox.locator('[role="option"]').first().click()
 
-    await gameForm.fillGameTitle('Test Game')
+    await expect(page.getByText(/mario/i).first()).toBeVisible()
+  })
 
-    // Look for cancel button
-    const hasCancelButton = await gameForm.cancelButton.isVisible({ timeout: 2000 })
-    test.skip(!hasCancelButton, 'No cancel button found on form')
+  test('should populate device autocomplete options on search', async ({ page }) => {
+    const deviceInput = page.getByPlaceholder(/search for a device/i)
+    await deviceInput.click()
+    await deviceInput.fill('Rog')
 
-    await gameForm.cancelForm()
+    const listbox = page.locator('[role="listbox"]')
+    await expect(listbox).toBeVisible()
 
-    // Should navigate away from form
-    await expect(page).not.toHaveURL(/\/games\/new/)
+    const options = listbox.locator('[role="option"]')
+    await expect(options.first()).toBeVisible()
   })
+})
 
-  test('should preserve form data during navigation', async ({ page }) => {
-    const listingForm = new ListingFormPage(page)
-
-    await listingForm.goto()
-
-    // Check if form is accessible
-    const hasForm = await page.locator('form').isVisible({ timeout: 2000 })
-    test.skip(!hasForm, 'Listing form not accessible - auth required')
+test.describe('Listing Form - Navigation', () => {
+  test.use({ storageState: 'tests/.auth/user.json' })
 
-    // Select some options
-    await listingForm.selectGameByIndex(1)
-    await listingForm.selectDeviceByIndex(1)
+  test('should navigate to listing form from listings page', async ({ page }) => {
+    const listingsPage = new ListingsPage(page)
+    await listingsPage.goto()
 
-    // Get selected values
-    const selectedGame = await listingForm.gameSelect.inputValue()
-    const selectedDevice = await listingForm.deviceSelect.inputValue()
+    await expect(listingsPage.addReportButton).toBeVisible()
 
-    expect(selectedGame).toBeTruthy()
-    expect(selectedDevice).toBeTruthy()
+    await listingsPage.addReportButton.click()
+    await expect(
+      page.getByRole('heading', { name: /create.*handheld.*compatibility.*report/i }),
+    ).toBeVisible()
   })
 
-  test('should handle browser back navigation from forms', async ({ page }) => {
+  test('should handle browser back navigation from form', async ({ page }) => {
     const gamesPage = new GamesPage(page)
-    const gameForm = new GameFormPage(page)
 
-    // Start at games page
     await gamesPage.goto()
     const gamesUrl = page.url()
 
-    // Navigate to add game form
-    await gameForm.goto()
-
-    // Use browser back button
+    await page.goto('/listings/new', { waitUntil: 'domcontentloaded' })
     await page.goBack()
 
-    // Should be back at games page
     await expect(page).toHaveURL(gamesUrl)
     await gamesPage.verifyPageLoaded()
   })
diff --git a/tests/full-listing-flow.spec.ts b/tests/full-listing-flow.spec.ts
index 5e27c58be..be47f2c71 100644
--- a/tests/full-listing-flow.spec.ts
+++ b/tests/full-listing-flow.spec.ts
@@ -1,28 +1,16 @@
 import { test, expect } from '@playwright/test'
 
-/**
- * E2E tests for the listing (compatibility report) creation flow.
- *
- * Handheld listing creation: /listings/new
- * Form fields: Game (autocomplete), Device (autocomplete), Emulator (autocomplete),
- *   Performance (select), Notes (markdown editor), Custom Fields (dynamic).
- * Submit button: "Create Compatibility Report"
- * Success redirect to /listings.
- */
-
-test.describe('Full Listing Creation Flow', () => {
+test.describe('Handheld Listing Creation Form', () => {
   test.use({ storageState: 'tests/.auth/user.json' })
 
-  test('should display handheld listing creation form', async ({ page }) => {
-    await page.goto('/listings/new')
-    await page.waitForLoadState('domcontentloaded')
-
-    // Page heading
+  test.beforeEach(async ({ page }) => {
+    await page.goto('/listings/new', { waitUntil: 'domcontentloaded' })
     await expect(
       page.getByRole('heading', { name: /create.*handheld.*compatibility.*report/i }),
     ).toBeVisible()
+  })
 
-    // Core form fields
+  test('should display the form heading and core field labels', async ({ page }) => {
     await expect(page.getByText(/^game$/i).first()).toBeVisible()
     await expect(page.getByText(/^device$/i).first()).toBeVisible()
     await expect(page.getByText(/^emulator$/i).first()).toBeVisible()
@@ -30,112 +18,49 @@ test.describe('Full Listing Creation Flow', () => {
     await expect(page.getByText(/^notes$/i).first()).toBeVisible()
   })
 
-  test('should have game search autocomplete', async ({ page }) => {
-    await page.goto('/listings/new')
-    await page.waitForLoadState('domcontentloaded')
-
-    // Game autocomplete with search placeholder
+  test('should have game search autocomplete and add-game link', async ({ page }) => {
     const gameInput = page.getByPlaceholder(/search for a game/i)
     await expect(gameInput).toBeVisible()
 
-    // "Add it here" link for missing games
     const addGameLink = page.getByRole('link', { name: /add it here/i })
-    const hasLink = await addGameLink.isVisible().catch(() => false)
-
-    if (hasLink) {
-      await expect(addGameLink).toHaveAttribute('href', /\/games\/new/)
-    }
+    await expect(addGameLink).toBeVisible()
+    await expect(addGameLink).toHaveAttribute('href', /\/games\/new/)
   })
 
   test('should have device search autocomplete', async ({ page }) => {
-    await page.goto('/listings/new')
-    await page.waitForLoadState('domcontentloaded')
-
     const deviceInput = page.getByPlaceholder(/search for a device/i)
     await expect(deviceInput).toBeVisible()
   })
 
-  test('should have emulator search autocomplete', async ({ page }) => {
-    await page.goto('/listings/new')
-    await page.waitForLoadState('domcontentloaded')
-
-    // Emulator field requires a game to be selected first
-    // Shows warning if no game is selected
-    const emulatorSection = page.getByText(/emulator/i).first()
-    await expect(emulatorSection).toBeVisible()
-
-    // Without game selected, may show warning
-    const warning = page.getByText(/please select a game first/i)
-    const hasWarning = await warning.isVisible().catch(() => false)
-
-    if (hasWarning) {
-      await expect(warning).toBeVisible()
-    }
+  test('should show emulator prerequisite warning before game selection', async ({ page }) => {
+    await expect(page.getByText(/please select a game first/i)).toBeVisible()
   })
 
-  test('should have notes markdown editor', async ({ page }) => {
-    await page.goto('/listings/new')
-    await page.waitForLoadState('domcontentloaded')
-
-    // Wait for the form to actually render (React hydration)
-    await page
-      .getByRole('heading', { name: /create.*handheld.*compatibility.*report/i })
-      .waitFor({ state: 'visible', timeout: 15000 })
+  test('should show custom fields placeholder before emulator selection', async ({ page }) => {
+    await expect(page.getByText(/select an emulator/i)).toBeVisible()
+  })
 
-    // Notes section with markdown editor may only appear after selecting an emulator.
-    // Check if it's visible or if the form shows a prerequisite message instead.
+  test('should have notes section with markdown editor', async ({ page }) => {
     const notesPlaceholder = page.getByPlaceholder(/share your experience/i)
-    const hasNotes = await notesPlaceholder.isVisible({ timeout: 3000 }).catch(() => false)
-
-    if (hasNotes) {
-      await expect(notesPlaceholder).toBeVisible()
-
-      // Toolbar buttons (Bold, Italic, etc.)
-      const boldButton = page.getByRole('button', { name: /bold/i })
-      const hasBold = await boldButton.isVisible().catch(() => false)
-
-      if (hasBold) {
-        await expect(boldButton).toBeVisible()
-      }
-    } else {
-      // Notes section requires emulator selection — form shows prerequisite message
-      await expect(page.getByText(/select an emulator/i)).toBeVisible()
-    }
+    await expect(notesPlaceholder).toBeVisible()
   })
 
   test('should have submit button', async ({ page }) => {
-    await page.goto('/listings/new')
-    await page.waitForLoadState('domcontentloaded')
-
     const submitButton = page.getByRole('button', { name: /create compatibility report/i })
     await expect(submitButton).toBeVisible()
   })
+})
 
-  test('should show custom fields placeholder before emulator selection', async ({ page }) => {
-    await page.goto('/listings/new')
-    await page.waitForLoadState('domcontentloaded')
-
-    // Right column shows custom fields section
-    const placeholder = page.getByText(/select an emulator/i)
-    const hasPlaceholder = await placeholder.isVisible().catch(() => false)
-
-    if (hasPlaceholder) {
-      await expect(placeholder).toBeVisible()
-    }
-  })
+test.describe('PC Listing Creation Form', () => {
+  test.use({ storageState: 'tests/.auth/user.json' })
 
-  test.describe('PC Listing Creation', () => {
-    test('should display PC listing creation form', async ({ page }) => {
-      await page.goto('/pc-listings/new')
-      await page.waitForLoadState('domcontentloaded')
+  test('should display PC listing form with game search', async ({ page }) => {
+    await page.goto('/pc-listings/new', { waitUntil: 'domcontentloaded' })
 
-      // PC listing form should have similar structure but with PC-specific fields
-      const heading = page.getByRole('heading', { level: 1 })
-      await expect(heading).toBeVisible()
+    const heading = page.getByRole('heading', { level: 1 })
+    await expect(heading).toBeVisible()
 
-      // Game autocomplete should be present
-      const gameInput = page.getByPlaceholder(/search for a game/i)
-      await expect(gameInput).toBeVisible()
-    })
+    const gameInput = page.getByPlaceholder(/search for a game/i)
+    await expect(gameInput).toBeVisible()
   })
 })
diff --git a/tests/game-management.spec.ts b/tests/game-management.spec.ts
index 9723794f8..8e5033564 100644
--- a/tests/game-management.spec.ts
+++ b/tests/game-management.spec.ts
@@ -1,172 +1,106 @@
 import { test, expect } from '@playwright/test'
-
-/**
- * E2E tests for game management pages.
- *
- * Public games page: /games (heading: "Games Library", card grid layout)
- * Game detail: /games/[id]
- * Admin games: /admin/games (heading: "Games Management", table layout)
- * Game creation: /games/new (three method cards: IGDB, TGDB, Manual)
- */
+import { GameFormPage } from './pages/GameFormPage'
+import { GamesPage } from './pages/GamesPage'
 
 test.describe('Game Management', () => {
   test.describe('Public Games Page', () => {
-    test('should display games library with heading and cards', async ({ page }) => {
-      await page.goto('/games')
-      await page.waitForLoadState('domcontentloaded')
-
-      // Page heading
-      await expect(page.getByRole('heading', { name: /games library/i })).toBeVisible()
+    test('should display games library heading and search input', async ({ page }) => {
+      const gamesPage = new GamesPage(page)
+      await gamesPage.goto()
 
-      // Search input
+      await expect(gamesPage.pageHeading).toBeVisible()
       await expect(page.getByPlaceholder(/search games/i)).toBeVisible()
+    })
 
-      // Wait for data to finish loading
-      await page
-        .getByText(/loading games/i)
-        .waitFor({ state: 'hidden', timeout: 15000 })
-        .catch(() => {})
+    test('should display game cards', async ({ page }) => {
+      const gamesPage = new GamesPage(page)
+      await gamesPage.goto()
 
-      // Game cards or empty state
       const gameCards = page.locator('a[href^="/games/"]').filter({
         has: page.locator('h2'),
       })
-      const emptyState = page.getByText(/no games found/i)
-
-      const hasGames = (await gameCards.count()) > 0
-      const hasEmptyState = await emptyState.isVisible().catch(() => false)
 
-      expect(hasGames || hasEmptyState).toBe(true)
+      await expect(gameCards.first()).toBeVisible()
+      const count = await gameCards.count()
+      expect(count).toBeGreaterThan(0)
     })
 
     test('should search games by title', async ({ page }) => {
-      await page.goto('/games')
-      await page.waitForLoadState('domcontentloaded')
+      const gamesPage = new GamesPage(page)
+      await gamesPage.goto()
 
       const searchInput = page.getByPlaceholder(/search games/i)
       await searchInput.fill('test')
 
-      // URL should reflect search
       await expect(page).toHaveURL(/search=test/)
     })
 
     test('should navigate to game detail page', async ({ page }) => {
-      await page.goto('/games')
-      await page.waitForLoadState('domcontentloaded')
+      const gamesPage = new GamesPage(page)
+      await gamesPage.goto()
 
       const gameCards = page.locator('a[href^="/games/"]').filter({
         has: page.locator('h2'),
       })
+      await expect(gameCards.first()).toBeVisible()
 
-      const hasGames = (await gameCards.count()) > 0
-
-      if (hasGames) {
-        const firstCard = gameCards.first()
-        const href = await firstCard.getAttribute('href')
-        await firstCard.click()
-
-        // Should navigate to game detail
-        await expect(page).toHaveURL(/\/games\//)
-        expect(href).toMatch(/\/games\//)
-
-        // Game detail page should have a heading
-        await expect(page.getByRole('heading', { level: 1 })).toBeVisible()
-      }
-    })
-
-    test('should show pagination when many games exist', async ({ page }) => {
-      await page.goto('/games')
-      await page.waitForLoadState('domcontentloaded')
-
-      const pagination = page.getByRole('navigation', { name: /pagination/i })
-      const hasPagination = await pagination.isVisible().catch(() => false)
+      await gameCards.first().click()
 
-      if (hasPagination) {
-        const nextButton = page.getByRole('button', { name: /go to next page/i })
-        await expect(nextButton).toBeVisible()
-      }
+      await expect(page).toHaveURL(/\/games\//)
+      await expect(page.getByRole('heading', { level: 1 })).toBeVisible()
     })
   })
 
   test.describe('Game Detail Page', () => {
-    test('should display game information and report buttons', async ({ page }) => {
-      await page.goto('/games')
-      await page.waitForLoadState('domcontentloaded')
+    test('should display game information', async ({ page }) => {
+      const gamesPage = new GamesPage(page)
+      await gamesPage.goto()
 
       const gameCards = page.locator('a[href^="/games/"]').filter({
         has: page.locator('h2'),
       })
+      await expect(gameCards.first()).toBeVisible()
+
+      const href = await gameCards.first().getAttribute('href')
+      expect(href).toBeTruthy()
+
+      await page.goto(href!)
+      await page.waitForLoadState('domcontentloaded')
 
-      if ((await gameCards.count()) > 0) {
-        const firstCard = gameCards.first()
-        const href = await firstCard.getAttribute('href')
-        await firstCard.click()
-
-        try {
-          await expect(page).toHaveURL(/\/games\/[^/]+/, { timeout: 5000 })
-        } catch {
-          if (href) {
-            await page.goto(href)
-            await expect(page).toHaveURL(/\/games\/[^/]+/)
-          }
-        }
-        await page.waitForLoadState('domcontentloaded')
-
-        // Game title heading
-        await expect(page.getByRole('heading', { level: 1 })).toBeVisible()
-
-        // Report buttons (handheld and/or PC)
-        const addReportButton = page.getByRole('link', { name: /add.*(report|listing)/i }).first()
-        const hasAddButton = await addReportButton.isVisible().catch(() => false)
-
-        if (hasAddButton) {
-          await expect(addReportButton).toBeVisible()
-        }
-      }
+      await expect(page).toHaveURL(/\/games\/[^/]+/)
+      await expect(page.getByRole('heading', { level: 1 })).toBeVisible()
     })
   })
 
   test.describe('Game Creation Page', () => {
-    // Admin/moderator can see the method selection page; regular users redirect to IGDB
     test.use({ storageState: 'tests/.auth/admin.json' })
 
     test('should display add new game page with method selection', async ({ page }) => {
-      await page.goto('/games/new')
-      await page.waitForLoadState('domcontentloaded')
+      const gameForm = new GameFormPage(page)
+      await gameForm.goto()
 
-      // Page heading
-      await expect(page.getByRole('heading', { name: /add new game/i })).toBeVisible()
+      await expect(gameForm.pageHeading).toBeVisible()
 
-      // Three method cards: IGDB (recommended), TheGamesDB, Manual
       await expect(page.getByRole('heading', { name: /igdb search/i, level: 3 })).toBeVisible()
       await expect(page.getByRole('heading', { name: /thegamesdb/i, level: 3 })).toBeVisible()
       await expect(page.getByRole('heading', { name: /^manual entry$/i, level: 3 })).toBeVisible()
 
-      // IGDB should be marked as recommended
       await expect(page.getByText(/recommended/i)).toBeVisible()
     })
 
     test('should show manual entry form on the page', async ({ page }) => {
-      await page.goto('/games/new')
-      await page.waitForLoadState('domcontentloaded')
-
-      // Manual entry form should have game title and system fields
-      const titleInput = page.getByPlaceholder(/^enter game title$/i)
-      await expect(titleInput).toBeVisible()
+      const gameForm = new GameFormPage(page)
+      await gameForm.goto()
 
-      // System autocomplete (combobox)
-      const systemInput = page.getByRole('combobox', { name: /system/i })
-      await expect(systemInput).toBeVisible()
-
-      // Submit button
-      await expect(page.getByRole('button', { name: /add game to database/i })).toBeVisible()
+      await expect(page.getByPlaceholder(/^enter game title$/i)).toBeVisible()
+      await expect(page.getByRole('combobox', { name: /system/i })).toBeVisible()
+      await expect(gameForm.submitButton).toBeVisible()
     })
 
     test('should navigate to IGDB search from method card', async ({ page }) => {
-      await page.goto('/games/new')
-      await page.waitForLoadState('domcontentloaded')
+      const gameForm = new GameFormPage(page)
+      await gameForm.goto()
 
-      // Find the IGDB "Use This Method" link
       const igdbLink = page.getByRole('link', { name: /use this method/i }).first()
       await igdbLink.click()
 
@@ -181,17 +115,12 @@ test.describe('Game Management', () => {
       await page.goto('/admin/games')
       await page.waitForLoadState('domcontentloaded')
 
-      // Page heading
       await expect(page.getByRole('heading', { name: /games management/i })).toBeVisible()
 
-      // Stats display
       const stats = page.locator('text=/total games|used in reports|rejected/i')
       await expect(stats.first()).toBeVisible()
 
-      // Search input
       await expect(page.getByPlaceholder(/search games by title/i)).toBeVisible()
-
-      // Table should be present
       await expect(page.locator('table')).toBeVisible()
     })
 
diff --git a/tests/helpers/cookie-helper.ts b/tests/helpers/cookie-helper.ts
deleted file mode 100644
index b99c1cde1..000000000
--- a/tests/helpers/cookie-helper.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { type Page, test } from '@playwright/test'
-import { CookieBanner } from '../pages/CookieBanner'
-
-/**
- * Helper to ensure cookie banners are properly handled
- */
-export async function ensureCookieBannerDismissed(page: Page) {
-  const cookieBanner = new CookieBanner(page)
-
-  // First try to set consent in localStorage to prevent banner
-  try {
-    await page.evaluate(() => {
-      const PREFIX = '@TestEmuReady_'
-      localStorage.setItem(`${PREFIX}cookie_consent`, 'true')
-      localStorage.setItem(
-        `${PREFIX}cookie_preferences`,
-        JSON.stringify({
-          necessary: true,
-          analytics: false,
-          performance: false,
-        }),
-      )
-      localStorage.setItem(`${PREFIX}cookie_consent_date`, new Date().toISOString())
-    })
-  } catch {
-    // Ignore if localStorage is not available
-  }
-
-  // Wait for page to be ready before checking cookie banner
-  await page.waitForLoadState('domcontentloaded')
-
-  // Try to dismiss cookie banner if it appears
-  await cookieBanner.dismissIfPresent()
-
-  // Double-check it's gone
-  const stillVisible = await cookieBanner.isVisible()
-  if (stillVisible) {
-    console.warn('Cookie banner still visible after dismissal attempt')
-    try {
-      await page.getByLabel('Close cookie banner').click({ force: true })
-    } catch {
-      await page.reload()
-    }
-  }
-}
-
-/**
- * Custom test fixture that handles cookie banners automatically
- */
-export const testWithCookieHandling = test.extend<{
-  autoHandleCookies: void
-}>({
-  autoHandleCookies: [
-    async ({ page }, use) => {
-      // Handle cookies before each test
-      await ensureCookieBannerDismissed(page)
-
-      // Run the test
-      await use()
-    },
-    { auto: true },
-  ],
-})
diff --git a/tests/helpers/data-factory.ts b/tests/helpers/data-factory.ts
new file mode 100644
index 000000000..5edb2bde4
--- /dev/null
+++ b/tests/helpers/data-factory.ts
@@ -0,0 +1,372 @@
+import { expect } from '@playwright/test'
+import type { Browser, Page } from '@playwright/test'
+
+export async function selectAutocompleteOption(
+  page: Page,
+  placeholderPattern: RegExp,
+  searchTerm: string,
+) {
+  const input = page.getByPlaceholder(placeholderPattern)
+  await input.click()
+  if (searchTerm) {
+    await input.fill(searchTerm)
+  }
+
+  const listbox = page.locator('[role="listbox"]')
+  await expect(listbox).toBeVisible()
+
+  const options = listbox.locator('[role="option"]')
+
+  if (searchTerm) {
+    // Waiting on a filtered match avoids racing the debounced search and
+    // clicking an unfiltered option before the filter has applied.
+    const matchingOption = options.filter({ hasText: new RegExp(searchTerm, 'i') })
+    await expect(matchingOption.first()).toBeVisible()
+    await matchingOption.first().click()
+  } else {
+    await expect(options.first()).toBeVisible()
+    await options.first().click()
+  }
+
+  await expect(listbox).toBeHidden()
+}
+
+async function fillEmptySelects(page: Page) {
+  const selects = page.locator('form select')
+  const selectCount = await selects.count()
+
+  for (let i = 0; i < selectCount; i++) {
+    const sel = selects.nth(i)
+    if (!(await sel.isVisible())) continue
+
+    const currentValue = await sel.inputValue()
+    if (currentValue && currentValue.length > 0) continue
+
+    const options = sel.locator('option')
+    const optionCount = await options.count()
+    if (optionCount < 2) continue
+
+    for (let j = 0; j < optionCount; j++) {
+      const value = await options.nth(j).getAttribute('value')
+      if (value && value.length > 0) {
+        await sel.selectOption(value)
+        break
+      }
+    }
+  }
+}
+
+async function fillRequiredComboboxes(page: Page) {
+  // Must run before fillEmptyTextInputs — combobox inputs match the generic
+  // text-input selector otherwise and get stuffed with a URL instead of a
+  // selected option. Game/device/emulator comboboxes are already replaced
+  // with cards by the caller, so remaining visible empty comboboxes are
+  // custom-field autocompletes (e.g., Driver Version for Eden).
+  const comboboxes = page.locator('form [role="combobox"]')
+  const comboboxCount = await comboboxes.count()
+
+  for (let i = 0; i < comboboxCount; i++) {
+    const combobox = comboboxes.nth(i)
+    if (!(await combobox.isVisible())) continue
+
+    const value = await combobox.inputValue()
+    if (value && value.length > 0) continue
+
+    // Optional comboboxes (e.g., GPU) live inside a section whose label
+    // contains "Optional" — leave them empty.
+    const containerText = await combobox
+      .locator('xpath=ancestor::div[position() <= 3]')
+      .first()
+      .textContent()
+    if (containerText && /optional/i.test(containerText)) continue
+
+    await combobox.click()
+    const listbox = page.locator('[role="listbox"]')
+    await expect(listbox).toBeVisible()
+    await listbox.locator('[role="option"]').first().click()
+    await expect(listbox).toBeHidden()
+  }
+}
+
+async function fillEmptyTextInputs(page: Page) {
+  // `https://example.com` is valid for both plain-text and url-typed custom
+  // fields (e.g., YouTube, Screenshots). A plain word like "test" would pass
+  // the text fields but fail URL validation on the URL ones.
+  const textInputs = page.locator(
+    'form input[type="text"]:not([role="combobox"]):not([placeholder*="Search" i]):not([placeholder*="Select for" i]), form input[type="number"], form input[type="url"]',
+  )
+  const inputCount = await textInputs.count()
+
+  for (let i = 0; i < inputCount; i++) {
+    const input = textInputs.nth(i)
+    if (!(await input.isVisible())) continue
+
+    const currentValue = await input.inputValue()
+    if (currentValue && currentValue.length > 0) continue
+
+    const type = await input.getAttribute('type')
+    if (type === 'number') {
+      await input.fill('1')
+    } else {
+      await input.fill('https://example.com')
+    }
+  }
+}
+
+async function checkRequiredBooleanFields(page: Page) {
+  // Required boolean custom fields start as `undefined` and the form validates
+  // them as missing until explicitly set. Clicking the checkbox converts
+  // undefined → true, satisfying the validation.
+  const checkboxes = page.locator('form input[type="checkbox"]')
+  const checkboxCount = await checkboxes.count()
+
+  for (let i = 0; i < checkboxCount; i++) {
+    const checkbox = checkboxes.nth(i)
+    if (!(await checkbox.isVisible())) continue
+    if (await checkbox.isChecked()) continue
+    await checkbox.check()
+  }
+}
+
+async function fillAllRequiredCustomFields(page: Page) {
+  await fillEmptySelects(page)
+  await fillRequiredComboboxes(page)
+  await fillEmptyTextInputs(page)
+  await checkRequiredBooleanFields(page)
+}
+
+export async function createHandheldListing(page: Page): Promise<void> {
+  await page.goto('/listings/new')
+  await expect(
+    page.getByRole('heading', { name: /create.*handheld.*compatibility.*report/i }),
+  ).toBeVisible()
+
+  await selectAutocompleteOption(page, /search for a game/i, 'Mario')
+  await selectAutocompleteOption(page, /search for a device/i, 'Rog')
+  await selectAutocompleteOption(page, /search for emulators/i, '')
+
+  await expect(page.getByText('Loading emulator-specific fields...')).toBeHidden()
+
+  await fillAllRequiredCustomFields(page)
+
+  const notesField = page.getByPlaceholder(/share your experience/i)
+  await expect(notesField).toBeVisible()
+  await notesField.fill('E2E test handheld listing')
+
+  const submitBtn = page.getByRole('button', { name: /create compatibility report/i })
+  await submitBtn.click()
+
+  await expect(page).toHaveURL(/\/listings(?!\/new)/)
+}
+
+export async function createPcListing(page: Page): Promise<void> {
+  await page.goto('/pc-listings/new')
+  await page.waitForLoadState('domcontentloaded')
+
+  await selectAutocompleteOption(page, /search for a game/i, 'Zelda')
+  await selectAutocompleteOption(page, /select a cpu/i, 'Intel')
+
+  const memoryInput = page.getByPlaceholder(/e\.g\., 16/i)
+  await expect(memoryInput).toBeVisible()
+  await memoryInput.fill('16')
+
+  const osSelect = page.locator('select').filter({ hasText: /windows|linux|macos/i })
+  await expect(osSelect).toBeVisible()
+  await osSelect.selectOption({ index: 1 })
+
+  const osVersionInput = page.getByPlaceholder(/windows 11|ubuntu/i)
+  await expect(osVersionInput).toBeVisible()
+  await osVersionInput.fill('Windows 11')
+
+  await selectAutocompleteOption(page, /search for emulators/i, '')
+
+  await expect(page.getByText('Loading emulator-specific fields...')).toBeHidden()
+
+  await fillAllRequiredCustomFields(page)
+
+  const notesField = page.getByPlaceholder(/share your experience/i)
+  await expect(notesField).toBeVisible()
+  await notesField.fill('E2E test PC listing')
+
+  await page.getByRole('button', { name: /create compatibility report/i }).click()
+
+  await expect(page).toHaveURL(/\/pc-listings/)
+}
+
+export async function approveFirstPendingListing(
+  page: Page,
+  approvalPath: '/admin/approvals' | '/admin/pc-listing-approvals',
+): Promise<void> {
+  await page.goto(approvalPath, { waitUntil: 'domcontentloaded' })
+  await expect(page.getByText(/loading/i)).toBeHidden()
+
+  // Handheld buttons have title="Approve Listing", PC has "Approve PC Listing".
+  const approveButton = page.locator('button[title^="Approve"]').first()
+  await expect(approveButton).toBeVisible()
+
+  await approveButton.click()
+
+  const dialog = page.locator('[role="dialog"]')
+  await expect(dialog).toBeVisible()
+
+  const confirmBtn = dialog
+    .locator('button')
+    .filter({ hasText: /confirm|approve/i })
+    .last()
+  await expect(confirmBtn).toBeVisible()
+  await confirmBtn.click()
+
+  await expect(dialog).toBeHidden()
+}
+
+export async function rejectFirstPendingListing(
+  page: Page,
+  approvalPath: '/admin/approvals' | '/admin/pc-listing-approvals',
+): Promise<void> {
+  await page.goto(approvalPath, { waitUntil: 'domcontentloaded' })
+  await expect(page.getByText(/loading/i)).toBeHidden()
+
+  const rejectButton = page.locator('button[title^="Reject"]').first()
+  await expect(rejectButton).toBeVisible()
+
+  await rejectButton.click()
+
+  const dialog = page.locator('[role="dialog"]')
+  await expect(dialog).toBeVisible()
+
+  const textarea = dialog.locator('textarea')
+  await expect(textarea).toBeVisible()
+  await textarea.fill('Rejected by E2E test')
+
+  const confirmBtn = dialog
+    .locator('button')
+    .filter({ hasText: /confirm|reject/i })
+    .last()
+  await expect(confirmBtn).toBeVisible()
+  await confirmBtn.click()
+
+  await expect(dialog).toBeHidden()
+}
+
+export async function createReport(page: Page): Promise<void> {
+  await page.goto('/listings')
+
+  const rows = page.locator('table tbody tr')
+  await expect(rows.first()).toBeVisible()
+
+  // The Report button only renders for non-author viewers, so we iterate
+  // over several listings until we find one the current user didn't author.
+  const rowCount = await rows.count()
+  const maxAttempts = Math.min(rowCount, 15)
+
+  for (let i = 0; i < maxAttempts; i++) {
+    await page.goto('/listings')
+    await expect(rows.first()).toBeVisible()
+
+    const link = rows.nth(i).locator('a[href*="/listings/"]').first()
+    await link.click()
+    await expect(page).toHaveURL(/\/listings\/[^/]+$/)
+    await expect(page.getByRole('heading', { level: 1 })).toBeVisible()
+
+    const reportButton = page.getByRole('button', { name: /^report$/i })
+    if ((await reportButton.count()) === 0) continue
+    await reportButton.click()
+
+    const dialog = page.locator('[role="dialog"]')
+    await expect(dialog).toBeVisible()
+
+    const reasonSelect = dialog.locator('select')
+    await expect(reasonSelect).toBeVisible()
+    await reasonSelect.selectOption({ index: 1 })
+
+    const textarea = dialog.locator('textarea')
+    await expect(textarea).toBeVisible()
+    await textarea.fill('E2E test report for admin-reports testing')
+
+    const submitBtn = dialog.getByRole('button', { name: /submit report/i })
+    await submitBtn.click()
+
+    // "Already reported" is a valid outcome — the goal is that a report
+    // exists in the DB, not that this specific run created it.
+    const alreadyReportedError = dialog.getByText(/already reported/i)
+    const resolved = await Promise.race([
+      dialog.waitFor({ state: 'hidden' }).then(() => 'success' as const),
+      alreadyReportedError.waitFor({ state: 'visible' }).then(() => 'already-exists' as const),
+    ])
+
+    if (resolved === 'success' || resolved === 'already-exists') return
+  }
+
+  throw new Error(`createReport: could not find a listing to report after ${maxAttempts} attempts`)
+}
+
+export async function withContext(
+  browser: Browser,
+  storageState: string,
+  fn: (page: Page) => Promise<void>,
+) {
+  const ctx = await browser.newContext({ storageState })
+  await ctx.addInitScript(() => {
+    const PREFIX = '@StagingEmuReady_'
+    localStorage.setItem(`${PREFIX}cookie_consent`, 'true')
+    localStorage.setItem(
+      `${PREFIX}cookie_preferences`,
+      JSON.stringify({
+        necessary: true,
+        analytics: false,
+        performance: false,
+      }),
+    )
+    localStorage.setItem(`${PREFIX}cookie_consent_date`, new Date().toISOString())
+    localStorage.setItem(`${PREFIX}analytics_enabled`, 'false')
+    localStorage.setItem(`${PREFIX}performance_enabled`, 'false')
+  })
+  const page = await ctx.newPage()
+  try {
+    await fn(page)
+  } finally {
+    await ctx.close()
+  }
+}
+
+export async function resetUserTrustScore(page: Page, targetUserEmail: string): Promise<void> {
+  await page.goto('/admin/users', { waitUntil: 'domcontentloaded' })
+  await expect(page.getByText(/loading/i)).toBeHidden()
+
+  await page.getByPlaceholder(/search.*user/i).fill(targetUserEmail)
+
+  const userRow = page.locator('table tbody tr').filter({ hasText: targetUserEmail }).first()
+  await expect(userRow).toBeVisible()
+  await userRow.locator('button').first().click()
+
+  const dialog = page.locator('[role="dialog"]')
+  await expect(dialog).toBeVisible()
+
+  const scoreElement = dialog.getByLabel('Trust score value')
+  await expect(scoreElement).toBeVisible()
+  const currentScore = parseInt((await scoreElement.textContent()) ?? '0', 10)
+
+  if (currentScore === 0) return
+
+  await dialog.getByRole('button', { name: /trust actions/i }).click()
+
+  let remaining = currentScore
+
+  while (remaining > 0) {
+    const batch = Math.min(remaining, 1000)
+
+    await dialog.getByLabel('Reason for trust adjustment').fill('E2E: resetting trust score')
+    await dialog.getByLabel('Custom trust adjustment value').fill(String(-batch))
+    await dialog.getByRole('button', { name: /apply/i }).click()
+    await expect(page.getByText(/trust score adjusted/i)).toBeVisible()
+
+    remaining -= batch
+  }
+
+  await dialog.getByRole('button', { name: /close modal/i }).click()
+  await expect(dialog).toBeHidden()
+
+  await userRow.locator('button').first().click()
+  await expect(dialog).toBeVisible()
+  await expect(dialog.getByLabel('Trust score value')).toHaveText('0')
+}
diff --git a/tests/helpers/navigation.ts b/tests/helpers/navigation.ts
deleted file mode 100644
index 67a4de687..000000000
--- a/tests/helpers/navigation.ts
+++ /dev/null
@@ -1,334 +0,0 @@
-import type { Page } from '@playwright/test'
-
-export class NavigationHelpers {
-  constructor(private page: Page) {}
-
-  /**
-   * Navigate to a page with better error handling
-   */
-  async navigateTo(path: string, options: { timeout?: number; waitForLoad?: boolean } = {}) {
-    const { timeout = 10000, waitForLoad = true } = options
-
-    try {
-      if (waitForLoad) {
-        await this.page.goto(path, { waitUntil: 'load', timeout })
-      } else {
-        await this.page.goto(path, { timeout })
-      }
-    } catch (error) {
-      throw new Error(`Failed to navigate to ${path}: ${error}`)
-    }
-  }
-
-  /**
-   * Click navigation link with better error handling
-   */
-  async clickNavLink(linkText: string, options: { mobile?: boolean; timeout?: number } = {}) {
-    const { mobile = false, timeout = 10000 } = options
-
-    // Wait for navigation to be ready
-    await this.page.waitForLoadState('domcontentloaded')
-
-    try {
-      const linkSelector = mobile
-        ? `a:has-text("${linkText}"):nth-of-type(2)` // Mobile menu version
-        : `a:has-text("${linkText}"):nth-of-type(1)` // Desktop version
-
-      const link = this.page.locator(linkSelector)
-
-      if (!(await link.isVisible({ timeout: 5000 }))) {
-        throw new Error(
-          `Navigation link "${linkText}" not found (mobile: ${mobile}). Available links: ${await this.getAvailableLinks()}`,
-        )
-      }
-
-      await link.click({ timeout })
-    } catch (error) {
-      throw new Error(`Failed to click navigation link "${linkText}": ${error}`)
-    }
-  }
-
-  /**
-   * Wait for URL to change with better error handling
-   */
-  async waitForURLChange(
-    expectedPath: string,
-    options: { timeout?: number; exact?: boolean } = {},
-  ) {
-    const { timeout = 10000, exact = true } = options
-
-    try {
-      if (exact) {
-        await this.page.waitForURL(expectedPath, { timeout })
-      } else {
-        await this.page.waitForURL(new RegExp(expectedPath), { timeout })
-      }
-    } catch {
-      const currentURL = this.page.url()
-      throw new Error(
-        `URL did not change to "${expectedPath}" within ${timeout}ms. Current URL: ${currentURL}`,
-      )
-    }
-  }
-
-  /**
-   * Open mobile menu
-   */
-  async openMobileMenu() {
-    const menuButton = this.page.getByRole('button', { name: /menu/i })
-
-    if (!(await menuButton.isVisible({ timeout: 5000 }))) {
-      throw new Error('Mobile menu button not found')
-    }
-
-    await menuButton.click()
-
-    // Wait for menu to open
-    const mobileMenu = this.page.locator('nav').locator('.md\\:hidden').last()
-    await mobileMenu.waitFor({ state: 'visible', timeout: 2000 }).catch(() => {})
-  }
-
-  /**
-   * Close mobile menu by clicking outside
-   */
-  async closeMobileMenu() {
-    try {
-      // Try multiple approaches to close the mobile menu
-
-      // Approach 1: Try clicking the hamburger menu button again to toggle it closed
-      const menuButton = this.page.getByRole('button', { name: /menu/i })
-      if ((await menuButton.count()) > 0) {
-        const isExpanded = await menuButton.getAttribute('aria-expanded')
-        if (isExpanded === 'true') {
-          await menuButton.click()
-          await this.page.waitForLoadState('domcontentloaded')
-          return
-        }
-      }
-
-      // Approach 2: Try pressing Escape key
-      await this.page.keyboard.press('Escape')
-      await this.page.waitForLoadState('domcontentloaded')
-
-      // Approach 3: Try clicking on overlay/backdrop
-      const overlaySelectors = ['.menu-overlay', '.mobile-overlay', '.backdrop', '[data-overlay]']
-      for (const selector of overlaySelectors) {
-        const overlay = this.page.locator(selector)
-        if ((await overlay.count()) > 0 && (await overlay.first().isVisible({ timeout: 300 }))) {
-          await overlay.first().click()
-          await this.page.waitForLoadState('domcontentloaded')
-          return
-        }
-      }
-
-      // Approach 4: Try clicking outside the navigation area (on body)
-      await this.page.locator('body').click({ position: { x: 50, y: 200 }, force: true })
-      await this.page.waitForLoadState('domcontentloaded')
-    } catch (error) {
-      console.log('Error closing mobile menu:', error)
-      // Fallback: wait for DOM to stabilize
-      await this.page.waitForLoadState('domcontentloaded')
-    }
-  }
-
-  /**
-   * Check if mobile menu is open
-   */
-  async isMobileMenuOpen(): Promise<boolean> {
-    try {
-      // Wait for potential animations to complete
-      await this.page.waitForLoadState('domcontentloaded')
-
-      // Try multiple approaches to detect if mobile menu is open
-
-      // Approach 1: Check hamburger button state first (most reliable)
-      const menuButton = this.page.getByRole('button', { name: /menu/i })
-      if ((await menuButton.count()) > 0) {
-        const menuAria = (await menuButton.getAttribute('aria-expanded')) ?? ''
-        if (menuAria === 'true') {
-          console.log('Mobile menu detected: aria-expanded=true')
-          return true
-        }
-
-        const menuClasses = (await menuButton.getAttribute('class')) ?? ''
-        if (
-          menuClasses.includes('active') ||
-          menuClasses.includes('open') ||
-          menuClasses.includes('expanded')
-        ) {
-          console.log('Mobile menu detected: button has active class')
-          return true
-        }
-      }
-
-      // Approach 2: Look for multiple instances of navigation links (mobile + desktop)
-      try {
-        const navigationLinks = await this.page
-          .locator('a[href="/"], a[href="/games"], a[href="/listings"]')
-          .all()
-        const visibleCount = await Promise.all(
-          navigationLinks.map((link) => link.isVisible({ timeout: 500 }).catch(() => false)),
-        ).then((results) => results.filter(Boolean).length)
-
-        if (visibleCount > 3) {
-          // More than one set of navigation visible
-          console.log('Mobile menu detected: multiple navigation sets visible')
-          return true
-        }
-      } catch {
-        // Continue to next check
-      }
-
-      // Approach 3: Look for mobile-specific navigation elements
-      const mobileNavSelectors = [
-        'nav[data-mobile="true"]',
-        '[data-testid*="mobile"]',
-        '.mobile-menu',
-        '.mobile-nav',
-        '.mobile-navigation',
-        'nav.md\\:hidden',
-      ]
-
-      for (const selector of mobileNavSelectors) {
-        try {
-          const element = this.page.locator(selector)
-          const count = await element.count()
-          if (count > 0) {
-            const isVisible = await element.first().isVisible({ timeout: 500 })
-            if (isVisible) {
-              console.log(`Mobile menu detected: ${selector} is visible`)
-              return true
-            }
-          }
-        } catch {
-          // Continue to next selector
-        }
-      }
-
-      // Approach 4: Check for body/html classes that indicate menu state
-      try {
-        const bodyClasses = (await this.page.locator('body').getAttribute('class')) ?? ''
-        const htmlClasses = (await this.page.locator('html').getAttribute('class')) ?? ''
-
-        const openIndicators = ['menu-open', 'mobile-menu-open', 'nav-open', 'navigation-open']
-        for (const indicator of openIndicators) {
-          if (bodyClasses.includes(indicator) || htmlClasses.includes(indicator)) {
-            console.log(`Mobile menu detected: ${indicator} class found`)
-            return true
-          }
-        }
-      } catch {
-        // Continue to next check
-      }
-
-      // Approach 5: Check for overlay/backdrop elements
-      const overlaySelectors = ['.menu-overlay', '.mobile-overlay', '.backdrop', '[data-overlay]']
-
-      for (const selector of overlaySelectors) {
-        try {
-          const overlay = this.page.locator(selector)
-          if ((await overlay.count()) > 0) {
-            const isVisible = await overlay.first().isVisible({ timeout: 500 })
-            if (isVisible) {
-              console.log(`Mobile menu detected: ${selector} overlay is visible`)
-              return true
-            }
-          }
-        } catch {
-          // Continue to next selector
-        }
-      }
-
-      // Approach 6: Look for mobile-only visible navigation items
-      try {
-        // Check if there are navigation items that are normally hidden but now visible
-        const hiddenNavItems = this.page
-          .locator('nav a, nav button')
-          .filter({ hasText: /home|games|listings|profile/i })
-        const itemCount = await hiddenNavItems.count()
-
-        if (itemCount > 4) {
-          // More nav items than usual (indicates mobile menu open)
-          console.log('Mobile menu detected: extra navigation items visible')
-          return true
-        }
-      } catch {
-        // Continue
-      }
-
-      // Approach 7: Check for specific Tailwind mobile classes
-      try {
-        const mobileVisibleElements = this.page.locator('.block.md\\:hidden, .flex.md\\:hidden')
-        const elementCount = await mobileVisibleElements.count()
-
-        if (elementCount > 0) {
-          for (let i = 0; i < Math.min(elementCount, 3); i++) {
-            const element = mobileVisibleElements.nth(i)
-            const isVisible = await element.isVisible({ timeout: 300 }).catch(() => false)
-            if (isVisible) {
-              // Check if this element contains navigation
-              const hasNavigation = (await element.locator('a[href], button').count()) > 0
-              if (hasNavigation) {
-                console.log('Mobile menu detected: mobile-visible navigation found')
-                return true
-              }
-            }
-          }
-        }
-      } catch {
-        // Final approach failed
-      }
-
-      console.log('Mobile menu state: CLOSED (no open indicators found)')
-      return false
-    } catch (error) {
-      console.log('Error checking mobile menu state:', error)
-      return false
-    }
-  }
-
-  /**
-   * Helper to debug available links
-   */
-  private async getAvailableLinks(): Promise<string> {
-    const links = await this.page.locator('a').all()
-    const linkTexts = await Promise.all(
-      links.map(async (link) => {
-        try {
-          const text = await link.textContent()
-          const href = await link.getAttribute('href')
-          const visible = await link.isVisible()
-          return `"${text}" (href: ${href}, visible: ${visible})`
-        } catch {
-          return 'error reading link'
-        }
-      }),
-    )
-    return linkTexts.join(', ')
-  }
-
-  /**
-   * Navigate using Next.js Link component
-   */
-  async navigateWithLink(
-    linkText: string,
-    expectedPath: string,
-    options: { mobile?: boolean } = {},
-  ) {
-    await this.clickNavLink(linkText, options)
-    await this.waitForURLChange(expectedPath)
-  }
-
-  /**
-   * Set viewport size for responsive testing
-   */
-  async setViewport(size: 'mobile' | 'tablet' | 'desktop') {
-    const viewports = {
-      mobile: { width: 375, height: 667 },
-      tablet: { width: 768, height: 1024 },
-      desktop: { width: 1200, height: 800 },
-    }
-
-    await this.page.setViewportSize(viewports[size])
-  }
-}
diff --git a/tests/helpers/test-config.ts b/tests/helpers/test-config.ts
index 2a5368952..f40eb0ea1 100644
--- a/tests/helpers/test-config.ts
+++ b/tests/helpers/test-config.ts
@@ -1,89 +1,30 @@
 import type { Page } from '@playwright/test'
 
 /**
- * Centralized test configuration for E2E tests
- * Optimized for production builds to prevent timeouts
+ * Centralized test configuration for E2E tests.
  */
 
 export const TEST_CONFIG = {
-  // Timeouts
   timeouts: {
-    navigation: 30000, // 30s for page navigation
-    action: 10000, // 10s for element actions
-    wait: 5000, // 5s for general waits
-    pageLoad: 20000, // 20s for initial page load
-    apiCall: 15000, // 15s for API calls
+    navigation: 30000,
+    action: 10000,
+    wait: 5000,
+    pageLoad: 20000,
+    apiCall: 15000,
   },
 
-  // Retry configuration
-  retries: {
-    click: 3,
-    navigation: 2,
-    assertion: 3,
-  },
-
-  // Wait strategies
-  waitStrategies: {
-    // Wait for network to be idle
-    networkIdle: { waitUntil: 'networkidle' as const, timeout: 30000 },
-    // Wait for DOM to be ready
-    domContentLoaded: {
-      waitUntil: 'domcontentloaded' as const,
-      timeout: 20000,
-    },
-    // Wait for full page load
-    load: { waitUntil: 'load' as const, timeout: 30000 },
-  },
-
-  // Viewport sizes
   viewports: {
     desktop: { width: 1280, height: 720 },
     tablet: { width: 768, height: 1024 },
     mobile: { width: 375, height: 667 },
   },
 
-  // Test data
   testData: {
-    defaultWaitForSelector: '[data-testid], [role], a, button',
-    cookieConsentKey: '@TestEmuReady_cookie_consent',
-    cookiePrefix: '@TestEmuReady_',
+    cookieConsentKey: '@StagingEmuReady_cookie_consent',
+    cookiePrefix: '@StagingEmuReady_',
   },
 } as const
 
-/**
- * Helper to wait for page stability
- */
 export async function waitForPageStability(page: Page, timeout = 5000) {
   await page.waitForLoadState('domcontentloaded', { timeout })
 }
-
-/**
- * Retry helper for flaky operations
- */
-export async function retryOperation<T>(
-  operation: () => Promise<T>,
-  maxRetries = 3,
-  delay = 1000,
-): Promise<T> {
-  let lastError: Error | undefined
-
-  for (let i = 0; i < maxRetries; i++) {
-    try {
-      return await operation()
-    } catch (error) {
-      lastError = error as Error
-      if (i < maxRetries - 1) {
-        console.log(`Retry ${i + 1}/${maxRetries} after error:`, lastError.message)
-        await new Promise((resolve) => setTimeout(resolve, delay))
-      }
-    }
-  }
-
-  throw lastError || new Error('Operation failed after retries')
-}
-
-export async function gotoWithRetry(page: Page, url: string) {
-  return retryOperation(async () => {
-    await page.goto(url, TEST_CONFIG.waitStrategies.domContentLoaded)
-  }, TEST_CONFIG.retries.navigation)
-}
diff --git a/tests/igdb-search.spec.ts b/tests/igdb-search.spec.ts
index 813985446..b94c6af78 100644
--- a/tests/igdb-search.spec.ts
+++ b/tests/igdb-search.spec.ts
@@ -1,14 +1,5 @@
 import { test, expect } from '@playwright/test'
 
-/**
- * E2E tests for the IGDB game search page.
- *
- * Route: /games/new/search/v2
- * Heading: "Search Game Database"
- * Requires system selection before search.
- * Results displayed as card grid with preview modal.
- */
-
 test.describe('IGDB Search', () => {
   test.use({ storageState: 'tests/.auth/user.json' })
 
@@ -16,10 +7,7 @@ test.describe('IGDB Search', () => {
     await page.goto('/games/new/search/v2')
     await page.waitForLoadState('domcontentloaded')
 
-    // Page heading
     await expect(page.getByRole('heading', { name: /search game database/i })).toBeVisible()
-
-    // IGDB badge
     await expect(page.getByText(/powered by igdb/i)).toBeVisible()
   })
 
@@ -27,72 +15,48 @@ test.describe('IGDB Search', () => {
     await page.goto('/games/new/search/v2')
     await page.waitForLoadState('domcontentloaded')
 
-    // System autocomplete (required)
-    const systemInput = page.getByPlaceholder(/choose a system/i)
-    await expect(systemInput).toBeVisible()
-
-    // Game title input (required)
-    const titleInput = page.getByPlaceholder(/enter game title/i)
-    await expect(titleInput).toBeVisible()
-
-    // Form search button (submit type, distinct from navbar search icon)
-    const searchButton = page.locator('button[type="submit"]').filter({ hasText: /search/i })
-    await expect(searchButton).toBeVisible()
+    await expect(page.getByPlaceholder(/choose a system/i)).toBeVisible()
+    await expect(page.getByPlaceholder(/enter game title/i)).toBeVisible()
+    await expect(page.locator('button[type="submit"]').filter({ hasText: /search/i })).toBeVisible()
   })
 
   test('should require system selection before search', async ({ page }) => {
     await page.goto('/games/new/search/v2')
     await page.waitForLoadState('domcontentloaded')
 
-    // Type a game name without selecting a system
-    const titleInput = page.getByPlaceholder(/enter game title/i)
-    await titleInput.fill('Mario')
-
-    // Click the form search button
+    await page.getByPlaceholder(/enter game title/i).fill('Mario')
     await page
       .locator('button[type="submit"]')
       .filter({ hasText: /search/i })
       .click()
 
-    // Should show a warning about selecting a system first
-    // Toast or inline warning: "Please select a system before searching"
-    const warning = page.getByText(/please select a system/i)
-    const hasWarning = await warning.isVisible({ timeout: 3000 }).catch(() => false)
-
-    if (hasWarning) {
-      await expect(warning).toBeVisible()
-    }
+    await expect(page.getByText(/please select a system/i)).toBeVisible()
   })
 
   test('should have search button disabled for empty query', async ({ page }) => {
     await page.goto('/games/new/search/v2')
     await page.waitForLoadState('domcontentloaded')
 
-    // Form search button should be disabled when query is empty
-    const searchButton = page.locator('button[type="submit"]').filter({ hasText: /search/i })
-
-    // Button is disabled when query is empty or < 2 chars
-    await expect(searchButton).toBeDisabled()
+    await expect(
+      page.locator('button[type="submit"]').filter({ hasText: /search/i }),
+    ).toBeDisabled()
   })
 
   test('should enable search button when query has 2+ characters', async ({ page }) => {
     await page.goto('/games/new/search/v2')
     await page.waitForLoadState('domcontentloaded')
 
-    const titleInput = page.getByPlaceholder(/enter game title/i)
-    await titleInput.fill('Ma')
+    await page.getByPlaceholder(/enter game title/i).fill('Ma')
 
-    // Form search button should become enabled
-    const searchButton = page.locator('button[type="submit"]').filter({ hasText: /search/i })
-    await expect(searchButton).toBeEnabled()
+    await expect(page.locator('button[type="submit"]').filter({ hasText: /search/i })).toBeEnabled()
   })
 
   test('should show search description text', async ({ page }) => {
     await page.goto('/games/new/search/v2')
     await page.waitForLoadState('domcontentloaded')
 
-    // Description about IGDB capabilities
-    const description = page.getByText(/comprehensive.*game.*information|metadata.*images/i)
-    await expect(description.first()).toBeVisible()
+    await expect(
+      page.getByText(/comprehensive.*game.*information|metadata.*images/i).first(),
+    ).toBeVisible()
   })
 })
diff --git a/tests/listing-approval.spec.ts b/tests/listing-approval.spec.ts
index c8705f470..94378d07e 100644
--- a/tests/listing-approval.spec.ts
+++ b/tests/listing-approval.spec.ts
@@ -1,171 +1,90 @@
 import { test, expect } from '@playwright/test'
 
 test.describe('Listing Approval Workflow Tests - Requires Admin Role', () => {
-  test.use({ storageState: 'tests/.auth/admin.json' })
+  test.use({ storageState: 'tests/.auth/super_admin.json' })
   test.beforeEach(async ({ page }) => {
     await page.goto('/admin/approvals', { waitUntil: 'domcontentloaded' })
     await expect(page).toHaveURL(/\/admin\/approvals/)
+
+    await expect(page.locator('main').first()).toBeVisible()
   })
 
   test('should display pending listings for approval with required information', async ({
     page,
   }) => {
-    const mainContent = page.locator('main').first()
-    await expect(mainContent).toBeVisible()
-
-    // Wait for loading to finish
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
-
     const table = page.locator('table').first()
-    const hasTable = await table.isVisible({ timeout: 10000 }).catch(() => false)
-
-    if (hasTable) {
-      const rows = table.locator('tbody tr')
-      const rowCount = await rows.count()
-
-      if (rowCount > 0) {
-        // Table has listing rows — verify action buttons exist
-        const approveButtons = page.locator('button[title="Approve Listing"]')
-        const rejectButtons = page.locator('button[title="Reject Listing"]')
-        const hasApprove = (await approveButtons.count()) > 0
-        const hasReject = (await rejectButtons.count()) > 0
-        expect(hasApprove || hasReject).toBe(true)
-      } else {
-        // Empty table — verify table structure exists
-        const headers = table.locator('thead th')
-        expect(await headers.count()).toBeGreaterThan(0)
-      }
-    } else {
-      // No table — page should still have main content
-      await expect(mainContent).toBeVisible()
-    }
-  })
+    await expect(table).toBeVisible()
 
-  test('should show submission details for review', async ({ page }) => {
-    const viewButtons = page.locator('button[title="View Details"]')
-    const hasViewButtons = (await viewButtons.count()) > 0
-    test.skip(!hasViewButtons, 'No View Details buttons found -- no pending listings to review')
+    const approveButtons = page.locator('button[title="Approve Listing"]')
+    const rejectButtons = page.locator('button[title="Reject Listing"]')
 
-    await viewButtons.first().click()
+    expect(await approveButtons.count()).toBeGreaterThan(0)
+    expect(await rejectButtons.count()).toBeGreaterThan(0)
+  })
 
-    const detailModal = page.locator('[role="dialog"]')
-    const modalVisible = await detailModal.isVisible({ timeout: 3000 })
-    test.skip(!modalVisible, 'Detail modal did not appear')
+  test('should track approval metrics', async ({ page }) => {
+    const statsText = page.getByText(/total|pending|approved|rejected/i)
+    await expect(statsText.first()).toBeVisible()
 
-    const modalContent = await detailModal.textContent()
-    expect(modalContent).toBeTruthy()
-    expect(modalContent!.length).toBeGreaterThan(0)
+    const mainContent = page.locator('main').first()
+    const numbersOnPage = await mainContent.locator('text=/\\d+/').count()
+    expect(numbersOnPage).toBeGreaterThan(0)
   })
 
-  test('should support quick approval actions', async ({ page }) => {
-    const approveButton = page.locator('button[title="Approve Listing"]').first()
-    const approveVisible = await approveButton.isVisible({ timeout: 3000 }).catch(() => false)
-    test.skip(!approveVisible, 'No approve button found -- no pending listings')
+  test('should show submission details for review', async ({ page }) => {
+    const table = page.locator('table').first()
+    await expect(table).toBeVisible()
 
-    await approveButton.click()
+    const viewLinks = page.locator('tbody a:has(button[title="View Details"])')
+    await expect(viewLinks.first()).toBeVisible()
 
-    // Confirmation dialog may appear
-    const confirmDialog = page.locator('[role="dialog"]')
-    const hasConfirm = await confirmDialog.isVisible({ timeout: 3000 }).catch(() => false)
-
-    if (hasConfirm) {
-      const confirmButton = confirmDialog
-        .locator('button')
-        .filter({ hasText: /confirm.*approval|approve/i })
-      if (await confirmButton.isVisible({ timeout: 2000 }).catch(() => false)) {
-        await confirmButton.click()
-      } else {
-        // Cancel if no confirm button found
-        const cancelButton = confirmDialog.locator('button').filter({ hasText: /cancel/i })
-        await cancelButton.click()
-      }
-    }
-
-    // Page should still be on approvals
-    await expect(page).toHaveURL(/\/admin\/approvals/)
+    const href = await viewLinks.first().getAttribute('href')
+    expect(href).toMatch(/\/listings\/[^/]+/)
   })
 
   test('should handle rejection with feedback', async ({ page }) => {
     const rejectButtons = page.locator('button[title="Reject Listing"]')
-    const hasRejectButtons = (await rejectButtons.count()) > 0
-    test.skip(!hasRejectButtons, 'No reject buttons found -- no pending listings')
+    await expect(rejectButtons.first()).toBeVisible()
 
     await rejectButtons.first().click()
 
     const rejectDialog = page.locator('[role="dialog"]')
-    const dialogVisible = await rejectDialog.isVisible({ timeout: 3000 })
-    test.skip(!dialogVisible, 'Rejection dialog did not appear')
+    await expect(rejectDialog).toBeVisible()
 
-    // Feedback textarea
     const feedbackField = rejectDialog.locator('textarea')
-    if (await feedbackField.isVisible().catch(() => false)) {
-      await feedbackField.fill('Please provide more detailed performance notes')
-    }
+    await expect(feedbackField).toBeVisible()
+    await feedbackField.fill('Please provide more detailed performance notes')
 
-    // Cancel to avoid modifying data
     const cancelButton = rejectDialog.locator('button').filter({ hasText: /cancel/i })
     await expect(cancelButton).toBeVisible()
     await cancelButton.click()
   })
 
-  test('should batch process approvals', async ({ page }) => {
-    const selectAll = page.locator('thead input[type="checkbox"]')
-    const selectAllVisible = await selectAll.isVisible({ timeout: 3000 }).catch(() => false)
-    test.skip(!selectAllVisible, 'No select-all checkbox found -- no pending listings')
-
-    await selectAll.check()
-
-    // BulkActions component renders buttons like "Approve Selected", "Reject Selected"
-    const approveSelected = page.locator('button').filter({ hasText: /approve selected/i })
-    const rejectSelected = page.locator('button').filter({ hasText: /reject selected/i })
+  test('should support quick approval actions', async ({ page }) => {
+    const approveButton = page.locator('button[title="Approve Listing"]').first()
+    await expect(approveButton).toBeVisible()
 
-    const hasApproveSelected = await approveSelected.isVisible({ timeout: 3000 }).catch(() => false)
-    const hasRejectSelected = await rejectSelected.isVisible({ timeout: 3000 }).catch(() => false)
+    await approveButton.click()
 
-    test.skip(
-      !hasApproveSelected && !hasRejectSelected,
-      'Bulk actions did not appear after selecting all',
-    )
+    const confirmDialog = page.locator('[role="dialog"]')
+    await expect(confirmDialog).toBeVisible()
 
-    expect(hasApproveSelected || hasRejectSelected).toBe(true)
+    const cancelButton = confirmDialog.locator('button').filter({ hasText: /cancel/i })
+    await expect(cancelButton).toBeVisible()
+    await cancelButton.click()
 
-    // Uncheck to avoid modifying data
-    await selectAll.uncheck()
+    await expect(page).toHaveURL(/\/admin\/approvals/)
   })
 
-  test('should track approval metrics', async ({ page }) => {
-    // AdminStatsDisplay renders stat cards at top of page
-    const statsText = page.getByText(/total|pending|approved|rejected/i)
-    const hasStats = await statsText
-      .first()
-      .isVisible({ timeout: 5000 })
-      .catch(() => false)
-    test.skip(!hasStats, 'No approval metrics section found')
+  test('should batch process approvals', async ({ page }) => {
+    const selectAll = page.locator('thead input[type="checkbox"]')
+    await expect(selectAll).toBeVisible()
 
-    // Verify stat values contain numbers
-    const mainContent = page.locator('main').first()
-    const numbersOnPage = await mainContent.locator('text=/\\d+/').count()
-    expect(numbersOnPage).toBeGreaterThan(0)
-  })
+    await selectAll.check()
 
-  test('should handle reported user submissions specially', async ({ page }) => {
-    // AuthorRiskIndicator component renders warning indicators for risky authors
-    const riskIndicators = page.locator('[title*="risk"], [aria-label*="risk"]')
-    const warningBadges = page.locator('text=/high risk|medium risk|reported/i')
-
-    const hasRisk = (await riskIndicators.count()) > 0
-    const hasWarning = (await warningBadges.count()) > 0
-    test.skip(!hasRisk && !hasWarning, 'No submissions from reported users found')
-
-    if (hasRisk) {
-      const firstIndicator = riskIndicators.first()
-      await expect(firstIndicator).toBeVisible()
-    } else {
-      const firstWarning = warningBadges.first()
-      await expect(firstWarning).toBeVisible()
-    }
+    const approveSelected = page.locator('button').filter({ hasText: /approve selected/i })
+    await expect(approveSelected).toBeVisible()
+
+    await selectAll.uncheck()
   })
 })
diff --git a/tests/listings-success-rate-sorting.spec.ts b/tests/listings-success-rate-sorting.spec.ts
index 9e62a0ea8..7a35ee350 100644
--- a/tests/listings-success-rate-sorting.spec.ts
+++ b/tests/listings-success-rate-sorting.spec.ts
@@ -1,14 +1,6 @@
 import path from 'path'
 import { test, expect } from '@playwright/test'
 
-/**
- * E2E tests for success rate sorting on the listings page.
- *
- * The listings table has a "Verified" column (field: successRate) using
- * SortableHeader. Click cycle: asc → desc → clear. Success rate cells
- * display "X% (Y votes)" via SuccessRateBar.
- */
-
 test.describe('Success Rate Sorting', () => {
   test.use({ storageState: path.join(__dirname, '.auth/user.json') })
 
@@ -16,10 +8,14 @@ test.describe('Success Rate Sorting', () => {
     await page.goto('/listings')
     await page.waitForLoadState('domcontentloaded')
 
-    // Dismiss community support banner if present (can intercept header clicks)
+    // The community support banner overlays the table header and intercepts
+    // sort clicks if it's still present.
     const dismissBanner = page.getByRole('button', { name: /dismiss community support/i })
-    if (await dismissBanner.isVisible({ timeout: 1000 }).catch(() => false)) {
+    try {
+      await dismissBanner.waitFor({ state: 'visible', timeout: 1000 })
       await dismissBanner.click()
+    } catch {
+      // Banner not present — nothing to dismiss
     }
   })
 
@@ -27,88 +23,63 @@ test.describe('Success Rate Sorting', () => {
     const verifiedHeader = page.locator('th').filter({ hasText: /verified/i })
     await verifiedHeader.click()
 
-    // URL should reflect ascending sort
     await expect(page).toHaveURL(/sortField=successRate/)
     await expect(page).toHaveURL(/sortDirection=asc/)
-
-    // Active sort indicator should appear
     await expect(verifiedHeader.locator('.text-blue-500')).toBeVisible()
 
-    // Wait for sorted data to load (tRPC refetch after URL change)
     await page.waitForLoadState('domcontentloaded')
 
-    // Verify data loaded in sorted order
-    const rows = page.locator('tbody tr')
-    const rowCount = await rows.count()
-    expect(rowCount).toBeGreaterThan(0)
+    expect(await page.locator('tbody tr').count()).toBeGreaterThan(0)
   })
 
   test('should sort by success rate descending on second click', async ({ page }) => {
     const verifiedHeader = page.locator('th').filter({ hasText: /verified/i })
 
-    // First click: asc, second click: desc
     await verifiedHeader.click()
     await expect(page).toHaveURL(/sortDirection=asc/)
     await verifiedHeader.click()
 
     await expect(page).toHaveURL(/sortField=successRate/)
     await expect(page).toHaveURL(/sortDirection=desc/)
-
-    // Active sort indicator should appear
     await expect(verifiedHeader.locator('.text-blue-500')).toBeVisible()
 
-    // Wait for sorted data to load
     await page.waitForLoadState('domcontentloaded')
 
-    const rows = page.locator('tbody tr')
-    const rowCount = await rows.count()
-    expect(rowCount).toBeGreaterThan(0)
+    expect(await page.locator('tbody tr').count()).toBeGreaterThan(0)
   })
 
   test('should clear sorting on third click (return to default)', async ({ page }) => {
     const verifiedHeader = page.locator('th').filter({ hasText: /verified/i })
 
-    // Click three times: asc → desc → clear
     await verifiedHeader.click()
     await page.waitForURL(/sortDirection=asc/)
     await verifiedHeader.click()
     await page.waitForURL(/sortDirection=desc/)
     await verifiedHeader.click()
 
-    // Sort params should be cleared from URL
     await expect(page).not.toHaveURL(/sortField=successRate/)
     await expect(page).not.toHaveURL(/sortDirection/)
-
-    // No active sort indicator on the Verified header
-    const activeIcon = verifiedHeader.locator('.text-blue-500')
-    await expect(activeIcon).not.toBeVisible()
+    await expect(verifiedHeader.locator('.text-blue-500')).not.toBeVisible()
   })
 
   test('should maintain sort across pagination', async ({ page }) => {
     const verifiedHeader = page.locator('th').filter({ hasText: /verified/i })
     await verifiedHeader.click()
     await page.waitForURL(/sortDirection=asc/)
-
-    // Switch to descending
     await verifiedHeader.click()
     await page.waitForURL(/sortDirection=desc/)
     await page.waitForLoadState('domcontentloaded')
 
     const nextButton = page.getByRole('button', { name: /go to next page/i })
-    const hasNextPage = await nextButton.isVisible().catch(() => false)
+    await expect(nextButton).toBeVisible()
+    await expect(nextButton).toBeEnabled()
 
-    if (hasNextPage && (await nextButton.isEnabled())) {
-      await nextButton.click()
-      await page.waitForLoadState('domcontentloaded')
-
-      // Sort params should persist on page 2
-      await expect(page).toHaveURL(/sortField=successRate/)
-      await expect(page).toHaveURL(/sortDirection=desc/)
+    await nextButton.click()
+    await page.waitForLoadState('domcontentloaded')
 
-      // Table should still have rows
-      const rows = page.locator('tbody tr')
-      expect(await rows.count()).toBeGreaterThan(0)
-    }
+    await expect(page).toHaveURL(/sortField=successRate/)
+    await expect(page).toHaveURL(/sortDirection=desc/)
+    expect(await page.locator('tbody tr').count()).toBeGreaterThan(0)
   })
 
   test('should sort within reasonable time', async ({ page }) => {
@@ -119,7 +90,6 @@ test.describe('Success Rate Sorting', () => {
     await page.waitForLoadState('domcontentloaded')
     const elapsed = Date.now() - startTime
 
-    // Sorting + re-render should complete within 5 seconds
     expect(elapsed).toBeLessThan(5000)
   })
 })
diff --git a/tests/navigation.spec.ts b/tests/navigation.spec.ts
index c88762b39..ccb49ebcd 100644
--- a/tests/navigation.spec.ts
+++ b/tests/navigation.spec.ts
@@ -3,182 +3,130 @@ import { GamesPage } from './pages/GamesPage'
 import { HomePage } from './pages/HomePage'
 import { ListingsPage } from './pages/ListingsPage'
 
-test.describe('Modern Navigation Tests', () => {
-  test('should navigate between main pages using page objects', async ({ page }) => {
-    // Start at home page
+test.describe('Navigation Tests', () => {
+  test('should navigate between main pages', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Verify home page loads correctly
     await homePage.verifyHeroSectionVisible()
     await homePage.verifyNavigationVisible()
     await homePage.verifyAuthButtonsVisible()
 
-    // Navigate to games page
     await homePage.waitForOverlaysToDisappear()
     await homePage.navigateToGames()
 
     const gamesPage = new GamesPage(page)
     await gamesPage.verifyPageLoaded()
 
-    // Navigate to listings page (Handheld)
     await gamesPage.waitForOverlaysToDisappear()
     await gamesPage.navigateToHandheld()
 
     const listingsPage = new ListingsPage(page)
     await listingsPage.verifyPageLoaded()
 
-    // Navigate back to home
     await listingsPage.waitForOverlaysToDisappear()
     await listingsPage.navigateToHome()
 
-    // Verify we're back at home
     await homePage.verifyHeroSectionVisible()
   })
 
-  test('should display correct page headings and content', async ({ page }) => {
-    // Test Home Page
+  test('should display correct page headings', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
-
     await expect(homePage.heroHeading).toBeVisible()
     await expect(homePage.latestReportsSection).toBeVisible()
 
-    // Test Games Page
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
-
     await expect(gamesPage.pageHeading).toBeVisible()
 
-    // Test Listings Page
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
-
     await expect(listingsPage.pageHeading).toBeVisible()
-
-    // On mobile, filters might be hidden
-    const isMobile = page.viewportSize()?.width ? page.viewportSize()!.width < 768 : false
-    if (!isMobile) {
-      await expect(listingsPage.filtersHeading).toBeVisible()
-    }
   })
 
-  test('should have working logo navigation', async ({ page }) => {
+  test('should navigate home via logo click', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Navigate to games page
     await homePage.navigateToGames()
     await expect(page).toHaveURL('/games')
 
-    // Click logo to return home
     const gamesPage = new GamesPage(page)
     await gamesPage.waitForOverlaysToDisappear()
     await gamesPage.clickLogo()
     await expect(page).toHaveURL('/')
 
-    // Verify we're back at home
     await homePage.verifyHeroSectionVisible()
   })
 
-  test('should show authentication buttons when not logged in', async ({ page }) => {
+  test('should show sign-in and sign-up buttons when not logged in', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Verify user is not authenticated
-    const isAuthenticated = await homePage.isAuthenticated()
-    expect(isAuthenticated).toBe(false)
-
-    // Verify auth buttons are visible
     await expect(homePage.signInButton).toBeVisible()
     await expect(homePage.signUpButton).toBeVisible()
   })
 
-  test('should handle mobile navigation', async ({ page }) => {
-    // Set mobile viewport
-    await page.setViewportSize({ width: 375, height: 667 })
-
+  test('should handle browser back and forward navigation', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
+    await homePage.navigateToGames()
+    await homePage.navigateToHandheld()
 
-    // Mobile menu button should be visible
-    await expect(homePage.mobileMenuButton).toBeVisible()
+    await expect(page).toHaveURL('/listings')
 
-    // Open mobile menu
-    await homePage.openMobileMenu()
+    await page.goBack()
+    await expect(page).toHaveURL('/games')
 
-    // Navigation links should still work (some may be hidden/shown)
-    const gamesLink = homePage.gamesLink
-    if (await gamesLink.isVisible()) {
-      await gamesLink.click()
-      await expect(page).toHaveURL('/games')
-    }
+    await page.goBack()
+    await expect(page).toHaveURL('/')
+
+    await page.goForward()
+    await expect(page).toHaveURL('/games')
+
+    await page.goForward()
+    await expect(page).toHaveURL('/listings')
   })
 })
 
-test.describe('Modern Page Content Tests', () => {
-  test('should display games page content correctly', async ({ page }) => {
+test.describe('Page Content Tests', () => {
+  test('should display games page with heading and game items', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Verify page structure
     await gamesPage.verifyPageLoaded()
-    await gamesPage.verifyGameHeadingsVisible()
-
-    // Check if games are present or empty state is shown
-    await gamesPage.verifyGamesVisible()
+    await expect(gamesPage.gameItems.first()).toBeVisible()
   })
 
-  test('should display listings page content correctly', async ({ page }) => {
+  test('should display listings page with heading and listing items', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    // Verify page structure
     await listingsPage.verifyPageLoaded()
-    await listingsPage.verifyFiltersHeadingVisible()
-
-    // Check if listings are present or empty state is shown
-    await listingsPage.verifyListingsVisible()
+    await expect(listingsPage.filtersHeading).toBeVisible()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
   })
 
-  test('should handle search functionality if available', async ({ page }) => {
+  test('should search games from the games page', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Check if search is available
-    try {
-      await gamesPage.verifySearchVisible()
-
-      // Test search functionality
-      await gamesPage.searchGames('mario')
-
-      // Verify search was performed (URL change or results update)
-      // The page should either show results or no results message
-      await page.waitForLoadState('domcontentloaded')
-
-      const hasResults = (await gamesPage.getGameCount()) > 0
-      const hasNoResults = await gamesPage.noGamesMessage.isVisible()
+    await gamesPage.searchGames('mario')
 
-      // Either results or no results message should be shown
-      expect(hasResults || hasNoResults).toBe(true)
-    } catch {
-      // Search not available on this page - verify page is still functional
-      await gamesPage.verifyPageLoaded()
-    }
+    await expect(page).toHaveURL(/[?&]search=/)
   })
 
-  test('should support keyboard navigation through main menu', async ({ page }) => {
+  test('should support keyboard tab navigation through main menu', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Focus on first navigation link
     await page.keyboard.press('Tab')
 
-    // Navigate through main menu items with Tab and collect focused link texts
     const navigationLinks = ['Home', 'Handheld', 'PC', 'Games', 'Emulators']
     const focusedTexts: string[] = []
 
-    for (const _linkText of navigationLinks) {
+    for (let i = 0; i < navigationLinks.length; i++) {
       const focusedElement = page.locator(':focus')
       const text = await focusedElement.textContent()
       if (text) {
@@ -187,63 +135,55 @@ test.describe('Modern Page Content Tests', () => {
       await page.keyboard.press('Tab')
     }
 
-    // At least one navigation link should have received focus during tabbing
     const matchedLinks = navigationLinks.filter((link) =>
       focusedTexts.some((text) => text.includes(link.toLowerCase())),
     )
     expect(matchedLinks.length).toBeGreaterThan(0)
   })
+})
 
-  test('should handle browser back and forward navigation', async ({ page }) => {
-    const homePage = new HomePage(page)
-    const gamesPage = new GamesPage(page)
-    const listingsPage = new ListingsPage(page)
+test.describe('Mobile Navigation', () => {
+  test.use({ viewport: { width: 375, height: 667 } })
 
-    // Navigate through multiple pages
+  test('should show mobile menu button', async ({ page }) => {
+    const homePage = new HomePage(page)
     await homePage.goto()
-    await homePage.navigateToGames()
-    await gamesPage.navigateToHandheld()
-
-    // Verify we're on listings page
-    const isOnListingsPage = await listingsPage.isOnListingsPage()
-    expect(isOnListingsPage).toBe(true)
-
-    // Test browser back button
-    await page.goBack()
-    await expect(page).toHaveURL('/games')
 
-    await page.goBack()
-    await expect(page).toHaveURL('/')
+    await expect(homePage.mobileMenuButton).toBeVisible()
+  })
 
-    // Test browser forward button
-    await page.goForward()
-    await expect(page).toHaveURL('/games')
+  test('should open mobile menu and show navigation links', async ({ page }) => {
+    const homePage = new HomePage(page)
+    await homePage.goto()
 
-    await page.goForward()
-    await expect(page).toHaveURL('/listings')
+    await expect(homePage.mobileMenuButton).toBeVisible()
+    await homePage.openMobileMenu()
+    await expect(homePage.mobileMenu).toBeVisible()
   })
 
-  test('should toggle mobile menu correctly', async ({ page }) => {
-    // Set mobile viewport
-    await page.setViewportSize({ width: 375, height: 667 })
-
+  test('should toggle mobile menu open and closed', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Mobile menu button should be visible
     await expect(homePage.mobileMenuButton).toBeVisible()
 
-    // Open mobile menu
     await homePage.mobileMenuButton.click()
 
-    // Verify menu is visible after animation
     const mobileMenuDiv = page.locator('.md\\:hidden').filter({ hasText: 'Handheld' }).last()
     await expect(mobileMenuDiv).toHaveClass(/opacity-100/)
 
-    // Close menu by clicking the button again (toggle)
     await homePage.mobileMenuButton.click()
-
-    // Verify menu is hidden after animation
     await expect(mobileMenuDiv).toHaveClass(/opacity-0/)
   })
+
+  test('should show Home, Handheld, and PC links in mobile menu', async ({ page }) => {
+    const homePage = new HomePage(page)
+    await homePage.goto()
+
+    await homePage.openMobileMenu()
+
+    await expect(homePage.mobileMenu.getByRole('link', { name: /^home$/i })).toBeVisible()
+    await expect(homePage.mobileMenu.getByRole('link', { name: /handheld/i })).toBeVisible()
+    await expect(homePage.mobileMenu.getByRole('link', { name: /^pc$/i })).toBeVisible()
+  })
 })
diff --git a/tests/notification-system.spec.ts b/tests/notification-system.spec.ts
index ebcaba3ed..cb38db5da 100644
--- a/tests/notification-system.spec.ts
+++ b/tests/notification-system.spec.ts
@@ -1,13 +1,12 @@
 import { test, expect } from '@playwright/test'
+import type { Page } from '@playwright/test'
 
-/**
- * E2E tests for the notification system.
- *
- * Bell icon: In navbar when authenticated, shows unread count badge.
- * Dropdown: "Notifications" heading, mark all read, view all link.
- * Full page: /notifications with search, filters, bulk actions.
- * Preferences: On /profile page with per-category toggles.
- */
+function bellButton(page: Page) {
+  return page
+    .locator('button')
+    .filter({ has: page.locator('svg.lucide-bell, svg[class*="bell"]') })
+    .first()
+}
 
 test.describe('Notification System', () => {
   test.describe('Notification Bell', () => {
@@ -17,30 +16,15 @@ test.describe('Notification System', () => {
       await page.goto('/')
       await page.waitForLoadState('domcontentloaded')
 
-      // Bell button is in the navbar for authenticated users
-      // The Bell icon is inside a button element
-      const bellButton = page.locator('button').filter({
-        has: page.locator('svg.lucide-bell, svg[class*="bell"]'),
-      })
-
-      // Bell button should be visible on the page
-      await expect(bellButton.first()).toBeVisible()
+      await expect(bellButton(page)).toBeVisible()
     })
 
     test('should open notification dropdown on bell click', async ({ page }) => {
       await page.goto('/')
       await page.waitForLoadState('domcontentloaded')
 
-      const bellButton = page
-        .locator('button')
-        .filter({
-          has: page.locator('svg.lucide-bell, svg[class*="bell"]'),
-        })
-        .first()
-
-      await bellButton.click()
+      await bellButton(page).click()
 
-      // Dropdown should show "Notifications" heading
       await expect(page.getByText('Notifications').first()).toBeVisible()
     })
 
@@ -48,20 +32,10 @@ test.describe('Notification System', () => {
       await page.goto('/')
       await page.waitForLoadState('domcontentloaded')
 
-      const bellButton = page
-        .locator('button')
-        .filter({
-          has: page.locator('svg.lucide-bell, svg[class*="bell"]'),
-        })
-        .first()
-
-      await bellButton.click()
+      await bellButton(page).click()
 
-      // Close button with aria-label
       const closeButton = page.getByRole('button', { name: /close notifications/i })
       await expect(closeButton).toBeVisible()
-
-      // Clicking close should hide the dropdown
       await closeButton.click()
     })
 
@@ -69,31 +43,14 @@ test.describe('Notification System', () => {
       await page.goto('/')
       await page.waitForLoadState('domcontentloaded')
 
-      const bellButton = page
-        .locator('button')
-        .filter({
-          has: page.locator('svg.lucide-bell, svg[class*="bell"]'),
-        })
-        .first()
-
-      await bellButton.click()
+      await bellButton(page).click()
 
-      // Wait for notification content to load
       const emptyState = page.getByText(/no notifications yet/i)
-      const notificationItems = page.locator('[class*="hover:bg-gray"]').filter({
-        has: page.locator('[class*="font-medium"]'),
-      })
+      const notificationItems = page
+        .locator('[class*="hover:bg-gray"]')
+        .filter({ has: page.locator('[class*="font-medium"]') })
 
-      // Wait for either empty state or notification items to appear
-      await emptyState
-        .or(notificationItems.first())
-        .waitFor({ state: 'visible', timeout: 10000 })
-        .catch(() => {})
-
-      const hasEmpty = await emptyState.isVisible().catch(() => false)
-      const hasItems = (await notificationItems.count()) > 0
-
-      expect(hasEmpty || hasItems).toBe(true)
+      await expect(emptyState.or(notificationItems.first())).toBeVisible()
     })
   })
 
@@ -104,10 +61,7 @@ test.describe('Notification System', () => {
       await page.goto('/notifications')
       await page.waitForLoadState('domcontentloaded')
 
-      // Page heading
       await expect(page.getByRole('heading', { name: /notifications/i })).toBeVisible()
-
-      // Search input
       await expect(page.getByPlaceholder(/search notifications/i)).toBeVisible()
     })
 
@@ -115,22 +69,17 @@ test.describe('Notification System', () => {
       await page.goto('/notifications')
       await page.waitForLoadState('domcontentloaded')
 
-      // Category cards: All, Engagement, Content, System, Moderation
-      const allCategory = page.getByText(/^all$/i)
-      const hasCategories = await allCategory.isVisible().catch(() => false)
-
-      if (hasCategories) {
-        await expect(allCategory).toBeVisible()
-      }
+      // "All" label appears in filter cards, select options, and elsewhere.
+      await expect(page.getByText(/^all$/i).first()).toBeVisible()
+      await expect(page.getByText(/engagement/i).first()).toBeVisible()
+      await expect(page.getByText(/moderation/i).first()).toBeVisible()
     })
 
     test('should search notifications', async ({ page }) => {
       await page.goto('/notifications')
       await page.waitForLoadState('domcontentloaded')
 
-      const searchInput = page.getByPlaceholder(/search notifications/i)
-      await searchInput.fill('test')
-
+      await page.getByPlaceholder(/search notifications/i).fill('test')
       await page.waitForLoadState('domcontentloaded')
     })
   })
@@ -140,13 +89,10 @@ test.describe('Notification System', () => {
       await page.goto('/')
       await page.waitForLoadState('domcontentloaded')
 
-      // Bell button should not be visible for anonymous users
-      const bellButton = page.locator('button').filter({
-        has: page.locator('svg.lucide-bell, svg[class*="bell"]'),
-      })
-
-      // Anonymous users should not see the notification bell
-      await expect(bellButton).toHaveCount(0)
+      const bell = page
+        .locator('button')
+        .filter({ has: page.locator('svg.lucide-bell, svg[class*="bell"]') })
+      await expect(bell).toHaveCount(0)
     })
   })
 })
diff --git a/tests/pages/AuthPage.ts b/tests/pages/AuthPage.ts
index 86ca2a784..29fb79ea8 100644
--- a/tests/pages/AuthPage.ts
+++ b/tests/pages/AuthPage.ts
@@ -1,3 +1,4 @@
+import { expect } from '@playwright/test'
 import { BasePage } from './BasePage'
 import type { Page } from '@playwright/test'
 
@@ -6,196 +7,19 @@ export class AuthPage extends BasePage {
     super(page)
   }
 
-  // Clerk authentication elements
-  get clerkSignInModal() {
-    return this.page.locator('.cl-modal, .cl-modalContent, .cl-component, .cl-signIn-root')
-  }
-
-  get clerkSignUpModal() {
-    return this.page.locator('.cl-modal, .cl-modalContent, .cl-component, .cl-signUp-root')
-  }
-
-  get emailInput() {
-    return this.page.getByLabel(/email/i).or(this.page.getByPlaceholder(/email/i))
-  }
-
-  get passwordInput() {
-    return this.page.getByLabel(/password/i).or(this.page.getByPlaceholder(/password/i))
-  }
-
-  get continueButton() {
-    return this.page.getByRole('button', { name: /continue/i })
-  }
-
-  get submitButton() {
-    return this.page.getByRole('button', { name: /sign in|sign up|submit/i })
-  }
-
   get userButton() {
-    return this.page.locator('[data-testid="user-button"]')
-  }
-
-  get userMenu() {
-    return this.page.locator('[data-testid="user-menu"], .cl-userButtonPopover')
-  }
-
-  get signOutButton() {
-    return this.page.getByRole('button', { name: /sign out/i })
-  }
-
-  // Protected route elements
-  get authRequiredMessage() {
-    return this.page.getByText(/you need to be logged in|please sign in|sign in required/i)
-  }
-
-  get protectedContent() {
-    return this.page.locator('main').getByRole('button', { name: /sign in/i })
-  }
-
-  // Actions
-  async openSignInModal() {
-    await this.signInButton.click()
-    await this.waitForSignInModal()
-  }
-
-  async openSignUpModal() {
-    await this.signUpButton.click()
-    await this.waitForSignUpModal()
-  }
-
-  async waitForSignInModal() {
-    await this.clerkSignInModal.waitFor({ state: 'visible', timeout: 10000 })
-  }
-
-  async waitForSignUpModal() {
-    await this.clerkSignUpModal.waitFor({ state: 'visible', timeout: 10000 })
-  }
-
-  async fillSignInForm(email: string, password: string) {
-    await this.emailInput.fill(email)
-    await this.passwordInput.fill(password)
-  }
-
-  async submitSignIn() {
-    await this.submitButton.click()
-  }
-
-  async signIn(email: string, password: string) {
-    await this.openSignInModal()
-    await this.fillSignInForm(email, password)
-    await this.submitSignIn()
-    // Wait for authentication to complete
-    await this.page.waitForLoadState('domcontentloaded')
-  }
-
-  async openUserMenu() {
-    await this.userButton.click()
-    await this.userMenu.waitFor({ state: 'visible', timeout: 5000 })
-  }
-
-  async signOut() {
-    await this.openUserMenu()
-    await this.signOutButton.click()
-    // Wait for sign out to complete
-    await this.page.waitForLoadState('domcontentloaded')
-  }
-
-  // Verification methods
-  async verifySignInModalVisible() {
-    await this.clerkSignInModal.waitFor({ state: 'visible' })
-    await this.emailInput.waitFor({ state: 'visible' })
-  }
-
-  async verifySignUpModalVisible() {
-    await this.clerkSignUpModal.waitFor({ state: 'visible' })
-    await this.emailInput.waitFor({ state: 'visible' })
-  }
-
-  async verifyUserAuthenticated() {
-    await this.userButton.waitFor({ state: 'visible' })
-    // Auth buttons should not be visible when authenticated
-    await this.signInButton.waitFor({ state: 'hidden' })
-    await this.signUpButton.waitFor({ state: 'hidden' })
+    // Clerk's UserButton renders with aria-label "Open user menu"
+    return this.page.getByRole('button', { name: /open user menu/i })
   }
 
   async verifyUserNotAuthenticated() {
-    // Wait for page to stabilize first
-    await this.page.waitForLoadState('domcontentloaded')
-
-    // Wait for auth UI to render (either sign in button or user button from Clerk)
-    await this.signInButton.or(this.userButton).waitFor({ state: 'visible', timeout: 10000 })
-
-    // Verify sign in button is visible
-    const signInVisible = await this.signInButton.isVisible().catch(() => false)
-    if (!signInVisible) {
-      // Try alternative selectors for webkit
-      const altSignIn = this.page.getByText(/sign in/i).first()
-      await altSignIn.waitFor({ state: 'visible', timeout: 5000 }).catch(() => {
-        throw new Error('Sign in button not found with any selector')
-      })
-    }
-
-    await this.signUpButton.waitFor({ state: 'visible', timeout: 5000 })
-
-    // User button should not be visible when not authenticated
-    const userButtonVisible = await this.userButton.isVisible({ timeout: 1000 })
-    if (userButtonVisible) {
-      throw new Error('User button is visible when user should not be authenticated')
-    }
+    await expect(this.signInButton.or(this.page.getByText(/sign in/i).first())).toBeVisible()
   }
 
   async verifyAuthRequired() {
-    try {
-      await this.authRequiredMessage.waitFor({
-        state: 'visible',
-        timeout: 3000,
-      })
-    } catch {
-      try {
-        // Check for sign in button in main content
-        await this.protectedContent.waitFor({ state: 'visible', timeout: 3000 })
-      } catch {
-        // Check if we got redirected
-        const currentUrl = this.page.url()
-        if (!currentUrl.includes('/games/new') && !currentUrl.includes('/listings/new')) {
-          console.log('Page redirected - likely requires authentication')
-          return
-        }
-        throw new Error('No authentication requirement indicators found')
-      }
-    }
-  }
-
-  async hasAuthRequirement(): Promise<boolean> {
-    try {
-      // Check if auth required message is visible
-      const hasAuthMessage = await this.authRequiredMessage.isVisible({
-        timeout: 2000,
-      })
-      if (hasAuthMessage) return true
-
-      // Check if sign in button is in main content (not navigation)
-      const hasProtectedContent = await this.protectedContent.isVisible({
-        timeout: 2000,
-      })
-      if (hasProtectedContent) return true
-
-      // Check if page shows authentication related content
-      const pageText = await this.page.textContent('body')
-      const authKeywords = ['sign in', 'log in', 'authentication required', 'please sign in']
-      return authKeywords.some((keyword) => pageText?.toLowerCase().includes(keyword))
-    } catch {
-      return false
-    }
-  }
-
-  async verifyCanAccessProtectedContent() {
-    // Should not see auth required messages
-    const hasAuthMessage = await this.authRequiredMessage.isVisible({
-      timeout: 2000,
-    })
-    if (hasAuthMessage) {
-      throw new Error('Authentication required message visible when user should be authenticated')
-    }
+    // The app may either show an auth required message or redirect to a
+    // non-protected page. Both are valid signs of auth enforcement.
+    // Asserting the sign in button is visible means the user is not logged in.
+    await expect(this.signInButton).toBeVisible()
   }
 }
diff --git a/tests/pages/BasePage.ts b/tests/pages/BasePage.ts
index 170782ea5..e31fd3d1c 100644
--- a/tests/pages/BasePage.ts
+++ b/tests/pages/BasePage.ts
@@ -1,20 +1,29 @@
 import { expect } from '@playwright/test'
-import { CookieBanner } from './CookieBanner'
-import { TEST_CONFIG, waitForPageStability } from '../helpers/test-config'
+import { waitForPageStability } from '../helpers/test-config'
 import type { Page } from '@playwright/test'
 
+const COOKIE_CONSENT_SCRIPT = () => {
+  const PREFIX = '@StagingEmuReady_'
+  localStorage.setItem(`${PREFIX}cookie_consent`, 'true')
+  localStorage.setItem(
+    `${PREFIX}cookie_preferences`,
+    JSON.stringify({
+      necessary: true,
+      analytics: false,
+      performance: false,
+    }),
+  )
+  localStorage.setItem(`${PREFIX}cookie_consent_date`, new Date().toISOString())
+  localStorage.setItem(`${PREFIX}analytics_enabled`, 'false')
+  localStorage.setItem(`${PREFIX}performance_enabled`, 'false')
+}
+
 export abstract class BasePage {
   readonly page: Page
-  readonly cookieBanner: CookieBanner
 
-  constructor(page: Page) {
+  protected constructor(page: Page) {
     this.page = page
-    this.cookieBanner = new CookieBanner(page)
-  }
-
-  // Navigation elements that appear on all pages
-  get navigation() {
-    return this.page.locator('nav').first()
+    page.context().addInitScript(COOKIE_CONSENT_SCRIPT)
   }
 
   get logo() {
@@ -33,16 +42,6 @@ export abstract class BasePage {
     return this.page.getByRole('link', { name: /^pc$/i }).first()
   }
 
-  get gamesLink() {
-    return this.page.getByRole('link', { name: /^games$/i }).first()
-  }
-
-  // Navbar search button (expands into search input, submits to /games?search=)
-  get searchButton() {
-    return this.page.getByRole('button', { name: /search games/i }).first()
-  }
-
-  // Authentication elements
   get signInButton() {
     return this.page.getByRole('button', { name: /sign in/i }).first()
   }
@@ -51,259 +50,58 @@ export abstract class BasePage {
     return this.page.getByRole('button', { name: /sign up/i }).first()
   }
 
-  // Mobile navigation
   get mobileMenuButton() {
     return this.page.getByRole('button', { name: /open main menu/i })
   }
 
   get mobileMenu() {
-    // Mobile menu is the div that appears below navbar with transition classes
     return this.page
       .locator('.md\\:hidden')
       .filter({ has: this.page.locator('a[href="/"]') })
       .last()
   }
 
-  // Common actions
   async navigateToHome() {
-    const isMobile = this.page.viewportSize()?.width ? this.page.viewportSize()!.width < 768 : false
-
-    if (isMobile) {
-      // On mobile, use direct navigation to avoid overlay issues
-      await this.page.goto('/')
-    } else {
-      await this.waitForOverlaysToDisappear()
-
-      try {
-        await this.homeLink.click({ timeout: 5000 })
-      } catch (error) {
-        console.log(
-          'First click failed, trying alternative approach:',
-          error instanceof Error ? error.message : String(error),
-        )
-
-        await this.homeLink.scrollIntoViewIfNeeded()
-
-        try {
-          await this.homeLink.click({ timeout: 5000 })
-        } catch {
-          console.log('Regular click failed, using force click')
-          await this.homeLink.click({ force: true })
-        }
-      }
-    }
-
-    // Use expect().toHaveURL which auto-retries without needing a 'load' event —
-    // waitForURL hangs on client-side navigation (pushState) since no load fires
-    await expect(this.page).toHaveURL('/', { timeout: 15000 })
+    await this.page.goto('/')
+    await expect(this.page).toHaveURL('/')
   }
 
   async navigateToHandheld() {
-    const isMobile = this.page.viewportSize()?.width ? this.page.viewportSize()!.width < 768 : false
-
-    if (isMobile) {
-      // On mobile, use mobile menu or direct navigation
-      const mobileMenuVisible = await this.mobileMenuButton
-        .isVisible({ timeout: 1000 })
-        .catch(() => false)
-
-      if (mobileMenuVisible) {
-        await this.mobileMenuButton.click()
-        await this.mobileMenu.waitFor({ state: 'visible', timeout: 2000 }).catch(() => {})
-        const mobileHandheldLink = this.page.getByRole('link', { name: /handheld/i }).last()
-        await mobileHandheldLink.click()
-      } else {
-        await this.page.goto('/listings')
-      }
-    } else {
-      await this.waitForOverlaysToDisappear()
-
-      try {
-        await this.handheldLink.click({ timeout: 5000 })
-      } catch (error) {
-        console.log(
-          'First click failed, trying alternative approach:',
-          error instanceof Error ? error.message : String(error),
-        )
-
-        await this.handheldLink.scrollIntoViewIfNeeded()
-
-        try {
-          await this.handheldLink.click({ timeout: 5000 })
-        } catch {
-          console.log('Regular click failed, using force click')
-          await this.handheldLink.click({ force: true })
-        }
-      }
-    }
-
-    await expect(this.page).toHaveURL('/listings', { timeout: 15000 })
+    await this.page.goto('/listings')
+    await expect(this.page).toHaveURL('/listings')
   }
 
   async navigateToPC() {
-    const isMobile = this.page.viewportSize()?.width ? this.page.viewportSize()!.width < 768 : false
-
-    if (isMobile) {
-      // On mobile, use mobile menu or direct navigation
-      const mobileMenuVisible = await this.mobileMenuButton
-        .isVisible({ timeout: 1000 })
-        .catch(() => false)
-
-      if (mobileMenuVisible) {
-        await this.mobileMenuButton.click()
-        await this.mobileMenu.waitFor({ state: 'visible', timeout: 2000 }).catch(() => {})
-        const mobilePCLink = this.page.getByRole('link', { name: /^pc$/i }).last()
-        await mobilePCLink.click()
-      } else {
-        await this.page.goto('/pc-listings')
-      }
-    } else {
-      await this.waitForOverlaysToDisappear()
-
-      try {
-        await this.pcLink.click({ timeout: 5000 })
-      } catch (error) {
-        console.log(
-          'First click failed, trying alternative approach:',
-          error instanceof Error ? error.message : String(error),
-        )
-
-        await this.pcLink.scrollIntoViewIfNeeded()
-
-        try {
-          await this.pcLink.click({ timeout: 5000 })
-        } catch {
-          console.log('Regular click failed, using force click')
-          await this.pcLink.click({ force: true })
-        }
-      }
-    }
-
-    await expect(this.page).toHaveURL('/pc-listings', { timeout: 15000 })
+    await this.page.goto('/pc-listings')
+    await expect(this.page).toHaveURL('/pc-listings')
   }
 
   async navigateToGames() {
-    // No direct "Games" link in the navbar; the navbar has a search button
-    // that navigates to /games?search=<query>. For clean navigation without
-    // a search query, use direct goto.
     await this.page.goto('/games')
-
-    await expect(this.page).toHaveURL(/\/games/, { timeout: 15000 })
-  }
-
-  async searchViaNavbar(query: string) {
-    const isMobile = this.page.viewportSize()?.width ? this.page.viewportSize()!.width < 768 : false
-
-    if (isMobile) {
-      // Mobile uses a search icon that opens a full-screen overlay
-      await this.searchButton.click()
-
-      const searchInput = this.page.getByPlaceholder(/search games/i).last()
-      await searchInput.waitFor({ state: 'visible', timeout: 3000 })
-      await searchInput.fill(query)
-      await searchInput.press('Enter')
-    } else {
-      // Desktop: click search button to expand, then type and submit
-      await this.searchButton.click()
-
-      const searchInput = this.page.getByPlaceholder(/search games/i).first()
-      await searchInput.waitFor({ state: 'visible', timeout: 3000 })
-      await searchInput.fill(query)
-      await searchInput.press('Enter')
-    }
-
-    await expect(this.page).toHaveURL(/\/games\?search=/, { timeout: 15000 })
+    await expect(this.page).toHaveURL(/\/games/)
   }
 
   async clickLogo() {
-    const isMobile = this.page.viewportSize()?.width ? this.page.viewportSize()!.width < 768 : false
-
-    if (isMobile) {
-      // On mobile, use direct navigation for reliability
-      await this.page.goto('/')
-    } else {
-      // Check for and dismiss any overlay before clicking
-      await this.cookieBanner.dismissIfPresent()
-
-      try {
-        await this.logo.click()
-      } catch (error) {
-        console.log(
-          'First click failed, trying force click:',
-          error instanceof Error ? error.message : String(error),
-        )
-        // If normal click fails due to overlay, force click
-        await this.logo.click({ force: true })
-      }
-    }
-
-    await expect(this.page).toHaveURL('/', { timeout: 10000 })
+    await this.logo.click()
+    await expect(this.page).toHaveURL('/')
   }
 
   async openMobileMenu() {
-    // Check if menu is already open
-    const menuClass = (await this.mobileMenuButton.locator('..').getAttribute('class')) || ''
-    const isOpen = menuClass.includes('opacity-100') || menuClass.includes('max-h-screen')
-
-    if (!isOpen) {
-      await this.mobileMenuButton.click()
-      await this.mobileMenu.waitFor({ state: 'visible', timeout: 2000 }).catch(() => {})
-    }
+    await this.mobileMenuButton.click()
+    await expect(this.mobileMenu).toBeVisible()
   }
 
   async waitForPageLoad() {
-    await waitForPageStability(this.page, TEST_CONFIG.timeouts.pageLoad)
-    await this.cookieBanner.dismissIfPresent()
+    await waitForPageStability(this.page)
   }
 
   async waitForOverlaysToDisappear() {
-    // Dismiss Clerk authentication modals if present
     const clerkModal = this.page.locator('.cl-modal, .cl-modalContent')
-    const count = await clerkModal.count().catch(() => 0)
-
+    const count = await clerkModal.count()
     if (count > 0) {
       for (let i = 0; i < count; i++) {
-        await clerkModal
-          .nth(i)
-          .waitFor({ state: 'hidden', timeout: 3000 })
-          .catch(() => {})
-      }
-    }
-  }
-
-  // Check if user is authenticated (looks for user button or auth state)
-  async isAuthenticated(): Promise<boolean> {
-    try {
-      // Look for user button or any indicator that user is logged in
-      const userButton = this.page.locator('[data-testid="user-button"]')
-      return await userButton.isVisible({ timeout: 1000 })
-    } catch {
-      return false
-    }
-  }
-
-  // Check if authentication is required (looks for sign in buttons in main content)
-  async requiresAuthentication(): Promise<boolean> {
-    try {
-      const authMessages = [
-        'you need to be logged in',
-        'please sign in',
-        'sign in required',
-        'authentication required',
-      ]
-
-      for (const message of authMessages) {
-        const hasMessage = await this.page
-          .getByText(message, { exact: false })
-          .isVisible({ timeout: 1000 })
-        if (hasMessage) return true
+        await expect(clerkModal.nth(i)).toBeHidden()
       }
-
-      // Check for sign in button in main content (not navigation)
-      const mainSignIn = this.page.locator('main').getByRole('button', { name: /sign in/i })
-      return await mainSignIn.isVisible({ timeout: 1000 })
-    } catch {
-      return false
     }
   }
 }
diff --git a/tests/pages/CookieBanner.ts b/tests/pages/CookieBanner.ts
deleted file mode 100644
index f4ef907bf..000000000
--- a/tests/pages/CookieBanner.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import type { Page } from '@playwright/test'
-
-export class CookieBanner {
-  readonly page: Page
-
-  constructor(page: Page) {
-    this.page = page
-  }
-
-  get cookieBanner() {
-    return this.page.getByTestId('cookie-consent')
-  }
-
-  get acceptButton() {
-    return this.page.getByRole('button', { name: /accept all/i })
-  }
-
-  get necessaryOnlyButton() {
-    return this.page.getByRole('button', { name: /necessary only/i })
-  }
-
-  get customizeButton() {
-    return this.page.getByRole('button', { name: /customize/i })
-  }
-
-  get closeButton() {
-    return this.page.getByRole('button', { name: /close cookie banner/i })
-  }
-
-  get backdrop() {
-    return this.cookieBanner.locator('.bg-black\\/30')
-  }
-
-  async isVisible(): Promise<boolean> {
-    try {
-      return await this.cookieBanner.isVisible({ timeout: 2000 })
-    } catch {
-      return false
-    }
-  }
-
-  async acceptCookies() {
-    if (await this.isVisible()) {
-      await this.acceptButton.click()
-      await this.cookieBanner.waitFor({ state: 'hidden', timeout: 5000 })
-    }
-  }
-
-  async dismissIfPresent() {
-    try {
-      const bannerVisible = await this.cookieBanner.isVisible({ timeout: 2000 }).catch(() => false)
-      if (!bannerVisible) {
-        return
-      }
-
-      // Click "Accept All" to dismiss
-      const acceptAllVisible = await this.acceptButton
-        .isVisible({ timeout: 1000 })
-        .catch(() => false)
-      if (acceptAllVisible) {
-        await this.acceptButton.click()
-        await this.cookieBanner.waitFor({ state: 'hidden', timeout: 2000 }).catch(() => {})
-        return
-      }
-
-      // Fall back to the close button (X icon)
-      const closeButtonVisible = await this.closeButton
-        .isVisible({ timeout: 1000 })
-        .catch(() => false)
-      if (closeButtonVisible) {
-        await this.closeButton.click()
-        await this.cookieBanner.waitFor({ state: 'hidden', timeout: 2000 }).catch(() => {})
-        return
-      }
-
-      // Fall back to clicking the backdrop overlay
-      const backdropVisible = await this.backdrop.isVisible({ timeout: 1000 }).catch(() => false)
-      if (backdropVisible) {
-        await this.backdrop.click({ force: true })
-        await this.cookieBanner.waitFor({ state: 'hidden', timeout: 2000 }).catch(() => {})
-        return
-      }
-    } catch (error) {
-      console.log('Error dismissing cookie banner:', error)
-    }
-  }
-}
diff --git a/tests/pages/GameFormPage.ts b/tests/pages/GameFormPage.ts
index e15686bbe..8bf5f39a7 100644
--- a/tests/pages/GameFormPage.ts
+++ b/tests/pages/GameFormPage.ts
@@ -1,4 +1,3 @@
-import { expect } from '@playwright/test'
 import { BasePage } from './BasePage'
 import type { Page } from '@playwright/test'
 
@@ -7,201 +6,36 @@ export class GameFormPage extends BasePage {
     super(page)
   }
 
-  // Form elements
   get pageHeading() {
     return this.page.getByRole('heading', { name: /add new game/i })
   }
 
   get titleInput() {
-    return this.page.getByLabel(/game title|title/i)
+    return this.page.getByPlaceholder(/enter game title/i).first()
   }
 
   get systemSelect() {
-    return this.page.getByLabel(/system/i).first()
-  }
-
-  get descriptionTextarea() {
-    return this.page.getByLabel(/description/i)
-  }
-
-  get yearInput() {
-    return this.page.getByLabel(/year|release year/i)
-  }
-
-  get developerInput() {
-    return this.page.getByLabel(/developer/i)
-  }
-
-  get publisherInput() {
-    return this.page.getByLabel(/publisher/i)
-  }
-
-  get genreInput() {
-    return this.page.getByLabel(/genre/i)
+    return this.page.getByRole('combobox', { name: /system/i }).first()
   }
 
   get submitButton() {
-    return this.page.getByRole('button', {
-      name: /submit|add game|create|save/i,
-    })
-  }
-
-  get cancelButton() {
-    return this.page.getByRole('button', { name: /cancel/i })
+    return this.page.getByRole('button', { name: /add game to database/i })
   }
 
   get errorMessages() {
     return this.page.locator('.error, [role="alert"], .text-red-500')
   }
 
-  get successMessage() {
-    return this.page.getByText(/successfully|added|created/i)
-  }
-
   get form() {
     return this.page.locator('form')
   }
 
-  // Image selection elements
-  get imageSelectionArea() {
-    return this.page.locator('input[type="file"], [data-testid="image-selector"]')
-  }
-
-  get selectedImage() {
-    return this.page.locator('img[alt*="cover"], img[alt*="game"], .selected-image')
-  }
-
-  // Actions
   async goto() {
     await this.page.goto('/games/new')
     await this.waitForPageLoad()
   }
 
-  async fillGameTitle(title: string) {
-    await this.titleInput.fill(title)
-  }
-
-  async selectSystem(systemName: string) {
-    await this.systemSelect.click()
-    await this.page.getByRole('option', { name: systemName }).click()
-  }
-
-  async selectSystemByIndex(index: number) {
-    await this.systemSelect.selectOption({ index })
-  }
-
-  async fillDescription(description: string) {
-    await this.descriptionTextarea.fill(description)
-  }
-
-  async fillYear(year: string) {
-    await this.yearInput.fill(year)
-  }
-
-  async fillDeveloper(developer: string) {
-    await this.developerInput.fill(developer)
-  }
-
-  async fillPublisher(publisher: string) {
-    await this.publisherInput.fill(publisher)
-  }
-
-  async fillGenre(genre: string) {
-    await this.genreInput.fill(genre)
-  }
-
   async submitForm() {
     await this.submitButton.click()
   }
-
-  async cancelForm() {
-    await this.cancelButton.click()
-  }
-
-  async fillRequiredFields(title: string, systemIndex: number = 1) {
-    await this.fillGameTitle(title)
-    await this.selectSystemByIndex(systemIndex)
-  }
-
-  async fillCompleteForm(gameData: {
-    title: string
-    system?: string
-    description?: string
-    year?: string
-    developer?: string
-    publisher?: string
-    genre?: string
-  }) {
-    await this.fillGameTitle(gameData.title)
-
-    if (gameData.system) {
-      await this.selectSystem(gameData.system)
-    } else {
-      await this.selectSystemByIndex(1)
-    }
-
-    if (gameData.description) {
-      await this.fillDescription(gameData.description)
-    }
-
-    if (gameData.year) {
-      await this.fillYear(gameData.year)
-    }
-
-    if (gameData.developer) {
-      await this.fillDeveloper(gameData.developer)
-    }
-
-    if (gameData.publisher) {
-      await this.fillPublisher(gameData.publisher)
-    }
-
-    if (gameData.genre) {
-      await this.fillGenre(gameData.genre)
-    }
-  }
-
-  async isFormVisible(): Promise<boolean> {
-    try {
-      return await this.form.isVisible({ timeout: 2000 })
-    } catch {
-      return false
-    }
-  }
-
-  // Verification methods
-  async verifyFormVisible() {
-    await this.form.waitFor({ state: 'visible' })
-    await this.titleInput.waitFor({ state: 'visible' })
-    await this.systemSelect.waitFor({ state: 'visible' })
-  }
-
-  async verifyValidationError(expectedError?: string) {
-    await this.errorMessages.first().waitFor({ state: 'visible' })
-
-    if (expectedError) {
-      const errorText = await this.errorMessages.first().textContent()
-      if (!errorText?.toLowerCase().includes(expectedError.toLowerCase())) {
-        throw new Error(`Expected error containing "${expectedError}" but got "${errorText}"`)
-      }
-    }
-  }
-
-  async verifySuccessMessage() {
-    await this.successMessage.waitFor({ state: 'visible', timeout: 10000 })
-  }
-
-  async verifyRedirectAfterSuccess() {
-    // Should redirect to game detail page or games list
-    await expect(this.page).toHaveURL(/\/games\/((?!new).)*/, { timeout: 10000 })
-  }
-
-  async hasValidationErrors(): Promise<boolean> {
-    try {
-      await this.errorMessages.first().waitFor({ state: 'visible', timeout: 2000 })
-      return true
-    } catch {
-      return false
-    }
-  }
 }
diff --git a/tests/pages/GamesPage.ts b/tests/pages/GamesPage.ts
index 724da0265..7bd44cebd 100644
--- a/tests/pages/GamesPage.ts
+++ b/tests/pages/GamesPage.ts
@@ -7,28 +7,19 @@ export class GamesPage extends BasePage {
     super(page)
   }
 
-  // Page-specific elements
   get pageHeading() {
     return this.page.getByRole('heading', { name: /games library/i })
   }
 
-  // Games page doesn't have filters heading, it has individual game headings
-  get gameHeadings() {
-    return this.page.locator('h2').filter({ hasNotText: /emuready|about|community/i })
-  }
-
   get searchInput() {
     return this.page.getByRole('textbox', { name: /search/i })
   }
 
   get addGameButton() {
+    // On the /games library page, "Add Game" is an anchor link to /games/new.
     return this.page.getByRole('link', { name: /add game/i })
   }
 
-  get gameCards() {
-    return this.page.locator('[data-testid="game-card"]')
-  }
-
   get gameItems() {
     return this.page
       .locator('main a[href*="/games/"]')
@@ -39,11 +30,6 @@ export class GamesPage extends BasePage {
     return this.page.getByText(/no games found/i)
   }
 
-  get loadingIndicator() {
-    return this.page.getByText(/loading/i)
-  }
-
-  // Actions specific to games page
   async goto() {
     await this.page.goto('/games')
     await this.waitForPageLoad()
@@ -51,10 +37,7 @@ export class GamesPage extends BasePage {
 
   async searchGames(query: string) {
     await this.searchInput.fill(query)
-    // Search is debounced (500ms) — wait for URL to reflect the search param
-    await expect(this.page).toHaveURL(/[?&]search=/, { timeout: 10000 })
-    // Wait for search results to load (resolves immediately if loading indicator never appeared)
-    await this.page.getByText(/loading/i).waitFor({ state: 'hidden', timeout: 10000 })
+    await expect(this.page).toHaveURL(/[?&]search=/)
   }
 
   async clickAddGame() {
@@ -64,80 +47,12 @@ export class GamesPage extends BasePage {
 
   async clickFirstGame() {
     const firstGame = this.gameItems.first()
-    // Get the href to verify navigation target
-    const href = await firstGame.getAttribute('href')
+    await expect(firstGame).toBeVisible()
     await firstGame.click()
-
-    try {
-      // Use toHaveURL which auto-retries — waitForURL hangs on client-side navigation
-      await expect(this.page).toHaveURL(/\/games\/[^/]+/, { timeout: 5000 })
-    } catch {
-      // If click didn't navigate (React re-render absorbed the click), use direct navigation
-      if (href) {
-        await this.page.goto(href)
-      }
-      await expect(this.page).toHaveURL(/\/games\/[^/]+/)
-    }
+    await expect(this.page).toHaveURL(/\/games\/[^/]+/)
   }
 
-  async getGameCount(): Promise<number> {
-    try {
-      return await this.gameItems.count()
-    } catch {
-      return 0
-    }
-  }
-
-  // Verification methods
   async verifyPageLoaded() {
-    await this.pageHeading.waitFor({ state: 'visible' })
-  }
-
-  async verifyGameHeadingsVisible() {
-    // Wait for games to load
-    await this.page.waitForLoadState('domcontentloaded')
-    const count = await this.gameHeadings.count()
-    // Games page might not have any games yet, which is okay
-    if (count === 0) {
-      // No game headings found — games list might be empty
-      // Check if there's at least a page heading
-      await this.pageHeading.waitFor({ state: 'visible' })
-    } else {
-      expect(count).toBeGreaterThan(0)
-      await this.gameHeadings.first().waitFor({ state: 'visible' })
-    }
-  }
-
-  async verifySearchVisible() {
-    await this.searchInput.waitFor({ state: 'visible' })
-  }
-
-  async verifyGamesVisible() {
-    const gameCount = await this.getGameCount()
-    if (gameCount === 0) {
-      // If no games, check if there's an empty state or loading message
-      try {
-        await this.noGamesMessage.waitFor({ state: 'visible', timeout: 2000 })
-      } catch {
-        // No explicit empty message, but that's ok if there are really no games
-        // No explicit empty state message
-      }
-    } else {
-      // If games exist, verify at least one is visible
-      await this.gameItems.first().waitFor({ state: 'visible' })
-    }
-  }
-
-  async verifyAddGameButtonVisible() {
-    await this.addGameButton.waitFor({ state: 'visible' })
-  }
-
-  async isOnGamesPage(): Promise<boolean> {
-    try {
-      const url = this.page.url()
-      return url.includes('/games') && !url.includes('/games/new') && !url.includes('/games/')
-    } catch {
-      return false
-    }
+    await expect(this.pageHeading).toBeVisible()
   }
 }
diff --git a/tests/pages/HomePage.ts b/tests/pages/HomePage.ts
index 79772cffa..b58077232 100644
--- a/tests/pages/HomePage.ts
+++ b/tests/pages/HomePage.ts
@@ -1,4 +1,3 @@
-import { expect } from '@playwright/test'
 import { BasePage } from './BasePage'
 import type { Page } from '@playwright/test'
 
@@ -7,7 +6,6 @@ export class HomePage extends BasePage {
     super(page)
   }
 
-  // Page-specific elements
   get heroHeading() {
     return this.page.getByRole('heading', { name: /know before you load/i })
   }
@@ -16,91 +14,11 @@ export class HomePage extends BasePage {
     return this.page.getByRole('heading', { name: /latest compatibility reports/i })
   }
 
-  get popularDevicesSection() {
-    return this.page.getByRole('heading', { name: /popular devices/i })
-  }
-
-  get popularEmulatorsSection() {
-    return this.page.getByRole('heading', { name: /most popular emulators/i })
-  }
-
-  get topContributorsSection() {
-    return this.page.getByRole('heading', { name: /spotlight on top contributors/i })
-  }
-
-  get joinCommunityButton() {
-    return this.page.getByRole('button', { name: /join the community/i })
-  }
-
-  get createAccountButton() {
-    return this.page.getByRole('button', { name: /create an account/i })
-  }
-
-  get browseReportsLink() {
-    return this.page.getByRole('link', {
-      name: /browse compatibility reports/i,
-    })
-  }
-
-  // Actions specific to home page
   async goto() {
-    // Set cookie consent before navigation
-    await this.page.addInitScript(() => {
-      const PREFIX = '@TestEmuReady_'
-      localStorage.setItem(`${PREFIX}cookie_consent`, 'true')
-      localStorage.setItem(
-        `${PREFIX}cookie_preferences`,
-        JSON.stringify({
-          necessary: true,
-          analytics: false,
-          performance: false,
-        }),
-      )
-      localStorage.setItem(`${PREFIX}cookie_consent_date`, new Date().toISOString())
-      localStorage.setItem(`${PREFIX}analytics_enabled`, 'false')
-      localStorage.setItem(`${PREFIX}performance_enabled`, 'false')
-    })
-
     await this.page.goto('/')
     await this.waitForPageLoad()
-
-    // Force dismiss cookie banner if it still appears
-    const maxAttempts = 3
-    let attempts = 0
-
-    while ((await this.cookieBanner.isVisible()) && attempts < maxAttempts) {
-      console.log(`Cookie banner still visible, attempt ${attempts + 1} to dismiss...`)
-      await this.cookieBanner.dismissIfPresent()
-
-      if (await this.cookieBanner.isVisible()) {
-        try {
-          await this.page.getByRole('button', { name: /accept all/i }).click({ timeout: 2000 })
-        } catch {
-          // Banner may have disappeared between check and click
-        }
-      }
-
-      attempts++
-      await this.cookieBanner.cookieBanner
-        .waitFor({ state: 'hidden', timeout: 1000 })
-        .catch(() => {})
-    }
-  }
-
-  async clickJoinCommunity() {
-    await this.joinCommunityButton.click()
-  }
-
-  async clickCreateAccount() {
-    await this.createAccountButton.click()
-  }
-
-  async clickBrowseReports() {
-    await this.browseReportsLink.click()
-    await expect(this.page).toHaveURL('/listings')
   }
 
-  // Verification methods
   async verifyHeroSectionVisible() {
     await this.heroHeading.waitFor({ state: 'visible' })
   }
diff --git a/tests/pages/ListingFormPage.ts b/tests/pages/ListingFormPage.ts
deleted file mode 100644
index c987295f7..000000000
--- a/tests/pages/ListingFormPage.ts
+++ /dev/null
@@ -1,255 +0,0 @@
-import { expect } from '@playwright/test'
-import { BasePage } from './BasePage'
-import type { Page } from '@playwright/test'
-
-export class ListingFormPage extends BasePage {
-  constructor(page: Page) {
-    super(page)
-  }
-
-  // Form elements
-  get pageHeading() {
-    return this.page.getByRole('heading', {
-      name: /create listing|add listing|new listing/i,
-    })
-  }
-
-  get gameSelect() {
-    return this.page.getByLabel(/game/i).first()
-  }
-
-  get deviceSelect() {
-    return this.page.getByLabel(/device/i).first()
-  }
-
-  get emulatorSelect() {
-    return this.page.getByLabel(/emulator/i).first()
-  }
-
-  get performanceSelect() {
-    return this.page.getByLabel(/performance/i)
-  }
-
-  get performanceRating() {
-    return this.page.locator('[data-testid="performance-rating"], .performance-rating')
-  }
-
-  get notesTextarea() {
-    return this.page.getByLabel(/notes|comments|description/i)
-  }
-
-  get settingsTextarea() {
-    return this.page.getByLabel(/settings|configuration/i)
-  }
-
-  get submitButton() {
-    return this.page.getByRole('button', {
-      name: /submit|create listing|add listing|save/i,
-    })
-  }
-
-  get cancelButton() {
-    return this.page.getByRole('button', { name: /cancel/i })
-  }
-
-  get errorMessages() {
-    return this.page.locator('.error, [role="alert"], .text-red-500')
-  }
-
-  get successMessage() {
-    return this.page.getByText(/successfully|created|added/i)
-  }
-
-  get form() {
-    return this.page.locator('form')
-  }
-
-  // Custom fields section
-  get customFieldsSection() {
-    return this.page.locator('[data-testid="custom-fields"], .custom-fields, fieldset')
-  }
-
-  get customFieldInputs() {
-    return this.customFieldsSection.locator('input, textarea, select')
-  }
-
-  // Actions
-  async goto() {
-    await this.page.goto('/listings/new')
-    await this.waitForPageLoad()
-  }
-
-  async selectGame(gameName: string) {
-    await this.gameSelect.click()
-    await this.page.getByRole('option', { name: gameName }).click()
-  }
-
-  async selectGameByIndex(index: number) {
-    await this.gameSelect.selectOption({ index })
-  }
-
-  async selectDevice(deviceName: string) {
-    await this.deviceSelect.click()
-    await this.page.getByRole('option', { name: deviceName }).click()
-  }
-
-  async selectDeviceByIndex(index: number) {
-    await this.deviceSelect.selectOption({ index })
-  }
-
-  async selectEmulator(emulatorName: string) {
-    await this.emulatorSelect.click()
-    await this.page.getByRole('option', { name: emulatorName }).click()
-  }
-
-  async selectEmulatorByIndex(index: number) {
-    await this.emulatorSelect.selectOption({ index })
-  }
-
-  async selectPerformance(performance: string) {
-    await this.performanceSelect.click()
-    await this.page.getByRole('option', { name: performance }).click()
-  }
-
-  async selectPerformanceByIndex(index: number) {
-    await this.performanceSelect.selectOption({ index })
-  }
-
-  async fillNotes(notes: string) {
-    await this.notesTextarea.fill(notes)
-  }
-
-  async fillSettings(settings: string) {
-    await this.settingsTextarea.fill(settings)
-  }
-
-  async submitForm() {
-    await this.submitButton.click()
-  }
-
-  async cancelForm() {
-    await this.cancelButton.click()
-  }
-
-  async fillRequiredFields(gameIndex: number = 1, deviceIndex: number = 1) {
-    await this.selectGameByIndex(gameIndex)
-    await this.selectDeviceByIndex(deviceIndex)
-  }
-
-  async fillCompleteForm(listingData: {
-    game?: string
-    device?: string
-    emulator?: string
-    performance?: string
-    notes?: string
-    settings?: string
-  }) {
-    if (listingData.game) {
-      await this.selectGame(listingData.game)
-    } else {
-      await this.selectGameByIndex(1)
-    }
-
-    if (listingData.device) {
-      await this.selectDevice(listingData.device)
-    } else {
-      await this.selectDeviceByIndex(1)
-    }
-
-    if (listingData.emulator) {
-      await this.selectEmulator(listingData.emulator)
-    }
-
-    if (listingData.performance) {
-      await this.selectPerformance(listingData.performance)
-    }
-
-    if (listingData.notes) {
-      await this.fillNotes(listingData.notes)
-    }
-
-    if (listingData.settings) {
-      await this.fillSettings(listingData.settings)
-    }
-  }
-
-  async fillCustomField(fieldName: string, value: string) {
-    const field = this.customFieldsSection.getByLabel(fieldName)
-    await field.fill(value)
-  }
-
-  async selectCustomFieldOption(fieldName: string, optionName: string) {
-    const field = this.customFieldsSection.getByLabel(fieldName)
-    await field.click()
-    await this.page.getByRole('option', { name: optionName }).click()
-  }
-
-  async toggleCustomFieldCheckbox(fieldName: string) {
-    const checkbox = this.customFieldsSection.getByLabel(fieldName)
-    await checkbox.check()
-  }
-
-  // Verification methods
-  async verifyFormVisible() {
-    await this.form.waitFor({ state: 'visible' })
-    await this.gameSelect.waitFor({ state: 'visible' })
-    await this.deviceSelect.waitFor({ state: 'visible' })
-  }
-
-  async verifyValidationError(expectedError?: string) {
-    await this.errorMessages.first().waitFor({ state: 'visible' })
-
-    if (expectedError) {
-      const errorText = await this.errorMessages.first().textContent()
-      if (!errorText?.toLowerCase().includes(expectedError.toLowerCase())) {
-        throw new Error(`Expected error containing "${expectedError}" but got "${errorText}"`)
-      }
-    }
-  }
-
-  async verifySuccessMessage() {
-    await this.successMessage.waitFor({ state: 'visible', timeout: 10000 })
-  }
-
-  async verifyRedirectAfterSuccess() {
-    // Should redirect to listing detail page or listings list
-    await expect(this.page).toHaveURL(/\/listings\/((?!new).)*/, { timeout: 10000 })
-  }
-
-  async hasValidationErrors(): Promise<boolean> {
-    try {
-      await this.errorMessages.first().waitFor({ state: 'visible', timeout: 2000 })
-      return true
-    } catch {
-      return false
-    }
-  }
-
-  async verifyCustomFieldsVisible() {
-    await this.customFieldsSection.waitFor({ state: 'visible' })
-    const fieldCount = await this.customFieldInputs.count()
-    if (fieldCount === 0) {
-      throw new Error('No custom fields found')
-    }
-  }
-
-  async getCustomFieldCount(): Promise<number> {
-    try {
-      await this.customFieldsSection.waitFor({
-        state: 'visible',
-        timeout: 2000,
-      })
-      return await this.customFieldInputs.count()
-    } catch {
-      return 0
-    }
-  }
-
-  async isFormVisible(): Promise<boolean> {
-    try {
-      return await this.form.isVisible({ timeout: 2000 })
-    } catch {
-      return false
-    }
-  }
-}
diff --git a/tests/pages/ListingsPage.ts b/tests/pages/ListingsPage.ts
index bb4640c25..63b7c17be 100644
--- a/tests/pages/ListingsPage.ts
+++ b/tests/pages/ListingsPage.ts
@@ -7,7 +7,6 @@ export class ListingsPage extends BasePage {
     super(page)
   }
 
-  // Page-specific elements
   get pageHeading() {
     return this.page.getByRole('heading', { name: /handheld reports/i }).first()
   }
@@ -20,51 +19,42 @@ export class ListingsPage extends BasePage {
     return this.page.getByRole('textbox', { name: /search/i })
   }
 
-  get addListingButton() {
-    return this.page.getByRole('link', { name: /add listing/i })
-  }
-
-  get createListingButton() {
-    return this.page.getByRole('button', { name: /create listing/i })
-  }
-
-  get listingCards() {
-    return this.page.locator('[data-testid="listing-card"]')
+  get addReportButton() {
+    return this.page.getByRole('link', { name: /add.*(?:report|listing)/i })
   }
 
   get listingItems() {
-    return this.page
-      .locator('main a[href*="/listings/"]')
-      .filter({ hasNotText: /handheld reports|add listing|add/i })
-      .and(this.page.locator(':visible'))
+    return this.page.locator('table tbody tr')
   }
 
   get noListingsMessage() {
     return this.page.getByText(/no listings found|no results|empty|nothing found/i)
   }
 
-  get loadingIndicator() {
-    return this.page.getByText(/loading/i)
-  }
-
-  // Filter elements
+  // Filters are MultiSelect components rendered as buttons with aria-label
+  // like "Devices multi-select", "Emulators multi-select", etc.
   get deviceFilter() {
-    return this.page.getByRole('combobox', { name: /device/i })
+    return this.page.getByRole('button', { name: /devices multi-select/i })
   }
 
   get emulatorFilter() {
-    return this.page.getByRole('combobox', { name: /emulator/i })
+    return this.page.getByRole('button', { name: /emulators multi-select/i })
   }
 
   get performanceFilter() {
-    return this.page.getByRole('combobox', { name: /performance/i })
+    return this.page.getByRole('button', { name: /performance multi-select/i })
+  }
+
+  get systemFilter() {
+    return this.page.getByRole('button', { name: /systems multi-select/i })
   }
 
   get clearFiltersButton() {
-    return this.page.getByRole('button', { name: /clear filters/i })
+    // The Active Filters section renders a "Clear All" button when filters
+    // are applied (scoped to avoid "Clear all selections" per-field buttons).
+    return this.page.getByRole('button', { name: /^clear all$/i })
   }
 
-  // Actions specific to listings page
   async goto() {
     await this.page.goto('/listings')
     await this.waitForPageLoad()
@@ -73,151 +63,18 @@ export class ListingsPage extends BasePage {
   async searchListings(query: string) {
     await this.searchInput.fill(query)
     await this.page.keyboard.press('Enter')
-    // Wait for search to be reflected in URL params
-    await expect(this.page).toHaveURL(/[?&]search=/, { timeout: 10000 })
-    // Wait for data to finish loading
-    await this.page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 10000 })
-      .catch(() => {})
+    await expect(this.page).toHaveURL(/[?&]search=/)
   }
 
-  async clickAddListing() {
-    // Try both possible button variations
-    try {
-      await this.addListingButton.click()
-    } catch {
-      await this.createListingButton.click()
-    }
-    await expect(this.page).toHaveURL('/listings/new')
+  async clickFirstListing() {
+    const firstRow = this.listingItems.first()
+    await expect(firstRow).toBeVisible()
+    const link = firstRow.locator('a[href*="/listings/"]').first()
+    await link.click()
+    await expect(this.page).toHaveURL(/\/listings\/[^/]+/)
   }
 
-  async clickFirstListing() {
-    const firstListing = this.listingItems.first()
-    await firstListing.waitFor({ state: 'visible', timeout: 10000 })
-    const href = await firstListing.getAttribute('href')
-    await firstListing.click()
-    try {
-      // Use toHaveURL which auto-retries — waitForURL hangs on client-side navigation
-      await expect(this.page).toHaveURL(/\/listings\/[^/]+/, { timeout: 5000 })
-    } catch {
-      // Fallback: if click didn't navigate (e.g. tooltip intercept), use direct navigation
-      if (href) {
-        await this.page.goto(href)
-        await expect(this.page).toHaveURL(/\/listings\/[^/]+/)
-      }
-    }
-  }
-
-  async filterByDevice(deviceName: string) {
-    await this.deviceFilter.click()
-    await this.page.getByRole('option', { name: deviceName }).click()
-    // Wait for filter to apply
-    await this.page.waitForLoadState('domcontentloaded')
-  }
-
-  async filterByEmulator(emulatorName: string) {
-    await this.emulatorFilter.click()
-    await this.page.getByRole('option', { name: emulatorName }).click()
-    // Wait for filter to apply
-    await this.page.waitForLoadState('domcontentloaded')
-  }
-
-  async filterByPerformance(performance: string) {
-    await this.performanceFilter.click()
-    await this.page.getByRole('option', { name: performance }).click()
-    // Wait for filter to apply
-    await this.page.waitForLoadState('domcontentloaded')
-  }
-
-  async clearAllFilters() {
-    await this.clearFiltersButton.click()
-    // Wait for filters to clear
-    await this.page.waitForLoadState('domcontentloaded')
-  }
-
-  async getListingCount(): Promise<number> {
-    try {
-      return await this.listingItems.count()
-    } catch {
-      return 0
-    }
-  }
-
-  // Verification methods
   async verifyPageLoaded() {
-    await this.pageHeading.waitFor({ state: 'visible' })
-  }
-
-  async verifyFiltersHeadingVisible() {
-    const isMobile = this.page.viewportSize()?.width ? this.page.viewportSize()!.width < 768 : false
-
-    if (!isMobile) {
-      // Filters heading is only visible on desktop
-      await this.filtersHeading.waitFor({ state: 'visible' })
-    }
-  }
-
-  async verifySearchVisible() {
-    await this.searchInput.waitFor({ state: 'visible' })
-  }
-
-  async verifyListingsVisible() {
-    const listingCount = await this.getListingCount()
-    if (listingCount === 0) {
-      // If no listings, check if there's an empty state message
-      try {
-        await this.noListingsMessage.waitFor({
-          state: 'visible',
-          timeout: 2000,
-        })
-      } catch {
-        // No explicit empty message, but that's ok if there are really no listings
-        // No explicit empty state message
-      }
-    } else {
-      // If listings exist, verify at least one is visible
-      await this.listingItems.first().waitFor({ state: 'visible' })
-    }
-  }
-
-  async verifyFiltersVisible() {
-    // Not all filters may be present, so check each individually
-    const filters = [this.deviceFilter, this.emulatorFilter, this.performanceFilter]
-    let visibleFilterCount = 0
-
-    for (const filter of filters) {
-      try {
-        await filter.waitFor({ state: 'visible', timeout: 2000 })
-        visibleFilterCount++
-      } catch {
-        // Filter not visible, continue
-      }
-    }
-
-    // At least one filter should be visible
-    if (visibleFilterCount === 0) {
-      throw new Error('No filters visible on listings page')
-    }
-  }
-
-  async isOnListingsPage(): Promise<boolean> {
-    try {
-      const url = this.page.url()
-      return (
-        url.includes('/listings') && !url.includes('/listings/new') && !url.includes('/listings/')
-      )
-    } catch {
-      return false
-    }
-  }
-
-  async verifyAddListingButtonVisible() {
-    try {
-      await this.addListingButton.waitFor({ state: 'visible' })
-    } catch {
-      // Try alternative button
-      await this.createListingButton.waitFor({ state: 'visible' })
-    }
+    await expect(this.pageHeading).toBeVisible()
   }
 }
diff --git a/tests/pagination.spec.ts b/tests/pagination.spec.ts
index 520700304..6bbf5072f 100644
--- a/tests/pagination.spec.ts
+++ b/tests/pagination.spec.ts
@@ -1,271 +1,102 @@
 import { test, expect } from '@playwright/test'
 import { GamesPage } from './pages/GamesPage'
 import { ListingsPage } from './pages/ListingsPage'
+import type { Page } from '@playwright/test'
+
+// The Pagination component renders its navigation buttons twice in the same
+// <nav> — once inside a `hidden md:flex` desktop wrapper and once inside a
+// `md:hidden` mobile wrapper — so every aria-label resolves to 2 elements.
+// Scoping to the desktop wrapper gives strict-mode locators a single match.
+function desktopPagination(page: Page) {
+  return page.locator('nav[aria-label="Pagination navigation"] > div.hidden.md\\:flex')
+}
 
 test.describe('Pagination Tests', () => {
-  test('should display pagination controls when content exceeds page limit', async ({ page }) => {
+  test('should display pagination controls with previous disabled on first page', async ({
+    page,
+  }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
-    await listingsPage.verifyPageLoaded()
-
-    // Check if pagination exists
-    const paginationControls = page.locator(
-      '[data-testid="pagination"], nav[aria-label*="pagination"], .pagination',
-    )
-    const hasPagination = await paginationControls.isVisible({ timeout: 3000 }).catch(() => false)
-
-    if (hasPagination) {
-      // Verify pagination elements
-      const nextButton = page
-        .getByRole('button', { name: /next/i })
-        .or(page.getByLabel(/next page/i))
-      const prevButton = page
-        .getByRole('button', { name: /previous/i })
-        .or(page.getByLabel(/previous page/i))
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-      // First page: previous should be disabled
-      const isPrevDisabled = await prevButton.isDisabled().catch(() => true)
-      expect(isPrevDisabled).toBe(true)
-
-      // Next button should be either enabled (more pages) or disabled (single page)
-      const isNextEnabled = await nextButton.isEnabled().catch(() => false)
-      expect(typeof isNextEnabled).toBe('boolean')
-    } else {
-      // If no pagination, there should be few items
-      const listingCount = await listingsPage.getListingCount()
-      expect(listingCount).toBeLessThanOrEqual(20)
-    }
+    const pagination = desktopPagination(page)
+    await expect(pagination).toBeVisible()
+    await expect(pagination.getByRole('button', { name: 'Go to previous page' })).toBeDisabled()
+    await expect(pagination.getByRole('button', { name: 'Go to next page' })).toBeEnabled()
+    await expect(pagination.locator('button[aria-current="page"]')).toHaveText('1')
   })
 
-  test('should navigate through pages using pagination controls', async ({ page }) => {
+  test('should navigate to next page and back with pagination buttons', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    // Look for pagination
-    const nextButton = page.getByRole('button', { name: /next/i }).or(page.getByLabel(/next page/i))
-    const hasNext = await nextButton.isVisible({ timeout: 3000 }).catch(() => false)
-
-    if (hasNext && !(await nextButton.isDisabled())) {
-      // Get first item on page 1
-      const firstItemPage1 = await listingsPage.listingItems.first().textContent()
-
-      // Click next
-      await nextButton.click()
-      await expect(listingsPage.listingItems.first()).toBeVisible()
-
-      // Get first item on page 2
-      const firstItemPage2 = await listingsPage.listingItems.first().textContent()
-
-      // Items should be different
-      expect(firstItemPage1).not.toBe(firstItemPage2)
-
-      // Previous button should now be enabled
-      const prevButton = page
-        .getByRole('button', { name: /previous/i })
-        .or(page.getByLabel(/previous page/i))
-      await expect(prevButton).toBeEnabled()
+    const pagination = desktopPagination(page)
+    const currentPage = pagination.locator('button[aria-current="page"]')
+    const nextButton = pagination.getByRole('button', { name: 'Go to next page' })
+    const prevButton = pagination.getByRole('button', { name: 'Go to previous page' })
 
-      // Go back to page 1
-      await prevButton.click()
-      await expect(listingsPage.listingItems.first()).toBeVisible()
+    await expect(currentPage).toHaveText('1')
 
-      // Should see same first item as before
-      const firstItemPage1Again = await listingsPage.listingItems.first().textContent()
-      expect(firstItemPage1Again).toBe(firstItemPage1)
-    } else {
-      // No pagination available; verify page still loaded with content
-      await expect(listingsPage.pageHeading).toBeVisible()
-    }
-  })
-
-  test('should update URL with page parameter', async ({ page }) => {
-    const gamesPage = new GamesPage(page)
-    await gamesPage.goto()
-
-    // Check for pagination
-    const pageNumbers = page
-      .locator('button[aria-label*="page"], a[aria-label*="page"]')
-      .filter({ hasText: /^[0-9]+$/ })
-    const hasPageNumbers = (await pageNumbers.count()) > 0
+    await nextButton.click()
+    await expect(currentPage).toHaveText('2')
+    await expect(page).toHaveURL(/[?&]page=2/)
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    if (hasPageNumbers) {
-      // Click page 2
-      const page2Button = pageNumbers.filter({ hasText: '2' }).first()
-      if (await page2Button.isVisible()) {
-        await page2Button.click()
-        await page.waitForURL(/[?&]page=2|[?&]p=2/)
-
-        // URL should include page parameter
-        const url = page.url()
-        expect(url).toMatch(/[?&]page=2|[?&]p=2/)
-      }
-    }
-
-    // Or try with next button
-    const nextButton = page.getByRole('button', { name: /next/i })
-    if ((await nextButton.isVisible({ timeout: 2000 })) && !(await nextButton.isDisabled())) {
-      const currentUrl = page.url()
-      await nextButton.click()
-      await page.waitForURL((url) => url.toString() !== currentUrl)
-
-      const newUrl = page.url()
-      // URL should have changed
-      expect(newUrl).not.toBe(currentUrl)
-    }
+    await expect(prevButton).toBeEnabled()
+    await prevButton.click()
+    await expect(currentPage).toHaveText('1')
+    await expect(page).not.toHaveURL(/[?&]page=2/)
+    await expect(listingsPage.listingItems.first()).toBeVisible()
   })
 
-  test('should persist pagination with filters', async ({ page }) => {
+  test('should show "Showing X to Y of Z results" label', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    // Apply a filter if available
-    const deviceFilter = listingsPage.deviceFilter
-    if (await deviceFilter.isVisible({ timeout: 3000 })) {
-      await deviceFilter.click()
-      const firstOption = page.getByRole('option').first()
-      if (await firstOption.isVisible({ timeout: 2000 })) {
-        const filterText = await firstOption.textContent()
-        await firstOption.click()
-        await expect(
-          listingsPage.listingItems.first().or(listingsPage.noListingsMessage),
-        ).toBeVisible()
-
-        // Check if pagination still works with filter
-        const nextButton = page.getByRole('button', { name: /next/i })
-        if ((await nextButton.isVisible()) && !(await nextButton.isDisabled())) {
-          await nextButton.click()
-          await expect(listingsPage.listingItems.first()).toBeVisible()
-
-          // Filter should still be applied
-          // Verify by checking if filter is still selected
-          const selectedFilter = await deviceFilter.textContent()
-          expect(selectedFilter).toContain(filterText || '')
-        }
-      }
-    }
+    await expect(
+      desktopPagination(page).getByText(/Showing \d+ to \d+ of \d+ results/),
+    ).toBeVisible()
   })
 
-  test('should show correct page indicators', async ({ page }) => {
+  test('should highlight current page number', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    // Look for page indicators
-    const pageIndicators = [
-      page.getByText(/page\s+\d+\s+of\s+\d+/i),
-      page.getByText(/showing\s+\d+[-–]\d+\s+of\s+\d+/i),
-      page.locator('[aria-current="page"]').first(),
-    ]
-
-    let foundIndicator = false
-    for (const indicator of pageIndicators) {
-      if (await indicator.isVisible({ timeout: 2000 })) {
-        foundIndicator = true
-        const text = await indicator.textContent()
-
-        // Should show page 1 initially
-        expect(text).toMatch(/1|first/i)
-        break
-      }
-    }
+    const pagination = desktopPagination(page)
+    const currentPage = pagination.locator('button[aria-current="page"]')
 
-    if (!foundIndicator) {
-      // No pagination needed; verify item count is within a single page
-      const itemCount = await listingsPage.getListingCount()
-      expect(itemCount).toBeLessThanOrEqual(20)
-    }
+    await expect(currentPage).toHaveText('1')
+    await pagination.getByRole('button', { name: 'Go to next page' }).click()
+    await expect(currentPage).toHaveText('2')
   })
 
-  test('should handle keyboard navigation for pagination', async ({ page }) => {
+  test('should navigate via numbered page button', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
+    await expect(gamesPage.gameItems.first()).toBeVisible()
 
-    // Focus on pagination area
-    const pagination = page
-      .locator('[data-testid="pagination"], nav[aria-label*="pagination"], .pagination')
-      .first()
-
-    if (await pagination.isVisible({ timeout: 3000 })) {
-      // Tab to pagination controls
-      await page.keyboard.press('Tab')
-      await page.keyboard.press('Tab')
-      await page.keyboard.press('Tab')
-
-      // Check if we can navigate with keyboard
-      const focusedElement = page.locator(':focus')
-      const focusedText = await focusedElement.textContent().catch(() => '')
+    const pagination = desktopPagination(page)
+    const page2Button = pagination.getByRole('button', { name: 'Go to page 2' })
+    await expect(page2Button).toBeVisible()
+    await page2Button.click()
 
-      // Try arrow keys or Enter on focused pagination element
-      if (focusedText) {
-        await page.keyboard.press('Enter')
-        await page.waitForLoadState('domcontentloaded')
-
-        // Page content should have updated after keyboard navigation
-        await expect(page.locator('body')).toBeVisible()
-      }
-    }
+    await expect(pagination.locator('button[aria-current="page"]')).toHaveText('2')
   })
 
-  test('should handle edge cases in pagination', async ({ page }) => {
+  test('should disable next button on the last page', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-    // Try to go to a very high page number directly
-    const url = new URL(page.url())
-    url.searchParams.set('page', '9999')
-    await page.goto(url.toString(), { waitUntil: 'domcontentloaded' })
-
-    // Should either:
-    // 1. Redirect to last valid page
-    // 2. Show no results
-    // 3. Show first page
-
-    // Wait for data to load
-    await page
-      .locator('tbody tr')
-      .first()
-      .or(page.getByText(/no results|no listings found|empty/i))
-      .waitFor({ state: 'visible', timeout: 15000 })
-      .catch(() => {})
-
-    const noResults = await page
-      .getByText(/no results|no listings found|empty/i)
-      .isVisible()
-      .catch(() => false)
-    const hasListings = (await page.locator('tbody tr').count()) > 0
-    const isFirstPage = page.url().includes('page=1') || !page.url().includes('page=')
-    const currentUrl = page.url()
-    const redirectedToValid = !currentUrl.includes('page=9999')
-
-    // One of these should be true: no results shown, has listings, redirected to first page, or redirected to valid page
-    const handledGracefully = noResults || hasListings || isFirstPage || redirectedToValid
-
-    expect(handledGracefully).toBe(true)
-  })
-
-  test('should update pagination when changing items per page', async ({ page }) => {
-    const gamesPage = new GamesPage(page)
-    await gamesPage.goto()
-
-    // Look for items per page selector
-    const perPageSelectors = [
-      page.getByRole('combobox', { name: /items per page/i }),
-      page.getByRole('combobox', { name: /show.*entries/i }),
-      page.locator('select').filter({ hasText: /10|20|50|100/ }),
-    ]
-
-    for (const selector of perPageSelectors) {
-      if (await selector.isVisible({ timeout: 2000 })) {
-        const currentValue = await selector.inputValue()
-
-        // Change the value
-        await selector.selectOption({ index: 1 }) // Select second option
-        await page.waitForLoadState('domcontentloaded')
+    const pagination = desktopPagination(page)
+    const lastPageButton = pagination.getByRole('button', { name: 'Go to last page' })
+    await expect(lastPageButton).toBeVisible()
+    await lastPageButton.click()
 
-        // Pagination should update
-        const newValue = await selector.inputValue()
-        expect(newValue).not.toBe(currentValue)
-        break
-      }
-    }
+    await expect(pagination.getByRole('button', { name: 'Go to next page' })).toBeDisabled()
+    await expect(pagination.getByRole('button', { name: 'Go to previous page' })).toBeEnabled()
   })
 })
diff --git a/tests/pc-listings.spec.ts b/tests/pc-listings.spec.ts
index 819b09603..743fec005 100644
--- a/tests/pc-listings.spec.ts
+++ b/tests/pc-listings.spec.ts
@@ -1,49 +1,29 @@
 import path from 'path'
 import { test, expect } from '@playwright/test'
 
-/**
- * E2E tests for the PC listings page.
- *
- * Route: /pc-listings
- * Heading: "PC Reports"
- * Table layout with PC-specific columns: CPU, GPU, Memory, OS.
- * Sorting via SortableHeader, pagination with aria-labels.
- */
-
 test.describe('PC Listings', () => {
   test.describe('PC Listings Page', () => {
     test('should display PC Reports heading and table structure', async ({ page }) => {
       await page.goto('/pc-listings')
       await page.waitForLoadState('domcontentloaded')
 
-      // Page heading
       await expect(page.getByRole('heading', { name: /pc reports/i }).first()).toBeVisible()
 
-      // Table should be present
       const table = page.locator('table')
       await expect(table).toBeVisible()
 
-      // PC-specific column headers: CPU, GPU, Memory
       await expect(page.locator('th').filter({ hasText: /^cpu$/i })).toBeVisible()
       await expect(page.locator('th').filter({ hasText: /^gpu$/i })).toBeVisible()
       await expect(page.locator('th').filter({ hasText: /memory/i })).toBeVisible()
 
-      // Shared columns: Game, Emulator
       await expect(page.locator('th').filter({ hasText: /game/i })).toBeVisible()
       await expect(page.locator('th').filter({ hasText: /emulator/i })).toBeVisible()
     })
 
-    test('should display table rows or empty state', async ({ page }) => {
+    test('should display table rows', async ({ page }) => {
       await page.goto('/pc-listings')
-      await page.waitForLoadState('domcontentloaded')
-
-      const rows = page.locator('tbody tr')
-      const emptyState = page.getByText(/no.*listings|no.*reports|no.*found/i)
 
-      const hasRows = (await rows.count()) > 0
-      const hasEmptyState = await emptyState.isVisible().catch(() => false)
-
-      expect(hasRows || hasEmptyState).toBe(true)
+      await expect(page.locator('tbody tr').first()).toBeVisible()
     })
 
     test('should sort by CPU column', async ({ page }) => {
@@ -53,11 +33,9 @@ test.describe('PC Listings', () => {
       const cpuHeader = page.locator('th').filter({ hasText: /^cpu$/i })
       await cpuHeader.click()
 
-      // URL should reflect sort params
       await expect(page).toHaveURL(/sortField=cpu/)
       await expect(page).toHaveURL(/sortDirection=asc/)
 
-      // Click again for descending
       await cpuHeader.click()
       await expect(page).toHaveURL(/sortDirection=desc/)
     })
@@ -90,21 +68,12 @@ test.describe('PC Listings', () => {
       await page.goto('/pc-listings')
       await page.waitForLoadState('domcontentloaded')
 
-      // Wait for table data to load
       const rows = page.locator('tbody tr')
-      await rows
-        .first()
-        .waitFor({ state: 'visible', timeout: 15000 })
-        .catch(() => {})
-
-      if ((await rows.count()) > 0) {
-        // Click the game name link inside the first row cell
-        const link = rows.first().locator('a[href*="/pc-listings/"]').first()
-        await link.click()
-        // Use toHaveURL which auto-retries — waitForURL hangs on client-side navigation
-        // because it waits for a 'load' event that never fires with pushState
-        await expect(page).toHaveURL(/\/pc-listings\/[a-z0-9]/)
-      }
+      await expect(rows.first()).toBeVisible()
+
+      const link = rows.first().locator('a[href*="/pc-listings/"]').first()
+      await link.click()
+      await expect(page).toHaveURL(/\/pc-listings\/[a-z0-9]/)
     })
 
     test('should show Add PC Report button', async ({ page }) => {
@@ -112,11 +81,8 @@ test.describe('PC Listings', () => {
       await page.waitForLoadState('domcontentloaded')
 
       const addButton = page.getByRole('link', { name: /add pc report/i })
-      const hasButton = await addButton.isVisible().catch(() => false)
-
-      if (hasButton) {
-        await expect(addButton).toHaveAttribute('href', '/pc-listings/new')
-      }
+      await expect(addButton).toBeVisible()
+      await expect(addButton).toHaveAttribute('href', '/pc-listings/new')
     })
   })
 
@@ -128,12 +94,10 @@ test.describe('PC Listings', () => {
       await page.waitForLoadState('domcontentloaded')
 
       const myReportsButton = page.getByRole('button', { name: /my reports/i })
-      const hasMyReports = await myReportsButton.isVisible().catch(() => false)
+      await expect(myReportsButton).toBeVisible()
 
-      if (hasMyReports) {
-        await myReportsButton.click()
-        await expect(page).toHaveURL(/myListings=true/)
-      }
+      await myReportsButton.click()
+      await expect(page).toHaveURL(/myListings=true/)
     })
 
     test('should show display toggle buttons', async ({ page }) => {
@@ -141,14 +105,12 @@ test.describe('PC Listings', () => {
       await page.waitForLoadState('domcontentloaded')
 
       const systemToggle = page.getByRole('button', { name: /system icons|system names/i }).first()
-      const hasToggle = await systemToggle.isVisible().catch(() => false)
-
-      if (hasToggle) {
-        await systemToggle.click()
-        await expect(
-          page.getByRole('button', { name: /system icons|system names/i }).first(),
-        ).toBeVisible()
-      }
+      await expect(systemToggle).toBeVisible()
+
+      await systemToggle.click()
+      await expect(
+        page.getByRole('button', { name: /system icons|system names/i }).first(),
+      ).toBeVisible()
     })
   })
 })
diff --git a/tests/pc-voting.spec.ts b/tests/pc-voting.spec.ts
new file mode 100644
index 000000000..10d355191
--- /dev/null
+++ b/tests/pc-voting.spec.ts
@@ -0,0 +1,131 @@
+import { type Page, test, expect } from '@playwright/test'
+
+// Mirrors voting.spec.ts for handheld listings — PcVoteButtons wraps the
+// shared VoteButtons component, so both should behave identically.
+
+async function navigateToFirstPcListing(page: Page) {
+  await page.goto('/pc-listings')
+  await page.waitForLoadState('domcontentloaded')
+
+  const rows = page.locator('tbody tr')
+  await expect(rows.first()).toBeVisible()
+
+  await rows.first().locator('a').first().click()
+  await page.waitForLoadState('domcontentloaded')
+}
+
+test.describe('PC Listing Voting Functionality Tests', () => {
+  test('should display vote section on PC listing detail page', async ({ page }) => {
+    await navigateToFirstPcListing(page)
+
+    const verificationHeading = page.getByRole('heading', {
+      name: /community verification/i,
+    })
+    await expect(verificationHeading).toBeVisible()
+  })
+
+  test('should display confirm and inaccurate vote buttons', async ({ page }) => {
+    await navigateToFirstPcListing(page)
+
+    const confirmButton = page.getByRole('button', { name: /confirm/i })
+    const inaccurateButton = page.getByRole('button', { name: /inaccurate/i })
+
+    await expect(confirmButton).toBeVisible()
+    await expect(inaccurateButton).toBeVisible()
+  })
+
+  test('should display success rate percentage and voter count', async ({ page }) => {
+    await navigateToFirstPcListing(page)
+
+    const successRate = page.getByText(/\d+%/)
+    await expect(successRate.first()).toBeVisible()
+
+    const verifiedByText = page.getByText(/verified by \d+ users/i)
+    await expect(verifiedByText).toBeVisible()
+  })
+
+  test('should show help button for voting explanation', async ({ page }) => {
+    await navigateToFirstPcListing(page)
+
+    const helpButton = page.getByTitle('How does verification work?')
+    await expect(helpButton).toBeVisible()
+  })
+
+  test('should show sign-in prompt for unauthenticated users', async ({ page }) => {
+    await navigateToFirstPcListing(page)
+
+    const signInToVerify = page
+      .getByText(/sign in/i)
+      .locator('..')
+      .filter({ hasText: /to verify/i })
+    await expect(signInToVerify).toBeVisible()
+  })
+
+  test('should have vote buttons disabled for unauthenticated users', async ({ page }) => {
+    await navigateToFirstPcListing(page)
+
+    const confirmButton = page.getByRole('button', { name: /confirm/i })
+    await expect(confirmButton).toBeVisible()
+    await expect(confirmButton).toBeDisabled()
+
+    const inaccurateButton = page.getByRole('button', { name: /inaccurate/i })
+    await expect(inaccurateButton).toBeDisabled()
+  })
+
+  test('should display progress bar for success rate', async ({ page }) => {
+    await navigateToFirstPcListing(page)
+
+    const verificationHeading = page.getByRole('heading', {
+      name: /community verification/i,
+    })
+    await expect(verificationHeading).toBeVisible()
+
+    await expect(page.getByText(/\d+%/)).toBeVisible()
+    await expect(page.getByText(/verified by \d+ users/i)).toBeVisible()
+  })
+})
+
+test.describe('PC Listing Vote — Toggle and Change Flows (Authenticated)', () => {
+  test.use({ storageState: 'tests/.auth/user.json' })
+
+  test('toggling the same vote twice returns the button to its unpressed state', async ({
+    page,
+  }) => {
+    await navigateToFirstPcListing(page)
+
+    const confirmButton = page.getByRole('button', { name: /confirm/i })
+    await expect(confirmButton).toBeVisible()
+
+    const initial = await confirmButton.getAttribute('aria-pressed')
+
+    // Click once → flip
+    await confirmButton.click()
+    await expect(confirmButton).toHaveAttribute(
+      'aria-pressed',
+      initial === 'true' ? 'false' : 'true',
+    )
+
+    // Click again → flip back
+    await confirmButton.click()
+    await expect(confirmButton).toHaveAttribute('aria-pressed', initial ?? 'false')
+  })
+
+  test('changing from upvote to downvote flips aria-pressed on both buttons', async ({ page }) => {
+    await navigateToFirstPcListing(page)
+
+    const confirmButton = page.getByRole('button', { name: /confirm/i })
+    const inaccurateButton = page.getByRole('button', { name: /inaccurate/i })
+
+    // Ensure upvote is active
+    const confirmPressed = await confirmButton.getAttribute('aria-pressed')
+    if (confirmPressed !== 'true') {
+      await confirmButton.click()
+      await expect(confirmButton).toHaveAttribute('aria-pressed', 'true')
+    }
+
+    // Change to downvote
+    await inaccurateButton.click()
+    await expect(inaccurateButton).toHaveAttribute('aria-pressed', 'true')
+    await expect(confirmButton).toHaveAttribute('aria-pressed', 'false')
+  })
+})
diff --git a/tests/performance.spec.ts b/tests/performance.spec.ts
index 5ca60f266..83c6a08ac 100644
--- a/tests/performance.spec.ts
+++ b/tests/performance.spec.ts
@@ -12,70 +12,50 @@ test.describe('Performance Tests', () => {
 
     const loadTime = Date.now() - startTime
 
-    // Page should load within 10 seconds (production build on test server)
     expect(loadTime).toBeLessThan(10000)
-
-    // Check for Largest Contentful Paint using existing entries
-    const lcp = await page
-      .evaluate(() => {
-        const entries = performance.getEntriesByType('largest-contentful-paint')
-        return entries.length > 0 ? entries[entries.length - 1].startTime : null
-      })
-      .catch(() => null)
-
-    if (lcp) {
-      expect(lcp).toBeLessThan(5000)
-    }
   })
 
   test('should have efficient image loading', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Check if images use lazy loading
     const images = page.locator('img')
     const imageCount = await images.count()
 
+    // Skip if not enough images to warrant lazy loading
+    test.skip(imageCount <= 5, 'Not enough images to test lazy loading')
+
     let lazyLoadCount = 0
     for (let i = 0; i < imageCount; i++) {
-      const img = images.nth(i)
-      const loading = await img.getAttribute('loading')
-
+      const loading = await images.nth(i).getAttribute('loading')
       if (loading === 'lazy') lazyLoadCount++
     }
 
-    // At least some images should use lazy loading when there are many images
-    if (imageCount > 5) {
-      expect(lazyLoadCount).toBeGreaterThan(0)
-    }
+    expect(lazyLoadCount).toBeGreaterThan(0)
   })
 
-  test('should not have memory leaks during navigation', async ({ page }) => {
-    // Get initial memory usage
+  test('should not have memory leaks during navigation', async ({ page, browserName }) => {
+    test.skip(browserName !== 'chromium', 'performance.memory API is only available in Chromium')
+
     const getMemoryUsage = () =>
       page.evaluate(() => {
-        // performance.memory is a non-standard Chrome API
         const perf = performance as Performance & {
-          memory?: {
-            usedJSHeapSize: number
-            totalJSHeapSize: number
-          }
+          memory?: { usedJSHeapSize: number; totalJSHeapSize: number }
         }
-        if (perf.memory) {
-          return {
-            usedJSHeapSize: perf.memory.usedJSHeapSize,
-            totalJSHeapSize: perf.memory.totalJSHeapSize,
-          }
-        }
-        return null
+        return perf.memory
+          ? {
+              usedJSHeapSize: perf.memory.usedJSHeapSize,
+              totalJSHeapSize: perf.memory.totalJSHeapSize,
+            }
+          : null
       })
 
     const homePage = new HomePage(page)
     await homePage.goto()
 
     const initialMemory = await getMemoryUsage()
+    test.skip(initialMemory === null, 'performance.memory API not available')
 
-    // Navigate through multiple pages
     for (let i = 0; i < 5; i++) {
       await homePage.navigateToGames()
       await homePage.navigateToHandheld()
@@ -83,128 +63,77 @@ test.describe('Performance Tests', () => {
       await homePage.navigateToHome()
     }
 
-    // Force garbage collection if available
     await page.evaluate(() => {
-      if (window.gc) {
-        window.gc()
-      }
+      if (window.gc) window.gc()
     })
 
     await page.waitForLoadState('domcontentloaded')
 
     const finalMemory = await getMemoryUsage()
+    expect(finalMemory).not.toBeNull()
 
-    if (initialMemory && finalMemory) {
-      const memoryIncrease = finalMemory.usedJSHeapSize - initialMemory.usedJSHeapSize
-      const percentIncrease = (memoryIncrease / initialMemory.usedJSHeapSize) * 100
+    const memoryIncrease = finalMemory!.usedJSHeapSize - initialMemory!.usedJSHeapSize
+    const percentIncrease = (memoryIncrease / initialMemory!.usedJSHeapSize) * 100
 
-      // Memory shouldn't increase by more than 50%
-      expect(percentIncrease).toBeLessThan(50)
-    } else {
-      // performance.memory not available (non-Chromium browser) -- skip
-      test.skip(true, 'performance.memory API not available in this browser')
-    }
+    expect(percentIncrease).toBeLessThan(50)
   })
 
   test('should handle large datasets efficiently', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
 
-    // Measure time to load listings page
     const startTime = Date.now()
     await listingsPage.goto()
     const loadTime = Date.now() - startTime
 
-    // Page should load in reasonable time
     expect(loadTime).toBeLessThan(15000)
 
-    // Check if content uses virtualization or pagination
-    const allListings = await listingsPage.listingItems.all()
-    const visibleCount = allListings.length
-
-    // Check for pagination
-    const hasPagination = await page
-      .locator('[data-testid="pagination"], .pagination')
-      .isVisible({ timeout: 1000 })
-      .catch(() => false)
-
-    // Large datasets should use pagination or virtualization
-    if (visibleCount > 50) {
-      // If rendering many items, pagination or virtualization should be in use
-      const initialCount = visibleCount
-      await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight))
-      await page.waitForLoadState('domcontentloaded')
-      const afterScrollCount = await listingsPage.listingItems.count()
-
-      // Either pagination exists, or item count changed (virtualization), or count is managed
-      expect(hasPagination || afterScrollCount !== initialCount || visibleCount <= 100).toBe(true)
-    }
+    await expect(listingsPage.pageHeading).toBeVisible()
   })
 
   test('should optimize bundle size and resources', async ({ page }) => {
-    const resourceSizes = {
-      scripts: 0,
-      styles: 0,
-      images: 0,
-      total: 0,
-    }
+    let scriptBytes = 0
 
-    // Track resource loading
     page.on('response', (response) => {
       const url = response.url()
       const size = parseInt(response.headers()['content-length'] || '0')
 
       if (url.match(/\.(js|mjs)$/)) {
-        resourceSizes.scripts += size
-      } else if (url.match(/\.css$/)) {
-        resourceSizes.styles += size
-      } else if (url.match(/\.(jpg|jpeg|png|gif|webp|svg)$/)) {
-        resourceSizes.images += size
+        scriptBytes += size
       }
-
-      resourceSizes.total += size
     })
 
     const homePage = new HomePage(page)
     await homePage.goto()
-
-    // Wait for page to fully load
     await page.waitForLoadState('domcontentloaded')
 
-    // JavaScript bundle should be reasonable
-    expect(resourceSizes.scripts).toBeLessThan(3 * 1024 * 1024) // 3MB
+    expect(scriptBytes).toBeLessThan(3 * 1024 * 1024)
   })
 
   test('should have smooth scrolling performance', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
-
-    // Wait for content
     await gamesPage.verifyPageLoaded()
 
-    // Measure scroll performance
-    const scrollPerformance = await page.evaluate(() => {
-      return new Promise((resolve) => {
-        let frameCount = 0
+    const frameCount = await page.evaluate(() => {
+      return new Promise<number>((resolve) => {
+        let count = 0
         const startTime = performance.now()
 
         const measureFrames = () => {
-          frameCount++
-
+          count++
           if (performance.now() - startTime < 1000) {
             requestAnimationFrame(measureFrames)
           } else {
-            resolve(frameCount)
+            resolve(count)
           }
         }
 
-        // Start scrolling
         window.scrollTo({ top: 1000, behavior: 'smooth' })
         measureFrames()
       })
     })
 
-    // Should maintain at least 30 FPS
-    expect(scrollPerformance).toBeGreaterThan(30)
+    expect(frameCount).toBeGreaterThan(30)
   })
 
   test('should cache API responses appropriately', async ({ page }) => {
@@ -215,7 +144,6 @@ test.describe('Performance Tests', () => {
     }
     const apiCalls: ApiCall[] = []
 
-    // Monitor API calls
     page.on('response', (response) => {
       if (response.url().includes('/api/')) {
         apiCalls.push({
@@ -229,54 +157,17 @@ test.describe('Performance Tests', () => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Navigate away and back
     await gamesPage.navigateToHome()
     await gamesPage.goto()
 
-    // Verify API calls were tracked
     expect(apiCalls.length).toBeGreaterThan(0)
   })
-
-  test('should handle concurrent operations efficiently', async ({ page }) => {
-    const listingsPage = new ListingsPage(page)
-    await listingsPage.goto()
-
-    // Perform multiple operations concurrently
-    const operations = []
-
-    // Search
-    if (await listingsPage.searchInput.isVisible()) {
-      operations.push(listingsPage.searchListings('test'))
-    }
-
-    // Apply filters
-    if (await listingsPage.deviceFilter.isVisible()) {
-      operations.push(listingsPage.deviceFilter.click())
-    }
-
-    if (await listingsPage.emulatorFilter.isVisible()) {
-      operations.push(listingsPage.emulatorFilter.click())
-    }
-
-    // Execute all at once
-    const startTime = Date.now()
-    await Promise.all(operations)
-    const executionTime = Date.now() - startTime
-
-    // Should handle concurrent operations without significant delay
-    expect(executionTime).toBeLessThan(2000)
-
-    // Page should remain responsive
-    await expect(listingsPage.pageHeading).toBeVisible()
-  })
 })
 
 test.describe('Mobile Performance Tests', () => {
   test('should have optimized mobile performance', async ({ page }) => {
-    // Set mobile viewport
     await page.setViewportSize({ width: 375, height: 667 })
 
-    // Enable CPU throttling to simulate mobile device
     const client = await page.context().newCDPSession(page)
     await client.send('Emulation.setCPUThrottlingRate', { rate: 4 })
 
@@ -285,21 +176,17 @@ test.describe('Mobile Performance Tests', () => {
     await homePage.goto()
     const loadTime = Date.now() - startTime
 
-    // Should still load within reasonable time on mobile (4x CPU throttling adds overhead)
     expect(loadTime).toBeLessThan(15000)
 
-    // Check for mobile optimizations
     const viewportMeta = await page.locator('meta[name="viewport"]').getAttribute('content')
     expect(viewportMeta).toContain('width=device-width')
   })
 
   test('should minimize layout shifts', async ({ page }) => {
-    // Monitor layout shifts
     await page.addInitScript(() => {
       let cls = 0
       new PerformanceObserver((list) => {
         for (const entry of list.getEntries()) {
-          // Layout shift entries have these properties in Chrome
           const layoutShiftEntry = entry as PerformanceEntry & {
             hadRecentInput?: boolean
             value?: number
@@ -316,11 +203,10 @@ test.describe('Mobile Performance Tests', () => {
     await gamesPage.goto()
     await page.waitForLoadState('domcontentloaded')
 
-    // Get CLS value
-    const cumulativeLayoutShift = await page.evaluate(() => (window as any).getCLS?.() || 0)
+    const cumulativeLayoutShift = await page.evaluate(
+      () => (window as Window & { getCLS?: () => number }).getCLS?.() ?? 0,
+    )
 
-    // CLS should be less than 0.25 for acceptable user experience
-    // Google's threshold: Good < 0.1, Needs Improvement 0.1-0.25, Poor > 0.25
     expect(cumulativeLayoutShift).toBeLessThan(0.25)
   })
 })
diff --git a/tests/router-coverage.spec.ts b/tests/router-coverage.spec.ts
index d456049e9..0d7f32f4f 100644
--- a/tests/router-coverage.spec.ts
+++ b/tests/router-coverage.spec.ts
@@ -1,9 +1,5 @@
 import { test, expect } from '@playwright/test'
 
-/**
- * API security and validation tests that verify endpoint protection.
- */
-
 test.describe('Router Security Verification', () => {
   test('protected endpoints should reject unauthenticated requests', async ({ request }) => {
     const protectedEndpoints = [
@@ -16,12 +12,8 @@ test.describe('Router Security Verification', () => {
     for (const endpoint of protectedEndpoints) {
       const response = await request.post(endpoint, {
         data: {},
-        headers: {
-          'Content-Type': 'application/json',
-        },
+        headers: { 'Content-Type': 'application/json' },
       })
-
-      // tRPC returns 401 for UNAUTHORIZED errors
       expect([401, 403]).toContain(response.status())
     }
   })
@@ -36,12 +28,8 @@ test.describe('Router Security Verification', () => {
     for (const endpoint of adminOnlyEndpoints) {
       const response = await request.post(endpoint, {
         data: {},
-        headers: {
-          'Content-Type': 'application/json',
-        },
+        headers: { 'Content-Type': 'application/json' },
       })
-
-      // Should reject unauthenticated requests to admin endpoints
       expect([401, 403]).toContain(response.status())
     }
   })
@@ -55,24 +43,19 @@ test.describe('Data Validation Verification', () => {
         gameId: 'invalid-id',
         deviceId: -1,
       },
-      headers: {
-        'Content-Type': 'application/json',
-      },
+      headers: { 'Content-Type': 'application/json' },
     })
 
-    // Should return 400 (bad input) or 401/403 (unauthenticated)
+    // 400 = bad input; 401/403 = unauthenticated (both are acceptable outcomes)
     expect([400, 401, 403]).toContain(response.status())
   })
 
   test('should handle malformed tRPC requests', async ({ request }) => {
     const response = await request.post('/api/trpc/nonExistentRouter.nonExistentProcedure', {
       data: {},
-      headers: {
-        'Content-Type': 'application/json',
-      },
+      headers: { 'Content-Type': 'application/json' },
     })
 
-    // Should return an error status, not 200
     expect(response.status()).toBeGreaterThanOrEqual(400)
   })
 })
diff --git a/tests/search.spec.ts b/tests/search.spec.ts
index 445119784..a45e9c160 100644
--- a/tests/search.spec.ts
+++ b/tests/search.spec.ts
@@ -1,165 +1,77 @@
 import { test, expect } from '@playwright/test'
 import { GamesPage } from './pages/GamesPage'
-import { HomePage } from './pages/HomePage'
 import { ListingsPage } from './pages/ListingsPage'
 
 test.describe('Search Functionality Tests', () => {
   test('should search for games by title', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
-
-    // Wait for games to load
     await gamesPage.verifyPageLoaded()
 
-    // Search for a specific game
-    const searchQuery = 'Mario'
-    await gamesPage.searchGames(searchQuery)
+    await gamesPage.searchGames('Mario')
 
-    // Verify search results
-    const gameCards = await gamesPage.gameCards.all()
-
-    if (gameCards.length > 0) {
-      // Check that results contain the search term
-      const firstGameTitle = await gameCards[0].textContent()
-      expect(firstGameTitle?.toLowerCase()).toContain(searchQuery.toLowerCase())
-    } else {
-      // If no results, verify empty state is shown or page is empty
-      const noResults = await page
-        .getByText(/no games found|no results|nothing found/i)
-        .isVisible({ timeout: 2000 })
-        .catch(() => false)
-      const hasEmptyState = await page
-        .locator('[data-testid="empty-state"], .empty-state')
-        .isVisible({ timeout: 2000 })
-        .catch(() => false)
-
-      // Either show empty state message or have no game cards
-      const isEmptyHandled = noResults || hasEmptyState || gameCards.length === 0
-      expect(isEmptyHandled).toBe(true)
-    }
+    await expect(gamesPage.gameItems.first().or(gamesPage.noGamesMessage)).toBeVisible()
   })
 
   test('should search for listings with filters', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
-
-    // Wait for listings to load
     await listingsPage.verifyPageLoaded()
 
-    // Search for listings
-    const searchQuery = 'Pokemon'
-    await listingsPage.searchListings(searchQuery)
-
-    // Verify search updates the results
-    const initialCount = await listingsPage.getListingCount()
+    await listingsPage.searchListings('Pokemon')
 
-    // Clear search and verify count changes
-    await listingsPage.searchInput.clear()
-    await page.keyboard.press('Enter')
     await expect(listingsPage.listingItems.first().or(listingsPage.noListingsMessage)).toBeVisible()
-
-    const clearedCount = await listingsPage.getListingCount()
-
-    // Results should be different after clearing search
-    // (unless there are no listings at all or search term matched everything)
-    if (clearedCount > 0 && initialCount !== clearedCount) {
-      expect(clearedCount).not.toBe(initialCount)
-    }
   })
 
   test('should handle empty search results gracefully', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Search for something unlikely to exist
-    const searchQuery = 'xyznonexistentgame123'
-    await gamesPage.searchGames(searchQuery)
-
-    // Should show no results message, 0 games, or fuzzy-matched results
-    const noResultsVisible = await page
-      .getByText(/no games found|no results|nothing found|empty/i)
-      .isVisible({ timeout: 5000 })
-      .catch(() => false)
+    await gamesPage.searchGames('xyznonexistentgame123')
 
-    const gameCount = await gamesPage.getGameCount()
-
-    // Fuzzy search may return results for gibberish queries; verify page handles search gracefully
-    // by confirming the page is in a valid state (no results message, zero games, or fuzzy matches)
-    const pageHandledGracefully = noResultsVisible || gameCount >= 0
-    expect(pageHandledGracefully).toBe(true)
+    await expect(gamesPage.gameItems.first().or(gamesPage.noGamesMessage)).toBeVisible()
   })
 
   test('should maintain search state during navigation', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Perform a search
     const searchQuery = 'Zelda'
     await gamesPage.searchGames(searchQuery)
 
-    // Navigate to a game detail if available
-    const gameCount = await gamesPage.getGameCount()
-    if (gameCount > 0) {
-      await gamesPage.clickFirstGame()
-
-      await page.goBack()
-      await page.waitForLoadState('domcontentloaded')
+    await expect(gamesPage.gameItems.first()).toBeVisible()
+    await gamesPage.clickFirstGame()
 
-      // Wait for games page to fully load after back navigation
-      await gamesPage.pageHeading.waitFor({ state: 'visible', timeout: 10000 })
+    await page.goBack()
+    await page.waitForLoadState('domcontentloaded')
 
-      // Search state should be preserved in URL query params or restored in input
-      const searchValue = await gamesPage.searchInput.inputValue().catch(() => '')
-      const currentUrl = page.url()
-      const urlHasSearch =
-        currentUrl.includes(encodeURIComponent(searchQuery)) || currentUrl.includes(searchQuery)
+    await expect(gamesPage.pageHeading).toBeVisible()
 
-      expect(searchValue === searchQuery || urlHasSearch).toBe(true)
-    }
+    await expect(page).toHaveURL(new RegExp(searchQuery))
   })
 
   test('should support search with special characters', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Test various special characters
-    const specialSearches = ['Mario & Luigi', 'Pokémon', 'Street Fighter II']
+    const specialSearches = ['Mario & Luigi', 'Pok\u00e9mon', 'Street Fighter II']
 
     for (const searchQuery of specialSearches) {
       await gamesPage.searchGames(searchQuery)
-
       await expect(gamesPage.searchInput).toBeVisible()
-
-      // Clear for next search
       await gamesPage.searchInput.clear()
     }
 
-    // Page should still be functional
     await expect(gamesPage.searchInput).toBeVisible()
   })
 
-  test('should provide search suggestions or autocomplete if available', async ({ page }) => {
+  test('should search input accept partial queries', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Start typing a search
     await gamesPage.searchInput.fill('Mar')
 
-    // Check if autocomplete dropdown appears
-    const hasAutocomplete = await page
-      .locator('[role="listbox"], [data-testid="search-suggestions"], .search-suggestions')
-      .isVisible({ timeout: 2000 })
-      .catch(() => false)
-
-    if (hasAutocomplete) {
-      // Try selecting a suggestion with keyboard
-      await page.keyboard.press('ArrowDown')
-      await page.keyboard.press('Enter')
-
-      await page.waitForLoadState('domcontentloaded')
-    }
-
-    // Search should still work regardless
+    await expect(gamesPage.searchInput).toHaveValue('Mar')
     await expect(gamesPage.searchInput).toBeVisible()
   })
 })
@@ -169,130 +81,56 @@ test.describe('Search Performance Tests', () => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Rapidly change search input
     const searches = ['M', 'Ma', 'Mar', 'Mari', 'Mario']
 
     for (const partial of searches) {
       await gamesPage.searchInput.fill(partial)
-      // Don't wait between inputs
     }
 
-    // Final search should work
     await page.keyboard.press('Enter')
     await expect(gamesPage.pageHeading).toBeVisible()
-
-    // Page should remain responsive
     await expect(gamesPage.searchInput).toBeVisible()
-    await expect(gamesPage.pageHeading).toBeVisible()
   })
 
   test('should cancel previous search requests', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    // Start multiple searches quickly
     await listingsPage.searchListings('Pokemon')
     await listingsPage.searchListings('Mario')
     await listingsPage.searchListings('Zelda')
 
-    // Only last search should be active
-    const searchValue = await listingsPage.searchInput.inputValue()
-    expect(searchValue).toBe('Zelda')
+    await expect(listingsPage.searchInput).toHaveValue('Zelda')
 
-    // Results should be stable after final search
-    await expect(listingsPage.listingItems.first().or(listingsPage.noListingsMessage)).toBeVisible()
-    const count1 = await listingsPage.getListingCount()
     await expect(listingsPage.listingItems.first().or(listingsPage.noListingsMessage)).toBeVisible()
-    const count2 = await listingsPage.getListingCount()
-
-    // Count shouldn't change if search is stable
-    expect(count1).toBe(count2)
   })
 })
 
 test.describe('Cross-Page Search Tests', () => {
   test('should search across different content types', async ({ page }) => {
-    const homePage = new HomePage(page)
     const gamesPage = new GamesPage(page)
     const listingsPage = new ListingsPage(page)
 
-    await homePage.goto()
-
-    // Search on games page
     await gamesPage.goto()
     await gamesPage.searchGames('Mario')
-    // Wait for search results to load
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
-    const gameResults = await gamesPage.getGameCount()
-
-    // Search on listings page
+    await expect(gamesPage.gameItems.first().or(gamesPage.noGamesMessage)).toBeVisible()
+
     await listingsPage.goto()
     await listingsPage.searchListings('Mario')
-    // Wait for search results to load
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 15000 })
-      .catch(() => {})
-    // Also wait for table rows or no-results message
-    await listingsPage.listingItems
-      .first()
-      .or(listingsPage.noListingsMessage)
-      .waitFor({ state: 'visible', timeout: 10000 })
-      .catch(() => {})
-    const listingResults = await listingsPage.getListingCount()
-
-    // At least one content type should have results for a common search term
-    // Skip if test environment has no seed data matching "Mario"
-    test.skip(
-      gameResults + listingResults === 0,
-      'No results for "Mario" in test environment - no matching seed data',
-    )
-    expect(gameResults + listingResults).toBeGreaterThan(0)
+    await expect(listingsPage.listingItems.first().or(listingsPage.noListingsMessage)).toBeVisible()
   })
 
   test('should handle search with no results across pages', async ({ page }) => {
     const searchQuery = 'nonexistentgame12345xyz'
 
-    // Test on games page
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
     await gamesPage.searchGames(searchQuery)
+    await expect(gamesPage.gameItems.first().or(gamesPage.noGamesMessage)).toBeVisible()
 
-    // Wait for search results to load
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 10000 })
-      .catch(() => {})
-
-    const gamesNoResults = await page
-      .getByText(/no games found|no results/i)
-      .isVisible()
-      .catch(() => false)
-    const gamesZeroCount = (await gamesPage.getGameCount()) === 0
-
-    // Test on listings page
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
     await listingsPage.searchListings(searchQuery)
-
-    // Wait for search results to load
-    await page
-      .locator('tbody tr')
-      .first()
-      .or(page.getByText(/no listings found|no results/i))
-      .waitFor({ state: 'visible', timeout: 10000 })
-      .catch(() => {})
-
-    const listingsNoResults = await page
-      .getByText(/no listings found|no results/i)
-      .isVisible()
-      .catch(() => false)
-    const listingsZeroCount = (await listingsPage.getListingCount()) === 0
-
-    // At least one page should show no results (either via message or zero items)
-    expect(gamesNoResults || gamesZeroCount || listingsNoResults || listingsZeroCount).toBe(true)
+    await expect(listingsPage.listingItems.first().or(listingsPage.noListingsMessage)).toBeVisible()
   })
 })
diff --git a/tests/trust-integration.spec.ts b/tests/trust-integration.spec.ts
new file mode 100644
index 000000000..ffd6f8809
--- /dev/null
+++ b/tests/trust-integration.spec.ts
@@ -0,0 +1,215 @@
+import { test, expect } from '@playwright/test'
+import {
+  approveFirstPendingListing,
+  createPcListing,
+  rejectFirstPendingListing,
+  resetUserTrustScore,
+  withContext,
+} from './helpers/data-factory'
+import type { Page } from '@playwright/test'
+
+async function verifyTrustLogContains(page: Page, actionText: string) {
+  await page.goto('/admin/trust-logs', { waitUntil: 'domcontentloaded' })
+  await expect(page.getByText(/loading/i)).toBeHidden()
+
+  const table = page.locator('table')
+  await expect(table).toBeVisible()
+
+  const rows = page.locator('table tbody tr').filter({ hasText: new RegExp(actionText, 'i') })
+  expect(await rows.count()).toBeGreaterThan(0)
+}
+
+test.describe('Trust Effects E2E — Self-Contained', () => {
+  test.setTimeout(60000)
+
+  test('listing creation records LISTING_CREATED trust action', async ({ browser }) => {
+    await withContext(browser, 'tests/.auth/user.json', async (page) => {
+      await createPcListing(page)
+    })
+
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await verifyTrustLogContains(page, 'created a listing')
+    })
+  })
+
+  test('approving a listing records LISTING_APPROVED trust action', async ({ browser }) => {
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await approveFirstPendingListing(page, '/admin/approvals')
+    })
+
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await verifyTrustLogContains(page, 'approved')
+    })
+  })
+
+  test('admin trust adjustment resets score, user listing stays pending, admin approval triggers LISTING_APPROVED', async ({
+    browser,
+  }) => {
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await resetUserTrustScore(page, 'user@emuready')
+    })
+
+    await withContext(browser, 'tests/.auth/user.json', async (page) => {
+      await createPcListing(page)
+    })
+
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await approveFirstPendingListing(page, '/admin/approvals')
+    })
+
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await verifyTrustLogContains(page, 'approved')
+    })
+  })
+
+  test('rejecting a listing records LISTING_REJECTED trust action', async ({ browser }) => {
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await rejectFirstPendingListing(page, '/admin/approvals')
+    })
+
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await verifyTrustLogContains(page, 'rejected')
+    })
+  })
+
+  test('voting on a PC listing records UPVOTE trust action', async ({ browser }) => {
+    await withContext(browser, 'tests/.auth/user.json', async (page) => {
+      await page.goto('/pc-listings')
+
+      const rows = page.locator('tbody tr')
+      await expect(rows.first()).toBeVisible()
+
+      await rows.first().locator('a').first().click()
+      await page.waitForLoadState('domcontentloaded')
+
+      const confirmButton = page.getByRole('button', { name: /confirm/i })
+      await expect(confirmButton).toBeVisible()
+
+      const wasPressed = await confirmButton.getAttribute('aria-pressed')
+      await confirmButton.click()
+
+      await expect(confirmButton).toHaveAttribute(
+        'aria-pressed',
+        wasPressed === 'true' ? 'false' : 'true',
+      )
+    })
+
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await verifyTrustLogContains(page, 'upvoted a listing')
+    })
+  })
+
+  test('voting on a handheld listing records UPVOTE trust action', async ({ browser }) => {
+    await withContext(browser, 'tests/.auth/user.json', async (page) => {
+      await page.goto('/listings')
+
+      const rows = page.locator('table tbody tr')
+      await expect(rows.first()).toBeVisible()
+
+      const link = rows.first().locator('a[href*="/listings/"]').first()
+      await link.click()
+      await page.waitForLoadState('domcontentloaded')
+
+      const confirmButton = page.getByRole('button', { name: /confirm/i })
+      await expect(confirmButton).toBeVisible()
+
+      const wasPressed = await confirmButton.getAttribute('aria-pressed')
+      await confirmButton.click()
+
+      await expect(confirmButton).toHaveAttribute(
+        'aria-pressed',
+        wasPressed === 'true' ? 'false' : 'true',
+      )
+    })
+
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await verifyTrustLogContains(page, 'upvoted a listing')
+    })
+  })
+
+  test('changing a vote (upvote → downvote) reverses previous then applies new trust', async ({
+    browser,
+  }) => {
+    // User upvotes a listing, then changes to downvote
+    await withContext(browser, 'tests/.auth/user.json', async (page) => {
+      await page.goto('/listings')
+      const rows = page.locator('table tbody tr')
+      await expect(rows.first()).toBeVisible()
+
+      const link = rows.first().locator('a[href*="/listings/"]').first()
+      await link.click()
+      await page.waitForLoadState('domcontentloaded')
+
+      const confirmButton = page.getByRole('button', { name: /confirm/i })
+      const inaccurateButton = page.getByRole('button', { name: /inaccurate/i })
+      await expect(confirmButton).toBeVisible()
+      await expect(inaccurateButton).toBeVisible()
+
+      // Ensure upvote is active
+      const wasConfirmed = await confirmButton.getAttribute('aria-pressed')
+      if (wasConfirmed !== 'true') {
+        await confirmButton.click()
+        await expect(confirmButton).toHaveAttribute('aria-pressed', 'true')
+      }
+
+      // Change to downvote — click the "Inaccurate" button
+      await inaccurateButton.click()
+      await expect(inaccurateButton).toHaveAttribute('aria-pressed', 'true')
+      await expect(confirmButton).toHaveAttribute('aria-pressed', 'false')
+    })
+
+    // Admin verifies trust log has the reversal description
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await verifyTrustLogContains(page, 'Trust reversal due to vote change or removal')
+    })
+  })
+
+  test('toggling off an upvote reverses the trust award', async ({ browser }) => {
+    await withContext(browser, 'tests/.auth/user.json', async (page) => {
+      await page.goto('/listings')
+      const rows = page.locator('table tbody tr')
+      await expect(rows.first()).toBeVisible()
+
+      const link = rows.first().locator('a[href*="/listings/"]').first()
+      await link.click()
+      await page.waitForLoadState('domcontentloaded')
+
+      const confirmButton = page.getByRole('button', { name: /confirm/i })
+      await expect(confirmButton).toBeVisible()
+
+      // Ensure upvote is active
+      const wasPressed = await confirmButton.getAttribute('aria-pressed')
+      if (wasPressed !== 'true') {
+        await confirmButton.click()
+        await expect(confirmButton).toHaveAttribute('aria-pressed', 'true')
+      }
+
+      // Toggle off
+      await confirmButton.click()
+      await expect(confirmButton).toHaveAttribute('aria-pressed', 'false')
+    })
+
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await verifyTrustLogContains(page, 'Trust reversal due to vote change or removal')
+    })
+  })
+
+  test('trust logs table shows action data with weights', async ({ browser }) => {
+    await withContext(browser, 'tests/.auth/super_admin.json', async (page) => {
+      await page.goto('/admin/trust-logs', { waitUntil: 'domcontentloaded' })
+      await expect(page.getByText(/loading/i)).toBeHidden()
+
+      const table = page.locator('table')
+      await expect(table).toBeVisible()
+
+      const headerText = await page.locator('thead').textContent()
+      expect(headerText?.toLowerCase()).toContain('action')
+      expect(headerText?.toLowerCase()).toContain('weight')
+
+      const rows = page.locator('table tbody tr')
+      expect(await rows.count()).toBeGreaterThan(0)
+
+      await expect(rows.first()).toContainText(/[+-]?\d+/)
+    })
+  })
+})
diff --git a/tests/trust-system.spec.ts b/tests/trust-system.spec.ts
index 78878fc4d..410c4f57d 100644
--- a/tests/trust-system.spec.ts
+++ b/tests/trust-system.spec.ts
@@ -1,40 +1,21 @@
 import { test, expect } from '@playwright/test'
 
-/**
- * E2E tests for the trust system.
- *
- * Trust display: TrustLevelBadge on /profile (own) and /users/[id] (public).
- * Levels: Contributor, Trusted, Verified, Elite, Core.
- * Admin trust logs: /admin/trust-logs (heading: "Trust System Logs").
- * No dedicated /profile/trust page exists.
- */
-
 test.describe('Trust System', () => {
   test.describe('Trust Score Display', () => {
     test.use({ storageState: 'tests/.auth/user.json' })
 
     test('should display trust level on user profile', async ({ page }) => {
-      await page.goto('/profile')
-      await page.waitForLoadState('domcontentloaded')
+      await page.goto('/profile', { waitUntil: 'domcontentloaded' })
 
-      // Trust Level card is shown in the profile header stats grid
       const trustLabel = page.getByText(/trust level/i)
       await expect(trustLabel).toBeVisible()
     })
 
-    test('should display trust score in profile stats', async ({ page }) => {
-      await page.goto('/profile')
-      await page.waitForLoadState('domcontentloaded')
-
-      // Profile stats may show "Trust Score" or "Trust Level" depending on UI version
-      const trustScoreLabel = page.getByText(/trust score/i)
-      const hasTrustScore = await trustScoreLabel.isVisible({ timeout: 3000 }).catch(() => false)
-      test.skip(
-        !hasTrustScore,
-        'Trust Score label not present on profile page (Trust Level is shown instead)',
-      )
+    test('should display trust info in profile stats', async ({ page }) => {
+      await page.goto('/profile', { waitUntil: 'domcontentloaded' })
 
-      await expect(trustScoreLabel).toBeVisible()
+      await expect(page.getByText(/trust level/i)).toBeVisible()
+      await expect(page.getByText(/newcomer/i)).toBeVisible()
     })
   })
 
@@ -44,35 +25,15 @@ test.describe('Trust System', () => {
     test('should access trust system logs page', async ({ page }) => {
       await page.goto('/admin/trust-logs', { waitUntil: 'domcontentloaded' })
 
-      // Wait for page to fully hydrate
-      await page
-        .getByText(/loading/i)
-        .waitFor({ state: 'hidden', timeout: 15000 })
-        .catch(() => {})
-
-      // Page heading
-      await expect(page.getByRole('heading', { name: /trust system logs/i })).toBeVisible({
-        timeout: 10000,
-      })
-
-      // Search and filter controls
-      await expect(page.getByPlaceholder(/search users/i)).toBeVisible({ timeout: 10000 })
+      await expect(page.getByRole('heading', { name: /trust system logs/i })).toBeVisible()
+      await expect(page.getByPlaceholder(/search users/i)).toBeVisible()
     })
 
     test('should display trust logs table', async ({ page }) => {
       await page.goto('/admin/trust-logs', { waitUntil: 'domcontentloaded' })
 
-      // Wait for loading to finish
-      await page
-        .getByText(/loading/i)
-        .waitFor({ state: 'hidden', timeout: 15000 })
-        .catch(() => {})
-
       const table = page.locator('table')
-      await table.waitFor({ state: 'visible', timeout: 10000 }).catch(() => {})
-
-      const hasTable = await table.isVisible().catch(() => false)
-      test.skip(!hasTable, 'Trust logs table not visible — may be empty or still loading')
+      await expect(table).toBeVisible()
 
       const headerText = await page.locator('thead').textContent()
       expect(headerText?.toLowerCase()).toMatch(/user/)
@@ -82,32 +43,18 @@ test.describe('Trust System', () => {
     test('should show search and filter controls', async ({ page }) => {
       await page.goto('/admin/trust-logs', { waitUntil: 'domcontentloaded' })
 
-      // Wait for page to fully hydrate
-      await page
-        .getByText(/loading/i)
-        .waitFor({ state: 'hidden', timeout: 15000 })
-        .catch(() => {})
-
-      // Search input for users
       const searchInput = page.locator('input[type="text"]').first()
-      await expect(searchInput).toBeVisible({ timeout: 10000 })
+      await expect(searchInput).toBeVisible()
 
-      // Action filter dropdown
       const actionFilter = page.locator('select').first()
-      await expect(actionFilter).toBeVisible({ timeout: 10000 })
+      await expect(actionFilter).toBeVisible()
     })
 
     test('should show Run Monthly Bonus button', async ({ page }) => {
       await page.goto('/admin/trust-logs', { waitUntil: 'domcontentloaded' })
 
-      // Wait for page to fully hydrate before checking for button
-      await page
-        .getByText(/loading/i)
-        .waitFor({ state: 'hidden', timeout: 15000 })
-        .catch(() => {})
-
       const monthlyBonusButton = page.getByRole('button', { name: /run monthly bonus/i })
-      await expect(monthlyBonusButton).toBeVisible({ timeout: 10000 })
+      await expect(monthlyBonusButton).toBeVisible()
     })
   })
 })
diff --git a/tests/user-flows.spec.ts b/tests/user-flows.spec.ts
index ea07d6fd6..c28b2301d 100644
--- a/tests/user-flows.spec.ts
+++ b/tests/user-flows.spec.ts
@@ -4,384 +4,120 @@ import { HomePage } from './pages/HomePage'
 import { ListingsPage } from './pages/ListingsPage'
 
 test.describe('User Flow Tests', () => {
-  test('should complete flow: browse games → view game → check listings', async ({ page }) => {
-    // Start at home
+  test('should complete flow: browse games → search → view game detail', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Navigate to games
     await homePage.navigateToGames()
     const gamesPage = new GamesPage(page)
     await gamesPage.verifyPageLoaded()
 
-    // Search for a specific game
     await gamesPage.searchGames('Mario')
 
-    // Wait for search results to load
-    await page
-      .getByText(/loading/i)
-      .waitFor({ state: 'hidden', timeout: 10000 })
-      .catch(() => {})
-
-    // Wait for Mario search results specifically (not just any game items)
     const marioGame = gamesPage.gameItems.filter({ hasText: /mario/i }).first()
-    const hasMarioResults = await marioGame
-      .waitFor({ state: 'visible', timeout: 10000 })
-      .then(() => true)
-      .catch(() => false)
-
-    if (hasMarioResults) {
-      // Click the first Mario game directly
-      const firstGameTitle = await marioGame.textContent()
-      const href = await marioGame.getAttribute('href')
-      await marioGame.click()
-
-      // Verify navigation to game detail
-      try {
-        await expect(page).toHaveURL(/\/games\/[^\/]+/, { timeout: 5000 })
-      } catch {
-        // Fallback to direct navigation if click didn't work
-        if (href) await page.goto(href)
-        await expect(page).toHaveURL(/\/games\/[^\/]+/)
-      }
-
-      // Look for game title on detail page
-      const detailTitle = await page.locator('h1').first().textContent()
-      expect(detailTitle?.toLowerCase()).toContain('mario')
-
-      // Verify we're viewing the same game we clicked on
-      if (firstGameTitle) {
-        expect(detailTitle?.toLowerCase()).toContain(firstGameTitle.toLowerCase().split(' ')[0])
-      }
-
-      // Check for listings section
-      const listingsSection = page
-        .locator('[data-testid="game-listings"], section')
-        .filter({ hasText: /listing|compatibility/i })
-
-      if (await listingsSection.isVisible({ timeout: 3000 })) {
-        await expect(listingsSection).toBeVisible()
-
-        // Click on a listing if available
-        const listingLinks = page.locator('a[href*="/listings/"]')
-        if ((await listingLinks.count()) > 0) {
-          await listingLinks.first().click()
-          await expect(page).toHaveURL(/\/listings\/[^\/]+/)
-        }
-      }
-    }
-  })
+    await expect(marioGame).toBeVisible()
 
-  test('should complete flow: search listings → filter → view details', async ({ page }) => {
-    const listingsPage = new ListingsPage(page)
-    await listingsPage.goto()
+    await marioGame.click()
+    await expect(page).toHaveURL(/\/games\/[^/]+/)
 
-    // Search for listings
-    await listingsPage.searchListings('Pokemon')
-
-    // Apply a filter if available
-    if (await listingsPage.performanceFilter.isVisible({ timeout: 2000 })) {
-      await listingsPage.filterByPerformance('Perfect')
-    }
-
-    // Check results
-    const listingCount = await listingsPage.getListingCount()
-
-    if (listingCount > 0) {
-      // Remember first listing details
-      const firstListing = listingsPage.listingItems.first()
-      const listingText = await firstListing.textContent()
-
-      // Click to view details
-      await listingsPage.clickFirstListing()
-
-      // Should be on listing detail page
-      await expect(page).toHaveURL(/\/listings\/[^\/]+/)
-
-      // Should show listing information
-      const hasGameTitle = await page.getByRole('heading').first().isVisible()
-      expect(hasGameTitle).toBe(true)
-
-      // Verify the listing details page contains info from the listing we clicked
-      if (listingText) {
-        const pageContent = await page.textContent('body')
-        // Check if the page contains part of the listing text (game name, device, etc)
-        const listingWords = listingText.split(' ').filter((word) => word.length > 3)
-        const hasListingContent = listingWords.some((word) =>
-          pageContent?.toLowerCase().includes(word.toLowerCase()),
-        )
-        expect(hasListingContent).toBe(true)
-      }
-
-      // Navigate back
-      await page.goBack()
-
-      // Filters should be preserved
-      if (await listingsPage.performanceFilter.isVisible()) {
-        const filterValue = await listingsPage.performanceFilter.textContent()
-        expect(filterValue).toContain('Perfect')
-      }
-    }
+    const detailHeading = page.locator('h1').first()
+    await expect(detailHeading).toContainText(/mario/i)
   })
 
-  test('should handle multi-step filtering workflow', async ({ page }) => {
+  test('should complete flow: browse listings → view listing detail', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
+    await listingsPage.verifyPageLoaded()
 
-    const appliedFilters = []
-
-    // Step 1: Filter by device
-    if (await listingsPage.deviceFilter.isVisible({ timeout: 2000 })) {
-      await listingsPage.deviceFilter.click()
-      const deviceOption = page.getByRole('option').nth(1)
-      const deviceName = await deviceOption.textContent()
-      await deviceOption.click()
-      appliedFilters.push(`Device: ${deviceName}`)
-      await page.waitForLoadState('domcontentloaded')
-    }
-
-    // Step 2: Add emulator filter
-    if (await listingsPage.emulatorFilter.isVisible({ timeout: 2000 })) {
-      await listingsPage.emulatorFilter.click()
-      const emulatorOption = page.getByRole('option').nth(1)
-      const emulatorName = await emulatorOption.textContent()
-      await emulatorOption.click()
-      appliedFilters.push(`Emulator: ${emulatorName}`)
-      await page.waitForLoadState('domcontentloaded')
-    }
-
-    // Step 3: Add performance filter
-    if (await listingsPage.performanceFilter.isVisible({ timeout: 2000 })) {
-      await listingsPage.filterByPerformance('Perfect')
-      appliedFilters.push('Performance: Perfect')
-    }
-
-    await expect(listingsPage.pageHeading).toBeVisible()
-
-    // Check if we can clear filters
-    if (await listingsPage.clearFiltersButton.isVisible({ timeout: 2000 })) {
-      const filteredCount = await listingsPage.getListingCount()
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
-      await listingsPage.clearAllFilters()
+    await listingsPage.clickFirstListing()
+    await expect(page).toHaveURL(/\/listings\/[^/]+/)
 
-      const clearedCount = await listingsPage.getListingCount()
-      expect(clearedCount).toBeGreaterThanOrEqual(filteredCount)
-    }
+    const heading = page.getByRole('heading').first()
+    await expect(heading).toBeVisible()
   })
 
-  test('should handle user journey with back navigation', async ({ page }) => {
+  test('should navigate back through browser history correctly', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
+    const homeUrl = page.url()
 
-    // Build navigation history
-    const navigationPath: string[] = []
-
-    // Home → Games
-    navigationPath.push(page.url())
     await homePage.navigateToGames()
-    navigationPath.push(page.url())
+    const gamesUrl = page.url()
 
-    // Games → First Game Detail
     const gamesPage = new GamesPage(page)
-    if ((await gamesPage.getGameCount()) > 0) {
-      await gamesPage.clickFirstGame()
-      navigationPath.push(page.url())
-    }
+    await gamesPage.verifyPageLoaded()
+    await gamesPage.clickFirstGame()
+    await expect(page).toHaveURL(/\/games\/[^/]+/)
 
-    // Navigate back one step
     await page.goBack()
-    await expect(page).toHaveURL(navigationPath[navigationPath.length - 2])
-
-    // Only navigate further back if we have 3+ history entries
-    if (navigationPath.length >= 3) {
-      await page.goBack()
-      await expect(page).toHaveURL(navigationPath[0])
-
-      await page.goForward()
-      await expect(page).toHaveURL(navigationPath[1])
-    }
-  })
-
-  test('should maintain state during complex interactions', async ({ page }) => {
-    const gamesPage = new GamesPage(page)
-    await gamesPage.goto()
-
-    // Set up initial state
-    const searchTerm = 'Zelda'
-    await gamesPage.searchGames(searchTerm)
-    await page.waitForLoadState('domcontentloaded')
-
-    // Open game in new tab if possible
-    if ((await gamesPage.getGameCount()) > 0) {
-      const firstGame = gamesPage.gameItems.first()
-
-      // Middle-click or Ctrl+click to open in new tab
-      const newPagePromise = page.context().waitForEvent('page', { timeout: 5000 })
-      await firstGame.click({ modifiers: ['Control'] })
-
-      try {
-        const newPage = await newPagePromise
-        await newPage.waitForLoadState()
-
-        // Original page should maintain search
-        const searchValue = await gamesPage.searchInput.inputValue()
-        expect(searchValue).toBe(searchTerm)
-
-        await newPage.close()
-      } catch {
-        // New tab handling not supported in this environment; verify original page retained search
-        const searchValue = await gamesPage.searchInput.inputValue()
-        expect(searchValue).toBe(searchTerm)
-      }
-    }
-  })
-
-  test('should handle rapid user interactions', async ({ page }) => {
-    const listingsPage = new ListingsPage(page)
-    await listingsPage.goto()
-
-    // Simulate impatient user rapidly clicking
-    const actions = []
-
-    // Rapid filter changes
-    if (await listingsPage.deviceFilter.isVisible()) {
-      actions.push(listingsPage.deviceFilter.click())
-    }
-
-    // Don't wait between actions
-    if (await listingsPage.emulatorFilter.isVisible()) {
-      actions.push(listingsPage.emulatorFilter.click())
-    }
+    await expect(page).toHaveURL(gamesUrl)
 
-    if (await listingsPage.performanceFilter.isVisible()) {
-      actions.push(listingsPage.performanceFilter.click())
-    }
-
-    // Execute all rapidly
-    await Promise.all(actions)
-
-    await page.waitForLoadState('domcontentloaded')
-
-    // Page should still be functional
-    await expect(listingsPage.pageHeading).toBeVisible()
+    await page.goBack()
+    await expect(page).toHaveURL(homeUrl)
 
-    // Should be able to clear state
-    if (await listingsPage.clearFiltersButton.isVisible()) {
-      await listingsPage.clearAllFilters()
-      await expect(listingsPage.pageHeading).toBeVisible()
-    }
+    await page.goForward()
+    await expect(page).toHaveURL(gamesUrl)
   })
 
-  test('should complete mobile user journey', async ({ page }) => {
-    // Set mobile viewport
+  test('should complete mobile user journey: home → games → search → detail', async ({ page }) => {
     await page.setViewportSize({ width: 375, height: 667 })
 
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Games page is not in the mobile navbar, so navigate directly
     await homePage.navigateToGames()
-    await expect(page).toHaveURL('/games')
+    await expect(page).toHaveURL(/\/games/)
 
     const gamesPage = new GamesPage(page)
     await gamesPage.verifyPageLoaded()
 
-    // Search on mobile
     await gamesPage.searchGames('Mario')
-    await page.waitForLoadState('domcontentloaded')
 
-    // View game detail on mobile
-    if ((await gamesPage.getGameCount()) > 0) {
-      await gamesPage.clickFirstGame()
-      await expect(page).toHaveURL(/\/games\/[^\/]+/)
+    const marioGame = gamesPage.gameItems.filter({ hasText: /mario/i }).first()
+    await expect(marioGame).toBeVisible()
 
-      // Scroll on mobile to see content
-      await page.evaluate(() => window.scrollBy(0, 300))
+    await gamesPage.clickFirstGame()
+    await expect(page).toHaveURL(/\/games\/[^/]+/)
 
-      // Navigate back — URL retains ?search= param from the search above
-      await page.goBack()
-      await expect(page).toHaveURL(/\/games/)
-    }
+    await page.goBack()
+    await expect(page).toHaveURL(/\/games/)
   })
 
-  test('should handle interrupted flows gracefully', async ({ page }) => {
+  test('should recover from interrupted search by starting a new one', async ({ page }) => {
     const gamesPage = new GamesPage(page)
     await gamesPage.goto()
 
-    // Start searching
     await gamesPage.searchInput.fill('Final Fant')
 
-    // Interrupt by navigating away using direct goto to avoid racing
-    // with the search input's debounced URL change
     await page.goto('/')
-
-    // Go back to games
     await page.goBack()
 
-    // Search should be cleared or restored
-    const searchValue = await gamesPage.searchInput.inputValue()
-
-    // Either cleared or restored is acceptable
-    expect(searchValue === '' || searchValue === 'Final Fant').toBe(true)
-
-    // Should be able to continue searching
     await gamesPage.searchInput.clear()
     await gamesPage.searchGames('Pokemon')
 
     await expect(gamesPage.searchInput).toHaveValue('Pokemon')
+    await expect(page).toHaveURL(/[?&]search=Pokemon/)
   })
 })
 
 test.describe('Cross-Page User Flows', () => {
-  test('should handle flow between PC and handheld listings', async ({ page }) => {
+  test('should navigate between handheld and PC listings', async ({ page }) => {
     const homePage = new HomePage(page)
     await homePage.goto()
 
-    // Go to handheld listings
     await homePage.navigateToHandheld()
     await expect(page).toHaveURL('/listings')
 
     const listingsPage = new ListingsPage(page)
     await expect(listingsPage.pageHeading).toBeVisible()
 
-    // Switch to PC listings
     await homePage.navigateToPC()
     await expect(page).toHaveURL('/pc-listings')
 
-    // Verify different content
-    const pcContent = await page.content()
-    expect(pcContent).toContain('pc')
-
-    // Navigate back to handheld
     await homePage.navigateToHandheld()
     await expect(page).toHaveURL('/listings')
-  })
-
-  test('should remember user preferences across pages', async ({ page }) => {
-    const gamesPage = new GamesPage(page)
-    await gamesPage.goto()
-
-    // Look for any view preferences (grid/list toggle, sort options)
-    const viewToggles = page.locator('button').filter({ hasText: /view|display|layout|grid|list/i })
-
-    if ((await viewToggles.count()) > 0) {
-      const toggle = viewToggles.first()
-      const initialState = await toggle.textContent()
-
-      // Change view
-      await toggle.click()
-      await expect(toggle).toBeVisible()
-
-      const newState = await toggle.textContent()
-      expect(newState).not.toBe(initialState)
-
-      // Navigate away and back
-      await gamesPage.navigateToHome()
-      await gamesPage.goto()
-
-      // Preference should be either remembered or reset to default
-      const currentState = await toggle.textContent()
-      expect(currentState === newState || currentState === initialState).toBe(true)
-    }
+    await expect(listingsPage.pageHeading).toBeVisible()
   })
 })
diff --git a/tests/user-moderation.spec.ts b/tests/user-moderation.spec.ts
index e03b204da..8cc9ff8a8 100644
--- a/tests/user-moderation.spec.ts
+++ b/tests/user-moderation.spec.ts
@@ -1,14 +1,5 @@
 import { test, expect } from '@playwright/test'
 
-/**
- * E2E tests for the user ban management admin page.
- *
- * Route: /admin/user-bans
- * Heading: "User Ban Management"
- * Table layout with status filter dropdown (not tabs).
- * Ban creation via modal with user search, reason, duration.
- */
-
 test.describe('User Ban & Moderation System', () => {
   test.describe('Admin Ban Management Page', () => {
     test.use({ storageState: 'tests/.auth/super_admin.json' })
@@ -17,20 +8,10 @@ test.describe('User Ban & Moderation System', () => {
       await page.goto('/admin/user-bans')
       await page.waitForLoadState('domcontentloaded')
 
-      // Wait for data to load (page shows LoadingSpinner while query is pending)
-      await page
-        .getByText(/loading/i)
-        .waitFor({ state: 'hidden', timeout: 15000 })
-        .catch(() => {})
-
-      // Page heading
-      await expect(page.getByRole('heading', { name: /user ban management/i })).toBeVisible({
-        timeout: 10000,
-      })
+      await expect(page.getByRole('heading', { name: /user ban management/i })).toBeVisible()
 
-      // Stats display: Total Bans, Active, Expired, Permanent
       const statsText = page.locator('text=/total bans|active|expired|permanent/i')
-      await expect(statsText.first()).toBeVisible({ timeout: 10000 })
+      await expect(statsText.first()).toBeVisible()
     })
 
     test('should display ban statistics', async ({ page }) => {
@@ -46,15 +27,12 @@ test.describe('User Ban & Moderation System', () => {
       await page.waitForLoadState('domcontentloaded')
 
       const table = page.locator('table')
-      const hasTable = await table.isVisible().catch(() => false)
-
-      if (hasTable) {
-        // Expected columns: User, Reason, Status, Banned By, Banned At, Expires At, Actions
-        const headerText = await page.locator('thead').textContent()
-        expect(headerText?.toLowerCase()).toMatch(/user/)
-        expect(headerText?.toLowerCase()).toMatch(/reason/)
-        expect(headerText?.toLowerCase()).toMatch(/status/)
-      }
+      await expect(table).toBeVisible()
+
+      const headerText = await page.locator('thead').textContent()
+      expect(headerText?.toLowerCase()).toMatch(/user/)
+      expect(headerText?.toLowerCase()).toMatch(/reason/)
+      expect(headerText?.toLowerCase()).toMatch(/status/)
     })
 
     test('should search bans by user name', async ({ page }) => {
@@ -75,12 +53,10 @@ test.describe('User Ban & Moderation System', () => {
       const statusFilter = page
         .locator('select')
         .filter({ hasText: /all statuses|active|inactive/i })
-      const hasFilter = await statusFilter.isVisible().catch(() => false)
+      await expect(statusFilter).toBeVisible()
 
-      if (hasFilter) {
-        await statusFilter.selectOption('true')
-        await expect(page.locator('table')).toBeVisible()
-      }
+      await statusFilter.selectOption('true')
+      await expect(page.locator('table')).toBeVisible()
     })
 
     test('should open create ban modal', async ({ page }) => {
@@ -88,24 +64,15 @@ test.describe('User Ban & Moderation System', () => {
       await page.waitForLoadState('domcontentloaded')
 
       const newBanButton = page.getByRole('button', { name: /new ban/i })
-      const hasButton = await newBanButton.isVisible().catch(() => false)
-
-      if (hasButton) {
-        await newBanButton.click()
-
-        // Modal should open
-        await expect(page.getByText(/create user ban/i)).toBeVisible()
-
-        // User search input
-        await expect(page.getByPlaceholder(/search users/i)).toBeVisible()
+      await expect(newBanButton).toBeVisible()
 
-        // Reason textarea
-        await expect(page.getByPlaceholder(/provide a clear reason/i)).toBeVisible()
+      await newBanButton.click()
 
-        // Ban duration options
-        await expect(page.getByText(/permanent ban/i)).toBeVisible()
-        await expect(page.getByText(/temporary ban/i)).toBeVisible()
-      }
+      await expect(page.getByText(/create user ban/i)).toBeVisible()
+      await expect(page.getByPlaceholder(/search users/i)).toBeVisible()
+      await expect(page.getByPlaceholder(/provide a clear reason/i)).toBeVisible()
+      await expect(page.getByText(/permanent ban/i)).toBeVisible()
+      await expect(page.getByText(/temporary ban/i)).toBeVisible()
     })
 
     test('should view ban details', async ({ page }) => {
@@ -113,29 +80,13 @@ test.describe('User Ban & Moderation System', () => {
       await page.waitForLoadState('domcontentloaded')
 
       const rows = page.locator('tbody tr')
-      const hasRows = (await rows.count()) > 0
-
-      if (hasRows) {
-        const viewButton = rows.first().getByRole('button', { name: /view/i })
-        const hasView = await viewButton.isVisible().catch(() => false)
-
-        if (hasView) {
-          await viewButton.click()
-          await expect(page.getByText(/ban details/i).first()).toBeVisible()
-        }
-      }
-    })
-
-    test('should show pagination for bans list', async ({ page }) => {
-      await page.goto('/admin/user-bans')
-      await page.waitForLoadState('domcontentloaded')
+      await expect(rows.first()).toBeVisible()
 
-      const pagination = page.getByRole('navigation', { name: /pagination/i })
-      const hasPagination = await pagination.isVisible().catch(() => false)
+      const viewButton = rows.first().getByRole('button', { name: /view/i })
+      await expect(viewButton).toBeVisible()
 
-      if (hasPagination) {
-        await expect(page.getByRole('button', { name: /go to next page/i })).toBeVisible()
-      }
+      await viewButton.click()
+      await expect(page.getByText(/ban details/i).first()).toBeVisible()
     })
   })
 })
diff --git a/tests/validate-tests.spec.ts b/tests/validate-tests.spec.ts
deleted file mode 100644
index cc6aa4b74..000000000
--- a/tests/validate-tests.spec.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { test, expect } from '@playwright/test'
-
-test.describe('Test Suite Validation', () => {
-  test('should validate all page objects are accessible', async ({ page }) => {
-    // This test validates that our test infrastructure is working
-
-    // Import all page objects
-    const { HomePage } = await import('./pages/HomePage')
-    const { GamesPage } = await import('./pages/GamesPage')
-    const { ListingsPage } = await import('./pages/ListingsPage')
-    const { AuthPage } = await import('./pages/AuthPage')
-
-    // Create instances
-    const homePage = new HomePage(page)
-    const gamesPage = new GamesPage(page)
-    const listingsPage = new ListingsPage(page)
-    const authPage = new AuthPage(page)
-
-    // Verify page objects have required methods
-    expect(typeof homePage.goto).toBe('function')
-    expect(typeof gamesPage.searchGames).toBe('function')
-    expect(typeof listingsPage.searchListings).toBe('function')
-    expect(typeof authPage.isAuthenticated).toBe('function')
-
-    expect(typeof authPage.isAuthenticated).toBe('function')
-  })
-
-  test('should validate test file structure', async () => {
-    // List of test files that should exist
-    const testFiles = [
-      'auth.spec.ts',
-      'browsing.spec.ts',
-      'forms.spec.ts',
-      'navigation.spec.ts',
-      'search.spec.ts',
-      'pagination.spec.ts',
-      'filtering.spec.ts',
-      'error-handling.spec.ts',
-      'accessibility.spec.ts',
-      'user-flows.spec.ts',
-      'performance.spec.ts',
-    ]
-
-    // This test just validates the structure
-    expect(testFiles.length).toBe(11)
-    expect(testFiles.every((f) => f.endsWith('.spec.ts'))).toBe(true)
-  })
-})
diff --git a/tests/voting.spec.ts b/tests/voting.spec.ts
index 205eeebd0..6e15ef394 100644
--- a/tests/voting.spec.ts
+++ b/tests/voting.spec.ts
@@ -6,34 +6,30 @@ test.describe('Voting Functionality Tests', () => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test voting')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // The VoteButtons component renders a "Community Verification" heading
     const verificationHeading = page.getByRole('heading', {
       name: /community verification/i,
     })
-    await expect(verificationHeading).toBeVisible({ timeout: 10000 })
+    await expect(verificationHeading).toBeVisible()
   })
 
   test('should display confirm and inaccurate vote buttons', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test voting')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // Vote buttons use "Confirm" and "Inaccurate" labels
     const confirmButton = page.getByRole('button', { name: /confirm/i })
     const inaccurateButton = page.getByRole('button', { name: /inaccurate/i })
 
-    await expect(confirmButton).toBeVisible({ timeout: 10000 })
+    await expect(confirmButton).toBeVisible()
     await expect(inaccurateButton).toBeVisible()
   })
 
@@ -41,17 +37,14 @@ test.describe('Voting Functionality Tests', () => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test voting')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // Success rate is displayed as "{number}%" in a large font
     const successRate = page.getByText(/\d+%/)
-    await expect(successRate.first()).toBeVisible({ timeout: 10000 })
+    await expect(successRate.first()).toBeVisible()
 
-    // Voter count text: "verified by {N} users"
     const verifiedByText = page.getByText(/verified by \d+ users/i)
     await expect(verifiedByText).toBeVisible()
   })
@@ -60,48 +53,42 @@ test.describe('Voting Functionality Tests', () => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test voting')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // Help button has title "How does verification work?"
     const helpButton = page.getByTitle('How does verification work?')
-    await expect(helpButton).toBeVisible({ timeout: 10000 })
+    await expect(helpButton).toBeVisible()
   })
 
   test('should show sign-in prompt for unauthenticated users', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test voting')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // Unauthenticated users see "Sign in" link with "to verify" text
     const signInToVerify = page
       .getByText(/sign in/i)
       .locator('..')
       .filter({ hasText: /to verify/i })
-    await expect(signInToVerify).toBeVisible({ timeout: 10000 })
+    await expect(signInToVerify).toBeVisible()
   })
 
   test('should have vote buttons disabled for unauthenticated users', async ({ page }) => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test voting')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
 
-    // Vote buttons are disabled when not authenticated
     const confirmButton = page.getByRole('button', { name: /confirm/i })
-    await expect(confirmButton).toBeVisible({ timeout: 10000 })
+    await expect(confirmButton).toBeVisible()
     await expect(confirmButton).toBeDisabled()
 
     const inaccurateButton = page.getByRole('button', { name: /inaccurate/i })
@@ -112,8 +99,7 @@ test.describe('Voting Functionality Tests', () => {
     const listingsPage = new ListingsPage(page)
     await listingsPage.goto()
 
-    const listingCount = await listingsPage.getListingCount()
-    test.skip(listingCount === 0, 'No listings available to test voting')
+    await expect(listingsPage.listingItems.first()).toBeVisible()
 
     await listingsPage.clickFirstListing()
     await page.waitForLoadState('domcontentloaded')
@@ -121,9 +107,8 @@ test.describe('Voting Functionality Tests', () => {
     const verificationHeading = page.getByRole('heading', {
       name: /community verification/i,
     })
-    await expect(verificationHeading).toBeVisible({ timeout: 10000 })
+    await expect(verificationHeading).toBeVisible()
 
-    // Success rate percentage and "verified by N users" text appear alongside the progress bar
     await expect(page.getByText(/\d+%/)).toBeVisible()
     await expect(page.getByText(/verified by \d+ users/i)).toBeVisible()
   })
