Simeon Garratt commited on
Commit
dc507cf
Β·
1 Parent(s): b5ee4ac

fix: batch of 5 stability fixes

Browse files

- Swap spark/argue from Anthropic to DeepSeek (no ANTHROPIC_API_KEY)
- Guard leaderboard against mini-game modes
- Expand fallback question pool 20β†’54 for Streak mode offline
- Add custom 404 and error pages
- Add security headers + turbopack root to next.config.ts

Co-Authored-By: Claude <noreply@anthropic.com>

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

_._ _..._ .-', _.._(`))
'-. ` ' /-._.-' ',/
) \ '.
/ _ _ | \
| a a / |
\ .-. ;
'-('' ).-' ,' ;
'-; | .'
\ \ /
| 7 .__ _.-\ \
| | | `` | |
/,_| | /,_/ /
/,_/ '----'

Piggy Benissy

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Simeon Garratt | www.simeongarratt.com | @SimeonGarratt
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

next.config.ts CHANGED
@@ -1,7 +1,48 @@
1
  import type { NextConfig } from "next";
2
 
3
  const nextConfig: NextConfig = {
 
4
  output: "standalone",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  };
6
 
7
  export default nextConfig;
 
1
  import type { NextConfig } from "next";
2
 
3
  const nextConfig: NextConfig = {
4
+ // .next/standalone output for Docker deployment on Hugging Face
5
  output: "standalone",
6
+
7
+ // Silence the multi-lockfile warning when next is run with npm workspaces
8
+ turbopack: {
9
+ root: __dirname,
10
+ },
11
+
12
+ async headers() {
13
+ return [
14
+ {
15
+ source: "/(.*)",
16
+ headers: [
17
+ {
18
+ key: "X-Frame-Options",
19
+ value: "DENY",
20
+ },
21
+ {
22
+ key: "X-Content-Type-Options",
23
+ value: "nosniff",
24
+ },
25
+ {
26
+ key: "Referrer-Policy",
27
+ value: "strict-origin-when-cross-origin",
28
+ },
29
+ {
30
+ key: "X-XSS-Protection",
31
+ value: "1; mode=block",
32
+ },
33
+ ],
34
+ },
35
+ ];
36
+ },
37
+
38
+ images: {
39
+ remotePatterns: [
40
+ {
41
+ protocol: "https",
42
+ hostname: "thetriviaverse.com",
43
+ },
44
+ ],
45
+ },
46
  };
47
 
48
  export default nextConfig;
src/app/api/spark/argue/route.ts CHANGED
@@ -1,8 +1,11 @@
1
  import { NextRequest, NextResponse } from "next/server";
2
- import Anthropic from "@anthropic-ai/sdk";
3
 
4
  function getClient() {
5
- return new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
 
 
 
6
  }
7
 
