@mblinkov/whats-missing 20.0.42 → 20.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Game.tsx CHANGED
@@ -1,10 +1,8 @@
1
- import { useState, useEffect, useMemo, useRef, useLayoutEffect } from "react";
2
- import { createPortal } from "react-dom";
1
+ import { useState, useEffect, useMemo, useRef } from "react";
3
2
  import { themes } from "./themes";
4
3
  import { styles } from "./Game.styles";
5
4
  import type { Theme } from "./themes";
6
5
 
7
-
8
6
  type ImageItem = { src: string; name: string };
9
7
  type RoundResult = {
10
8
  round: number;
@@ -13,9 +11,40 @@ type RoundResult = {
13
11
  result: "correct" | "almost" | "wrong";
14
12
  };
15
13
 
16
- // ✅ globalReset удален - теперь используется Shadow DOM изоляция
14
+ // ✅ базовый reset
15
+ const globalReset = () => {
16
+ const style = document.createElement("style");
17
+ style.textContent = `
18
+ #whats-missing-root, #whats-missing-root * {
19
+ box-sizing: border-box;
20
+ font-family: "Geist", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
21
+ }
22
+ #whats-missing-root img {
23
+ max-width: 100%;
24
+ height: auto;
25
+ display: block;
26
+ user-select: none;
27
+ }
28
+ html, body {
29
+ margin: 0 !important;
30
+ padding: 0 !important;
31
+ width: 100% !important;
32
+ height: 100% !important;
33
+ overflow: hidden !important;
34
+ zoom: 1 !important; /* ✅ защита от подзума */
35
+ }
36
+ #root {
37
+ margin: 0 !important;
38
+ padding: 0 !important;
39
+ width: 100% !important;
40
+ height: 100% !important;
41
+ overflow: hidden !important;
42
+ }
43
+ `;
44
+ document.head.appendChild(style);
45
+ };
17
46
 
18
- // расстояние Левенштейна — для проверки “почти правильно”
47
+ // простая функция сравнения
19
48
  const levenshtein = (a: string, b: string) => {
20
49
  const dp = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
21
50
  for (let i = 0; i <= a.length; i++) dp[i][0] = i;
@@ -31,114 +60,6 @@ const levenshtein = (a: string, b: string) => {
31
60
  return dp[a.length][b.length];
32
61
  };
33
62
 
34
- // ✅ фикс визуального подзума
35
- export function useNeutralizeBodyZoom(
36
- ref: React.RefObject<HTMLDivElement | null>,
37
- opts: { minDesktopWidth?: number; forceResize?: boolean } = { minDesktopWidth: 1200, forceResize: false }
38
- ) {
39
- const saved = useRef<Record<string, string | null> | null>(null);
40
- const minDesktopWidth = opts.minDesktopWidth ?? 1200;
41
- const forceResize = !!opts.forceResize;
42
-
43
- useLayoutEffect(() => {
44
- if (typeof window === "undefined" || !ref || !ref.current) return;
45
- const el = ref.current;
46
-
47
- const getBodyScale = (): number => {
48
- try {
49
- const body = document.body;
50
- const cs = getComputedStyle(body) as CSSStyleDeclaration & { zoom?: string };
51
- const zoomInline = parseFloat((body.style && body.style.zoom) || "");
52
- if (zoomInline && !isNaN(zoomInline) && zoomInline > 0) return zoomInline;
53
- const zoomComputed = parseFloat((cs.zoom as string) || "");
54
- if (zoomComputed && !isNaN(zoomComputed) && zoomComputed > 0) return zoomComputed;
55
- const probe = document.createElement("div");
56
- probe.style.position = "absolute";
57
- probe.style.left = "0";
58
- probe.style.top = "0";
59
- probe.style.width = "100px";
60
- probe.style.height = "1px";
61
- probe.style.visibility = "hidden";
62
- document.documentElement.appendChild(probe);
63
- const rect = probe.getBoundingClientRect();
64
- document.documentElement.removeChild(probe);
65
- if (rect && rect.width > 0) {
66
- const measured = rect.width / 100;
67
- if (!isNaN(measured) && measured > 0) return measured;
68
- }
69
- } catch {}
70
- return 1;
71
- };
72
-
73
- const apply = () => {
74
- if (minDesktopWidth && window.innerWidth < minDesktopWidth) {
75
- restore();
76
- return;
77
- }
78
- const scale = getBodyScale();
79
- if (!scale || scale === 1) {
80
- restore();
81
- return;
82
- }
83
- if (!saved.current) {
84
- saved.current = {
85
- transform: el.style.transform || null,
86
- transformOrigin: el.style.transformOrigin || null,
87
- width: el.style.width || null,
88
- height: el.style.height || null,
89
- willChange: el.style.willChange || null,
90
- };
91
- }
92
- const inv = 1 / scale;
93
- if (forceResize) {
94
- el.style.width = `${scale * 100}%`;
95
- el.style.height = `${scale * 100}%`;
96
- }
97
- el.style.transformOrigin = "50% 50%";
98
- el.style.transform = `scale(${inv})`;
99
- el.style.willChange = "transform";
100
- };
101
-
102
- const restore = () => {
103
- if (!saved.current) return;
104
- const s = saved.current;
105
- el.style.transform = s.transform ?? "";
106
- el.style.transformOrigin = s.transformOrigin ?? "";
107
- el.style.width = s.width ?? "";
108
- el.style.height = s.height ?? "";
109
- el.style.willChange = s.willChange ?? "";
110
- saved.current = null;
111
- };
112
-
113
- let raf = 0;
114
- const scheduled = () => {
115
- cancelAnimationFrame(raf);
116
- raf = requestAnimationFrame(apply);
117
- };
118
- const mo = new MutationObserver((m) => {
119
- for (const x of m)
120
- if (x.type === "attributes" && (x.attributeName === "style" || x.attributeName === "class")) {
121
- scheduled();
122
- break;
123
- }
124
- });
125
- mo.observe(document.body, { attributes: true, attributeFilter: ["style", "class"] });
126
- window.addEventListener("resize", scheduled);
127
- scheduled();
128
-
129
- return () => {
130
- cancelAnimationFrame(raf);
131
- mo.disconnect();
132
- window.removeEventListener("resize", scheduled);
133
- restore();
134
- };
135
- }, [ref, minDesktopWidth, forceResize]);
136
- }
137
-
138
- // =====================================
139
- // ИГРА
140
- // =====================================
141
-
142
63
  export default function Game({
143
64
  gameCubeSize,
144
65
  screenHeight,
@@ -148,120 +69,10 @@ export default function Game({
148
69
  screenHeight?: number;
149
70
  screenWidth?: number;
150
71
  }) {
151
- const containerRef = useRef<HTMLDivElement | null>(null);
152
- const squareRef = useRef<HTMLDivElement | null>(null);
153
- const [squareSize, setSquareSize] = useState<number | null>(null);
154
- const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null);
155
- useNeutralizeBodyZoom(containerRef, { minDesktopWidth: 1024, forceResize: false });
156
-
157
- // ✅ Shadow DOM изоляция - создаем сначала, потом рендерим
158
- useLayoutEffect(() => {
159
- const container = containerRef.current;
160
- if (!container) return;
161
-
162
- // Создаем Shadow DOM если его нет
163
- let root = container.shadowRoot;
164
- if (!root) {
165
- root = container.attachShadow({ mode: 'open' });
166
- }
167
-
168
- // Очищаем содержимое shadow root
169
- root.innerHTML = '';
170
-
171
- // Создаем стили для изоляции
172
- const style = document.createElement('style');
173
- style.textContent = `
174
- :host {
175
- position: fixed;
176
- inset: 0;
177
- width: 100vw;
178
- height: 100vh;
179
- display: flex;
180
- justify-content: center;
181
- align-items: center;
182
- background: linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%);
183
- z-index: 100;
184
- overflow: hidden;
185
- margin: 0;
186
- padding: 0;
187
- transform: none;
188
- zoom: 1;
189
- scale: 1;
190
- }
191
-
192
- #whats-missing-root, #whats-missing-root * {
193
- box-sizing: border-box;
194
- }
195
-
196
- #whats-missing-root img {
197
- max-width: 100%;
198
- height: auto;
199
- display: block;
200
- user-select: none;
201
- }
202
-
203
- #whats-missing-root {
204
- position: relative;
205
- width: 100%;
206
- height: 100%;
207
- max-width: 1200px;
208
- display: flex;
209
- justify-content: center;
210
- align-items: center;
211
- transform: none;
212
- zoom: 1;
213
- scale: 1;
214
- }
215
- `;
216
-
217
- // Добавляем стили в shadow root
218
- root.appendChild(style);
219
-
220
- // Устанавливаем shadow root для рендера
221
- setShadowRoot(root);
222
- }, []);
223
-
224
- useEffect(() => {
225
- const b = document.body;
226
- const h = document.documentElement;
227
-
228
- const oldBodyZoom = b.style.zoom;
229
- const oldBodyTransform = b.style.transform;
230
- const oldHtmlZoom = h.style.zoom;
231
- const oldHtmlTransform = h.style.transform;
232
-
233
- b.style.zoom = "1";
234
- b.style.transform = "none";
235
- h.style.zoom = "1";
236
- h.style.transform = "none";
237
-
238
- return () => {
239
- b.style.zoom = oldBodyZoom;
240
- b.style.transform = oldBodyTransform;
241
- h.style.zoom = oldHtmlZoom;
242
- h.style.transform = oldHtmlTransform;
243
- };
244
- }, []);
245
-
246
- // ✅ защита от слишком маленьких размеров игры
247
- const effectiveGameCubeSize = useMemo(() => {
248
- const baseSize = gameCubeSize ?? Math.min(screenWidth ?? 1000, screenHeight ?? 1000);
249
- const currentWidth = screenWidth ?? (typeof window !== "undefined" ? window.innerWidth : 1000);
250
-
251
- // минимальный предел для десктопа и планшета
252
- if (baseSize < 600 && currentWidth > 768) {
253
- return 600;
254
- }
255
-
256
- // для мобилок в портретной ориентации пусть остаётся адаптивным
257
- if (currentWidth <= 768) {
258
- return Math.min(baseSize, currentWidth - 20);
259
- }
260
-
261
- return baseSize;
262
- }, [gameCubeSize, screenWidth, screenHeight]);
72
+ const containerRef = useRef<HTMLDivElement>(null);
263
73
 
264
74
  useEffect(() => {
75
+ globalReset();
265
76
  return () => {
266
77
  document.body.style.overflow = "";
267
78
  };
@@ -286,44 +97,85 @@ export default function Game({
286
97
  const [resultsTable, setResultsTable] = useState<RoundResult[]>([]);
287
98
  const [usedHidden, setUsedHidden] = useState<string[]>([]);
288
99
  const [isMobile, setIsMobile] = useState(false);
100
+ const [scale, setScale] = useState(1);
101
+ const [containerSize, setContainerSize] = useState<number | null>(null);
102
+ const [isDesktopLayout, setIsDesktopLayout] = useState(false);
289
103
 
290
- // simple responsive flag; we still compute exact sizes from the measured square
104
+ // адаптив под мобилки, планшеты и десктоп
291
105
  useEffect(() => {
292
- if (typeof window === "undefined") return;
293
- const update = () => {
294
- const w = screenWidth ?? window.innerWidth;
295
- const hasCoarsePointer = window.matchMedia && window.matchMedia('(pointer: coarse)').matches;
296
- setIsMobile(w <= 768 || hasCoarsePointer);
297
- };
298
- update();
299
- window.addEventListener("resize", update);
300
- window.addEventListener("orientationchange", update);
301
- return () => {
302
- window.removeEventListener("resize", update);
303
- window.removeEventListener("orientationchange", update);
106
+ const resize = () => {
107
+ const width = screenWidth ?? window.innerWidth;
108
+ const height = screenHeight ?? window.innerHeight;
109
+ const mobile = width < 768;
110
+ const isLandscape = width > height && mobile;
111
+ const isSmallHeight = height < 700; // Nest Hub, маленькие экраны
112
+
113
+ // Определяем iPhone до 14 Pro Max в landscape режиме
114
+ // iPhone 14 Pro Max: 926x428 (landscape)
115
+ // iPhone 13 Pro Max: 926x428 (landscape)
116
+ // iPhone 12 Pro Max: 926x428 (landscape)
117
+ // iPhone 11 Pro Max: 896x414 (landscape)
118
+ // iPhone XR: 896x414 (landscape)
119
+ // iPhone XS Max: 896x414 (landscape)
120
+ // iPhone X: 812x375 (landscape)
121
+ // iPhone 8 Plus: 736x414 (landscape)
122
+ // iPhone 8: 667x375 (landscape)
123
+ // iPhone SE: 667x375 (landscape)
124
+
125
+ // iPhone XR: 896x414 (landscape)
126
+ const isIPhoneXRLandscape = isLandscape && width === 896 && height === 414;
127
+
128
+ // iPhone 12 Pro: 844x390 (landscape)
129
+ const isIPhone12ProLandscape = isLandscape && width === 844 && height === 390;
130
+
131
+ // Разрешение 1366x766 - ноутбуки и небольшие десктопы
132
+ const is1366x766 = width === 1366 && height === 766;
133
+
134
+ // Разрешение 1366x768 - ноутбуки и небольшие десктопы
135
+ const is1366x768 = width === 1366 && height === 768;
136
+
137
+ // Разрешение 1280x720 - ноутбуки и небольшие десктопы
138
+ const is1280x720 = width === 1280 && height === 720;
139
+
140
+ // Разрешение 1440x900 - ноутбуки и небольшие десктопы
141
+ const is1440x900 = width === 1440 && height === 900;
142
+
143
+ // Популярные разрешения для горизонтального расположения кнопки Check
144
+ const desktopLayout = width >= 1200 && height >= 600 && !mobile;
145
+ setIsDesktopLayout(desktopLayout);
146
+
147
+ const isOldIPhoneLandscape = isLandscape && height <= 428; // до iPhone 14 Pro Max включительно
148
+
149
+ // treat only phones as "mobile" so tablets use the desktop (square/centered) layout
150
+ setIsMobile(mobile);
151
+
152
+ // ✅ Используем gameCubeSize от родительского сайта, но с защитой от слишком маленьких размеров
153
+ if (mobile) {
154
+ // Мобилки: используем переданный gameCubeSize или полную ширину
155
+ setContainerSize(gameCubeSize && gameCubeSize >= 320 ? gameCubeSize : null);
156
+ setScale(1);
157
+ } else if (isSmallHeight) {
158
+ // Маленькие экраны: используем переданный gameCubeSize или полную высоту
159
+ setContainerSize(gameCubeSize && gameCubeSize >= 400 ? gameCubeSize : null);
160
+ setScale(1);
161
+ } else {
162
+ // Десктопы: используем переданный gameCubeSize с разумными ограничениями
163
+ const minSize = 400;
164
+ const maxSize = 1200;
165
+ const finalSize = gameCubeSize
166
+ ? Math.max(minSize, Math.min(maxSize, gameCubeSize))
167
+ : Math.min(1000, Math.min(width, height) * 0.9);
168
+ setContainerSize(finalSize);
169
+ setScale(1);
170
+ }
304
171
  };
305
- }, [screenWidth]);
172
+ resize();
173
+ window.addEventListener("resize", resize);
174
+ return () => window.removeEventListener("resize", resize);
175
+ }, [screenWidth, screenHeight, gameCubeSize]);
306
176
 
307
- useLayoutEffect(() => {
308
- const el = squareRef.current;
309
- if (!el || typeof window === "undefined") return;
310
- let ro: ResizeObserver | null = null;
311
- try {
312
- ro = new ResizeObserver((entries) => {
313
- for (const e of entries) {
314
- const cr = e.contentRect;
315
- setSquareSize(Math.round(Math.min(cr.width, cr.height)));
316
- }
317
- });
318
- ro.observe(el);
319
- setSquareSize(Math.round(Math.min(el.clientWidth, el.clientHeight)));
320
- } catch {
321
- setSquareSize(Math.round(Math.min(el.clientWidth, el.clientHeight)));
322
- }
323
- return () => ro?.disconnect();
324
- }, [isMobile]);
325
-
326
- const getRandomSix = (arr: ImageItem[]) => [...arr].sort(() => Math.random() - 0.5).slice(0, 6);
177
+ const getRandomSix = (arr: ImageItem[]) =>
178
+ [...arr].sort(() => Math.random() - 0.5).slice(0, 6);
327
179
 
328
180
  const startGame = () => {
329
181
  if (!theme) return;
@@ -432,279 +284,572 @@ export default function Game({
432
284
  };
433
285
 
434
286
  const MemoizedLogo = useMemo(
435
- () => (
436
- <div style={{ ...styles.gmLogoFixed, position: "absolute", top: 16, left: 16, zIndex: 30 }}>
437
- <picture>
438
- <source
439
- srcSet={window.origin + "/cloud/speakid/games/whatsmissing/logo.svg"}
440
- type="image/svg+xml"
441
- />
442
- <img
443
- src={window.origin + "/cloud/speakid/games/whatsmissing/logo.png"}
444
- alt="SPEAKID Logo"
445
- style={styles.gmLogoImg}
446
- loading="lazy"
447
- />
448
- </picture>
449
- </div>
450
- ),
451
- []
287
+ () => {
288
+ // Скрываем логотип на мобильных устройствах в landscape режиме и на малых экранах
289
+ if ((isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700) {
290
+ return null;
291
+ }
292
+
293
+ return (
294
+ // ensure logo is positioned inside the square container
295
+ <div style={{ ...styles.gmLogoFixed, position: "absolute", top: 16, left: 16, zIndex: 30 }}>
296
+ <picture>
297
+ <source
298
+ srcSet={window.origin + "/cloud/speakid/games/whatsmissing/logo.svg"}
299
+ type="image/svg+xml"
300
+ />
301
+ <img
302
+ src={window.origin + "/cloud/speakid/games/whatsmissing/logo.png"}
303
+ alt="SPEAKID Logo"
304
+ style={styles.gmLogoImg}
305
+ loading="lazy"
306
+ />
307
+ </picture>
308
+ </div>
309
+ );
310
+ },
311
+ [isMobile]
452
312
  );
453
313
 
454
- // ✅ масштаб интерфейса под размеры окна — с мягкими лимитами и кэшированием
455
- // оптимизированные базовые размеры для лучшего покрытия экрана на десктопах
456
- const baseWidth = 1200;
457
- const baseHeight = 700;
458
- const scale = useMemo(() => {
459
- const effectiveWidth = screenWidth ?? (typeof window !== "undefined" ? window.innerWidth : baseWidth);
460
- const effectiveHeight = screenHeight ?? (typeof window !== "undefined" ? window.innerHeight : baseHeight);
461
- const rawScale = Math.min(effectiveWidth / baseWidth, effectiveHeight / baseHeight);
462
- // мягкие границы роста/сжатия - увеличен верхний лимит для лучшего заполнения экрана
463
- return Math.min(1.35, Math.max(1.0, rawScale));
464
- }, [screenWidth, screenHeight, isMobile]);
465
-
466
314
  return (
467
- <div ref={containerRef} style={{ width: "100%", height: "100%" }}>
468
- {shadowRoot && createPortal(
315
+ <div
316
+ ref={containerRef}
317
+ style={{
318
+ width: "100%",
319
+ height: "100%",
320
+ display: "flex",
321
+ justifyContent: "center",
322
+ alignItems: "center",
323
+ background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
324
+ transition: "background 0.3s ease",
325
+ overflow: "hidden",
326
+ position: "absolute",
327
+ top: 0,
328
+ left: 0,
329
+ right: 0,
330
+ bottom: 0
331
+ }}
332
+ >
333
+ <div
334
+ style={{
335
+ width: isMobile ? "100%" : (containerSize || gameCubeSize || 1000),
336
+ height: isMobile ? "100%" : (containerSize || gameCubeSize || 1000),
337
+ display: "flex",
338
+ justifyContent: "center",
339
+ alignItems: "center",
340
+ overflow: "hidden",
341
+ borderRadius: isMobile ? 0 : "20px",
342
+ background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
343
+ boxShadow: isMobile ? "none" : "0 0 40px rgba(0,0,0,0.1)",
344
+ margin: isMobile ? "0 auto" : "unset",
345
+ position: "relative", // needed so absolute logo is inside the square
346
+ }}
347
+ >
469
348
  <div
470
- id="whats-missing-root"
471
- style={{ all: "unset" }}
349
+ style={{
350
+ transform: "none",
351
+ width: "100%",
352
+ height: "100%",
353
+ display: "flex",
354
+ justifyContent: "center",
355
+ alignItems: "center",
356
+ }}
472
357
  >
473
- <div
474
- ref={squareRef}
475
- style={{
476
- width: isMobile
477
- ? "100%"
478
- : `${effectiveGameCubeSize}px`,
479
- height: isMobile
480
- ? "100%"
481
- : `${effectiveGameCubeSize}px`,
482
- borderRadius: isMobile ? 0 : "20px",
483
- background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
484
- position: "relative",
485
- overflow: "hidden",
486
- boxShadow: isMobile ? "none" : "0 0 40px rgba(0,0,0,0.08)",
487
- display: "flex",
488
- justifyContent: "center",
489
- alignItems: "center",
490
- }}
491
- >
492
- <div
493
- style={{
494
- transform: `scale(${scale})`,
495
- transformOrigin: "center center",
496
- transition: "transform 0.2s ease-out",
497
- width: "100%",
498
- height: "100%",
499
- display: "flex",
500
- flexDirection: "column",
501
- justifyContent: "center",
502
- alignItems: "center",
503
- }}
504
- >
505
- {!isMobile && MemoizedLogo}
506
-
507
- {/* ====== ЛОББИ ====== */}
508
- {!theme && !started && (
509
- <div style={styles.gmCenterScreen}>
510
- <h1 style={styles.gmHeadline1}>WHAT'S MISSING?</h1>
511
- <p style={styles.gmBodyM}>Select a theme:</p>
512
- <div style={{ display: "flex", gap: 16 }}>
513
- {["animals", "food", "toys"].map((t) => (
514
- <button key={t} style={styles.gmButton} onClick={() => setTheme(t as Theme)}>
515
- {t === "animals" ? "🐶 Animals" : t === "food" ? "🍎 Food" : "🧸 Toys"}
516
- </button>
517
- ))}
518
- </div>
519
- <div style={{ marginTop: 24 }}>
520
- <p style={styles.gmBodyS}>Choose number of rounds:</p>
521
- <div style={{ display: "flex", gap: 12, marginTop: 8 }}>
522
- {[3, 4, 5].map((n) => (
523
- <button
524
- key={n}
525
- style={{
526
- ...styles.gmButton,
527
- ...(rounds === n ? styles.gmButtonActive : {}),
528
- }}
529
- onClick={() => setRounds(n)}
530
- >
531
- {n}
358
+ <div id="whats-missing-root">
359
+ {!isMobile && MemoizedLogo}
360
+ {/* ====== ИГРОВАЯ ЛОГИКА ====== */}
361
+ {!theme && !started && (
362
+ <div style={styles.gmCenterScreen}>
363
+ <h1 style={styles.gmHeadline1}>WHAT'S MISSING?</h1>
364
+ <p style={styles.gmBodyM}>Select a theme:</p>
365
+ <div style={{ display: "flex", gap: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "8px" : "16px" }}>
366
+ {["animals", "food", "toys"].map((t) => (
367
+ <button key={t} style={{
368
+ ...styles.gmButton,
369
+ padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "8px 12px" : "12px 24px",
370
+ fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "12px" : "16px",
371
+ minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "70px" : "auto"
372
+ }} onClick={() => setTheme(t as Theme)}>
373
+ {t === "animals" ? "🐶 Animals" : t === "food" ? "🍎 Food" : "🧸 Toys"}
532
374
  </button>
533
375
  ))}
534
376
  </div>
377
+ <div style={{ marginTop: 24 }}>
378
+ <p style={styles.gmBodyS}>Choose number of rounds:</p>
379
+ <div style={{ display: "flex", gap: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "6px" : "12px", marginTop: 8 }}>
380
+ {[3, 4, 5].map((n) => (
381
+ <button
382
+ key={n}
383
+ style={{
384
+ ...styles.gmButton,
385
+ ...(rounds === n ? styles.gmButtonActive : {}),
386
+ padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "6px 10px" : "12px 24px",
387
+ fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "12px" : "16px",
388
+ minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "40px" : "auto"
389
+ }}
390
+ onClick={() => setRounds(n)}
391
+ >
392
+ {n}
393
+ </button>
394
+ ))}
395
+ </div>
396
+ </div>
535
397
  </div>
536
- </div>
537
- )}
538
-
539
- {/* ====== ПОДТВЕРЖДЕНИЕ ====== */}
540
- {theme && !started && (
541
- <div style={styles.gmCenterScreen}>
542
- <h1 style={styles.gmHeadline1}>Theme selected: {theme}</h1>
543
- <p style={styles.gmBodyM}>Rounds: {rounds}</p>
544
- <button style={styles.gmButton} onClick={startGame}>
545
- Start game
546
- </button>
547
- </div>
548
- )}
549
-
550
- {/* ====== РЕЗУЛЬТАТЫ ====== */}
551
- {finished && (
552
- <div style={styles.gmCenterScreen}>
553
- <h1 style={styles.gmHeadline1}>Results</h1>
554
- <h2 style={styles.gmHeadline3}>
555
- Your score: {score} / {rounds}
556
- </h2>
557
- <p style={{ ...styles.gmBodyM, color: "#10b981", marginTop: 12 }}>
558
- Yahoo! You did it! 🍬✨
559
- </p>
560
- <table style={styles.gmTable}>
561
- <thead>
562
- <tr>
563
- <th>Round</th>
564
- <th>Your Answer</th>
565
- <th>Correct</th>
566
- <th>Result</th>
567
- </tr>
568
- </thead>
569
- <tbody>
570
- {resultsTable.map((r, i) => (
571
- <tr key={i}>
572
- <td style={styles.gmTableCell}>{r.round}</td>
573
- <td style={styles.gmTableCell}>{r.answer || "—"}</td>
574
- <td style={styles.gmTableCell}>{r.correct}</td>
575
- <td style={styles.gmTableCell}>
576
- {r.result === "correct"
577
- ? "✔ Correct"
578
- : r.result === "almost"
579
- ? "◐ Almost (0.5)"
580
- : "✘ Wrong"}
581
- </td>
582
- </tr>
583
- ))}
584
- </tbody>
585
- </table>
586
- <div style={{ display: "flex", gap: 12, marginTop: 24 }}>
587
- <button style={styles.gmButton} onClick={startGame}>
588
- 🔁 Play again
589
- </button>
590
- <button style={styles.gmButton} onClick={exitGame}>
591
- ⬅️ Choose theme
398
+ )}
399
+ {theme && !started && (
400
+ <div style={styles.gmCenterScreen}>
401
+ <h1 style={styles.gmHeadline1}>Theme selected: {theme}</h1>
402
+ <p style={styles.gmBodyM}>Rounds: {rounds}</p>
403
+ <button style={{
404
+ ...styles.gmButton,
405
+ padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "8px 16px" : "12px 24px",
406
+ fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "14px" : "16px",
407
+ minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "120px" : "auto"
408
+ }} onClick={startGame}>
409
+ ▶ Start game
592
410
  </button>
593
411
  </div>
594
- </div>
595
- )}
596
-
597
- {/* ====== ИГРОВОЙ ЭКРАН ====== */}
598
- {started && !finished && (
599
- <div style={styles.gmGameLayout}>
600
- <div
601
- style={{
602
- minHeight: 160,
603
- display: "flex",
604
- flexDirection: "column",
605
- justifyContent: "center",
606
- alignItems: "center",
607
- }}
608
- >
609
- {phase === "ready" && (
610
- <>
611
- <h1 style={{ ...styles.gmHeadline1, color: "#ec4c44" }}>GET READY</h1>
612
- <div style={styles.gmHourglass}>⏳</div>
613
- </>
614
- )}
615
- {phase === "memorize" && (
616
- <p style={{ ...styles.gmBodyM, color: "#10b981" }}>
617
- MEMORIZE ({memorizeTime})
618
- </p>
619
- )}
620
- {phase === "guess" && !answered && (
621
- <p style={styles.gmBodyM}>⏳ Time left: {timeLeft}s</p>
622
- )}
412
+ )}
413
+ {finished && (
414
+ <div style={styles.gmCenterScreen}>
415
+ <h1 style={styles.gmHeadline1}>Results</h1>
416
+ <h2 style={styles.gmHeadline3}>
417
+ Your score: {score} / {rounds}
418
+ </h2>
419
+ <p style={{ ...styles.gmBodyM, color: "#10b981", marginTop: 12 }}>Yahoo! You did it! 🍬✨</p>
420
+ <table style={styles.gmTable}>
421
+ <thead>
422
+ <tr>
423
+ <th>Round</th>
424
+ <th>Your Answer</th>
425
+ <th>Correct</th>
426
+ <th>Result</th>
427
+ </tr>
428
+ </thead>
429
+ <tbody>
430
+ {resultsTable.map((r, i) => (
431
+ <tr key={i}>
432
+ <td style={styles.gmTableCell}>{r.round}</td>
433
+ <td style={styles.gmTableCell}>{r.answer || ""}</td>
434
+ <td style={styles.gmTableCell}>{r.correct}</td>
435
+ <td style={styles.gmTableCell}>
436
+ {r.result === "correct"
437
+ ? "✔ Correct"
438
+ : r.result === "almost"
439
+ ? "◐ Almost (0.5)"
440
+ : "✘ Wrong"}
441
+ </td>
442
+ </tr>
443
+ ))}
444
+ </tbody>
445
+ </table>
446
+ <div style={{
447
+ display: "flex",
448
+ gap: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "6px" : "12px",
449
+ marginTop: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "2px" : (window.innerWidth === 1366 && window.innerHeight === 766) || (window.innerWidth === 1366 && window.innerHeight === 768) || (window.innerWidth === 1280 && window.innerHeight === 720) || (window.innerWidth === 1440 && window.innerHeight === 900) || isDesktopLayout ? "12px" : "24px"
450
+ }}>
451
+ <button style={{
452
+ ...styles.gmButton,
453
+ padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "6px 10px" : "12px 24px",
454
+ fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "12px" : "16px",
455
+ minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "80px" : "auto"
456
+ }} onClick={startGame}>
457
+ 🔁 Play again
458
+ </button>
459
+ <button style={{
460
+ ...styles.gmButton,
461
+ padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "6px 10px" : "12px 24px",
462
+ fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "12px" : "16px",
463
+ minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) ? "80px" : "auto"
464
+ }} onClick={exitGame}>
465
+ ⬅️ Choose theme
466
+ </button>
467
+ </div>
623
468
  </div>
624
-
625
- {/* ===== Сетка карточек (фикс 3×2 на десктопе, 2×3 на мобилках) ===== */}
626
- {phase !== "ready" && (
469
+ )}
470
+ {started && !finished && (
471
+ <div style={styles.gmGameLayout}>
627
472
  <div
628
473
  style={{
629
- ...styles.gmGrid,
630
- display: "grid",
631
- gridTemplateColumns: `repeat(${isMobile ? 2 : 3}, 1fr)`,
632
- gridTemplateRows: `repeat(${isMobile ? 3 : 2}, 1fr)`,
633
- gap: isMobile ? 12 : 20,
474
+ minHeight: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
475
+ ? "45px" // iPhone SE: еще более компактная высота
476
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
477
+ ? "50px" // iPhone до 14 Pro Max в landscape: очень компактная высота
478
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
479
+ ? "50px" // iPhone XR в landscape: очень компактная высота
480
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
481
+ ? "50px" // iPhone 12 Pro в landscape: очень компактная высота
482
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700
483
+ ? "60px" // iPhone landscape и малые экраны: компактная высота
484
+ : "160px",
485
+ display: "flex",
486
+ flexDirection: "column",
634
487
  justifyContent: "center",
635
- alignContent: "center",
636
- padding: isMobile ? 8 : 16,
637
- width: "100%",
638
- height: "100%",
639
- maxWidth: isMobile ? "100%" : 600,
640
- maxHeight: isMobile ? "100%" : 400,
641
- boxSizing: "border-box",
488
+ alignItems: "center",
489
+ paddingTop: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
490
+ ? "8px" // iPhone SE: поднимаем таймер еще выше
491
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
492
+ ? "8px" // iPhone до 14 Pro Max в landscape: поднимаем еще выше
493
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
494
+ ? "8px" // iPhone XR в landscape: поднимаем еще выше
495
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
496
+ ? "8px" // iPhone 12 Pro в landscape: поднимаем еще выше
497
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700
498
+ ? "15px" // iPhone landscape и малые экраны: поднимаем выше
499
+ : "40px"
642
500
  }}
643
501
  >
644
- {files.map((file, i) => {
645
- const isHidden = hiddenIndex === i && phase === "guess" && !answered;
646
- return (
647
- <div
648
- key={i}
649
- style={{
650
- ...styles.gmCard,
651
- width: "100%",
652
- height: "100%",
653
- ...(result === "correct" && hiddenIndex === i ? styles.gmCorrect : {}),
654
- ...(result === "wrong" && hiddenIndex === i ? styles.gmWrong : {}),
655
- }}
656
- >
657
- {!isHidden && (
658
- <img
659
- src={file.src}
660
- alt={file.name}
502
+ {phase === "ready" && (
503
+ <>
504
+ <h1 style={{ ...styles.gmHeadline1, color: "#ec4c44" }}>GET READY</h1>
505
+ <div style={styles.gmHourglass}>⏳</div>
506
+ </>
507
+ )}
508
+ {phase === "memorize" && (
509
+ <p style={{ ...styles.gmBodyM, color: "#10b981" }}>
510
+ MEMORIZE ({memorizeTime})
511
+ </p>
512
+ )}
513
+ {phase === "guess" && !answered && (
514
+ <p style={styles.gmBodyM}>⏳ Time left: {timeLeft}s</p>
515
+ )}
516
+ </div>
517
+ {phase !== "ready" && (
518
+ <div
519
+ style={{
520
+ ...styles.gmGrid,
521
+ gridTemplateColumns: isMobile && window.innerWidth > window.innerHeight
522
+ ? "repeat(3, 1fr)" // mobile landscape: 3 колонки
523
+ : isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
524
+ ? "repeat(3, 1fr)" // iPhone SE: 3 колонки для лучшего использования пространства
525
+ : isMobile
526
+ ? "repeat(2, 1fr)" // mobile portrait: 2 колонки
527
+ : window.innerHeight < 700
528
+ ? "repeat(3, 1fr)" // Nest Hub и малые экраны: 3 колонки
529
+ : "repeat(3, 210px)", // desktop: 3 колонки фиксированного размера
530
+ gridAutoRows: isMobile && window.innerWidth > window.innerHeight
531
+ ? "120px" // mobile landscape: меньшая высота
532
+ : isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
533
+ ? "100px" // iPhone SE: очень компактная высота
534
+ : isMobile
535
+ ? "150px" // mobile portrait: стандартная высота
536
+ : window.innerHeight < 700
537
+ ? "140px" // Nest Hub: компактная высота
538
+ : "210px", // desktop: стандартная высота
539
+ gap: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
540
+ ? "12px" // iPhone SE: больше отступов
541
+ : isMobile || window.innerHeight < 700 ? "8px" : "20px",
542
+ justifyItems: "center",
543
+ maxWidth: isMobile && window.innerWidth > window.innerHeight
544
+ ? "90%" // mobile landscape: используем больше ширины
545
+ : isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
546
+ ? "95%" // iPhone SE: используем почти всю ширину
547
+ : "100%",
548
+ padding: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
549
+ ? "8px" // iPhone SE: минимальные внутренние отступы
550
+ : "16px",
551
+ }}
552
+ >
553
+ {files.map((file, i) => {
554
+ const isHidden =
555
+ hiddenIndex === i && phase === "guess" && !answered;
556
+ return (
557
+ <div
558
+ key={i}
559
+ style={{
560
+ width: "100%",
561
+ height: "100%",
562
+ borderRadius: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
563
+ ? "6px" // iPhone SE: меньшие скругления
564
+ : "12px",
565
+ overflow: "hidden",
566
+ boxShadow: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
567
+ ? "0 1px 4px rgba(0,0,0,0.1)" // iPhone SE: меньшие тени
568
+ : "0 4px 12px rgba(0,0,0,0.15)",
569
+ transition: "all 0.3s ease",
570
+ cursor: "pointer",
571
+ ...(result === "correct" && hiddenIndex === i
572
+ ? {
573
+ boxShadow: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
574
+ ? "0 0 15px #10b981"
575
+ : "0 0 20px #10b981",
576
+ transform: "scale(1.03)"
577
+ }
578
+ : {}),
579
+ ...(result === "wrong" && hiddenIndex === i
580
+ ? {
581
+ boxShadow: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
582
+ ? "0 0 15px #ef4444"
583
+ : "0 0 20px #ef4444",
584
+ transform: "scale(1.03)"
585
+ }
586
+ : {}),
587
+ }}
588
+ >
589
+ {!isHidden && (
590
+ <img
591
+ src={file.src}
592
+ alt={file.name}
593
+ style={{
594
+ width: "100%",
595
+ height: "100%",
596
+ objectFit: "cover",
597
+ }}
598
+ />
599
+ )}
600
+ </div>
601
+ );
602
+ })}
603
+ </div>
604
+ )}
605
+ <div style={{
606
+ marginTop: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
607
+ ? "1px" // iPhone SE: еще более минимальный отступ
608
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
609
+ ? "2px" // iPhone до 14 Pro Max в landscape: минимальный отступ
610
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
611
+ ? "2px" // iPhone XR в landscape: минимальный отступ
612
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
613
+ ? "2px" // iPhone 12 Pro в landscape: минимальный отступ
614
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700
615
+ ? "4px" // iPhone landscape и малые экраны: минимальный отступ
616
+ : "16px",
617
+ height: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
618
+ ? "40px" // iPhone SE: еще более компактная высота
619
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
620
+ ? "45px" // iPhone до 14 Pro Max в landscape: очень компактная высота
621
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
622
+ ? "45px" // iPhone XR в landscape: очень компактная высота
623
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
624
+ ? "45px" // iPhone 12 Pro в landscape: очень компактная высота
625
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700
626
+ ? "50px" // iPhone landscape и малые экраны: компактная высота
627
+ : "80px"
628
+ }}>
629
+ {phase === "guess" && !answered && (
630
+ <div style={{
631
+ display: "flex",
632
+ flexDirection: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 || (window.innerWidth === 1366 && window.innerHeight === 766) || (window.innerWidth === 1366 && window.innerHeight === 768) || (window.innerWidth === 1280 && window.innerHeight === 720) || (window.innerWidth === 1440 && window.innerHeight === 900) || isDesktopLayout ? "row" : "column",
633
+ gap: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
634
+ ? "3px" // iPhone SE: еще более минимальный отступ
635
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
636
+ ? "4px" // iPhone до 14 Pro Max в landscape: минимальный отступ
637
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
638
+ ? "4px" // iPhone XR в landscape: минимальный отступ
639
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
640
+ ? "4px" // iPhone 12 Pro в landscape: минимальный отступ
641
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
642
+ ? "8px" // 1366x766: отступ между инпутом и кнопкой
643
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
644
+ ? "8px" // 1366x768: отступ между инпутом и кнопкой
645
+ : (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
646
+ ? "8px" // 1280x720: отступ между инпутом и кнопкой
647
+ : (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
648
+ ? "8px" // 1440x900: отступ между инпутом и кнопкой
649
+ : isDesktopLayout // Десктопные разрешения
650
+ ? "8px" // Десктопные разрешения: отступ между инпутом и кнопкой
651
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "6px" : "12px",
652
+ alignItems: "center",
653
+ justifyContent: "center",
654
+ width: "100%",
655
+ maxWidth: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
656
+ ? "270px" // iPhone SE: еще более компактная ширина
657
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
658
+ ? "350px" // iPhone до 14 Pro Max в landscape: компактная ширина
659
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
660
+ ? "350px" // iPhone XR в landscape: компактная ширина
661
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
662
+ ? "350px" // iPhone 12 Pro в landscape: компактная ширина
663
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
664
+ ? "400px" // 1366x766: ширина контейнера
665
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
666
+ ? "400px" // 1366x768: ширина контейнера
667
+ : (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
668
+ ? "400px" // 1280x720: ширина контейнера
669
+ : (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
670
+ ? "400px" // 1440x900: ширина контейнера
671
+ : isDesktopLayout // Десктопные разрешения
672
+ ? "400px" // Десктопные разрешения: ширина контейнера
673
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "400px" : "300px"
674
+ }}>
675
+ <>
676
+ <input
677
+ type="text"
678
+ placeholder="Type the missing word"
679
+ value={inputValue}
680
+ onChange={(e) => setInputValue(e.target.value)}
661
681
  style={{
662
- width: "100%",
663
- height: "100%",
664
- objectFit: "cover",
665
- display: "block",
682
+ ...styles.gmInput,
683
+ width: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
684
+ ? "170px" // iPhone SE: еще более компактная ширина
685
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
686
+ ? "220px" // iPhone до 14 Pro Max в landscape: компактная ширина
687
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
688
+ ? "220px" // iPhone XR в landscape: компактная ширина
689
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
690
+ ? "220px" // iPhone 12 Pro в landscape: компактная ширина
691
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
692
+ ? "250px" // 1366x766: ширина инпута
693
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
694
+ ? "250px" // 1366x768: ширина инпута
695
+ : (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
696
+ ? "250px" // 1280x720: ширина инпута
697
+ : (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
698
+ ? "250px" // 1440x900: ширина инпута
699
+ : isDesktopLayout // Десктопные разрешения
700
+ ? "250px" // Десктопные разрешения: ширина инпута
701
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "250px" : "auto",
702
+ padding: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
703
+ ? "5px 6px" // iPhone SE: еще более компактный padding
704
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
705
+ ? "6px 8px" // iPhone до 14 Pro Max в landscape: компактный padding
706
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
707
+ ? "6px 8px" // iPhone XR в landscape: компактный padding
708
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
709
+ ? "6px 8px" // iPhone 12 Pro в landscape: компактный padding
710
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
711
+ ? "10px 12px" // 1366x766: padding инпута
712
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
713
+ ? "10px 12px" // 1366x768: padding инпута
714
+ : (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
715
+ ? "10px 12px" // 1280x720: padding инпута
716
+ : (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
717
+ ? "10px 12px" // 1440x900: padding инпута
718
+ : isDesktopLayout // Десктопные разрешения
719
+ ? "10px 12px" // Десктопные разрешения: padding инпута
720
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "10px 12px" : "12px 16px",
721
+ fontSize: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
722
+ ? "10px" // iPhone SE: еще меньший шрифт
723
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
724
+ ? "11px" // iPhone до 14 Pro Max в landscape: компактный шрифт
725
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
726
+ ? "11px" // iPhone XR в landscape: компактный шрифт
727
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
728
+ ? "11px" // iPhone 12 Pro в landscape: компактный шрифт
729
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
730
+ ? "14px" // 1366x766: размер шрифта инпута
731
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
732
+ ? "14px" // 1366x768: размер шрифта инпута
733
+ : (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
734
+ ? "14px" // 1280x720: размер шрифта инпута
735
+ : (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
736
+ ? "14px" // 1440x900: размер шрифта инпута
737
+ : isDesktopLayout // Десктопные разрешения
738
+ ? "14px" // Десктопные разрешения: размер шрифта инпута
739
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "14px" : "16px",
740
+ flex: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 || (window.innerWidth === 1366 && window.innerHeight === 766) || (window.innerWidth === 1366 && window.innerHeight === 768) || (window.innerWidth === 1280 && window.innerHeight === 720) || (window.innerWidth === 1440 && window.innerHeight === 900) || isDesktopLayout ? "1" : "none"
666
741
  }}
667
742
  />
668
- )}
669
- </div>
670
- );
671
- })}
672
- </div>
673
- )}
674
-
675
- {/* Инпут и кнопки */}
676
- <div style={{ marginTop: 16, height: 80 }}>
677
- {phase === "guess" && !answered && (
678
- <div>
679
- <input
680
- type="text"
681
- placeholder="Type the missing word"
682
- value={inputValue}
683
- onChange={(e) => setInputValue(e.target.value)}
684
- style={styles.gmInput}
685
- />
686
- <button
687
- style={{ ...styles.gmButton, marginLeft: 8 }}
688
- onClick={checkAnswer}
689
- disabled={animating}
690
- >
691
- {animating ? "..." : "Check"}
743
+ <button
744
+ style={{
745
+ ...styles.gmButton,
746
+ marginLeft: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 || (window.innerWidth === 1366 && window.innerHeight === 766) || (window.innerWidth === 1366 && window.innerHeight === 768) || (window.innerWidth === 1280 && window.innerHeight === 720) || (window.innerWidth === 1440 && window.innerHeight === 900) || isDesktopLayout ? "8px" : "0",
747
+ padding: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
748
+ ? "5px 8px" // iPhone SE: еще более компактный padding
749
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
750
+ ? "6px 10px" // iPhone до 14 Pro Max в landscape: компактный padding
751
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
752
+ ? "6px 10px" // iPhone XR в landscape: компактный padding
753
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
754
+ ? "6px 10px" // iPhone 12 Pro в landscape: компактный padding
755
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
756
+ ? "10px 16px" // 1366x766: padding кнопки Check
757
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
758
+ ? "10px 16px" // 1366x768: padding кнопки Check
759
+ : (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
760
+ ? "10px 16px" // 1280x720: padding кнопки Check
761
+ : (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
762
+ ? "10px 16px" // 1440x900: padding кнопки Check
763
+ : isDesktopLayout // Десктопные разрешения
764
+ ? "10px 16px" // Десктопные разрешения: padding кнопки Check
765
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "10px 16px" : "12px 24px",
766
+ fontSize: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
767
+ ? "10px" // iPhone SE: еще меньший шрифт
768
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
769
+ ? "11px" // iPhone до 14 Pro Max в landscape: компактный шрифт
770
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
771
+ ? "11px" // iPhone XR в landscape: компактный шрифт
772
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
773
+ ? "11px" // iPhone 12 Pro в landscape: компактный шрифт
774
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
775
+ ? "14px" // 1366x766: размер шрифта кнопки Check
776
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
777
+ ? "14px" // 1366x768: размер шрифта кнопки Check
778
+ : (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
779
+ ? "14px" // 1280x720: размер шрифта кнопки Check
780
+ : (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
781
+ ? "14px" // 1440x900: размер шрифта кнопки Check
782
+ : isDesktopLayout // Десктопные разрешения
783
+ ? "14px" // Десктопные разрешения: размер шрифта кнопки Check
784
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "14px" : "16px",
785
+ minWidth: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
786
+ ? "45px" // iPhone SE: еще более компактная ширина
787
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
788
+ ? "55px" // iPhone до 14 Pro Max в landscape: компактная ширина
789
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
790
+ ? "55px" // iPhone XR в landscape: компактная ширина
791
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
792
+ ? "55px" // iPhone 12 Pro в landscape: компактная ширина
793
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
794
+ ? "80px" // 1366x766: минимальная ширина кнопки Check
795
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
796
+ ? "80px" // 1366x768: минимальная ширина кнопки Check
797
+ : (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
798
+ ? "80px" // 1280x720: минимальная ширина кнопки Check
799
+ : (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
800
+ ? "80px" // 1440x900: минимальная ширина кнопки Check
801
+ : isDesktopLayout // Десктопные разрешения
802
+ ? "80px" // Десктопные разрешения: минимальная ширина кнопки Check
803
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "80px" : "100px",
804
+ flexShrink: 0
805
+ }}
806
+ onClick={checkAnswer}
807
+ disabled={animating}
808
+ >
809
+ {animating ? "..." : "Check"}
810
+ </button>
811
+ </>
812
+ </div>
813
+ )}
814
+ {answered && (
815
+ <button style={{
816
+ ...styles.gmButton,
817
+ padding: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
818
+ ? "5px 8px" // iPhone SE: еще более компактный padding
819
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
820
+ ? "6px 10px" // iPhone до 14 Pro Max в landscape: компактный padding
821
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
822
+ ? "6px 10px" // iPhone XR в landscape: компактный padding
823
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
824
+ ? "6px 10px" // iPhone 12 Pro в landscape: компактный padding
825
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
826
+ ? "10px 16px" // 1366x766: padding кнопки Next round
827
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
828
+ ? "10px 16px" // 1366x768: padding кнопки Next round
829
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "10px 16px" : "12px 24px",
830
+ fontSize: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
831
+ ? "10px" // iPhone SE: еще меньший шрифт
832
+ : (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
833
+ ? "11px" // iPhone до 14 Pro Max в landscape: компактный шрифт
834
+ : (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
835
+ ? "11px" // iPhone XR в landscape: компактный шрифт
836
+ : (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
837
+ ? "11px" // iPhone 12 Pro в landscape: компактный шрифт
838
+ : (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
839
+ ? "14px" // 1366x766: размер шрифта кнопки Next round
840
+ : (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
841
+ ? "14px" // 1366x768: размер шрифта кнопки Next round
842
+ : (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "14px" : "16px"
843
+ }} onClick={nextRound}>
844
+ Next round
692
845
  </button>
693
- </div>
694
- )}
695
- {answered && (
696
- <button type="button" style={styles.gmButton} onClick={nextRound}>
697
- Next round
698
- </button>
699
- )}
846
+ )}
847
+ </div>
700
848
  </div>
701
- </div>
702
- )}
703
- </div>
849
+ )}
704
850
  </div>
705
- </div>,
706
- shadowRoot
707
- )}
851
+ </div>
852
+ </div>
708
853
  </div>
709
854
  );
710
- }
855
+ }