speakid-build-a-sentence 1.0.24 → 1.0.26

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.
@@ -1 +1 @@
1
- {"version":3,"file":"speakid-build-a-sentence.cjs.js","sources":["../src/Game.styles.ts","../src/hooks/useValidation.ts","../src/utils/accessibility.ts","../src/Game.tsx","../src/components/ErrorBoundary.tsx"],"sourcesContent":["import { CSSProperties } from \"react\";\n\n// ===== Добавляем анимации в <head> =====\nconst keyframes = `\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n \n @keyframes pulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n }\n \n @keyframes shake {\n 0%, 100% { transform: translateX(0); }\n 25% { transform: translateX(-5px); }\n 75% { transform: translateX(5px); }\n }\n \n @keyframes slideIn {\n from { transform: translateY(-20px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n \n @keyframes bounce {\n 0%, 20%, 53%, 80%, 100% { transform: translate3d(0,0,0); }\n 40%, 43% { transform: translate3d(0, -8px, 0); }\n 70% { transform: translate3d(0, -4px, 0); }\n 90% { transform: translate3d(0, -2px, 0); }\n }\n \n @keyframes glow {\n 0%, 100% { box-shadow: 0 0 5px rgba(76, 175, 80, 0.5); }\n 50% { box-shadow: 0 0 20px rgba(76, 175, 80, 0.8), 0 0 30px rgba(76, 175, 80, 0.6); }\n }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"game-keyframes\")) {\n const styleTag = document.createElement(\"style\");\n styleTag.id = \"game-keyframes\";\n styleTag.innerHTML = keyframes;\n document.head.appendChild(styleTag);\n}\n\n// ===== Анимации =====\nconst animations = {\n spin: {\n animation: \"spin 1.4s linear infinite\",\n },\n pulse: {\n animation: \"pulse 0.6s ease-in-out\",\n },\n shake: {\n animation: \"shake 0.4s ease-in-out\",\n },\n slideIn: {\n animation: \"slideIn 0.3s ease-out\",\n },\n bounce: {\n animation: \"bounce 0.6s ease-in-out\",\n },\n glow: {\n animation: \"glow 1s ease-in-out infinite\",\n },\n};\n\n// ===== Основные стили =====\nexport const styles: Record<string, CSSProperties> = {\n gmCenterScreen: {\n position: \"relative\",\n zIndex: 1,\n // use percent so the component fits inside the centered square container\n // (100vh caused overflow on tablets because it measured the viewport, not the container)\n minHeight: \"100%\",\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n textAlign: \"center\",\n color: \"#1f2937\",\n padding: \"24px 16px\",\n boxSizing: \"border-box\",\n transform: \"translateY(20px)\", // чуть вниз, чтобы компенсировать логотип\n },\n\n gmHeadline1: {\n fontWeight: 700,\n fontSize: \"clamp(28px, 4vw, 40px)\",\n lineHeight: \"110%\",\n },\n\n gmHeadline2: {\n fontWeight: 600,\n fontSize: \"24px\",\n lineHeight: \"120%\",\n },\n\n gmHeadline3: {\n fontWeight: 600,\n fontSize: \"18px\",\n lineHeight: \"130%\",\n },\n\n gmBodyL: {\n fontWeight: 400,\n fontSize: \"18px\",\n lineHeight: \"140%\",\n },\n\n gmBodyM: {\n fontWeight: 400,\n fontSize: \"16px\",\n lineHeight: \"140%\",\n },\n\n gmBodyS: {\n fontWeight: 400,\n fontSize: \"14px\",\n lineHeight: \"140%\",\n color: \"#6b7280\",\n },\n\n gmButton: {\n fontFamily:\n '\"Geist\", system-ui, -apple-system, \"Segoe UI\", Roboto, Arial, \"Noto Sans\"',\n fontWeight: 600,\n fontSize: \"16px\",\n padding: \"10px 16px\",\n borderRadius: \"12px\",\n border: \"1px solid #e5e7eb\",\n background: \"#ec4c44\",\n color: \"#ffffff\",\n cursor: \"pointer\",\n boxShadow: \"0 6px 18px rgba(236, 76, 68, .18)\",\n transition:\n \"transform .06s ease, box-shadow .2s ease, background .2s ease, opacity .2s ease\",\n },\n\n gmButtonActive: {\n background: \"#333\",\n color: \"#fff\",\n },\n\n gmGameLayout: {\n position: \"relative\",\n width: \"100%\",\n // allow the layout to expand within the square container for tablets/laptops\n maxWidth: \"none\",\n // should fill the parent square, not the full viewport\n minHeight: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n textAlign: \"center\",\n color: \"#1f2937\",\n padding: \"16px 8px\",\n margin: \"0 auto\",\n },\n\n gmGrid: {\n width: \"100%\",\n // let the grid use all available space; sizing is controlled in Game.tsx\n maxWidth: \"none\",\n margin: \"0 auto\",\n display: \"grid\",\n // defaults; overridden responsively in Game.tsx\n gridTemplateColumns: \"repeat(3, 210px)\",\n gridAutoRows: \"210px\",\n gap: \"20px\",\n justifyContent: \"center\",\n },\n\n gmCard: {\n border: \"1px solid #e5e7eb\",\n borderRadius: \"16px\",\n background: \"#f9f9f9\",\n aspectRatio: \"1 / 1\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n overflow: \"hidden\",\n transition:\n \"transform .2s ease, box-shadow .2s ease, border-color .2s ease\",\n },\n\n gmCorrect: {\n border: \"2px solid #10b981\",\n boxShadow: \"0 0 18px rgba(16,185,129,.45)\",\n background: \"#ecfdf5\",\n },\n\n gmWrong: {\n border: \"2px solid #ec4c44\",\n boxShadow: \"0 0 18px rgba(236,76,68,.35)\",\n background: \"#fef2f2\",\n },\n\n gmInput: {\n padding: \"6px 10px\",\n borderRadius: \"6px\",\n border: \"1px solid #ccc\",\n fontSize: \"16px\",\n fontFamily: '\"Geist\", system-ui',\n width: \"160px\",\n },\n\n gmTable: {\n marginTop: \"20px\",\n marginBottom: \"32px\",\n borderCollapse: \"collapse\",\n width: \"100%\",\n maxWidth: \"520px\",\n tableLayout: \"fixed\",\n textAlign: \"center\",\n },\n\n gmTableCell: {\n padding: \"8px 12px\",\n borderBottom: \"1px solid #e5e7eb\",\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n\n // ✅ Обновлено только для качества логотипа (размер как в whats-missing)\n gmLogoFixed: {\n position: \"absolute\",\n top: \"16px\",\n left: \"16px\",\n width: \"auto\",\n zIndex: 10,\n pointerEvents: \"none\",\n background: \"transparent\",\n transform: \"none\",\n willChange: \"auto\",\n },\n\n gmLogoImg: {\n height: \"28px\",\n width: \"auto\",\n background: \"transparent\",\n objectFit: \"contain\",\n imageRendering: \"auto\",\n transform: \"translateZ(0)\",\n backfaceVisibility: \"hidden\",\n WebkitFontSmoothing: \"antialiased\",\n display: \"block\",\n },\n\n gmLogoFallback: {\n height: \"28px\",\n width: \"auto\",\n background: \"transparent\",\n color: \"#ec4c44\",\n fontSize: \"16px\",\n fontWeight: 700,\n display: \"block\",\n },\n\n gmReadyWrapper: {\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n // use percent so the wrapper scales with the container square\n height: \"60%\",\n gap: \"16px\",\n },\n\n gmHourglass: {\n fontSize: \"42px\",\n ...animations.spin,\n },\n\n // ===== Анимационные стили =====\n ...animations,\n};\n","import { useState, useCallback } from 'react';\n\nexport interface ValidationError {\n type: 'length' | 'characters' | 'empty' | 'duplicate';\n message: string;\n}\n\nexport interface ValidationResult {\n isValid: boolean;\n errors: ValidationError[];\n}\n\nexport const useValidation = () => {\n const [errors, setErrors] = useState<ValidationError[]>([]);\n\n const validateSentence = useCallback((sentence: string, index: number, allSentences: string[]): ValidationResult => {\n const newErrors: ValidationError[] = [];\n\n // Проверка на пустоту\n if (!sentence.trim()) {\n newErrors.push({\n type: 'empty',\n message: 'Sentence cannot be empty'\n });\n }\n\n // Проверка длины\n if (sentence.length > 41) {\n newErrors.push({\n type: 'length',\n message: `Sentence is too long (${sentence.length}/41 characters)`\n });\n }\n\n // Проверка символов (только латиница)\n const latinRegex = /^[a-zA-Z0-9\\s.,!?;:'\"-]*$/;\n if (sentence && !latinRegex.test(sentence)) {\n newErrors.push({\n type: 'characters',\n message: 'Only Latin characters, numbers, spaces and punctuation are allowed'\n });\n }\n\n // Проверка на дубликаты\n const duplicateIndex = allSentences.findIndex((s, i) => i !== index && s.toLowerCase().trim() === sentence.toLowerCase().trim());\n if (duplicateIndex !== -1) {\n newErrors.push({\n type: 'duplicate',\n message: `Duplicate sentence (same as sentence ${duplicateIndex + 1})`\n });\n }\n\n setErrors(newErrors);\n return {\n isValid: newErrors.length === 0,\n errors: newErrors\n };\n }, []);\n\n const validateAllSentences = useCallback((sentences: string[]): ValidationResult => {\n const allErrors: ValidationError[] = [];\n \n sentences.forEach((sentence, index) => {\n const result = validateSentence(sentence, index, sentences);\n allErrors.push(...result.errors.map(error => ({\n ...error,\n message: `Sentence ${index + 1}: ${error.message}`\n })));\n });\n\n return {\n isValid: allErrors.length === 0,\n errors: allErrors\n };\n }, [validateSentence]);\n\n const clearErrors = useCallback(() => {\n setErrors([]);\n }, []);\n\n return {\n errors,\n validateSentence,\n validateAllSentences,\n clearErrors,\n };\n};\n\n","// Утилиты для улучшения доступности\n\nexport const createAriaLabel = (action: string, word?: string, context?: string): string => {\n if (word && context) {\n return `${action} word \"${word}\" ${context}`;\n }\n if (word) {\n return `${action} word \"${word}\"`;\n }\n return action;\n};\n\nexport const getRoleForElement = (type: 'button' | 'draggable' | 'droppable' | 'input'): string => {\n const roles = {\n button: 'button',\n draggable: 'button',\n droppable: 'region',\n input: 'textbox'\n };\n return roles[type];\n};\n\nexport const handleKeyDown = (\n event: React.KeyboardEvent,\n action: () => void,\n allowedKeys: string[] = ['Enter', ' ']\n) => {\n if (allowedKeys.includes(event.key)) {\n event.preventDefault();\n action();\n }\n};\n\nexport const announceToScreenReader = (message: string) => {\n // Создаем временный элемент для объявления\n const announcement = document.createElement('div');\n announcement.setAttribute('aria-live', 'polite');\n announcement.setAttribute('aria-atomic', 'true');\n announcement.style.position = 'absolute';\n announcement.style.left = '-10000px';\n announcement.style.width = '1px';\n announcement.style.height = '1px';\n announcement.style.overflow = 'hidden';\n \n document.body.appendChild(announcement);\n announcement.textContent = message;\n \n // Удаляем элемент после объявления\n setTimeout(() => {\n document.body.removeChild(announcement);\n }, 1000);\n};\n\nexport const getFocusableElements = (container: HTMLElement): HTMLElement[] => {\n const focusableSelectors = [\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[role=\"button\"]:not([disabled])'\n ].join(', ');\n \n return Array.from(container.querySelectorAll(focusableSelectors));\n};\n\nexport const trapFocus = (container: HTMLElement) => {\n const focusableElements = getFocusableElements(container);\n \n if (focusableElements.length === 0) return;\n \n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n \n const handleTabKey = (e: KeyboardEvent) => {\n if (e.key === 'Tab') {\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n lastElement.focus();\n e.preventDefault();\n }\n } else {\n if (document.activeElement === lastElement) {\n firstElement.focus();\n e.preventDefault();\n }\n }\n }\n };\n \n container.addEventListener('keydown', handleTabKey);\n \n // Фокусируем первый элемент\n firstElement.focus();\n \n // Возвращаем функцию для очистки\n return () => {\n container.removeEventListener('keydown', handleTabKey);\n };\n};\n\n","// ======================\n// MAGIC SENTENCE GAME (SPEAKID)\n// React 18 + TypeScript + Vite\n// Адаптивная верстка для всех устройств\n// ======================\n\nimport * as React from \"react\";\nimport { useState, useEffect, useRef, useMemo } from \"react\";\nimport { styles } from \"./Game.styles\";\nimport { ErrorBoundary } from \"./components/ErrorBoundary\";\nimport { useValidation } from \"./hooks/useValidation\";\nimport { createAriaLabel, handleKeyDown, announceToScreenReader } from \"./utils/accessibility\";\n\ntype Stage = \"select\" | \"time\" | \"type\" | \"getready\" | \"play\" | \"results\";\ntype Token = { id: string; text: string };\n\n// ✅ базовый reset\nconst globalReset = () => {\n const style = document.createElement(\"style\");\n style.id = \"magic-sentence-reset\"; // ✅ Добавляем ID для удаления\n \n style.textContent = `\n #magic-sentence-root, #magic-sentence-root * {\n box-sizing: border-box;\n font-family: \"Geist\", system-ui, -apple-system, \"Segoe UI\", Roboto, Arial, sans-serif;\n }\n #magic-sentence-root img {\n max-width: 100%;\n height: auto;\n display: block;\n user-select: none;\n }\n html, body {\n margin: 0 !important;\n padding: 0 !important;\n width: 100% !important;\n height: 100% !important;\n overflow: hidden !important;\n zoom: 1 !important; /* ✅ защита от подзума */\n }\n #root {\n margin: 0 !important;\n padding: 0 !important;\n width: 100% !important;\n height: 100% !important;\n overflow: hidden !important;\n }\n `;\n \n // ✅ Удаляем старый стиль, если он есть\n const existingStyle = document.getElementById(\"magic-sentence-reset\");\n if (existingStyle) {\n existingStyle.remove();\n }\n \n document.head.appendChild(style);\n};\n\nconst shuffle = (arr: string[]) => [...arr].sort(() => Math.random() - 0.5);\n\nexport interface GameProps {\n logoUrl?: string;\n showLogo?: boolean;\n}\n\nfunction GameComponent(props: GameProps = {}) {\n const { logoUrl, showLogo = true } = props;\n const containerRef = useRef<HTMLDivElement>(null);\n \n // Новые хуки\n const { validateAllSentences, errors: validationErrors } = useValidation();\n\n // Функция для определения нужно ли переносить слова\n const shouldWrapWords = () => {\n // Переносим слова на всех мобильных устройствах\n return isMobile || window.innerWidth < 768;\n };\n\n // Универсальная функция для определения мобильных устройств\n const isMobileDevice = () => {\n return isMobile || \n window.innerWidth < 768 || \n (window.innerWidth >= 320 && window.innerWidth <= 932 && window.innerHeight >= 390 && window.innerHeight <= 932);\n };\n\n // Вспомогательная функция для мобильных размеров кнопок\n const getMobileButtonStyles = (type: 'small' | 'medium' | 'large' = 'medium') => {\n if (!isMobileDevice()) {\n return {\n padding: \"12px 24px\",\n fontSize: \"16px\",\n minWidth: \"auto\"\n };\n }\n\n switch (type) {\n case 'small':\n return {\n padding: \"4px 6px\",\n fontSize: \"9px\",\n minWidth: \"40px\"\n };\n case 'medium':\n return {\n padding: \"5px 8px\",\n fontSize: \"10px\",\n minWidth: \"50px\"\n };\n case 'large':\n return {\n padding: \"6px 10px\",\n fontSize: \"11px\",\n minWidth: \"60px\"\n };\n }\n };\n\n useEffect(() => {\n globalReset();\n return () => {\n // ✅ Восстанавливаем overflow на html и body\n document.documentElement.style.overflow = \"\";\n document.body.style.overflow = \"\";\n \n // ✅ Удаляем наш style элемент\n const style = document.getElementById(\"magic-sentence-reset\");\n if (style) {\n style.remove();\n }\n };\n }, []);\n\n const [stage, setStage] = useState<Stage>(\"select\");\n const [rounds, setRounds] = useState<number | null>(null);\n const [timePerRound, setTimePerRound] = useState<number | null>(null);\n const [sentences, setSentences] = useState<string[]>([]);\n const [currentRound, setCurrentRound] = useState(0);\n const [words, setWords] = useState<Token[]>([]);\n const [selected, setSelected] = useState<Token[]>([]);\n const [timeLeft, setTimeLeft] = useState(20);\n const [score, setScore] = useState(0);\n const [countdown, setCountdown] = useState<number | null>(null);\n const [resultType, setResultType] = useState<\"correct\" | \"almost\" | \"wrong\" | null>(null);\n const [timeExpired, setTimeExpired] = useState(false);\n const [isCorrect, setIsCorrect] = useState(false);\n const [bestScore, setBestScore] = useState<number>(\n Number(localStorage.getItem(\"magicSentenceBest\")) || 0\n );\n const timerRef = useRef<number | null>(null);\n const roundsRef = useRef<number | null>(null);\n const currentRoundRef = useRef<number>(0);\n const [hover, setHover] = useState<{ list: ListName | null; id: string | null; side: \"left\" | \"right\" | null }>({ list: null, id: null, side: null });\n\n // Адаптивность\n const [isMobile, setIsMobile] = useState(false);\n const [scale, setScale] = useState(1);\n const [containerSize, setContainerSize] = useState<number | null>(null);\n const [isDesktopLayout, setIsDesktopLayout] = useState(false);\n const [isIPadMiniPortrait, setIsIPadMiniPortrait] = useState(false);\n const [isIPadMiniLandscape, setIsIPadMiniLandscape] = useState(false);\n const [isIPadAirPortrait, setIsIPadAirPortrait] = useState(false);\n const [isIPadAirLandscape, setIsIPadAirLandscape] = useState(false);\n const [isSurfaceDuoPortrait, setIsSurfaceDuoPortrait] = useState(false);\n const [isSurfaceDuoLandscape, setIsSurfaceDuoLandscape] = useState(false);\n const [isIPadProPortrait, setIsIPadProPortrait] = useState(false);\n const [isIPadProLandscape, setIsIPadProLandscape] = useState(false);\n const [isHorizontalLayout, setIsHorizontalLayout] = useState(false);\n const [isWideScreen, setIsWideScreen] = useState(false);\n\n // ✅ адаптив под мобилки, планшеты и десктоп\n useEffect(() => {\n const resize = () => {\n const width = window.innerWidth;\n const height = window.innerHeight;\n const mobile = width < 768 || (width === 926 && height === 428) || (width === 932 && height === 430);\n const isLandscape = (width > height && mobile) || (width === 926 && height === 428) || (width === 932 && height === 430);\n const isSmallHeight = height < 700;\n const isWideScreen = width / height > 1.8; // ✅ Широкие экраны\n \n // iPad размеры\n const isIPadMiniPortrait = width === 768 && height === 1024;\n const isIPadMiniLandscape = width === 1024 && height === 768;\n const isIPadAirPortrait = width === 820 && height === 1180;\n const isIPadAirLandscape = width === 1180 && height === 820;\n \n // Surface DUO размеры\n const isSurfaceDuoPortrait = width === 540 && height === 720;\n const isSurfaceDuoLandscape = width === 720 && height === 540;\n \n // iPad Pro размеры\n const isIPadProPortrait = width === 1024 && height === 1366;\n const isIPadProLandscape = width === 1366 && height === 1024;\n \n // Десктопные разрешения\n const desktopLayout = width >= 1200 && height >= 600 && !mobile;\n setIsDesktopLayout(desktopLayout);\n \n // Установка состояний для iPad и Surface DUO\n setIsIPadMiniPortrait(isIPadMiniPortrait);\n setIsIPadMiniLandscape(isIPadMiniLandscape);\n setIsIPadAirPortrait(isIPadAirPortrait);\n setIsIPadAirLandscape(isIPadAirLandscape);\n setIsSurfaceDuoPortrait(isSurfaceDuoPortrait);\n setIsSurfaceDuoLandscape(isSurfaceDuoLandscape);\n setIsIPadProPortrait(isIPadProPortrait);\n setIsIPadProLandscape(isIPadProLandscape);\n setIsWideScreen(isWideScreen); // ✅ Сохраняем состояние для широких экранов\n \n // ✅ Вычисляем горизонтальный layout ОДИН РАЗ\n const isHorizontal = \n (mobile && width > height) || \n mobile || // ✅ ВСЕ мобильные устройства (включая portrait)\n height < 700 || \n isWideScreen || // ✅ Широкие экраны\n (width === 1366 && height === 766) || \n (width === 1366 && height === 768) || \n (width === 1280 && height === 720) || \n (width === 1440 && height === 900) || \n isIPadMiniPortrait || \n isIPadMiniLandscape || \n isIPadAirPortrait || \n isIPadAirLandscape || \n isSurfaceDuoPortrait || \n isSurfaceDuoLandscape || \n isIPadProPortrait || \n isIPadProLandscape || \n desktopLayout;\n setIsHorizontalLayout(isHorizontal);\n \n setIsMobile(mobile);\n\n // ✅ Адаптивные размеры контейнера\n if (mobile) {\n setContainerSize(null);\n setScale(1);\n } else if (isSmallHeight) {\n setContainerSize(null);\n setScale(1);\n } else if (isWideScreen) {\n // ✅ Широкие экраны: используем стандартный размер но уменьшаем масштаб\n const minSize = 400;\n const maxSize = 1200;\n const finalSize = Math.min(1000, Math.min(width, height) * 0.9);\n setContainerSize(finalSize);\n setScale(0.85); // Уменьшаем масштаб на 15%\n } else {\n const minSize = 400;\n const maxSize = 1200;\n const finalSize = Math.min(1000, Math.min(width, height) * 0.9);\n setContainerSize(finalSize);\n setScale(1);\n }\n };\n resize();\n window.addEventListener(\"resize\", resize);\n return () => window.removeEventListener(\"resize\", resize);\n }, []);\n\n // =========================\n // DND HELPERS\n // =========================\n type ListName = \"bank\" | \"selected\";\n type DragPayload = { from: ListName; id: string };\n\n const moveToken = (\n from: ListName,\n to: ListName,\n id: string,\n insertIndex: number | null\n ) => {\n // Блокируем перемещение если время истекло\n if (timeExpired) return;\n\n let nextWords = [...words];\n let nextSelected = [...selected];\n\n const takeFrom = from === \"bank\" ? nextWords : nextSelected;\n const putTo = to === \"bank\" ? nextWords : nextSelected;\n\n const idx = takeFrom.findIndex((t) => t.id === id);\n if (idx === -1) return;\n const [token] = takeFrom.splice(idx, 1);\n\n let targetIndex = insertIndex;\n if (\n from === to &&\n targetIndex !== null &&\n targetIndex !== undefined\n ) {\n if (targetIndex > idx) targetIndex = targetIndex - 1;\n }\n\n if (\n targetIndex === null ||\n targetIndex === undefined ||\n targetIndex < 0 ||\n targetIndex > putTo.length\n ) {\n putTo.push(token);\n } else {\n putTo.splice(targetIndex, 0, token);\n }\n\n if (from === \"bank\") nextWords = takeFrom; else nextSelected = takeFrom;\n if (to === \"bank\") nextWords = putTo; else nextSelected = putTo;\n\n setWords(nextWords);\n setSelected(nextSelected);\n };\n\n const handleDropTo = (\n e: React.DragEvent,\n to: ListName,\n insertIndex: number | null\n ) => {\n e.preventDefault();\n \n // Блокируем drop если время истекло\n if (timeExpired) {\n setHover({ list: null, id: null, side: null });\n return;\n }\n\n const raw = e.dataTransfer.getData(\"application/x-token\") ||\n (() => {\n const fallbackId = e.dataTransfer.getData(\"text/plain\");\n if (!fallbackId) return \"\";\n const inBank = words.some((t) => t.id === fallbackId);\n const inSelected = selected.some((t) => t.id === fallbackId);\n const from: ListName | null = inBank ? \"bank\" : inSelected ? \"selected\" : null;\n return from ? JSON.stringify({ from, id: fallbackId }) : \"\";\n })();\n if (!raw) return;\n try {\n const payload = JSON.parse(raw) as DragPayload;\n if (!payload || !payload.id || !payload.from) return;\n moveToken(payload.from, to, payload.id, insertIndex);\n } catch {\n // ignore bad payload\n }\n setHover({ list: null, id: null, side: null });\n };\n\n // =========================\n // START / SETUP\n // =========================\n const handleSelect = (num: number) => {\n setRounds(num);\n roundsRef.current = num;\n setSentences(Array(num).fill(\"\"));\n setStage(\"time\");\n };\n\n const handleTimeSelect = (time: number) => {\n setTimePerRound(time);\n setStage(\"type\");\n };\n\n const handleChange = (index: number, value: string) => {\n // Ограничение на 41 символ (включая пробелы и точку)\n if (value.length > 41) {\n return;\n }\n \n // Проверка на латинские символы (a-z, A-Z, пробелы, цифры, знаки препинания)\n const latinRegex = /^[a-zA-Z0-9\\s.,!?;:'\"-]*$/;\n if (value && !latinRegex.test(value)) {\n return; // Не сохраняем, если есть нелатинские символы\n }\n \n const updated = [...sentences];\n updated[index] = value;\n setSentences(updated);\n \n // Валидация при вводе\n const validation = validateAllSentences(updated);\n if (!validation.isValid) {\n console.warn('Validation errors:', validation.errors);\n }\n };\n\n const normalizeSentence = (s: string) => s.trim().replace(/\\s+/g, \" \");\n\n // Адаптивный размер шрифта в зависимости от количества слов\n const getAdaptiveFontSize = (wordCount: number) => {\n if (wordCount <= 3) return 20;\n if (wordCount <= 5) return 18;\n if (wordCount <= 7) return 16;\n if (wordCount <= 9) return 14;\n return 12;\n };\n\n const startGame = () => {\n const hasEmpty = sentences.some((s) => s.trim().length === 0);\n if (hasEmpty) {\n return;\n }\n setSentences((prev) => prev.map((s) => normalizeSentence(s)));\n setScore(0);\n setCurrentRound(0);\n currentRoundRef.current = 0;\n setCountdown(null); // Убираем цифровой отсчет\n setStage(\"getready\");\n };\n\n useEffect(() => {\n if (stage === \"getready\") {\n const t = setTimeout(() => startRound(0), 3000);\n return () => clearTimeout(t);\n }\n }, [stage]);\n\n const startRound = (index: number) => {\n const target = sentences[index];\n if (!target) return;\n const shuffled = shuffle(target\n .trim()\n .split(/\\s+/)\n .filter(Boolean)\n );\n const tokens: Token[] = shuffled.map((w, i) => ({\n id: `${Date.now()}-${index}-${i}-${Math.random().toString(36).slice(2)}`,\n text: w,\n }));\n setWords(tokens);\n setSelected([]);\n setCurrentRound(index);\n currentRoundRef.current = index;\n setTimeLeft(timePerRound || 20); // Используем выбранное время\n setResultType(null); // Сбрасываем результат\n setTimeExpired(false); // Сбрасываем состояние истечения времени\n setIsCorrect(false); // Сбрасываем флаг правильного ответа\n setStage(\"play\");\n };\n\n // Убираем автоматическую проверку во время сборки\n // Проверка происходит только при нажатии NEXT или при истечении времени\n\n // TIMER\n useEffect(() => {\n if (stage === \"play\" && !timeExpired) {\n if (timerRef.current !== null) window.clearTimeout(timerRef.current);\n if (timeLeft > 0) {\n timerRef.current = window.setTimeout(() => setTimeLeft((t) => t - 1), 1000);\n } else {\n // Время истекло - всегда проверяем предложение\n setTimeExpired(true);\n const correct = sentences[currentRound];\n if (!correct) {\n setResultType(null);\n return;\n }\n\n const correctWords = correct.trim().split(/\\s+/);\n const selectedTexts = selected.map((t) => t.text);\n \n // Всегда считаем ошибки, даже если выбрано меньше слов\n // Считаем ошибки: недостающие слова + лишние слова + неправильный порядок\n const missingWords = correctWords.filter(word => !selectedTexts.includes(word)).length;\n const extraWords = selectedTexts.filter(word => !correctWords.includes(word)).length;\n \n // Неправильный порядок: считаем только для тех слов, которые есть в обоих массивах\n const wrongOrder = correctWords.filter((w, i) => {\n // Если слово есть в выбранных, проверяем его позицию\n if (selectedTexts.includes(w)) {\n return selectedTexts[i] !== w;\n }\n // Если слова нет в выбранных, не считаем это как ошибку порядка (это уже missingWords)\n return false;\n }).length;\n \n // Общее количество ошибок = недостающие + лишние + неправильный порядок\n const errors = missingWords + extraWords + wrongOrder;\n\n // Показываем результат и засчитываем очки\n if (errors === 0) {\n setResultType(\"correct\");\n if (!isCorrect) {\n setIsCorrect(true);\n setScore((s) => s + 1);\n }\n playSound(\"correct\");\n announceToScreenReader(\"Correct! Well done!\");\n } else if (errors === 1) {\n setResultType(\"almost\");\n setScore((s) => s + 0.5);\n playSound(\"half\");\n announceToScreenReader(\"Almost correct! Just one mistake.\");\n } else {\n setResultType(\"wrong\");\n playSound(\"wrong\");\n announceToScreenReader(\"Not quite right. Keep trying!\");\n }\n }\n }\n return () => {\n if (timerRef.current !== null) window.clearTimeout(timerRef.current);\n };\n }, [stage, timeLeft, timeExpired, sentences, currentRound, selected, isCorrect]);\n\n // =========================\n // SCORING / NEXT ROUND\n // =========================\n const handleNext = (manual = true) => {\n // Если это ручное нажатие NEXT\n if (manual) {\n // Если время уже истекло, проверка уже была выполнена в TIMER\n // Просто переходим к следующему раунду\n if (timeExpired) {\n if (currentRound + 1 < (rounds || 0)) {\n startRound(currentRound + 1);\n } else {\n setStage(\"results\");\n setTimeout(() => triggerConfetti(), 600);\n }\n return;\n }\n\n // Если время не истекло, проверяем предложение вручную\n const correct = sentences[currentRound];\n if (!correct) {\n // Если нет правильного предложения, просто переходим дальше\n if (currentRound + 1 < (rounds || 0)) {\n startRound(currentRound + 1);\n } else {\n setStage(\"results\");\n setTimeout(() => triggerConfetti(), 600);\n }\n return;\n }\n\n const correctWords = correct.trim().split(/\\s+/);\n const selectedTexts = selected.map((t) => t.text);\n \n // Всегда считаем ошибки, даже если выбрано меньше слов\n // Считаем ошибки: недостающие слова + лишние слова + неправильный порядок\n const missingWords = correctWords.filter(word => !selectedTexts.includes(word)).length;\n const extraWords = selectedTexts.filter(word => !correctWords.includes(word)).length;\n \n // Неправильный порядок: считаем только для тех слов, которые есть в обоих массивах\n const wrongOrder = correctWords.filter((w, i) => {\n // Если слово есть в выбранных, проверяем его позицию\n if (selectedTexts.includes(w)) {\n return selectedTexts[i] !== w;\n }\n // Если слова нет в выбранных, не считаем это как ошибку порядка (это уже missingWords)\n return false;\n }).length;\n \n // Общее количество ошибок = недостающие + лишние + неправильный порядок\n const errors = missingWords + extraWords + wrongOrder;\n\n // Устанавливаем результат проверки и засчитываем очки\n if (errors === 0) {\n setScore((s) => s + 1);\n setResultType(\"correct\");\n setIsCorrect(true);\n playSound(\"correct\");\n announceToScreenReader(\"Correct! Well done!\");\n } else if (errors === 1) {\n setScore((s) => s + 0.5);\n setResultType(\"almost\");\n playSound(\"half\");\n announceToScreenReader(\"Almost correct! Just one mistake.\");\n } else {\n setResultType(\"wrong\");\n playSound(\"wrong\");\n announceToScreenReader(\"Not quite right. Keep trying!\");\n }\n\n // Переходим к следующему раунду после проверки\n if (currentRound + 1 < (rounds || 0)) {\n setTimeout(() => startRound(currentRound + 1), 800);\n } else {\n setTimeout(() => {\n setStage(\"results\");\n setTimeout(() => triggerConfetti(), 600);\n }, 800);\n }\n }\n };\n\n // =========================\n // SAVE BEST SCORE\n // =========================\n useEffect(() => {\n if (stage === \"results\" && score > bestScore) {\n setBestScore(score);\n localStorage.setItem(\"magicSentenceBest\", String(score));\n }\n }, [stage, score, bestScore]);\n\n // =========================\n // SOUND EFFECTS\n // =========================\n const playSound = (type: \"start\" | \"click\" | \"correct\" | \"half\" | \"wrong\") => {\n const ctx = new (window.AudioContext || (window as any).webkitAudioContext)();\n const osc = ctx.createOscillator();\n const gain = ctx.createGain();\n osc.connect(gain);\n gain.connect(ctx.destination);\n\n switch (type) {\n case \"start\": osc.frequency.value = 500; break;\n case \"click\": osc.frequency.value = 800; break;\n case \"correct\": osc.frequency.value = 1000; break;\n case \"half\": osc.frequency.value = 700; break;\n case \"wrong\": osc.frequency.value = 200; break;\n }\n gain.gain.setValueAtTime(0.1, ctx.currentTime);\n osc.start();\n osc.stop(ctx.currentTime + 0.2);\n };\n\n // =========================\n // CONFETTI EFFECT\n // =========================\n const triggerConfetti = () => {\n const duration = 2500;\n const end = Date.now() + duration;\n const colors = [\"#ec4c44\", \"#f7c948\", \"#6fcf97\", \"#56ccf2\", \"#bb6bd9\"];\n\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\")!;\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n canvas.style.position = \"fixed\";\n canvas.style.top = \"0\";\n canvas.style.left = \"0\";\n canvas.style.pointerEvents = \"none\";\n document.body.appendChild(canvas);\n\n const pieces = Array.from({ length: 100 }).map(() => ({\n x: Math.random() * canvas.width,\n y: Math.random() * canvas.height - canvas.height,\n size: 6 + Math.random() * 6,\n color: colors[Math.floor(Math.random() * colors.length)],\n speed: 2 + Math.random() * 4,\n tilt: Math.random() * 2 * Math.PI,\n }));\n\n const draw = () => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n pieces.forEach((p) => {\n ctx.fillStyle = p.color;\n ctx.beginPath();\n ctx.ellipse(p.x, p.y, p.size, p.size / 2, p.tilt, 0, 2 * Math.PI);\n ctx.fill();\n p.y += p.speed;\n p.x += Math.sin(p.tilt);\n });\n if (Date.now() < end) requestAnimationFrame(draw);\n else document.body.removeChild(canvas);\n };\n draw();\n };\n\n // =========================\n // RENDER SCREENS\n // =========================\n const renderSelect = () => (\n <div style={styles.gmCenterScreen}>\n <h1 style={styles.gmHeadline1}>MAGIC SENTENCE</h1>\n <p style={styles.gmBodyM}>Select number of rounds</p>\n <div style={{ \n display: \"flex\", \n gap: isMobileDevice() ? \"8px\" : \"16px\",\n justifyContent: \"center\" \n }}>\n {[3, 4, 5].map((num) => (\n <button \n key={num} \n onClick={() => handleSelect(num)} \n style={{\n ...styles.gmButton,\n ...getMobileButtonStyles('medium')\n }}\n >\n {num} ROUNDS\n </button>\n ))}\n </div>\n </div>\n );\n\n const renderTime = () => (\n <div style={styles.gmCenterScreen}>\n <h1 style={styles.gmHeadline1}>MAGIC SENTENCE</h1>\n <p style={styles.gmBodyM}>Select time per round</p>\n <div style={{ \n display: \"flex\", \n gap: isMobileDevice() ? \"8px\" : \"16px\",\n justifyContent: \"center\" \n }}>\n {[15, 20, 30].map((time) => (\n <button \n key={time} \n onClick={() => handleTimeSelect(time)} \n style={{\n ...styles.gmButton,\n ...getMobileButtonStyles('medium')\n }}\n >\n {time}s\n </button>\n ))}\n </div>\n </div>\n );\n\n const renderType = () => (\n <div style={styles.gmCenterScreen}>\n <h2 style={{...styles.gmBodyM, marginBottom: \"0px\"}}>\n Type down {rounds} sentence{rounds && rounds > 1 ? \"s\" : \"\"} for your student\n </h2>\n <p style={{...styles.gmBodyS, marginBottom: \"16px\", marginTop: \"0px\", color: \"#6b7280\"}}>\n Maximum 41 characters per sentence\n </p>\n <div style={{ \n display: \"flex\", \n flexDirection: \"column\", \n gap: 12, \n width: \"auto\", // Автоматическая ширина по содержимому\n minWidth: \"fit-content\", // Минимальная ширина по содержимому\n maxWidth: \"600px\" // Ограничиваем максимальную ширину\n }}>\n {sentences.map((text, i) => (\n <input\n key={i}\n value={text}\n placeholder={`Sentence ${i + 1}`}\n onChange={(e) => handleChange(i, e.target.value)}\n style={{\n ...styles.gmInput,\n padding: isMobileDevice() ? \"8px 12px\" : \"12px 16px\",\n fontSize: isMobileDevice() ? \"14px\" : \"16px\",\n width: \"100%\", // Поля теперь будут шире благодаря увеличенной ширине контейнера\n textAlign: \"center\" // Центрируем placeholder текст\n }}\n />\n ))}\n </div>\n <button\n onClick={startGame}\n disabled={sentences.some((s) => s.trim().length === 0)}\n style={{\n ...styles.gmButton,\n marginTop: 30,\n background: sentences.some((s) => s.trim().length === 0) ? \"#ccc\" : \"#ec4c44\",\n cursor: sentences.some((s) => s.trim().length === 0) ? \"not-allowed\" : \"pointer\",\n ...getMobileButtonStyles('large')\n }}\n >\n PLAY\n </button>\n </div>\n );\n\n const renderGetReady = () => (\n <div style={styles.gmReadyWrapper}>\n <h1 style={{ \n ...styles.gmHeadline1,\n color: \"#ec4c44\"\n }}>\n GET READY\n </h1>\n <div style={styles.gmHourglass}>⏳</div>\n </div>\n );\n\n const renderPlay = () => (\n <div style={styles.gmGameLayout}>\n <h2 style={{ \n marginBottom: isMobileDevice() ? \"5px\" : \"10px\",\n fontSize: isMobileDevice() ? \"16px\" : \"20px\"\n }}>\n Round {currentRound + 1}/{rounds} — {timeExpired ? \"TIME'S UP!\" : `Time: ${timeLeft}s`}\n </h2>\n\n {/* Progress bar */}\n <div\n style={{\n width: \"60%\",\n height: isMobileDevice() ? \"8px\" : \"14px\",\n borderRadius: 8,\n background: \"#eee\",\n overflow: \"hidden\",\n marginBottom: isMobileDevice() ? \"10px\" : \"20px\",\n }}\n >\n <div\n style={{\n height: \"100%\",\n width: `${(timeLeft / (timePerRound || 20)) * 100}%`,\n background: timeLeft <= 5 ? \"#ec4c44\" : \"#4caf50\",\n transition: \"width 1s linear\",\n }}\n ></div>\n </div>\n\n {/* Word bank */}\n <div\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => handleDropTo(e, \"bank\", null)}\n style={{\n display: \"flex\",\n flexWrap: shouldWrapWords() ? \"wrap\" : \"nowrap\",\n gap: isMobile || window.innerWidth < 768 ? \"6px\" : \"10px\",\n justifyContent: \"center\",\n marginBottom: isMobile || window.innerWidth < 768 ? \"15px\" : \"30px\",\n padding: isMobile || window.innerWidth < 768 ? \"5px\" : \"10px\",\n width: \"100%\",\n boxSizing: \"border-box\"\n }}\n >\n {words.map((t, idx) => (\n <div\n key={t.id}\n draggable={!timeExpired}\n role=\"button\"\n tabIndex={timeExpired ? -1 : 0}\n aria-label={timeExpired ? `Word: ${t.text} (time expired)` : createAriaLabel(\"Drag word\", t.text, \"to build sentence\")}\n onDragStart={(e) => {\n if (timeExpired) {\n e.preventDefault();\n return;\n }\n e.dataTransfer.setData(\n \"application/x-token\",\n JSON.stringify({ from: \"bank\", id: t.id })\n );\n e.dataTransfer.setData(\"text/plain\", t.id);\n announceToScreenReader(`Dragging word: ${t.text}`);\n }}\n onKeyDown={(e) => {\n if (timeExpired) return;\n handleKeyDown(e, () => moveToken(\"bank\", \"selected\", t.id, null));\n }}\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => {\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const mid = rect.left + rect.width / 2;\n const insertIndex = e.clientX > mid ? idx + 1 : idx;\n setHover({ list: null, id: null, side: null });\n e.stopPropagation();\n handleDropTo(e, \"bank\", insertIndex);\n }}\n onDragEnter={(e) => {\n if (timeExpired) return;\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const mid = rect.left + rect.width / 2;\n setHover({ list: \"bank\", id: t.id, side: e.clientX > mid ? \"right\" : \"left\" });\n }}\n onDragLeave={() => setHover({ list: null, id: null, side: null })}\n onClick={() => {\n if (timeExpired) return;\n moveToken(\"bank\", \"selected\", t.id, null);\n }}\n style={{\n padding: isMobile || window.innerWidth < 768 ? \"6px 10px\" : \"10px 16px\",\n borderRadius: isMobile || window.innerWidth < 768 ? \"6px\" : \"10px\",\n border: \"1px solid #ccc\",\n background: timeExpired ? \"#f0f0f0\" : \"#f9f9f9\",\n cursor: timeExpired ? \"not-allowed\" : (hover.list === \"bank\" && hover.id === t.id ? \"grabbing\" : \"grab\"),\n fontSize: isMobile || window.innerWidth < 768 ? \"12px\" : \"18px\",\n ...(hover.list === \"bank\" && hover.id === t.id && hover.side === \"left\" ? { borderLeft: \"3px solid #3b82f6\" } : {}),\n ...(hover.list === \"bank\" && hover.id === t.id && hover.side === \"right\" ? { borderRight: \"3px solid #3b82f6\" } : {}),\n flexShrink: 0,\n flexBasis: \"auto\",\n opacity: timeExpired ? 0.6 : 1,\n transition: \"opacity 0.3s ease, transform 0.2s ease\",\n ...(hover.list === \"bank\" && hover.id === t.id ? { \n transform: \"scale(1.05)\",\n boxShadow: \"0 2px 8px rgba(59, 130, 246, 0.3)\"\n } : {}),\n }}\n >\n {t.text}\n </div>\n ))}\n </div>\n\n {/* Selected sentence */}\n <div\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => {\n const rect = e.currentTarget.getBoundingClientRect();\n const children = Array.from(e.currentTarget.children) as HTMLElement[];\n \n if (children.length === 0) {\n handleDropTo(e, \"selected\", 0);\n return;\n }\n \n let closestIdx = selected.length;\n let minDistance = Infinity;\n \n children.forEach((child, idx) => {\n const childRect = child.getBoundingClientRect();\n const childCenter = childRect.left + childRect.width / 2;\n const distance = Math.abs(e.clientX - childCenter);\n \n if (distance < minDistance) {\n minDistance = distance;\n closestIdx = e.clientX < childCenter ? idx : idx + 1;\n }\n });\n \n if (e.clientX > rect.right - 30) {\n closestIdx = selected.length;\n }\n if (e.clientX < rect.left + 30) {\n closestIdx = 0;\n }\n \n handleDropTo(e, \"selected\", closestIdx);\n }}\n style={{\n minHeight: isMobile || window.innerWidth < 768 ? \"50px\" : \"70px\",\n width: \"auto\", // Автоматическая ширина в зависимости от содержимого\n maxWidth: \"none\", // Убираем ограничение максимальной ширины\n minWidth: \"245px\", // Минимальная ширина для растягивания\n border: resultType === \"correct\" ? \"2px dashed #4caf50\" : \n resultType === \"almost\" ? \"2px dashed #ff9800\" : \n resultType === \"wrong\" ? \"2px dashed #f44336\" : \"2px dashed #ccc\",\n borderRadius: isMobile || window.innerWidth < 768 ? \"8px\" : \"12px\",\n padding: isMobile || window.innerWidth < 768 ? \"8px\" : \"12px\",\n display: \"flex\",\n flexWrap: shouldWrapWords() ? \"wrap\" : \"nowrap\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: `${getAdaptiveFontSize(selected.length)}px`,\n background: resultType === \"correct\" ? \"#e8f5e8\" : \n resultType === \"almost\" ? \"#fff3e0\" : \n resultType === \"wrong\" ? \"#ffebee\" : \"#fafafa\",\n overflowX: shouldWrapWords() ? \"hidden\" : \"auto\",\n whiteSpace: shouldWrapWords() ? \"normal\" : \"nowrap\",\n }}\n >\n {selected.map((t, idx) => (\n <span\n key={t.id}\n draggable={!timeExpired}\n onDragStart={(e) => {\n if (timeExpired) {\n e.preventDefault();\n return;\n }\n e.dataTransfer.setData(\n \"application/x-token\",\n JSON.stringify({ from: \"selected\", id: t.id })\n );\n e.dataTransfer.setData(\"text/plain\", t.id);\n }}\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => {\n const rect = (e.currentTarget as HTMLSpanElement).getBoundingClientRect();\n const mid = rect.left + rect.width / 2;\n const zoneWidth = rect.width * 0.25;\n const insertIndex = e.clientX < mid - zoneWidth ? idx : \n e.clientX > mid + zoneWidth ? idx + 1 : \n e.clientX > mid ? idx + 1 : idx;\n setHover({ list: null, id: null, side: null });\n e.stopPropagation();\n handleDropTo(e, \"selected\", insertIndex);\n }}\n onDragEnter={(e) => {\n if (timeExpired) return;\n const rect = (e.currentTarget as HTMLSpanElement).getBoundingClientRect();\n const mid = rect.left + rect.width / 2;\n setHover({ list: \"selected\", id: t.id, side: e.clientX > mid ? \"right\" : \"left\" });\n }}\n onDragLeave={() => setHover({ list: null, id: null, side: null })}\n onClick={() => {\n if (timeExpired) return;\n moveToken(\"selected\", \"bank\", t.id, null);\n }}\n title={timeExpired ? \"Time expired\" : \"Click to remove back to bank\"}\n style={{\n padding: isMobileDevice() ? \"4px 6px\" : \"6px 10px\",\n margin: isMobileDevice() ? \"2px\" : \"4px\",\n borderRadius: isMobileDevice() ? \"4px\" : \"8px\",\n background: timeExpired ? \"#f0f0f0\" : \"#ffe9e7\",\n border: timeExpired ? \"1px solid #ccc\" : \"1px solid #ec4c44\",\n ...(hover.list === \"selected\" && hover.id === t.id && hover.side === \"left\" ? { borderLeft: \"3px solid #3b82f6\" } : {}),\n ...(hover.list === \"selected\" && hover.id === t.id && hover.side === \"right\" ? { borderRight: \"3px solid #3b82f6\" } : {}),\n cursor: timeExpired ? \"not-allowed\" : (hover.list === \"selected\" && hover.id === t.id ? \"grabbing\" : \"grab\"),\n userSelect: \"none\",\n fontSize: `${getAdaptiveFontSize(selected.length)}px`, // Адаптивный размер шрифта для слов\n fontFamily: '\"Roboto\", system-ui, -apple-system, \"Segoe UI\", Roboto, Arial, sans-serif', // Более плотный шрифт\n whiteSpace: \"nowrap\", // Запрещаем перенос слов\n opacity: timeExpired ? 0.6 : 1,\n transition: \"opacity 0.3s ease, transform 0.2s ease\",\n ...(hover.list === \"selected\" && hover.id === t.id ? { \n transform: \"scale(1.05)\",\n boxShadow: \"0 2px 8px rgba(59, 130, 246, 0.3)\"\n } : {}),\n }}\n >\n {t.text}\n </span>\n ))}\n </div>\n\n <button\n onClick={() => handleNext(true)}\n disabled={!timeExpired && selected.length === 0}\n style={{\n marginTop: isMobileDevice() ? \"15px\" : \"30px\",\n fontSize: isMobileDevice() ? \"14px\" : \"20px\",\n padding: isMobileDevice() ? \"6px 12px\" : \"10px 24px\",\n borderRadius: isMobileDevice() ? \"8px\" : \"12px\",\n background: (timeExpired || selected.length > 0) ? \"#ec4c44\" : \"#ccc\",\n color: \"white\",\n border: \"none\",\n cursor: (timeExpired || selected.length > 0) ? \"pointer\" : \"not-allowed\",\n }}\n >\n {timeExpired ? \"NEXT\" : \"NEXT\"}\n </button>\n </div>\n );\n\n const renderResults = () => (\n <div style={styles.gmCenterScreen}>\n <h1 style={{\n ...styles.gmHeadline1,\n marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"0px\" : \"0px\",\n marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"2px\" : \"10px\",\n fontSize: (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"32px\" : \"clamp(28px, 4vw, 40px)\"\n }}>\n Game Over 🎯\n </h1>\n <h2 style={{\n ...styles.gmHeadline2,\n marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"0px\" : \"0px\",\n marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"2px\" : \"16px\",\n fontSize: (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"18px\" : \"24px\"\n }}>\n Your score: {score} out of {rounds}\n </h2>\n <p style={{ \n ...styles.gmBodyM, \n color: \"#10b981\", \n marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"0px\" : \"12px\",\n marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"2px\" : \"16px\",\n fontSize: (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"14px\" : \"16px\"\n }}>\n Best score: {bestScore}\n </p>\n\n <div style={{ \n display: \"flex\", \n gap: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"6px\" : \"12px\", \n marginTop: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"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\"\n }}>\n <button\n onClick={() => {\n triggerConfetti();\n playSound(\"start\");\n setTimeout(() => {\n setStage(\"getready\");\n setCountdown(null);\n setTimeExpired(false);\n }, 800);\n }}\n style={{\n ...styles.gmButton,\n ...getMobileButtonStyles('medium')\n }}\n >\n 🔁 Play again\n </button>\n\n <button\n onClick={() => {\n playSound(\"click\");\n setStage(\"select\");\n setRounds(null);\n setTimePerRound(null);\n setSentences([]);\n setScore(0);\n setSelected([]);\n setTimeExpired(false);\n }}\n style={{\n ...styles.gmButton,\n ...getMobileButtonStyles('medium')\n }}\n >\n ⬅️ Exit\n </button>\n </div>\n </div>\n );\n\n // Условие для отображения логотипа (на desktop и планшетах)\n const shouldShowLogo = !isMobile && showLogo;\n\n const MemoizedLogo = useMemo(\n () => {\n // Скрываем логотип на мобильных устройствах в landscape режиме и на малых экранах\n if ((isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700) {\n return null;\n }\n \n if (!shouldShowLogo) {\n return null;\n }\n \n const logoBaseUrl = logoUrl || (typeof window !== 'undefined' && window.origin \n ? `${window.origin}/cloud/speakid/games/magic%20sentence/logo`\n : \"/cloud/speakid/games/magic%20sentence/logo\");\n \n return (\n // ensure logo is positioned inside the square container\n <div style={{ ...styles.gmLogoFixed, position: \"absolute\", top: 16, left: 16, zIndex: 30 }}>\n <picture>\n <source\n srcSet={`${logoBaseUrl}.svg`}\n type=\"image/svg+xml\"\n />\n <img\n src={`${logoBaseUrl}.png`}\n alt=\"SPEAKID Logo\"\n style={styles.gmLogoImg}\n loading=\"lazy\"\n />\n </picture>\n </div>\n );\n },\n [isMobile, shouldShowLogo, logoUrl]\n );\n\n return (\n <div\n ref={containerRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n background: \"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)\",\n transition: \"background 0.3s ease\",\n overflow: \"hidden\",\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n }}\n >\n <div\n style={{\n width: isMobile ? \"100%\" : (containerSize || 1000),\n height: isMobile ? \"100%\" : (containerSize || 1000),\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n overflow: \"hidden\",\n borderRadius: isMobile ? 0 : \"20px\",\n background: \"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)\",\n boxShadow: isMobile ? \"none\" : \"0 0 40px rgba(0,0,0,0.1)\",\n margin: isMobile ? \"0 auto\" : \"unset\",\n position: \"relative\", // needed so absolute logo is inside the square\n transform: `scale(${scale})`, // ✅ Применяем масштаб для широких экранов\n }}\n >\n <div\n style={{\n transform: \"translateZ(0)\",\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <div id=\"magic-sentence-root\">\n {MemoizedLogo}\n {stage === \"select\" ? renderSelect() : null}\n {stage === \"time\" ? renderTime() : null}\n {stage === \"type\" ? renderType() : null}\n {stage === \"getready\" ? renderGetReady() : null}\n {stage === \"play\" ? renderPlay() : null}\n {stage === \"results\" ? renderResults() : null}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nexport default GameComponent;","import React, { Component, ErrorInfo, ReactNode } from 'react';\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n error?: Error;\n errorInfo?: ErrorInfo;\n}\n\nexport class ErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(error: Error): State {\n return {\n hasError: true,\n error,\n };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n console.error('Game Error:', error, errorInfo);\n \n // В реальном приложении здесь можно отправить ошибку в аналитику\n // analytics.track('game_error', { error: error.message, stack: error.stack });\n \n this.setState({\n error,\n errorInfo,\n });\n }\n\n handleReset = () => {\n this.setState({ hasError: false, error: undefined, errorInfo: undefined });\n };\n\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '100vh',\n padding: '20px',\n textAlign: 'center',\n backgroundColor: '#fef2f2',\n color: '#dc2626',\n fontFamily: 'system-ui, sans-serif'\n }}>\n <h1 style={{ fontSize: '24px', marginBottom: '16px' }}>\n 🚨 Oops! Something went wrong\n </h1>\n <p style={{ fontSize: '16px', marginBottom: '24px', maxWidth: '500px' }}>\n The game encountered an unexpected error. Don't worry, your progress is saved!\n </p>\n \n <button\n onClick={this.handleReset}\n style={{\n padding: '12px 24px',\n fontSize: '16px',\n backgroundColor: '#dc2626',\n color: 'white',\n border: 'none',\n borderRadius: '8px',\n cursor: 'pointer',\n transition: 'background-color 0.2s'\n }}\n onMouseOver={(e) => e.currentTarget.style.backgroundColor = '#b91c1c'}\n onMouseOut={(e) => e.currentTarget.style.backgroundColor = '#dc2626'}\n >\n 🔄 Restart Game\n </button>\n \n {typeof process !== 'undefined' && process.env.NODE_ENV === 'development' && this.state.error && (\n <details style={{ marginTop: '20px', textAlign: 'left', maxWidth: '600px' }}>\n <summary style={{ cursor: 'pointer', fontSize: '14px' }}>\n Technical Details (Development Only)\n </summary>\n <pre style={{\n backgroundColor: '#f3f4f6',\n padding: '12px',\n borderRadius: '4px',\n fontSize: '12px',\n overflow: 'auto',\n marginTop: '8px'\n }}>\n {this.state.error.toString()}\n {this.state.errorInfo?.componentStack}\n </pre>\n </details>\n )}\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n"],"names":["keyframes","styleTag","animations","styles","useValidation","errors","setErrors","useState","validateSentence","useCallback","sentence","index","allSentences","newErrors","duplicateIndex","s","i","validateAllSentences","sentences","allErrors","result","error","clearErrors","createAriaLabel","action","word","context","handleKeyDown","event","allowedKeys","announceToScreenReader","message","announcement","globalReset","style","existingStyle","shuffle","arr","GameComponent","props","logoUrl","showLogo","containerRef","useRef","validationErrors","shouldWrapWords","isMobile","isMobileDevice","getMobileButtonStyles","type","useEffect","stage","setStage","rounds","setRounds","timePerRound","setTimePerRound","setSentences","currentRound","setCurrentRound","words","setWords","selected","setSelected","timeLeft","setTimeLeft","score","setScore","countdown","setCountdown","resultType","setResultType","timeExpired","setTimeExpired","isCorrect","setIsCorrect","bestScore","setBestScore","timerRef","roundsRef","currentRoundRef","hover","setHover","setIsMobile","scale","setScale","containerSize","setContainerSize","isDesktopLayout","setIsDesktopLayout","isIPadMiniPortrait","setIsIPadMiniPortrait","isIPadMiniLandscape","setIsIPadMiniLandscape","isIPadAirPortrait","setIsIPadAirPortrait","isIPadAirLandscape","setIsIPadAirLandscape","isSurfaceDuoPortrait","setIsSurfaceDuoPortrait","isSurfaceDuoLandscape","setIsSurfaceDuoLandscape","isIPadProPortrait","setIsIPadProPortrait","isIPadProLandscape","setIsIPadProLandscape","isHorizontalLayout","setIsHorizontalLayout","isWideScreen","setIsWideScreen","resize","width","height","mobile","isSmallHeight","desktopLayout","isHorizontal","finalSize","moveToken","from","to","id","insertIndex","nextWords","nextSelected","takeFrom","putTo","idx","t","token","targetIndex","handleDropTo","raw","fallbackId","inBank","inSelected","payload","handleSelect","num","handleTimeSelect","time","handleChange","value","updated","validation","normalizeSentence","getAdaptiveFontSize","wordCount","startGame","prev","startRound","target","tokens","w","correct","correctWords","selectedTexts","missingWords","extraWords","wrongOrder","playSound","handleNext","manual","triggerConfetti","ctx","osc","gain","end","colors","canvas","pieces","draw","p","renderSelect","jsxs","jsx","renderTime","renderType","text","e","renderGetReady","renderPlay","rect","mid","children","closestIdx","minDistance","child","childRect","childCenter","distance","zoneWidth","renderResults","shouldShowLogo","MemoizedLogo","useMemo","logoBaseUrl","ErrorBoundary","Component","errorInfo"],"mappings":"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"}
1
+ {"version":3,"file":"speakid-build-a-sentence.cjs.js","sources":["../src/Game.styles.ts","../src/hooks/useValidation.ts","../src/utils/accessibility.ts","../src/Game.tsx","../src/components/ErrorBoundary.tsx"],"sourcesContent":["import { CSSProperties } from \"react\";\n\n// ===== Добавляем анимации в <head> =====\nconst keyframes = `\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n \n @keyframes pulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n }\n \n @keyframes shake {\n 0%, 100% { transform: translateX(0); }\n 25% { transform: translateX(-5px); }\n 75% { transform: translateX(5px); }\n }\n \n @keyframes slideIn {\n from { transform: translateY(-20px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n \n @keyframes bounce {\n 0%, 20%, 53%, 80%, 100% { transform: translate3d(0,0,0); }\n 40%, 43% { transform: translate3d(0, -8px, 0); }\n 70% { transform: translate3d(0, -4px, 0); }\n 90% { transform: translate3d(0, -2px, 0); }\n }\n \n @keyframes glow {\n 0%, 100% { box-shadow: 0 0 5px rgba(76, 175, 80, 0.5); }\n 50% { box-shadow: 0 0 20px rgba(76, 175, 80, 0.8), 0 0 30px rgba(76, 175, 80, 0.6); }\n }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"game-keyframes\")) {\n const styleTag = document.createElement(\"style\");\n styleTag.id = \"game-keyframes\";\n styleTag.innerHTML = keyframes;\n document.head.appendChild(styleTag);\n}\n\n// ===== Анимации =====\nconst animations = {\n spin: {\n animation: \"spin 1.4s linear infinite\",\n },\n pulse: {\n animation: \"pulse 0.6s ease-in-out\",\n },\n shake: {\n animation: \"shake 0.4s ease-in-out\",\n },\n slideIn: {\n animation: \"slideIn 0.3s ease-out\",\n },\n bounce: {\n animation: \"bounce 0.6s ease-in-out\",\n },\n glow: {\n animation: \"glow 1s ease-in-out infinite\",\n },\n};\n\n// ===== Основные стили =====\nexport const styles: Record<string, CSSProperties> = {\n gmCenterScreen: {\n position: \"relative\",\n zIndex: 1,\n // use percent so the component fits inside the centered square container\n // (100vh caused overflow on tablets because it measured the viewport, not the container)\n minHeight: \"100%\",\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n textAlign: \"center\",\n color: \"#1f2937\",\n padding: \"24px 16px\",\n boxSizing: \"border-box\",\n transform: \"translateY(20px)\", // чуть вниз, чтобы компенсировать логотип\n },\n\n gmHeadline1: {\n fontWeight: 700,\n fontSize: \"clamp(28px, 4vw, 40px)\",\n lineHeight: \"110%\",\n },\n\n gmHeadline2: {\n fontWeight: 600,\n fontSize: \"24px\",\n lineHeight: \"120%\",\n },\n\n gmHeadline3: {\n fontWeight: 600,\n fontSize: \"18px\",\n lineHeight: \"130%\",\n },\n\n gmBodyL: {\n fontWeight: 400,\n fontSize: \"18px\",\n lineHeight: \"140%\",\n },\n\n gmBodyM: {\n fontWeight: 400,\n fontSize: \"16px\",\n lineHeight: \"140%\",\n },\n\n gmBodyS: {\n fontWeight: 400,\n fontSize: \"14px\",\n lineHeight: \"140%\",\n color: \"#6b7280\",\n },\n\n gmButton: {\n fontFamily:\n '\"Geist\", system-ui, -apple-system, \"Segoe UI\", Roboto, Arial, \"Noto Sans\"',\n fontWeight: 600,\n fontSize: \"16px\",\n padding: \"10px 16px\",\n borderRadius: \"12px\",\n border: \"1px solid #e5e7eb\",\n background: \"#ec4c44\",\n color: \"#ffffff\",\n cursor: \"pointer\",\n boxShadow: \"0 6px 18px rgba(236, 76, 68, .18)\",\n transition:\n \"transform .06s ease, box-shadow .2s ease, background .2s ease, opacity .2s ease\",\n },\n\n gmButtonActive: {\n background: \"#333\",\n color: \"#fff\",\n },\n\n gmGameLayout: {\n position: \"relative\",\n width: \"100%\",\n // allow the layout to expand within the square container for tablets/laptops\n maxWidth: \"none\",\n // should fill the parent square, not the full viewport\n minHeight: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n textAlign: \"center\",\n color: \"#1f2937\",\n padding: \"16px 8px\",\n margin: \"0 auto\",\n },\n\n gmGrid: {\n width: \"100%\",\n // let the grid use all available space; sizing is controlled in Game.tsx\n maxWidth: \"none\",\n margin: \"0 auto\",\n display: \"grid\",\n // defaults; overridden responsively in Game.tsx\n gridTemplateColumns: \"repeat(3, 210px)\",\n gridAutoRows: \"210px\",\n gap: \"20px\",\n justifyContent: \"center\",\n },\n\n gmCard: {\n border: \"1px solid #e5e7eb\",\n borderRadius: \"16px\",\n background: \"#f9f9f9\",\n aspectRatio: \"1 / 1\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n overflow: \"hidden\",\n transition:\n \"transform .2s ease, box-shadow .2s ease, border-color .2s ease\",\n },\n\n gmCorrect: {\n border: \"2px solid #10b981\",\n boxShadow: \"0 0 18px rgba(16,185,129,.45)\",\n background: \"#ecfdf5\",\n },\n\n gmWrong: {\n border: \"2px solid #ec4c44\",\n boxShadow: \"0 0 18px rgba(236,76,68,.35)\",\n background: \"#fef2f2\",\n },\n\n gmInput: {\n padding: \"6px 10px\",\n borderRadius: \"6px\",\n border: \"1px solid #ccc\",\n fontSize: \"16px\",\n fontFamily: '\"Geist\", system-ui',\n width: \"160px\",\n },\n\n gmTable: {\n marginTop: \"20px\",\n marginBottom: \"32px\",\n borderCollapse: \"collapse\",\n width: \"100%\",\n maxWidth: \"520px\",\n tableLayout: \"fixed\",\n textAlign: \"center\",\n },\n\n gmTableCell: {\n padding: \"8px 12px\",\n borderBottom: \"1px solid #e5e7eb\",\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n\n // ✅ Обновлено только для качества логотипа (размер как в whats-missing)\n gmLogoFixed: {\n position: \"absolute\",\n top: \"16px\",\n left: \"16px\",\n width: \"auto\",\n zIndex: 10,\n pointerEvents: \"none\",\n background: \"transparent\",\n transform: \"none\",\n willChange: \"auto\",\n },\n\n gmLogoImg: {\n height: \"28px\",\n width: \"auto\",\n background: \"transparent\",\n objectFit: \"contain\",\n imageRendering: \"auto\",\n transform: \"translateZ(0)\",\n backfaceVisibility: \"hidden\",\n WebkitFontSmoothing: \"antialiased\",\n display: \"block\",\n },\n\n gmLogoFallback: {\n height: \"28px\",\n width: \"auto\",\n background: \"transparent\",\n color: \"#ec4c44\",\n fontSize: \"16px\",\n fontWeight: 700,\n display: \"block\",\n },\n\n gmReadyWrapper: {\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n // use percent so the wrapper scales with the container square\n height: \"60%\",\n gap: \"16px\",\n },\n\n gmHourglass: {\n fontSize: \"42px\",\n ...animations.spin,\n },\n\n // ===== Анимационные стили =====\n ...animations,\n};\n","import { useState, useCallback } from 'react';\n\nexport interface ValidationError {\n type: 'length' | 'characters' | 'empty' | 'duplicate';\n message: string;\n}\n\nexport interface ValidationResult {\n isValid: boolean;\n errors: ValidationError[];\n}\n\nexport const useValidation = () => {\n const [errors, setErrors] = useState<ValidationError[]>([]);\n\n const validateSentence = useCallback((sentence: string, index: number, allSentences: string[]): ValidationResult => {\n const newErrors: ValidationError[] = [];\n\n // Проверка на пустоту\n if (!sentence.trim()) {\n newErrors.push({\n type: 'empty',\n message: 'Sentence cannot be empty'\n });\n }\n\n // Проверка длины\n if (sentence.length > 41) {\n newErrors.push({\n type: 'length',\n message: `Sentence is too long (${sentence.length}/41 characters)`\n });\n }\n\n // Проверка символов (только латиница)\n const latinRegex = /^[a-zA-Z0-9\\s.,!?;:'\"-]*$/;\n if (sentence && !latinRegex.test(sentence)) {\n newErrors.push({\n type: 'characters',\n message: 'Only Latin characters, numbers, spaces and punctuation are allowed'\n });\n }\n\n // Проверка на дубликаты\n const duplicateIndex = allSentences.findIndex((s, i) => i !== index && s.toLowerCase().trim() === sentence.toLowerCase().trim());\n if (duplicateIndex !== -1) {\n newErrors.push({\n type: 'duplicate',\n message: `Duplicate sentence (same as sentence ${duplicateIndex + 1})`\n });\n }\n\n setErrors(newErrors);\n return {\n isValid: newErrors.length === 0,\n errors: newErrors\n };\n }, []);\n\n const validateAllSentences = useCallback((sentences: string[]): ValidationResult => {\n const allErrors: ValidationError[] = [];\n \n sentences.forEach((sentence, index) => {\n const result = validateSentence(sentence, index, sentences);\n allErrors.push(...result.errors.map(error => ({\n ...error,\n message: `Sentence ${index + 1}: ${error.message}`\n })));\n });\n\n return {\n isValid: allErrors.length === 0,\n errors: allErrors\n };\n }, [validateSentence]);\n\n const clearErrors = useCallback(() => {\n setErrors([]);\n }, []);\n\n return {\n errors,\n validateSentence,\n validateAllSentences,\n clearErrors,\n };\n};\n\n","// Утилиты для улучшения доступности\n\nexport const createAriaLabel = (action: string, word?: string, context?: string): string => {\n if (word && context) {\n return `${action} word \"${word}\" ${context}`;\n }\n if (word) {\n return `${action} word \"${word}\"`;\n }\n return action;\n};\n\nexport const getRoleForElement = (type: 'button' | 'draggable' | 'droppable' | 'input'): string => {\n const roles = {\n button: 'button',\n draggable: 'button',\n droppable: 'region',\n input: 'textbox'\n };\n return roles[type];\n};\n\nexport const handleKeyDown = (\n event: React.KeyboardEvent,\n action: () => void,\n allowedKeys: string[] = ['Enter', ' ']\n) => {\n if (allowedKeys.includes(event.key)) {\n event.preventDefault();\n action();\n }\n};\n\nexport const announceToScreenReader = (message: string) => {\n // Создаем временный элемент для объявления\n const announcement = document.createElement('div');\n announcement.setAttribute('aria-live', 'polite');\n announcement.setAttribute('aria-atomic', 'true');\n announcement.style.position = 'absolute';\n announcement.style.left = '-10000px';\n announcement.style.width = '1px';\n announcement.style.height = '1px';\n announcement.style.overflow = 'hidden';\n \n document.body.appendChild(announcement);\n announcement.textContent = message;\n \n // Удаляем элемент после объявления\n setTimeout(() => {\n document.body.removeChild(announcement);\n }, 1000);\n};\n\nexport const getFocusableElements = (container: HTMLElement): HTMLElement[] => {\n const focusableSelectors = [\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[role=\"button\"]:not([disabled])'\n ].join(', ');\n \n return Array.from(container.querySelectorAll(focusableSelectors));\n};\n\nexport const trapFocus = (container: HTMLElement) => {\n const focusableElements = getFocusableElements(container);\n \n if (focusableElements.length === 0) return;\n \n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n \n const handleTabKey = (e: KeyboardEvent) => {\n if (e.key === 'Tab') {\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n lastElement.focus();\n e.preventDefault();\n }\n } else {\n if (document.activeElement === lastElement) {\n firstElement.focus();\n e.preventDefault();\n }\n }\n }\n };\n \n container.addEventListener('keydown', handleTabKey);\n \n // Фокусируем первый элемент\n firstElement.focus();\n \n // Возвращаем функцию для очистки\n return () => {\n container.removeEventListener('keydown', handleTabKey);\n };\n};\n\n","// ======================\n// MAGIC SENTENCE GAME (SPEAKID)\n// React 18 + TypeScript + Vite\n// Адаптивная верстка для всех устройств\n// ======================\n\nimport * as React from \"react\";\nimport { useState, useEffect, useRef, useMemo } from \"react\";\nimport { styles } from \"./Game.styles\";\nimport { ErrorBoundary } from \"./components/ErrorBoundary\";\nimport { useValidation } from \"./hooks/useValidation\";\nimport { createAriaLabel, handleKeyDown, announceToScreenReader } from \"./utils/accessibility\";\n\ntype Stage = \"select\" | \"time\" | \"type\" | \"getready\" | \"play\" | \"results\";\ntype Token = { id: string; text: string };\n\n// ✅ базовый reset\nconst globalReset = () => {\n const style = document.createElement(\"style\");\n style.id = \"magic-sentence-reset\"; // ✅ Добавляем ID для удаления\n \n style.textContent = `\n #magic-sentence-root, #magic-sentence-root * {\n box-sizing: border-box;\n font-family: \"Geist\", system-ui, -apple-system, \"Segoe UI\", Roboto, Arial, sans-serif;\n }\n #magic-sentence-root img {\n max-width: 100%;\n height: auto;\n display: block;\n user-select: none;\n }\n html, body {\n margin: 0 !important;\n padding: 0 !important;\n width: 100% !important;\n height: 100% !important;\n overflow: hidden !important;\n zoom: 1 !important; /* ✅ защита от подзума */\n }\n #root {\n margin: 0 !important;\n padding: 0 !important;\n width: 100% !important;\n height: 100% !important;\n overflow: hidden !important;\n }\n `;\n \n // ✅ Удаляем старый стиль, если он есть\n const existingStyle = document.getElementById(\"magic-sentence-reset\");\n if (existingStyle) {\n existingStyle.remove();\n }\n \n document.head.appendChild(style);\n};\n\nconst shuffle = (arr: string[]) => [...arr].sort(() => Math.random() - 0.5);\n\nexport interface GameProps {\n logoUrl?: string;\n showLogo?: boolean;\n}\n\nfunction GameComponent(props: GameProps = {}) {\n const { logoUrl, showLogo = true } = props;\n const containerRef = useRef<HTMLDivElement>(null);\n \n // Новые хуки\n const { validateAllSentences, errors: validationErrors } = useValidation();\n\n // Функция для определения нужно ли переносить слова\n const shouldWrapWords = () => {\n // Переносим слова на всех мобильных устройствах\n if (isMobile || window.innerWidth < 768) return true;\n \n // Для планшетов с шириной < 1300px всегда включаем перенос\n // чтобы слова помещались в word bank\n if (window.innerWidth >= 768 && window.innerWidth < 1300) {\n return true;\n }\n \n return false;\n };\n\n // Универсальная функция для определения мобильных устройств\n const isMobileDevice = () => {\n return isMobile || \n window.innerWidth < 768 || \n (window.innerWidth >= 320 && window.innerWidth <= 932 && window.innerHeight >= 390 && window.innerHeight <= 932);\n };\n\n // Вспомогательная функция для мобильных размеров кнопок\n const getMobileButtonStyles = (type: 'small' | 'medium' | 'large' = 'medium') => {\n if (!isMobileDevice()) {\n return {\n padding: \"12px 24px\",\n fontSize: \"16px\",\n minWidth: \"auto\"\n };\n }\n\n switch (type) {\n case 'small':\n return {\n padding: \"4px 6px\",\n fontSize: \"9px\",\n minWidth: \"40px\"\n };\n case 'medium':\n return {\n padding: \"5px 8px\",\n fontSize: \"10px\",\n minWidth: \"50px\"\n };\n case 'large':\n return {\n padding: \"6px 10px\",\n fontSize: \"11px\",\n minWidth: \"60px\"\n };\n }\n };\n\n useEffect(() => {\n globalReset();\n return () => {\n // ✅ Восстанавливаем overflow на html и body\n document.documentElement.style.overflow = \"\";\n document.body.style.overflow = \"\";\n \n // ✅ Удаляем наш style элемент\n const style = document.getElementById(\"magic-sentence-reset\");\n if (style) {\n style.remove();\n }\n };\n }, []);\n\n const [stage, setStage] = useState<Stage>(\"select\");\n const [rounds, setRounds] = useState<number | null>(null);\n const [timePerRound, setTimePerRound] = useState<number | null>(null);\n const [sentences, setSentences] = useState<string[]>([]);\n const [currentRound, setCurrentRound] = useState(0);\n const [words, setWords] = useState<Token[]>([]);\n const [selected, setSelected] = useState<Token[]>([]);\n const [timeLeft, setTimeLeft] = useState(20);\n const [score, setScore] = useState(0);\n const [countdown, setCountdown] = useState<number | null>(null);\n const [resultType, setResultType] = useState<\"correct\" | \"almost\" | \"wrong\" | null>(null);\n const [timeExpired, setTimeExpired] = useState(false);\n const [isCorrect, setIsCorrect] = useState(false);\n const [bestScore, setBestScore] = useState<number>(\n Number(localStorage.getItem(\"magicSentenceBest\")) || 0\n );\n const timerRef = useRef<number | null>(null);\n const roundsRef = useRef<number | null>(null);\n const currentRoundRef = useRef<number>(0);\n const [hover, setHover] = useState<{ list: ListName | null; id: string | null; side: \"left\" | \"right\" | null }>({ list: null, id: null, side: null });\n\n // Адаптивность\n const [isMobile, setIsMobile] = useState(false);\n const [scale, setScale] = useState(1);\n const [containerSize, setContainerSize] = useState<number | null>(null);\n const [isDesktopLayout, setIsDesktopLayout] = useState(false);\n const [isIPadMiniPortrait, setIsIPadMiniPortrait] = useState(false);\n const [isIPadMiniLandscape, setIsIPadMiniLandscape] = useState(false);\n const [isIPadAirPortrait, setIsIPadAirPortrait] = useState(false);\n const [isIPadAirLandscape, setIsIPadAirLandscape] = useState(false);\n const [isSurfaceDuoPortrait, setIsSurfaceDuoPortrait] = useState(false);\n const [isSurfaceDuoLandscape, setIsSurfaceDuoLandscape] = useState(false);\n const [isIPadProPortrait, setIsIPadProPortrait] = useState(false);\n const [isIPadProLandscape, setIsIPadProLandscape] = useState(false);\n const [isHorizontalLayout, setIsHorizontalLayout] = useState(false);\n const [isWideScreen, setIsWideScreen] = useState(false);\n\n // ✅ адаптив под мобилки, планшеты и десктоп\n useEffect(() => {\n const resize = () => {\n const width = window.innerWidth;\n const height = window.innerHeight;\n const mobile = width < 768 || (width === 926 && height === 428) || (width === 932 && height === 430);\n const isLandscape = (width > height && mobile) || (width === 926 && height === 428) || (width === 932 && height === 430);\n const isSmallHeight = height < 700;\n const isWideScreen = width / height > 1.8; // ✅ Широкие экраны\n \n // iPad размеры\n const isIPadMiniPortrait = width === 768 && height === 1024;\n const isIPadMiniLandscape = width === 1024 && height === 768;\n const isIPadAirPortrait = width === 820 && height === 1180;\n const isIPadAirLandscape = width === 1180 && height === 820;\n \n // Surface DUO размеры\n const isSurfaceDuoPortrait = width === 540 && height === 720;\n const isSurfaceDuoLandscape = width === 720 && height === 540;\n \n // iPad Pro размеры\n const isIPadProPortrait = width === 1024 && height === 1366;\n const isIPadProLandscape = width === 1366 && height === 1024;\n \n // Десктопные разрешения\n const desktopLayout = width >= 1200 && height >= 600 && !mobile;\n setIsDesktopLayout(desktopLayout);\n \n // Установка состояний для iPad и Surface DUO\n setIsIPadMiniPortrait(isIPadMiniPortrait);\n setIsIPadMiniLandscape(isIPadMiniLandscape);\n setIsIPadAirPortrait(isIPadAirPortrait);\n setIsIPadAirLandscape(isIPadAirLandscape);\n setIsSurfaceDuoPortrait(isSurfaceDuoPortrait);\n setIsSurfaceDuoLandscape(isSurfaceDuoLandscape);\n setIsIPadProPortrait(isIPadProPortrait);\n setIsIPadProLandscape(isIPadProLandscape);\n setIsWideScreen(isWideScreen); // ✅ Сохраняем состояние для широких экранов\n \n // ✅ Вычисляем горизонтальный layout ОДИН РАЗ\n const isHorizontal = \n (mobile && width > height) || \n mobile || // ✅ ВСЕ мобильные устройства (включая portrait)\n height < 700 || \n isWideScreen || // ✅ Широкие экраны\n (width === 1366 && height === 766) || \n (width === 1366 && height === 768) || \n (width === 1280 && height === 720) || \n (width === 1440 && height === 900) || \n isIPadMiniPortrait || \n isIPadMiniLandscape || \n isIPadAirPortrait || \n isIPadAirLandscape || \n isSurfaceDuoPortrait || \n isSurfaceDuoLandscape || \n isIPadProPortrait || \n isIPadProLandscape || \n desktopLayout;\n setIsHorizontalLayout(isHorizontal);\n \n setIsMobile(mobile);\n\n // ✅ Адаптивные размеры контейнера\n if (mobile) {\n setContainerSize(null);\n setScale(1);\n } else if (isSmallHeight) {\n setContainerSize(null);\n setScale(1);\n } else if (isWideScreen) {\n // ✅ Широкие экраны: используем стандартный размер но уменьшаем масштаб\n const minSize = 400;\n const maxSize = 1200;\n const finalSize = Math.min(1000, Math.min(width, height) * 0.9);\n setContainerSize(finalSize);\n setScale(0.85); // Уменьшаем масштаб на 15%\n } else {\n const minSize = 400;\n const maxSize = 1200;\n const finalSize = Math.min(1000, Math.min(width, height) * 0.9);\n setContainerSize(finalSize);\n setScale(1);\n }\n };\n resize();\n window.addEventListener(\"resize\", resize);\n return () => window.removeEventListener(\"resize\", resize);\n }, []);\n\n // =========================\n // DND HELPERS\n // =========================\n type ListName = \"bank\" | \"selected\";\n type DragPayload = { from: ListName; id: string };\n\n const moveToken = (\n from: ListName,\n to: ListName,\n id: string,\n insertIndex: number | null\n ) => {\n // Блокируем перемещение если время истекло\n if (timeExpired) return;\n\n let nextWords = [...words];\n let nextSelected = [...selected];\n\n const takeFrom = from === \"bank\" ? nextWords : nextSelected;\n const putTo = to === \"bank\" ? nextWords : nextSelected;\n\n const idx = takeFrom.findIndex((t) => t.id === id);\n if (idx === -1) return;\n const [token] = takeFrom.splice(idx, 1);\n\n let targetIndex = insertIndex;\n if (\n from === to &&\n targetIndex !== null &&\n targetIndex !== undefined\n ) {\n if (targetIndex > idx) targetIndex = targetIndex - 1;\n }\n\n if (\n targetIndex === null ||\n targetIndex === undefined ||\n targetIndex < 0 ||\n targetIndex > putTo.length\n ) {\n putTo.push(token);\n } else {\n putTo.splice(targetIndex, 0, token);\n }\n\n if (from === \"bank\") nextWords = takeFrom; else nextSelected = takeFrom;\n if (to === \"bank\") nextWords = putTo; else nextSelected = putTo;\n\n setWords(nextWords);\n setSelected(nextSelected);\n };\n\n const handleDropTo = (\n e: React.DragEvent,\n to: ListName,\n insertIndex: number | null\n ) => {\n e.preventDefault();\n \n // Блокируем drop если время истекло\n if (timeExpired) {\n setHover({ list: null, id: null, side: null });\n return;\n }\n\n const raw = e.dataTransfer.getData(\"application/x-token\") ||\n (() => {\n const fallbackId = e.dataTransfer.getData(\"text/plain\");\n if (!fallbackId) return \"\";\n const inBank = words.some((t) => t.id === fallbackId);\n const inSelected = selected.some((t) => t.id === fallbackId);\n const from: ListName | null = inBank ? \"bank\" : inSelected ? \"selected\" : null;\n return from ? JSON.stringify({ from, id: fallbackId }) : \"\";\n })();\n if (!raw) return;\n try {\n const payload = JSON.parse(raw) as DragPayload;\n if (!payload || !payload.id || !payload.from) return;\n moveToken(payload.from, to, payload.id, insertIndex);\n } catch {\n // ignore bad payload\n }\n setHover({ list: null, id: null, side: null });\n };\n\n // =========================\n // START / SETUP\n // =========================\n const handleSelect = (num: number) => {\n setRounds(num);\n roundsRef.current = num;\n setSentences(Array(num).fill(\"\"));\n setStage(\"time\");\n };\n\n const handleTimeSelect = (time: number) => {\n setTimePerRound(time);\n setStage(\"type\");\n };\n\n const handleChange = (index: number, value: string) => {\n // Ограничение на 41 символ (включая пробелы и точку)\n if (value.length > 41) {\n return;\n }\n \n // Проверка на латинские символы (a-z, A-Z, пробелы, цифры, знаки препинания)\n const latinRegex = /^[a-zA-Z0-9\\s.,!?;:'\"-]*$/;\n if (value && !latinRegex.test(value)) {\n return; // Не сохраняем, если есть нелатинские символы\n }\n \n const updated = [...sentences];\n updated[index] = value;\n setSentences(updated);\n \n // Валидация при вводе\n const validation = validateAllSentences(updated);\n if (!validation.isValid) {\n console.warn('Validation errors:', validation.errors);\n }\n };\n\n const normalizeSentence = (s: string) => s.trim().replace(/\\s+/g, \" \");\n\n // Адаптивный размер шрифта в зависимости от количества слов\n const getAdaptiveFontSize = (wordCount: number) => {\n const isTablet = window.innerWidth >= 768 && window.innerWidth < 1300;\n \n if (wordCount <= 3) return isTablet ? 18 : 20;\n if (wordCount <= 5) return isTablet ? 16 : 18;\n if (wordCount <= 7) return isTablet ? 14 : 16;\n if (wordCount <= 9) return isTablet ? 12 : 14;\n if (wordCount <= 12) return isTablet ? 10 : 12;\n return isTablet ? 9 : 10; // Для очень длинных предложений\n };\n\n const startGame = () => {\n const hasEmpty = sentences.some((s) => s.trim().length === 0);\n if (hasEmpty) {\n return;\n }\n setSentences((prev) => prev.map((s) => normalizeSentence(s)));\n setScore(0);\n setCurrentRound(0);\n currentRoundRef.current = 0;\n setCountdown(null); // Убираем цифровой отсчет\n setStage(\"getready\");\n };\n\n useEffect(() => {\n if (stage === \"getready\") {\n const t = setTimeout(() => startRound(0), 3000);\n return () => clearTimeout(t);\n }\n }, [stage]);\n\n const startRound = (index: number) => {\n const target = sentences[index];\n if (!target) return;\n const shuffled = shuffle(target\n .trim()\n .split(/\\s+/)\n .filter(Boolean)\n );\n const tokens: Token[] = shuffled.map((w, i) => ({\n id: `${Date.now()}-${index}-${i}-${Math.random().toString(36).slice(2)}`,\n text: w,\n }));\n setWords(tokens);\n setSelected([]);\n setCurrentRound(index);\n currentRoundRef.current = index;\n setTimeLeft(timePerRound || 20); // Используем выбранное время\n setResultType(null); // Сбрасываем результат\n setTimeExpired(false); // Сбрасываем состояние истечения времени\n setIsCorrect(false); // Сбрасываем флаг правильного ответа\n setStage(\"play\");\n };\n\n // Убираем автоматическую проверку во время сборки\n // Проверка происходит только при нажатии NEXT или при истечении времени\n\n // TIMER - только отсчет времени, без перезапуска при изменении selected\n useEffect(() => {\n if (stage === \"play\" && !timeExpired) {\n if (timerRef.current !== null) window.clearTimeout(timerRef.current);\n if (timeLeft > 0) {\n timerRef.current = window.setTimeout(() => setTimeLeft((t) => t - 1), 1000);\n } else {\n // Время истекло\n setTimeExpired(true);\n }\n }\n return () => {\n if (timerRef.current !== null) window.clearTimeout(timerRef.current);\n };\n }, [stage, timeLeft, timeExpired]);\n\n // Проверка при истечении времени - отдельный эффект\n useEffect(() => {\n if (stage === \"play\" && timeExpired && timeLeft === 0) {\n const correct = sentences[currentRound];\n if (!correct) {\n setResultType(null);\n return;\n }\n\n const correctWords = correct.trim().split(/\\s+/);\n const selectedTexts = selected.map((t) => t.text);\n \n // Всегда считаем ошибки, даже если выбрано меньше слов\n // Считаем ошибки: недостающие слова + лишние слова + неправильный порядок\n const missingWords = correctWords.filter(word => !selectedTexts.includes(word)).length;\n const extraWords = selectedTexts.filter(word => !correctWords.includes(word)).length;\n \n // Неправильный порядок: считаем только для тех слов, которые есть в обоих массивах\n const wrongOrder = correctWords.filter((w, i) => {\n // Если слово есть в выбранных, проверяем его позицию\n if (selectedTexts.includes(w)) {\n return selectedTexts[i] !== w;\n }\n // Если слова нет в выбранных, не считаем это как ошибку порядка (это уже missingWords)\n return false;\n }).length;\n \n // Общее количество ошибок = недостающие + лишние + неправильный порядок\n const errors = missingWords + extraWords + wrongOrder;\n\n // Показываем результат и засчитываем очки\n if (errors === 0) {\n setResultType(\"correct\");\n if (!isCorrect) {\n setIsCorrect(true);\n setScore((s) => s + 1);\n }\n playSound(\"correct\");\n announceToScreenReader(\"Correct! Well done!\");\n } else if (errors === 1) {\n setResultType(\"almost\");\n setScore((s) => s + 0.5);\n playSound(\"half\");\n announceToScreenReader(\"Almost correct! Just one mistake.\");\n } else {\n setResultType(\"wrong\");\n playSound(\"wrong\");\n announceToScreenReader(\"Not quite right. Keep trying!\");\n }\n }\n }, [stage, timeExpired, timeLeft, sentences, currentRound, selected, isCorrect]);\n\n // =========================\n // SCORING / NEXT ROUND\n // =========================\n const handleNext = (manual = true) => {\n // Если это ручное нажатие NEXT\n if (manual) {\n // Если время уже истекло, проверка уже была выполнена в TIMER\n // Просто переходим к следующему раунду\n if (timeExpired) {\n if (currentRound + 1 < (rounds || 0)) {\n startRound(currentRound + 1);\n } else {\n setStage(\"results\");\n setTimeout(() => triggerConfetti(), 600);\n }\n return;\n }\n\n // Если время не истекло, проверяем предложение вручную\n const correct = sentences[currentRound];\n if (!correct) {\n // Если нет правильного предложения, просто переходим дальше\n if (currentRound + 1 < (rounds || 0)) {\n startRound(currentRound + 1);\n } else {\n setStage(\"results\");\n setTimeout(() => triggerConfetti(), 600);\n }\n return;\n }\n\n const correctWords = correct.trim().split(/\\s+/);\n const selectedTexts = selected.map((t) => t.text);\n \n // Всегда считаем ошибки, даже если выбрано меньше слов\n // Считаем ошибки: недостающие слова + лишние слова + неправильный порядок\n const missingWords = correctWords.filter(word => !selectedTexts.includes(word)).length;\n const extraWords = selectedTexts.filter(word => !correctWords.includes(word)).length;\n \n // Неправильный порядок: считаем только для тех слов, которые есть в обоих массивах\n const wrongOrder = correctWords.filter((w, i) => {\n // Если слово есть в выбранных, проверяем его позицию\n if (selectedTexts.includes(w)) {\n return selectedTexts[i] !== w;\n }\n // Если слова нет в выбранных, не считаем это как ошибку порядка (это уже missingWords)\n return false;\n }).length;\n \n // Общее количество ошибок = недостающие + лишние + неправильный порядок\n const errors = missingWords + extraWords + wrongOrder;\n\n // Устанавливаем результат проверки и засчитываем очки\n if (errors === 0) {\n setScore((s) => s + 1);\n setResultType(\"correct\");\n setIsCorrect(true);\n playSound(\"correct\");\n announceToScreenReader(\"Correct! Well done!\");\n } else if (errors === 1) {\n setScore((s) => s + 0.5);\n setResultType(\"almost\");\n playSound(\"half\");\n announceToScreenReader(\"Almost correct! Just one mistake.\");\n } else {\n setResultType(\"wrong\");\n playSound(\"wrong\");\n announceToScreenReader(\"Not quite right. Keep trying!\");\n }\n\n // Переходим к следующему раунду после проверки\n if (currentRound + 1 < (rounds || 0)) {\n setTimeout(() => startRound(currentRound + 1), 800);\n } else {\n setTimeout(() => {\n setStage(\"results\");\n setTimeout(() => triggerConfetti(), 600);\n }, 800);\n }\n }\n };\n\n // =========================\n // SAVE BEST SCORE\n // =========================\n useEffect(() => {\n if (stage === \"results\" && score > bestScore) {\n setBestScore(score);\n localStorage.setItem(\"magicSentenceBest\", String(score));\n }\n }, [stage, score, bestScore]);\n\n // =========================\n // SOUND EFFECTS\n // =========================\n const playSound = (type: \"start\" | \"click\" | \"correct\" | \"half\" | \"wrong\") => {\n const ctx = new (window.AudioContext || (window as any).webkitAudioContext)();\n const osc = ctx.createOscillator();\n const gain = ctx.createGain();\n osc.connect(gain);\n gain.connect(ctx.destination);\n\n switch (type) {\n case \"start\": osc.frequency.value = 500; break;\n case \"click\": osc.frequency.value = 800; break;\n case \"correct\": osc.frequency.value = 1000; break;\n case \"half\": osc.frequency.value = 700; break;\n case \"wrong\": osc.frequency.value = 200; break;\n }\n gain.gain.setValueAtTime(0.1, ctx.currentTime);\n osc.start();\n osc.stop(ctx.currentTime + 0.2);\n };\n\n // =========================\n // CONFETTI EFFECT\n // =========================\n const triggerConfetti = () => {\n const duration = 2500;\n const end = Date.now() + duration;\n const colors = [\"#ec4c44\", \"#f7c948\", \"#6fcf97\", \"#56ccf2\", \"#bb6bd9\"];\n\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\")!;\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n canvas.style.position = \"fixed\";\n canvas.style.top = \"0\";\n canvas.style.left = \"0\";\n canvas.style.pointerEvents = \"none\";\n document.body.appendChild(canvas);\n\n const pieces = Array.from({ length: 100 }).map(() => ({\n x: Math.random() * canvas.width,\n y: Math.random() * canvas.height - canvas.height,\n size: 6 + Math.random() * 6,\n color: colors[Math.floor(Math.random() * colors.length)],\n speed: 2 + Math.random() * 4,\n tilt: Math.random() * 2 * Math.PI,\n }));\n\n const draw = () => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n pieces.forEach((p) => {\n ctx.fillStyle = p.color;\n ctx.beginPath();\n ctx.ellipse(p.x, p.y, p.size, p.size / 2, p.tilt, 0, 2 * Math.PI);\n ctx.fill();\n p.y += p.speed;\n p.x += Math.sin(p.tilt);\n });\n if (Date.now() < end) requestAnimationFrame(draw);\n else document.body.removeChild(canvas);\n };\n draw();\n };\n\n // =========================\n // RENDER SCREENS\n // =========================\n const renderSelect = () => (\n <div style={styles.gmCenterScreen}>\n <h1 style={styles.gmHeadline1}>MAGIC SENTENCE</h1>\n <p style={styles.gmBodyM}>Select number of rounds</p>\n <div style={{ \n display: \"flex\", \n gap: isMobileDevice() ? \"8px\" : \"16px\",\n justifyContent: \"center\" \n }}>\n {[3, 4, 5].map((num) => (\n <button \n key={num} \n onClick={() => handleSelect(num)} \n style={{\n ...styles.gmButton,\n ...getMobileButtonStyles('medium')\n }}\n >\n {num} ROUNDS\n </button>\n ))}\n </div>\n </div>\n );\n\n const renderTime = () => (\n <div style={styles.gmCenterScreen}>\n <h1 style={styles.gmHeadline1}>MAGIC SENTENCE</h1>\n <p style={styles.gmBodyM}>Select time per round</p>\n <div style={{ \n display: \"flex\", \n gap: isMobileDevice() ? \"8px\" : \"16px\",\n justifyContent: \"center\" \n }}>\n {[15, 20, 30].map((time) => (\n <button \n key={time} \n onClick={() => handleTimeSelect(time)} \n style={{\n ...styles.gmButton,\n ...getMobileButtonStyles('medium')\n }}\n >\n {time}s\n </button>\n ))}\n </div>\n </div>\n );\n\n const renderType = () => (\n <div style={styles.gmCenterScreen}>\n <h2 style={{...styles.gmBodyM, marginBottom: \"0px\"}}>\n Type down {rounds} sentence{rounds && rounds > 1 ? \"s\" : \"\"} for your student\n </h2>\n <p style={{...styles.gmBodyS, marginBottom: \"16px\", marginTop: \"0px\", color: \"#6b7280\"}}>\n Maximum 41 characters per sentence\n </p>\n <div style={{ \n display: \"flex\", \n flexDirection: \"column\", \n gap: 12, \n width: \"auto\", // Автоматическая ширина по содержимому\n minWidth: \"fit-content\", // Минимальная ширина по содержимому\n maxWidth: \"600px\" // Ограничиваем максимальную ширину\n }}>\n {sentences.map((text, i) => (\n <input\n key={i}\n value={text}\n placeholder={`Sentence ${i + 1}`}\n onChange={(e) => handleChange(i, e.target.value)}\n style={{\n ...styles.gmInput,\n padding: isMobileDevice() ? \"8px 12px\" : \"12px 16px\",\n fontSize: isMobileDevice() ? \"14px\" : \"16px\",\n width: \"100%\", // Поля теперь будут шире благодаря увеличенной ширине контейнера\n textAlign: \"center\" // Центрируем placeholder текст\n }}\n />\n ))}\n </div>\n <button\n onClick={startGame}\n disabled={sentences.some((s) => s.trim().length === 0)}\n style={{\n ...styles.gmButton,\n marginTop: 30,\n background: sentences.some((s) => s.trim().length === 0) ? \"#ccc\" : \"#ec4c44\",\n cursor: sentences.some((s) => s.trim().length === 0) ? \"not-allowed\" : \"pointer\",\n ...getMobileButtonStyles('large')\n }}\n >\n PLAY\n </button>\n </div>\n );\n\n const renderGetReady = () => (\n <div style={styles.gmReadyWrapper}>\n <h1 style={{ \n ...styles.gmHeadline1,\n color: \"#ec4c44\"\n }}>\n GET READY\n </h1>\n <div style={styles.gmHourglass}>⏳</div>\n </div>\n );\n\n const renderPlay = () => (\n <div style={styles.gmGameLayout}>\n <h2 style={{ \n marginBottom: isMobileDevice() ? \"5px\" : \"10px\",\n fontSize: isMobileDevice() ? \"16px\" : \"20px\"\n }}>\n Round {currentRound + 1}/{rounds} — {timeExpired ? \"TIME'S UP!\" : `Time: ${timeLeft}s`}\n </h2>\n\n {/* Progress bar */}\n <div\n style={{\n width: \"60%\",\n height: isMobileDevice() ? \"8px\" : \"14px\",\n borderRadius: 8,\n background: \"#eee\",\n overflow: \"hidden\",\n marginBottom: isMobileDevice() ? \"10px\" : \"20px\",\n }}\n >\n <div\n style={{\n height: \"100%\",\n width: `${(timeLeft / (timePerRound || 20)) * 100}%`,\n background: timeLeft <= 5 ? \"#ec4c44\" : \"#4caf50\",\n transition: \"width 1s linear\",\n }}\n ></div>\n </div>\n\n {/* Word bank */}\n <div\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => handleDropTo(e, \"bank\", null)}\n style={{\n display: \"flex\",\n flexWrap: shouldWrapWords() ? \"wrap\" : \"nowrap\",\n gap: isMobile || window.innerWidth < 768 ? \"6px\" : \"10px\",\n justifyContent: \"center\",\n marginBottom: isMobile || window.innerWidth < 768 ? \"15px\" : \"30px\",\n padding: isMobile || window.innerWidth < 768 ? \"5px\" : \"10px\",\n width: \"100%\",\n boxSizing: \"border-box\"\n }}\n >\n {words.map((t, idx) => (\n <div\n key={t.id}\n draggable={!timeExpired}\n role=\"button\"\n tabIndex={timeExpired ? -1 : 0}\n aria-label={timeExpired ? `Word: ${t.text} (time expired)` : createAriaLabel(\"Drag word\", t.text, \"to build sentence\")}\n onDragStart={(e) => {\n if (timeExpired) {\n e.preventDefault();\n return;\n }\n e.dataTransfer.setData(\n \"application/x-token\",\n JSON.stringify({ from: \"bank\", id: t.id })\n );\n e.dataTransfer.setData(\"text/plain\", t.id);\n announceToScreenReader(`Dragging word: ${t.text}`);\n }}\n onKeyDown={(e) => {\n if (timeExpired) return;\n handleKeyDown(e, () => moveToken(\"bank\", \"selected\", t.id, null));\n }}\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => {\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const mid = rect.left + rect.width / 2;\n const insertIndex = e.clientX > mid ? idx + 1 : idx;\n setHover({ list: null, id: null, side: null });\n e.stopPropagation();\n handleDropTo(e, \"bank\", insertIndex);\n }}\n onDragEnter={(e) => {\n if (timeExpired) return;\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const mid = rect.left + rect.width / 2;\n setHover({ list: \"bank\", id: t.id, side: e.clientX > mid ? \"right\" : \"left\" });\n }}\n onDragLeave={() => setHover({ list: null, id: null, side: null })}\n onClick={() => {\n if (timeExpired) return;\n moveToken(\"bank\", \"selected\", t.id, null);\n }}\n style={{\n padding: isMobile || window.innerWidth < 768 ? \"6px 10px\" : \"10px 16px\",\n borderRadius: isMobile || window.innerWidth < 768 ? \"6px\" : \"10px\",\n border: \"1px solid #ccc\",\n background: timeExpired ? \"#f0f0f0\" : \"#f9f9f9\",\n cursor: timeExpired ? \"not-allowed\" : (hover.list === \"bank\" && hover.id === t.id ? \"grabbing\" : \"grab\"),\n fontSize: isMobile || window.innerWidth < 768 \n ? \"12px\" \n : (window.innerWidth >= 768 && window.innerWidth < 1300)\n ? \"14px\" // Меньше для планшетов Mini и Air\n : \"18px\", // Полный размер только для Pro и больших экранов\n ...(hover.list === \"bank\" && hover.id === t.id && hover.side === \"left\" ? { borderLeft: \"3px solid #3b82f6\" } : {}),\n ...(hover.list === \"bank\" && hover.id === t.id && hover.side === \"right\" ? { borderRight: \"3px solid #3b82f6\" } : {}),\n flexShrink: 0,\n flexBasis: \"auto\",\n opacity: timeExpired ? 0.6 : 1,\n transition: \"opacity 0.3s ease, transform 0.2s ease\",\n ...(hover.list === \"bank\" && hover.id === t.id ? { \n transform: \"scale(1.05)\",\n boxShadow: \"0 2px 8px rgba(59, 130, 246, 0.3)\"\n } : {}),\n }}\n >\n {t.text}\n </div>\n ))}\n </div>\n\n {/* Selected sentence */}\n <div\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => {\n const rect = e.currentTarget.getBoundingClientRect();\n const children = Array.from(e.currentTarget.children) as HTMLElement[];\n \n if (children.length === 0) {\n handleDropTo(e, \"selected\", 0);\n return;\n }\n \n let closestIdx = selected.length;\n let minDistance = Infinity;\n \n children.forEach((child, idx) => {\n const childRect = child.getBoundingClientRect();\n const childCenter = childRect.left + childRect.width / 2;\n const distance = Math.abs(e.clientX - childCenter);\n \n if (distance < minDistance) {\n minDistance = distance;\n closestIdx = e.clientX < childCenter ? idx : idx + 1;\n }\n });\n \n if (e.clientX > rect.right - 30) {\n closestIdx = selected.length;\n }\n if (e.clientX < rect.left + 30) {\n closestIdx = 0;\n }\n \n handleDropTo(e, \"selected\", closestIdx);\n }}\n style={{\n minHeight: isMobile || window.innerWidth < 768 ? \"50px\" : \"70px\",\n width: \"auto\", // Автоматическая ширина в зависимости от содержимого\n maxWidth: \"none\", // Убираем ограничение максимальной ширины\n minWidth: \"245px\", // Минимальная ширина для растягивания\n border: resultType === \"correct\" ? \"2px dashed #4caf50\" : \n resultType === \"almost\" ? \"2px dashed #ff9800\" : \n resultType === \"wrong\" ? \"2px dashed #f44336\" : \"2px dashed #ccc\",\n borderRadius: isMobile || window.innerWidth < 768 ? \"8px\" : \"12px\",\n padding: isMobile || window.innerWidth < 768 ? \"8px\" : \"12px\",\n display: \"flex\",\n flexWrap: shouldWrapWords() ? \"wrap\" : \"nowrap\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: `${getAdaptiveFontSize(selected.length)}px`,\n background: resultType === \"correct\" ? \"#e8f5e8\" : \n resultType === \"almost\" ? \"#fff3e0\" : \n resultType === \"wrong\" ? \"#ffebee\" : \"#fafafa\",\n overflowX: shouldWrapWords() ? \"hidden\" : \"auto\",\n whiteSpace: shouldWrapWords() ? \"normal\" : \"nowrap\",\n }}\n >\n {selected.map((t, idx) => (\n <span\n key={t.id}\n draggable={!timeExpired}\n onDragStart={(e) => {\n if (timeExpired) {\n e.preventDefault();\n return;\n }\n e.dataTransfer.setData(\n \"application/x-token\",\n JSON.stringify({ from: \"selected\", id: t.id })\n );\n e.dataTransfer.setData(\"text/plain\", t.id);\n }}\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => {\n const rect = (e.currentTarget as HTMLSpanElement).getBoundingClientRect();\n const mid = rect.left + rect.width / 2;\n const zoneWidth = rect.width * 0.25;\n const insertIndex = e.clientX < mid - zoneWidth ? idx : \n e.clientX > mid + zoneWidth ? idx + 1 : \n e.clientX > mid ? idx + 1 : idx;\n setHover({ list: null, id: null, side: null });\n e.stopPropagation();\n handleDropTo(e, \"selected\", insertIndex);\n }}\n onDragEnter={(e) => {\n if (timeExpired) return;\n const rect = (e.currentTarget as HTMLSpanElement).getBoundingClientRect();\n const mid = rect.left + rect.width / 2;\n setHover({ list: \"selected\", id: t.id, side: e.clientX > mid ? \"right\" : \"left\" });\n }}\n onDragLeave={() => setHover({ list: null, id: null, side: null })}\n onClick={() => {\n if (timeExpired) return;\n moveToken(\"selected\", \"bank\", t.id, null);\n }}\n title={timeExpired ? \"Time expired\" : \"Click to remove back to bank\"}\n style={{\n padding: isMobileDevice() ? \"4px 6px\" : \"6px 10px\",\n margin: isMobileDevice() ? \"2px\" : \"4px\",\n borderRadius: isMobileDevice() ? \"4px\" : \"8px\",\n background: timeExpired ? \"#f0f0f0\" : \"#ffe9e7\",\n border: timeExpired ? \"1px solid #ccc\" : \"1px solid #ec4c44\",\n ...(hover.list === \"selected\" && hover.id === t.id && hover.side === \"left\" ? { borderLeft: \"3px solid #3b82f6\" } : {}),\n ...(hover.list === \"selected\" && hover.id === t.id && hover.side === \"right\" ? { borderRight: \"3px solid #3b82f6\" } : {}),\n cursor: timeExpired ? \"not-allowed\" : (hover.list === \"selected\" && hover.id === t.id ? \"grabbing\" : \"grab\"),\n userSelect: \"none\",\n fontSize: `${getAdaptiveFontSize(selected.length)}px`, // Адаптивный размер шрифта для слов\n fontFamily: '\"Roboto\", system-ui, -apple-system, \"Segoe UI\", Roboto, Arial, sans-serif', // Более плотный шрифт\n whiteSpace: \"nowrap\", // Запрещаем перенос слов\n opacity: timeExpired ? 0.6 : 1,\n transition: \"opacity 0.3s ease, transform 0.2s ease\",\n ...(hover.list === \"selected\" && hover.id === t.id ? { \n transform: \"scale(1.05)\",\n boxShadow: \"0 2px 8px rgba(59, 130, 246, 0.3)\"\n } : {}),\n }}\n >\n {t.text}\n </span>\n ))}\n </div>\n\n <button\n onClick={() => handleNext(true)}\n disabled={!timeExpired && selected.length === 0}\n style={{\n marginTop: isMobileDevice() ? \"15px\" : \"30px\",\n fontSize: isMobileDevice() ? \"14px\" : \"20px\",\n padding: isMobileDevice() ? \"6px 12px\" : \"10px 24px\",\n borderRadius: isMobileDevice() ? \"8px\" : \"12px\",\n background: (timeExpired || selected.length > 0) ? \"#ec4c44\" : \"#ccc\",\n color: \"white\",\n border: \"none\",\n cursor: (timeExpired || selected.length > 0) ? \"pointer\" : \"not-allowed\",\n }}\n >\n {timeExpired ? \"NEXT\" : \"NEXT\"}\n </button>\n </div>\n );\n\n const renderResults = () => (\n <div style={styles.gmCenterScreen}>\n <h1 style={{\n ...styles.gmHeadline1,\n marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"0px\" : \"0px\",\n marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"2px\" : \"10px\",\n fontSize: (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"32px\" : \"clamp(28px, 4vw, 40px)\"\n }}>\n Game Over 🎯\n </h1>\n <h2 style={{\n ...styles.gmHeadline2,\n marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"0px\" : \"0px\",\n marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"2px\" : \"16px\",\n fontSize: (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"18px\" : \"24px\"\n }}>\n Your score: {score} out of {rounds}\n </h2>\n <p style={{ \n ...styles.gmBodyM, \n color: \"#10b981\", \n marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"0px\" : \"12px\",\n marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? \"2px\" : \"16px\",\n fontSize: (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"14px\" : \"16px\"\n }}>\n Best score: {bestScore}\n </p>\n\n <div style={{ \n display: \"flex\", \n gap: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"6px\" : \"12px\", \n marginTop: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? \"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\"\n }}>\n <button\n onClick={() => {\n triggerConfetti();\n playSound(\"start\");\n setTimeout(() => {\n setStage(\"getready\");\n setCountdown(null);\n setTimeExpired(false);\n }, 800);\n }}\n style={{\n ...styles.gmButton,\n ...getMobileButtonStyles('medium')\n }}\n >\n 🔁 Play again\n </button>\n\n <button\n onClick={() => {\n playSound(\"click\");\n setStage(\"select\");\n setRounds(null);\n setTimePerRound(null);\n setSentences([]);\n setScore(0);\n setSelected([]);\n setTimeExpired(false);\n }}\n style={{\n ...styles.gmButton,\n ...getMobileButtonStyles('medium')\n }}\n >\n ⬅️ Exit\n </button>\n </div>\n </div>\n );\n\n // Условие для отображения логотипа (на desktop и планшетах)\n const shouldShowLogo = !isMobile && showLogo;\n\n const MemoizedLogo = useMemo(\n () => {\n // Скрываем логотип на мобильных устройствах в landscape режиме и на малых экранах\n if ((isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700) {\n return null;\n }\n \n if (!shouldShowLogo) {\n return null;\n }\n \n const logoBaseUrl = logoUrl || (typeof window !== 'undefined' && window.origin \n ? `${window.origin}/cloud/speakid/games/magic%20sentence/logo`\n : \"/cloud/speakid/games/magic%20sentence/logo\");\n \n return (\n // ensure logo is positioned inside the square container\n <div style={{ ...styles.gmLogoFixed, position: \"absolute\", top: 16, left: 16, zIndex: 30 }}>\n <picture>\n <source\n srcSet={`${logoBaseUrl}.svg`}\n type=\"image/svg+xml\"\n />\n <img\n src={`${logoBaseUrl}.png`}\n alt=\"SPEAKID Logo\"\n style={styles.gmLogoImg}\n loading=\"lazy\"\n />\n </picture>\n </div>\n );\n },\n [isMobile, shouldShowLogo, logoUrl]\n );\n\n return (\n <div\n ref={containerRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n background: \"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)\",\n transition: \"background 0.3s ease\",\n overflow: \"hidden\",\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n }}\n >\n <div\n style={{\n width: isMobile ? \"100%\" : (containerSize || 1000),\n height: isMobile ? \"100%\" : (containerSize || 1000),\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n overflow: \"hidden\",\n borderRadius: isMobile ? 0 : \"20px\",\n background: \"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)\",\n boxShadow: isMobile ? \"none\" : \"0 0 40px rgba(0,0,0,0.1)\",\n margin: isMobile ? \"0 auto\" : \"unset\",\n position: \"relative\", // needed so absolute logo is inside the square\n transform: `scale(${scale})`, // ✅ Применяем масштаб для широких экранов\n }}\n >\n <div\n style={{\n transform: \"translateZ(0)\",\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <div id=\"magic-sentence-root\">\n {MemoizedLogo}\n {stage === \"select\" ? renderSelect() : null}\n {stage === \"time\" ? renderTime() : null}\n {stage === \"type\" ? renderType() : null}\n {stage === \"getready\" ? renderGetReady() : null}\n {stage === \"play\" ? renderPlay() : null}\n {stage === \"results\" ? renderResults() : null}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nexport default GameComponent;","import React, { Component, ErrorInfo, ReactNode } from 'react';\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n error?: Error;\n errorInfo?: ErrorInfo;\n}\n\nexport class ErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(error: Error): State {\n return {\n hasError: true,\n error,\n };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n console.error('Game Error:', error, errorInfo);\n \n // В реальном приложении здесь можно отправить ошибку в аналитику\n // analytics.track('game_error', { error: error.message, stack: error.stack });\n \n this.setState({\n error,\n errorInfo,\n });\n }\n\n handleReset = () => {\n this.setState({ hasError: false, error: undefined, errorInfo: undefined });\n };\n\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n return (\n <div style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '100vh',\n padding: '20px',\n textAlign: 'center',\n backgroundColor: '#fef2f2',\n color: '#dc2626',\n fontFamily: 'system-ui, sans-serif'\n }}>\n <h1 style={{ fontSize: '24px', marginBottom: '16px' }}>\n 🚨 Oops! Something went wrong\n </h1>\n <p style={{ fontSize: '16px', marginBottom: '24px', maxWidth: '500px' }}>\n The game encountered an unexpected error. Don't worry, your progress is saved!\n </p>\n \n <button\n onClick={this.handleReset}\n style={{\n padding: '12px 24px',\n fontSize: '16px',\n backgroundColor: '#dc2626',\n color: 'white',\n border: 'none',\n borderRadius: '8px',\n cursor: 'pointer',\n transition: 'background-color 0.2s'\n }}\n onMouseOver={(e) => e.currentTarget.style.backgroundColor = '#b91c1c'}\n onMouseOut={(e) => e.currentTarget.style.backgroundColor = '#dc2626'}\n >\n 🔄 Restart Game\n </button>\n \n {typeof process !== 'undefined' && process.env.NODE_ENV === 'development' && this.state.error && (\n <details style={{ marginTop: '20px', textAlign: 'left', maxWidth: '600px' }}>\n <summary style={{ cursor: 'pointer', fontSize: '14px' }}>\n Technical Details (Development Only)\n </summary>\n <pre style={{\n backgroundColor: '#f3f4f6',\n padding: '12px',\n borderRadius: '4px',\n fontSize: '12px',\n overflow: 'auto',\n marginTop: '8px'\n }}>\n {this.state.error.toString()}\n {this.state.errorInfo?.componentStack}\n </pre>\n </details>\n )}\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n"],"names":["keyframes","styleTag","animations","styles","useValidation","errors","setErrors","useState","validateSentence","useCallback","sentence","index","allSentences","newErrors","duplicateIndex","s","i","validateAllSentences","sentences","allErrors","result","error","clearErrors","createAriaLabel","action","word","context","handleKeyDown","event","allowedKeys","announceToScreenReader","message","announcement","globalReset","style","existingStyle","shuffle","arr","GameComponent","props","logoUrl","showLogo","containerRef","useRef","validationErrors","shouldWrapWords","isMobile","isMobileDevice","getMobileButtonStyles","type","useEffect","stage","setStage","rounds","setRounds","timePerRound","setTimePerRound","setSentences","currentRound","setCurrentRound","words","setWords","selected","setSelected","timeLeft","setTimeLeft","score","setScore","countdown","setCountdown","resultType","setResultType","timeExpired","setTimeExpired","isCorrect","setIsCorrect","bestScore","setBestScore","timerRef","roundsRef","currentRoundRef","hover","setHover","setIsMobile","scale","setScale","containerSize","setContainerSize","isDesktopLayout","setIsDesktopLayout","isIPadMiniPortrait","setIsIPadMiniPortrait","isIPadMiniLandscape","setIsIPadMiniLandscape","isIPadAirPortrait","setIsIPadAirPortrait","isIPadAirLandscape","setIsIPadAirLandscape","isSurfaceDuoPortrait","setIsSurfaceDuoPortrait","isSurfaceDuoLandscape","setIsSurfaceDuoLandscape","isIPadProPortrait","setIsIPadProPortrait","isIPadProLandscape","setIsIPadProLandscape","isHorizontalLayout","setIsHorizontalLayout","isWideScreen","setIsWideScreen","resize","width","height","mobile","isSmallHeight","desktopLayout","isHorizontal","finalSize","moveToken","from","to","id","insertIndex","nextWords","nextSelected","takeFrom","putTo","idx","t","token","targetIndex","handleDropTo","raw","fallbackId","inBank","inSelected","payload","handleSelect","num","handleTimeSelect","time","handleChange","value","updated","validation","normalizeSentence","getAdaptiveFontSize","wordCount","isTablet","startGame","prev","startRound","target","tokens","w","correct","correctWords","selectedTexts","missingWords","extraWords","wrongOrder","playSound","handleNext","manual","triggerConfetti","ctx","osc","gain","end","colors","canvas","pieces","draw","p","renderSelect","jsxs","jsx","renderTime","renderType","text","e","renderGetReady","renderPlay","rect","mid","children","closestIdx","minDistance","child","childRect","childCenter","distance","zoneWidth","renderResults","shouldShowLogo","MemoizedLogo","useMemo","logoBaseUrl","ErrorBoundary","Component","errorInfo"],"mappings":"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"}