8
  export async function POST(req: NextRequest) {
@@ -41,15 +44,17 @@ export async function POST(req: NextRequest) {
41
  ? "You are OPENING this round. Present a strong, specific argument."
42
  : `Your opponent just argued:\n"${opponentArgument}"\n\nDirectly counter their specific claims while advancing your own case.`;
43
 
44
- const message = await getClient().messages.create({
45
- model: "claude-sonnet-4-6-20250514",
46
  max_tokens: 300,
47
  messages: [
 
 
 
 
48
  {
49
  role: "user",
50
- content: `You are a competitive debate opponent. You hold the ${aiSide} position on this topic.
51
-
52
- RULES:
53
  - 120-150 words maximum.
54
  - Directly counter the opponent's specific claims. If they cite a law, case, or fact, engage with it.
55
  - Take a clear position. Do not hedge, agree with both sides, or use filler phrases like "It's important to note" or "While I understand."
@@ -70,19 +75,15 @@ Respond with ONLY your argument. No labels, prefixes, or meta-commentary.`,
70
  });
71
 
72
  const argument =
73
- message.content[0]?.type === "text"
74
- ? message.content[0].text.trim()
75
- : "I stand by my position on this matter.";
76
 
77
  return NextResponse.json({ argument });
78
  } catch (error) {
79
  console.error("[spark/argue] Error:", error);
80
  return NextResponse.json(
81
- {
82
- argument:
83
- "While there are many angles to consider, the evidence broadly supports my position on this question.",
84
- },
85
- { status: 200 },
86
  );
87
  }
88
  }
 
1
  import { NextRequest, NextResponse } from "next/server";
2
+ import OpenAI from "openai";
3
 
4
  function getClient() {
5
+ return new OpenAI({
6
+ apiKey: process.env.DEEPSEEK_API_KEY,
7
+ baseURL: "https://api.deepseek.com/v1",
8
+ });
9
  }
10
 
11
  export async function POST(req: NextRequest) {
 
44
  ? "You are OPENING this round. Present a strong, specific argument."
45
  : `Your opponent just argued:\n"${opponentArgument}"\n\nDirectly counter their specific claims while advancing your own case.`;
46
 
47
+ const response = await getClient().chat.completions.create({
48
+ model: "deepseek-chat",
49
  max_tokens: 300,
50
  messages: [
51
+ {
52
+ role: "system",
53
+ content: `You are a competitive debate opponent. You hold the ${aiSide} position on this topic.`,
54
+ },
55
  {
56
  role: "user",
57
+ content: `RULES:
 
 
58
  - 120-150 words maximum.
59
  - Directly counter the opponent's specific claims. If they cite a law, case, or fact, engage with it.
60
  - Take a clear position. Do not hedge, agree with both sides, or use filler phrases like "It's important to note" or "While I understand."
 
75
  });
76
 
77
  const argument =
78
+ response.choices[0]?.message?.content?.trim() ??
79
+ "I stand by my position on this matter.";
 
80
 
81
  return NextResponse.json({ argument });
82
  } catch (error) {
83
  console.error("[spark/argue] Error:", error);
84
  return NextResponse.json(
85
+ { error: "Failed to generate argument" },
86
+ { status: 500 },
 
 
 
87
  );
88
  }
89
  }
src/app/error.tsx ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { useEffect } from "react";
4
+ import Link from "next/link";
5
+
6
+ export default function Error({
7
+ error,
8
+ reset,
9
+ }: {
10
+ error: Error & { digest?: string };
11
+ reset: () => void;
12
+ }) {
13
+ useEffect(() => {
14
+ console.error("[error.tsx]", error);
15
+ }, [error]);
16
+
17
+ return (
18
+ <div className="flex min-h-screen flex-col items-center justify-center gap-6 px-4 text-center">
19
+ <div className="select-none text-8xl font-black tracking-tighter text-white/10 sm:text-9xl">
20
+ 500
21
+ </div>
22
+ <h1 className="text-3xl font-black text-white sm:text-4xl">System Malfunction</h1>
23
+ <p className="max-w-md text-gray-400">
24
+ Something went wrong in the Triviaverse engine. The hyperdrive might need a reboot.
25
+ </p>
26
+ <div className="flex flex-wrap justify-center gap-3">
27
+ <button
28
+ onClick={reset}
29
+ className="inline-flex items-center gap-2 rounded-xl bg-magenta px-6 py-3 text-sm font-bold text-white transition-colors hover:bg-[#d81b60]"
30
+ >
31
+ Try Again
32
+ </button>
33
+ <Link
34
+ href="/"
35
+ className="inline-flex items-center gap-2 rounded-xl border border-white/20 px-6 py-3 text-sm font-bold text-white transition-colors hover:bg-white/10"
36
+ >
37
+ Back to Triviaverse
38
+ </Link>
39
+ </div>
40
+ {error.digest && (
41
+ <p className="text-xs text-gray-600">Error ID: {error.digest}</p>
42
+ )}
43
+ {/* Decorative blobs */}
44
+ <div
45
+ aria-hidden="true"
46
+ className="pointer-events-none fixed left-0 top-0 z-[-1] h-[400px] w-[400px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-magenta opacity-10 mix-blend-screen blur-[120px]"
47
+ />
48
+ <div
49
+ aria-hidden="true"
50
+ className="pointer-events-none fixed bottom-0 right-0 z-[-1] h-[500px] w-[500px] translate-x-1/3 translate-y-1/3 rounded-full bg-gold opacity-10 mix-blend-screen blur-[150px]"
51
+ />
52
+ </div>
53
+ );
54
+ }
src/app/not-found.tsx ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Link from "next/link";
2
+
3
+ export default function NotFound() {
4
+ return (
5
+ <div className="flex min-h-screen flex-col items-center justify-center gap-6 px-4 text-center">
6
+ <div className="select-none text-8xl font-black tracking-tighter text-white/10 sm:text-9xl">
7
+ 404
8
+ </div>
9
+ <h1 className="text-3xl font-black text-white sm:text-4xl">Lost in the Verse</h1>
10
+ <p className="max-w-md text-gray-400">
11
+ This cosmic coordinate doesn&apos;t exist. The page you&apos;re looking for has drifted into
12
+ a black hole or never existed at all.
13
+ </p>
14
+ <Link
15
+ href="/"
16
+ className="inline-flex items-center gap-2 rounded-xl bg-magenta px-6 py-3 text-sm font-bold text-white transition-colors hover:bg-[#d81b60]"
17
+ >
18
+ Back to Triviaverse
19
+ </Link>
20
+ {/* Decorative blobs */}
21
+ <div
22
+ aria-hidden="true"
23
+ className="pointer-events-none fixed left-0 top-0 z-[-1] h-[400px] w-[400px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-magenta opacity-10 mix-blend-screen blur-[120px]"
24
+ />
25
+ <div
26
+ aria-hidden="true"
27
+ className="pointer-events-none fixed bottom-0 right-0 z-[-1] h-[500px] w-[500px] translate-x-1/3 translate-y-1/3 rounded-full bg-gold opacity-10 mix-blend-screen blur-[150px]"
28
+ />
29
+ </div>
30
+ );
31
+ }
src/components/game/ResultsScreen.tsx CHANGED
@@ -13,7 +13,7 @@ import { getOrCreatePlayer } from "@/lib/supabase/queries";
13
  import { usePlayerProgress } from "@/hooks/usePlayerProgress";
14
  import { AchievementToast, type AchievementToastData } from "@/components/achievements/AchievementToast";
15
  import { ACHIEVEMENT_MAP } from "@/lib/achievements/definitions";
16
- import type { GameMode, TriviaMode } from "@/lib/game/types";
17
  import type { CoinBreakdown } from "@/lib/achievements/coin-calculator";
18
  import type { AchievementUpdate } from "@/lib/achievements/checker";
19
 
@@ -98,13 +98,19 @@ export function ResultsScreen({
98
  const { submitLeaderboardEntry } = await import(
99
  "@/lib/supabase/queries"
100
  );
101
- await submitLeaderboardEntry({
102
- player_id: playerId,
103
- player_name: player.displayName,
104
- mode,
105
- score,
106
- accuracy,
107
- });
 
 
 
 
 
 
108
  } catch (err) {
109
  console.error("[ResultsScreen] submission failed:", err);
110
  }
@@ -137,7 +143,16 @@ export function ResultsScreen({
137
  // Silently ignore -- results screen still displays
138
  }
139
  })();
140
- }, [processGameResult, mode, score, accuracy, streakMax, questionsCorrect, questionsAnswered]);
 
 
 
 
 
 
 
 
 
141
 
142
  const stats = [
143
  { label: "Score", value: score.toLocaleString(), color: "text-gold" },
 
13
  import { usePlayerProgress } from "@/hooks/usePlayerProgress";
14
  import { AchievementToast, type AchievementToastData } from "@/components/achievements/AchievementToast";
15
  import { ACHIEVEMENT_MAP } from "@/lib/achievements/definitions";
16
+ import type { GameMode, TriviaMode, MiniGameMode } from "@/lib/game/types";
17
  import type { CoinBreakdown } from "@/lib/achievements/coin-calculator";
18
  import type { AchievementUpdate } from "@/lib/achievements/checker";
19
 
 
98
  const { submitLeaderboardEntry } = await import(
99
  "@/lib/supabase/queries"
100
  );
101
+
102
+ // Only submit leaderboard entries for trivia modes β€” mini-games have
103
+ // their own local high-score tracking and don't map to the leaderboard.
104
+ const triviaModes: TriviaMode[] = ["quick-fire", "challenge-me", "streak", "multiplayer"];
105
+ if (triviaModes.includes(mode as TriviaMode)) {
106
+ await submitLeaderboardEntry({
107
+ player_id: playerId,
108
+ player_name: player.displayName,
109
+ mode,
110
+ score,
111
+ accuracy,
112
+ });
113
+ }
114
  } catch (err) {
115
  console.error("[ResultsScreen] submission failed:", err);
116
  }
 
143
  // Silently ignore -- results screen still displays
144
  }
145
  })();
146
+ }, [
147
+ processGameResult,
148
+ mode,
149
+ score,
150
+ accuracy,
151
+ streakMax,
152
+ questionsCorrect,
153
+ questionsAnswered,
154
+ phasesCompleted,
155
+ ]);
156
 
157
  const stats = [
158
  { label: "Score", value: score.toLocaleString(), color: "text-gold" },
src/lib/game/fallback-questions.ts CHANGED
@@ -1,14 +1,12 @@
1
  import type { Question } from "./types";
2
 
3
  /**
4
- * Hard-coded fallback trivia questions for Game Night mode when
5
- * Supabase is not configured or the DB fetch fails.
6
- *
7
- * 20 questions: 7 easy, 7 medium, 6 hard
8
- * Categories: Science, History, Geography, Entertainment, Sports, Technology, Nature
9
  */
10
  export const FALLBACK_QUESTIONS: Question[] = [
11
- // --- EASY (7) ---
12
  {
13
  id: "fb-1",
14
  category: "Science",
@@ -65,8 +63,96 @@ export const FALLBACK_QUESTIONS: Question[] = [
65
  correctAnswer: "Greece",
66
  incorrectAnswers: ["Italy", "Egypt", "Turkey"],
67
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
- // --- MEDIUM (7) ---
70
  {
71
  id: "fb-8",
72
  category: "Science",
@@ -123,8 +209,96 @@ export const FALLBACK_QUESTIONS: Question[] = [
123
  correctAnswer: "Whale Shark",
124
  incorrectAnswers: ["Great White Shark", "Hammerhead Shark", "Tiger Shark"],
125
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
- // --- HARD (6) ---
128
  {
129
  id: "fb-15",
130
  category: "Science",
@@ -173,4 +347,100 @@ export const FALLBACK_QUESTIONS: Question[] = [
173
  correctAnswer: "Bat",
174
  incorrectAnswers: ["Flying Squirrel", "Colugo", "Sugar Glider"],
175
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  ];
 
1
  import type { Question } from "./types";
2
 
3
  /**
4
+ * Hard-coded fallback trivia questions used when Supabase is unavailable.
5
+ * 54 questions across all categories and difficulties β€” enough to support
6
+ * Streak mode (50 questions) even without a DB connection.
 
 
7
  */
8
  export const FALLBACK_QUESTIONS: Question[] = [
9
+ // ── EASY (18) ──────────────────────────────────────────────────────────
10
  {
11
  id: "fb-1",
12
  category: "Science",
 
63
  correctAnswer: "Greece",
64
  incorrectAnswers: ["Italy", "Egypt", "Turkey"],
65
  },
66
+ {
67
+ id: "fb-21",
68
+ category: "General Knowledge",
69
+ difficulty: "easy",
70
+ questionText: "What is the capital of France?",
71
+ correctAnswer: "Paris",
72
+ incorrectAnswers: ["London", "Berlin", "Madrid"],
73
+ },
74
+ {
75
+ id: "fb-22",
76
+ category: "General Knowledge",
77
+ difficulty: "easy",
78
+ questionText: "How many continents are there?",
79
+ correctAnswer: "7",
80
+ incorrectAnswers: ["5", "6", "8"],
81
+ },
82
+ {
83
+ id: "fb-23",
84
+ category: "Science",
85
+ difficulty: "easy",
86
+ questionText: "What gas makes up most of Earth's atmosphere?",
87
+ correctAnswer: "Nitrogen",
88
+ incorrectAnswers: ["Oxygen", "Carbon Dioxide", "Argon"],
89
+ },
90
+ {
91
+ id: "fb-24",
92
+ category: "Geography",
93
+ difficulty: "easy",
94
+ questionText: "What is the longest river in the world?",
95
+ correctAnswer: "Nile",
96
+ incorrectAnswers: ["Amazon", "Mississippi", "Yangtze"],
97
+ },
98
+ {
99
+ id: "fb-25",
100
+ category: "Entertainment",
101
+ difficulty: "easy",
102
+ questionText: "Which Disney movie features a snowman named Olaf?",
103
+ correctAnswer: "Frozen",
104
+ incorrectAnswers: ["Tangled", "Moana", "The Snow Queen"],
105
+ },
106
+ {
107
+ id: "fb-26",
108
+ category: "Sports",
109
+ difficulty: "easy",
110
+ questionText: "In what sport would you perform a slam dunk?",
111
+ correctAnswer: "Basketball",
112
+ incorrectAnswers: ["Volleyball", "Tennis", "Football"],
113
+ },
114
+ {
115
+ id: "fb-27",
116
+ category: "Technology",
117
+ difficulty: "easy",
118
+ questionText: "What company developed the iPhone?",
119
+ correctAnswer: "Apple",
120
+ incorrectAnswers: ["Google", "Samsung", "Microsoft"],
121
+ },
122
+ {
123
+ id: "fb-28",
124
+ category: "History",
125
+ difficulty: "easy",
126
+ questionText: "Who painted the Mona Lisa?",
127
+ correctAnswer: "Leonardo da Vinci",
128
+ incorrectAnswers: ["Michelangelo", "Raphael", "Donatello"],
129
+ },
130
+ {
131
+ id: "fb-29",
132
+ category: "Nature",
133
+ difficulty: "easy",
134
+ questionText: "How many legs does a spider have?",
135
+ correctAnswer: "8",
136
+ incorrectAnswers: ["6", "10", "4"],
137
+ },
138
+ {
139
+ id: "fb-30",
140
+ category: "General Knowledge",
141
+ difficulty: "easy",
142
+ questionText: "What is the boiling point of water in Celsius?",
143
+ correctAnswer: "100",
144
+ incorrectAnswers: ["90", "110", "50"],
145
+ },
146
+ {
147
+ id: "fb-31",
148
+ category: "Geography",
149
+ difficulty: "easy",
150
+ questionText: "Which continent is the Sahara Desert located on?",
151
+ correctAnswer: "Africa",
152
+ incorrectAnswers: ["Asia", "Australia", "South America"],
153
+ },
154
 
155
+ // ── MEDIUM (18) ────────────────────────────────────────────────────────
156
  {
157
  id: "fb-8",
158
  category: "Science",
 
209
  correctAnswer: "Whale Shark",
210
  incorrectAnswers: ["Great White Shark", "Hammerhead Shark", "Tiger Shark"],
211
  },
212
+ {
213
+ id: "fb-32",
214
+ category: "General Knowledge",
215
+ difficulty: "medium",
216
+ questionText: "What does 'ATM' stand for?",
217
+ correctAnswer: "Automated Teller Machine",
218
+ incorrectAnswers: ["Automatic Transaction Module", "Automated Transfer Mechanism", "Advanced Teller Machine"],
219
+ },
220
+ {
221
+ id: "fb-33",
222
+ category: "Science",
223
+ difficulty: "medium",
224
+ questionText: "What element has the atomic number 1?",
225
+ correctAnswer: "Hydrogen",
226
+ incorrectAnswers: ["Helium", "Oxygen", "Carbon"],
227
+ },
228
+ {
229
+ id: "fb-34",
230
+ category: "History",
231
+ difficulty: "medium",
232
+ questionText: "What year did World War II end?",
233
+ correctAnswer: "1945",
234
+ incorrectAnswers: ["1944", "1946", "1943"],
235
+ },
236
+ {
237
+ id: "fb-35",
238
+ category: "Geography",
239
+ difficulty: "medium",
240
+ questionText: "What is the capital of Australia?",
241
+ correctAnswer: "Canberra",
242
+ incorrectAnswers: ["Sydney", "Melbourne", "Brisbane"],
243
+ },
244
+ {
245
+ id: "fb-36",
246
+ category: "Entertainment",
247
+ difficulty: "medium",
248
+ questionText: "Which actor played Iron Man in the Marvel movies?",
249
+ correctAnswer: "Robert Downey Jr.",
250
+ incorrectAnswers: ["Chris Evans", "Chris Hemsworth", "Mark Ruffalo"],
251
+ },
252
+ {
253
+ id: "fb-37",
254
+ category: "Sports",
255
+ difficulty: "medium",
256
+ questionText: "Which country has won the most FIFA World Cup titles?",
257
+ correctAnswer: "Brazil",
258
+ incorrectAnswers: ["Germany", "Italy", "Argentina"],
259
+ },
260
+ {
261
+ id: "fb-38",
262
+ category: "Technology",
263
+ difficulty: "medium",
264
+ questionText: "Who is considered the father of the World Wide Web?",
265
+ correctAnswer: "Tim Berners-Lee",
266
+ incorrectAnswers: ["Vint Cerf", "Bill Gates", "Steve Jobs"],
267
+ },
268
+ {
269
+ id: "fb-39",
270
+ category: "Nature",
271
+ difficulty: "medium",
272
+ questionText: "What is the fastest land animal?",
273
+ correctAnswer: "Cheetah",
274
+ incorrectAnswers: ["Lion", "Pronghorn Antelope", "Greyhound"],
275
+ },
276
+ {
277
+ id: "fb-40",
278
+ category: "General Knowledge",
279
+ difficulty: "medium",
280
+ questionText: "How many bones are in the adult human body?",
281
+ correctAnswer: "206",
282
+ incorrectAnswers: ["186", "226", "256"],
283
+ },
284
+ {
285
+ id: "fb-41",
286
+ category: "Science",
287
+ difficulty: "medium",
288
+ questionText: "What planet has the most moons?",
289
+ correctAnswer: "Saturn",
290
+ incorrectAnswers: ["Jupiter", "Uranus", "Neptune"],
291
+ },
292
+ {
293
+ id: "fb-42",
294
+ category: "History",
295
+ difficulty: "medium",
296
+ questionText: "Who was the first woman to fly solo across the Atlantic?",
297
+ correctAnswer: "Amelia Earhart",
298
+ incorrectAnswers: ["Harriet Quimby", "Bessie Coleman", "Jacqueline Cochran"],
299
+ },
300
 
301
+ // ── HARD (18) ──────────────────────────────────────────────────────────
302
  {
303
  id: "fb-15",
304
  category: "Science",
 
347
  correctAnswer: "Bat",
348
  incorrectAnswers: ["Flying Squirrel", "Colugo", "Sugar Glider"],
349
  },
350
+ {
351
+ id: "fb-43",
352
+ category: "General Knowledge",
353
+ difficulty: "hard",
354
+ questionText: "What is the rarest blood type?",
355
+ correctAnswer: "AB Negative",
356
+ incorrectAnswers: ["O Negative", "B Negative", "A Negative"],
357
+ },
358
+ {
359
+ id: "fb-44",
360
+ category: "Science",
361
+ difficulty: "hard",
362
+ questionText: "What particle is responsible for mediating the electromagnetic force?",
363
+ correctAnswer: "Photon",
364
+ incorrectAnswers: ["Gluon", "W Boson", "Graviton"],
365
+ },
366
+ {
367
+ id: "fb-45",
368
+ category: "History",
369
+ difficulty: "hard",
370
+ questionText: "In what year did the French Revolution begin?",
371
+ correctAnswer: "1789",
372
+ incorrectAnswers: ["1776", "1799", "1815"],
373
+ },
374
+ {
375
+ id: "fb-46",
376
+ category: "Geography",
377
+ difficulty: "hard",
378
+ questionText: "Which country has the longest coastline in the world?",
379
+ correctAnswer: "Canada",
380
+ incorrectAnswers: ["Australia", "Russia", "Indonesia"],
381
+ },
382
+ {
383
+ id: "fb-47",
384
+ category: "Entertainment",
385
+ difficulty: "hard",
386
+ questionText: "Which film won the Academy Award for Best Picture in 1994?",
387
+ correctAnswer: "Schindler's List",
388
+ incorrectAnswers: ["Forrest Gump", "The Shawshank Redemption", "Pulp Fiction"],
389
+ },
390
+ {
391
+ id: "fb-48",
392
+ category: "Sports",
393
+ difficulty: "hard",
394
+ questionText: "In which sport would you use a 'shuttlecock'?",
395
+ correctAnswer: "Badminton",
396
+ incorrectAnswers: ["Table Tennis", "Squash", "Cricket"],
397
+ },
398
+ {
399
+ id: "fb-49",
400
+ category: "Technology",
401
+ difficulty: "hard",
402
+ questionText: "What programming language was created by James Gosling at Sun Microsystems?",
403
+ correctAnswer: "Java",
404
+ incorrectAnswers: ["C++", "Python", "Ruby"],
405
+ },
406
+ {
407
+ id: "fb-50",
408
+ category: "Nature",
409
+ difficulty: "hard",
410
+ questionText: "What is the largest organ in the human body?",
411
+ correctAnswer: "Skin",
412
+ incorrectAnswers: ["Liver", "Brain", "Heart"],
413
+ },
414
+ {
415
+ id: "fb-51",
416
+ category: "General Knowledge",
417
+ difficulty: "hard",
418
+ questionText: "What is the only letter that does not appear in any US state name?",
419
+ correctAnswer: "Q",
420
+ incorrectAnswers: ["X", "Z", "J"],
421
+ },
422
+ {
423
+ id: "fb-52",
424
+ category: "Science",
425
+ difficulty: "hard",
426
+ questionText: "What is the chemical formula for ozone?",
427
+ correctAnswer: "O3",
428
+ incorrectAnswers: ["O2", "CO2", "H2O"],
429
+ },
430
+ {
431
+ id: "fb-53",
432
+ category: "History",
433
+ difficulty: "hard",
434
+ questionText: "Who was the last Pharaoh of Egypt?",
435
+ correctAnswer: "Cleopatra VII",
436
+ incorrectAnswers: ["Hatshepsut", "Nefertiti", "Ramesses II"],
437
+ },
438
+ {
439
+ id: "fb-54",
440
+ category: "Geography",
441
+ difficulty: "hard",
442
+ questionText: "What is the highest capital city in the world?",
443
+ correctAnswer: "La Paz",
444
+ incorrectAnswers: ["Quito", "Bogota", "Lhasa"],
445
+ },
446
  ];