Загрузка данных


```html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>🌈 Путешествие в мир фигур</title>
<style>
  /* ============================================
     БАЗОВЫЕ СТИЛИ И ПЕРЕМЕННЫЕ
     ============================================ */
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
    user-select: none;
  }

  :root {
    --bg-main: #FFF5F7;
    --bg-card: #FFFFFF;
    --pink: #FFB5C5;
    --pink-dark: #FF8FA8;
    --blue: #B5D8FF;
    --blue-dark: #8FC0FF;
    --mint: #B5EBCD;
    --mint-dark: #8FD9B0;
    --lavender: #E8D5FF;
    --lavender-dark: #C9A8FF;
    --peach: #FFD5B5;
    --peach-dark: #FFB88A;
    --yellow: #FFF3B0;
    --text-dark: #4A4A6A;
    --text-light: #7A7A9A;
    --success: #A8E6CF;
    --success-dark: #6BC99A;
    --error: #FFB3BA;
    --error-dark: #FF8A95;
    --shadow: 0 8px 24px rgba(74, 74, 106, 0.12);
    --shadow-hover: 0 12px 32px rgba(74, 74, 106, 0.2);
  }

  html, body {
    height: 100%;
    overflow: hidden;
  }

  body {
    font-family: 'Comic Sans MS', 'Chalkboard SE', 'Marker Felt', system-ui, sans-serif;
    background: linear-gradient(135deg, #FFF5F7 0%, #F0F7FF 50%, #F5FFF5 100%);
    color: var(--text-dark);
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 10px;
    position: relative;
    overflow: hidden;
  }

  /* Декоративные плавающие фигуры на фоне */
  .bg-shape {
    position: fixed;
    opacity: 0.15;
    pointer-events: none;
    z-index: 0;
    animation: float 20s infinite ease-in-out;
  }

  .bg-shape:nth-child(1) { top: 10%; left: 5%; width: 80px; height: 80px; background: var(--pink); border-radius: 50%; animation-delay: 0s; }
  .bg-shape:nth-child(2) { top: 70%; left: 85%; width: 100px; height: 100px; background: var(--blue); animation-delay: -5s; }
  .bg-shape:nth-child(3) { top: 80%; left: 10%; width: 70px; height: 70px; background: var(--mint); clip-path: polygon(50% 0%, 0% 100%, 100% 100%); animation-delay: -10s; }
  .bg-shape:nth-child(4) { top: 20%; left: 90%; width: 90px; height: 60px; background: var(--lavender); border-radius: 50%; animation-delay: -15s; }
  .bg-shape:nth-child(5) { top: 45%; left: 3%; width: 60px; height: 80px; background: var(--peach); animation-delay: -7s; }

  @keyframes float {
    0%, 100% { transform: translate(0, 0) rotate(0deg); }
    25% { transform: translate(20px, -30px) rotate(10deg); }
    50% { transform: translate(-15px, 20px) rotate(-10deg); }
    75% { transform: translate(25px, 15px) rotate(5deg); }
  }

  /* ============================================
     КОНТЕЙНЕР ПРИЛОЖЕНИЯ
     ============================================ */
  #app {
    width: 100%;
    max-width: 900px;
    height: 100vh;
    max-height: 700px;
    position: relative;
    z-index: 1;
  }

  .screen {
    position: absolute;
    inset: 0;
    display: none;
    flex-direction: column;
    padding: 20px;
    animation: fadeIn 0.5s ease-out;
  }

  .screen.active {
    display: flex;
  }

  @keyframes fadeIn {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
  }

  /* ============================================
     СТАРТОВЫЙ ЭКРАН
     ============================================ */
  #start-screen {
    justify-content: center;
    align-items: center;
    text-align: center;
    gap: 20px;
  }

  .mascot {
    font-size: clamp(80px, 15vw, 140px);
    animation: bounce 2s infinite ease-in-out;
    filter: drop-shadow(0 8px 16px rgba(0,0,0,0.1));
  }

  @keyframes bounce {
    0%, 100% { transform: translateY(0) rotate(-5deg); }
    50% { transform: translateY(-15px) rotate(5deg); }
  }

  .title {
    font-size: clamp(28px, 5vw, 44px);
    color: var(--text-dark);
    font-weight: bold;
    text-shadow: 2px 2px 0 var(--pink);
    line-height: 1.2;
  }

  .subtitle {
    font-size: clamp(16px, 2.5vw, 20px);
    color: var(--text-light);
    max-width: 500px;
    line-height: 1.5;
  }

  .btn {
    padding: 16px 40px;
    font-size: clamp(18px, 3vw, 24px);
    font-family: inherit;
    font-weight: bold;
    border: none;
    border-radius: 50px;
    cursor: pointer;
    transition: all 0.2s ease;
    box-shadow: var(--shadow);
    color: white;
    background: linear-gradient(135deg, var(--pink-dark), var(--pink));
    position: relative;
    overflow: hidden;
  }

  .btn:hover {
    transform: translateY(-3px) scale(1.03);
    box-shadow: var(--shadow-hover);
  }

  .btn:active {
    transform: translateY(0) scale(0.98);
  }

  .btn-secondary {
    background: linear-gradient(135deg, var(--blue-dark), var(--blue));
  }

  .btn-success {
    background: linear-gradient(135deg, var(--success-dark), var(--success));
  }

  .best-score {
    background: var(--bg-card);
    padding: 12px 24px;
    border-radius: 30px;
    box-shadow: var(--shadow);
    font-size: 16px;
    color: var(--text-light);
  }

  .best-score span {
    color: var(--pink-dark);
    font-weight: bold;
    font-size: 20px;
  }

  /* ============================================
     ИГРОВОЙ ЭКРАН
     ============================================ */
  #game-screen {
    gap: 15px;
  }

  .game-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
  }

  .stat-box {
    background: var(--bg-card);
    padding: 10px 18px;
    border-radius: 20px;
    box-shadow: var(--shadow);
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: clamp(14px, 2vw, 18px);
    font-weight: bold;
    flex: 1;
    min-width: 100px;
    justify-content: center;
  }

  .stat-box .icon {
    font-size: 1.3em;
  }

  .stat-box.timer { color: var(--pink-dark); }
  .stat-box.progress { color: var(--blue-dark); }
  .stat-box.score { color: var(--mint-dark); }

  .timer.warning {
    color: var(--error-dark);
    animation: pulse 1s infinite;
  }

  @keyframes pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.05); }
  }

  .progress-bar {
    width: 100%;
    height: 12px;
    background: var(--bg-card);
    border-radius: 10px;
    overflow: hidden;
    box-shadow: inset 0 2px 4px rgba(0,0,0,0.05);
  }

  .progress-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--mint), var(--blue), var(--lavender));
    border-radius: 10px;
    transition: width 0.5s ease;
    width: 0%;
  }

  .question-area {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 15px;
    background: var(--bg-card);
    border-radius: 30px;
    padding: 20px;
    box-shadow: var(--shadow);
    overflow: hidden;
    position: relative;
  }

  .helper {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    animation: slideIn 0.5s ease-out;
  }

  @keyframes slideIn {
    from { opacity: 0; transform: translateX(-20px); }
    to { opacity: 1; transform: translateX(0); }
  }

  .helper-avatar {
    font-size: clamp(40px, 7vw, 60px);
    flex-shrink: 0;
    animation: wiggle 3s infinite ease-in-out;
  }

  @keyframes wiggle {
    0%, 100% { transform: rotate(-3deg); }
    50% { transform: rotate(3deg); }
  }

  .helper-bubble {
    background: linear-gradient(135deg, var(--yellow), #FFF9D6);
    padding: 14px 20px;
    border-radius: 20px;
    border-top-left-radius: 5px;
    font-size: clamp(15px, 2.3vw, 19px);
    line-height: 1.4;
    color: var(--text-dark);
    box-shadow: 0 4px 12px rgba(0,0,0,0.08);
    position: relative;
    flex: 1;
  }

  .canvas-container {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 15px;
    flex-wrap: wrap;
    padding: 10px;
    min-height: 150px;
  }

  .shape-canvas {
    border-radius: 20px;
    background: linear-gradient(135deg, #FAFAFF, #F5F0FF);
    box-shadow: inset 0 2px 8px rgba(0,0,0,0.05);
    max-width: 100%;
    touch-action: manipulation;
  }

  .shape-canvas.clickable {
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
  }

  .shape-canvas.clickable:hover {
    transform: scale(1.05);
    box-shadow: 0 8px 20px rgba(143, 192, 255, 0.4);
  }

  .shape-canvas.selected {
    box-shadow: 0 0 0 4px var(--success-dark), 0 8px 20px rgba(107, 201, 154, 0.4);
    transform: scale(1.05);
  }

  .options-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 12px;
    margin-top: 10px;
  }

  .option-btn {
    padding: 14px 18px;
    font-size: clamp(15px, 2.3vw, 19px);
    font-family: inherit;
    font-weight: bold;
    border: 3px solid transparent;
    border-radius: 20px;
    cursor: pointer;
    transition: all 0.2s ease;
    background: var(--bg-card);
    color: var(--text-dark);
    box-shadow: var(--shadow);
    min-height: 55px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
  }

  .option-btn:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: var(--shadow-hover);
    border-color: var(--blue);
  }

  .option-btn:active:not(:disabled) {
    transform: translateY(0);
  }

  .option-btn.correct {
    background: var(--success);
    border-color: var(--success-dark);
    color: var(--text-dark);
    animation: correctPop 0.5s ease;
  }

  .option-btn.wrong {
    background: var(--error);
    border-color: var(--error-dark);
    animation: shake 0.5s ease;
  }

  .option-btn:disabled {
    cursor: not-allowed;
    opacity: 0.7;
  }

  @keyframes correctPop {
    0% { transform: scale(1); }
    50% { transform: scale(1.1); }
    100% { transform: scale(1); }
  }

  @keyframes shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-8px); }
    75% { transform: translateX(8px); }
  }

  .feedback {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: clamp(40px, 8vw, 80px);
    pointer-events: none;
    opacity: 0;
    z-index: 10;
  }

  .feedback.show {
    animation: feedbackPop 1.2s ease-out forwards;
  }

  @keyframes feedbackPop {
    0% { opacity: 0; transform: translate(-50%, -50%) scale(0.3) rotate(-20deg); }
    30% { opacity: 1; transform: translate(-50%, -50%) scale(1.3) rotate(10deg); }
    60% { opacity: 1; transform: translate(-50%, -50%) scale(1) rotate(0deg); }
    100% { opacity: 0; transform: translate(-50%, -80%) scale(0.8); }
  }

  /* ============================================
     ЭКРАН РЕЗУЛЬТАТОВ
     ============================================ */
  #result-screen {
    justify-content: center;
    align-items: center;
    text-align: center;
    gap: 20px;
  }

  .result-mascot {
    font-size: clamp(80px, 15vw, 140px);
    animation: celebrate 1s ease-out;
  }

  @keyframes celebrate {
    0% { transform: scale(0) rotate(-180deg); }
    50% { transform: scale(1.2) rotate(180deg); }
    100% { transform: scale(1) rotate(360deg); }
  }

  .result-title {
    font-size: clamp(28px, 5vw, 44px);
    color: var(--text-dark);
    font-weight: bold;
  }

  .result-stats {
    display: flex;
    gap: 15px;
    flex-wrap: wrap;
    justify-content: center;
  }

  .result-stat {
    background: var(--bg-card);
    padding: 18px 24px;
    border-radius: 25px;
    box-shadow: var(--shadow);
    min-width: 130px;
  }

  .result-stat .label {
    font-size: 14px;
    color: var(--text-light);
    margin-bottom: 5px;
  }

  .result-stat .value {
    font-size: clamp(22px, 4vw, 32px);
    font-weight: bold;
    color: var(--pink-dark);
  }

  .stars {
    display: flex;
    gap: 8px;
    font-size: clamp(30px, 6vw, 50px);
    justify-content: center;
  }

  .star {
    opacity: 0.3;
    transition: all 0.5s ease;
  }

  .star.earned {
    opacity: 1;
    animation: starPop 0.6s ease backwards;
  }

  @keyframes starPop {
    0% { transform: scale(0) rotate(-180deg); }
    60% { transform: scale(1.3) rotate(20deg); }
    100% { transform: scale(1) rotate(0); }
  }

  .result-message {
    font-size: clamp(16px, 2.5vw, 20px);
    color: var(--text-light);
    max-width: 500px;
    line-height: 1.5;
    padding: 0 10px;
  }

  .result-buttons {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    justify-content: center;
  }

  /* Конфетти */
  .confetti {
    position: fixed;
    width: 10px;
    height: 10px;
    pointer-events: none;
    z-index: 100;
    animation: confettiFall 3s linear forwards;
  }

  @keyframes confettiFall {
    0% { transform: translateY(-100vh) rotate(0deg); opacity: 1; }
    100% { transform: translateY(100vh) rotate(720deg); opacity: 0; }
  }

  /* ============================================
     АДАПТИВНОСТЬ
     ============================================ */
  @media (max-width: 600px) {
    .game-header {
      gap: 6px;
    }
    .stat-box {
      padding: 8px 12px;
      min-width: 80px;
      font-size: 13px;
    }
    .question-area {
      padding: 14px;
      border-radius: 22px;
    }
    .options-grid {
      grid-template-columns: repeat(2, 1fr);
      gap: 8px;
    }
    .option-btn {
      padding: 12px 10px;
      min-height: 48px;
      font-size: 14px;
    }
    .canvas-container {
      gap: 8px;
      min-height: 120px;
    }
  }

  @media (max-height: 600px) {
    .mascot, .result-mascot { font-size: 70px; }
    .title, .result-title { font-size: 24px; }
    .subtitle, .result-message { font-size: 14px; }
  }

  /* Скрытый скроллбар */
  ::-webkit-scrollbar { width: 0; }
</style>
</head>
<body>

<!-- Декоративные фигуры на фоне -->
<div class="bg-shape"></div>
<div class="bg-shape"></div>
<div class="bg-shape"></div>
<div class="bg-shape"></div>
<div class="bg-shape"></div>

<div id="app">

  <!-- ========== СТАРТОВЫЙ ЭКРАН ========== -->
  <div id="start-screen" class="screen active">
    <div class="mascot">🦉</div>
    <h1 class="title">Путешествие в мир фигур</h1>
    <p class="subtitle">
      Привет, юный исследователь! Меня зовут Мудрая Сова.
      Я приглашаю тебя в увлекательное путешествие, где мы будем изучать
      геометрические фигуры! 🎨✨
    </p>
    <div class="best-score" id="best-score-box" style="display:none;">
      🏆 Твой лучший результат: <span id="best-score-value">0</span>
    </div>
    <button class="btn" id="start-btn">🚀 Начать игру</button>
  </div>

  <!-- ========== ИГРОВОЙ ЭКРАН ========== -->
  <div id="game-screen" class="screen">
    <div class="game-header">
      <div class="stat-box timer" id="timer-box">
        <span class="icon">⏰</span>
        <span id="timer">15:00</span>
      </div>
      <div class="stat-box progress">
        <span class="icon">📍</span>
        <span id="progress">1/10</span>
      </div>
      <div class="stat-box score">
        <span class="icon">⭐</span>
        <span id="score">0</span>
      </div>
    </div>

    <div class="progress-bar">
      <div class="progress-fill" id="progress-fill"></div>
    </div>

    <div class="question-area" id="question-area">
      <div class="helper">
        <div class="helper-avatar">🦉</div>
        <div class="helper-bubble" id="helper-text">Задание появится здесь...</div>
      </div>
      <div class="canvas-container" id="canvas-container"></div>
      <div class="options-grid" id="options-grid"></div>
      <div class="feedback" id="feedback"></div>
    </div>
  </div>

  <!-- ========== ЭКРАН РЕЗУЛЬТАТОВ ========== -->
  <div id="result-screen" class="screen">
    <div class="result-mascot" id="result-mascot">🎉</div>
    <h1 class="result-title" id="result-title">Молодец!</h1>
    <div class="stars" id="stars">
      <span class="star">⭐</span>
      <span class="star">⭐</span>
      <span class="star">⭐</span>
      <span class="star">⭐</span>
      <span class="star">⭐</span>
    </div>
    <div class="result-stats">
      <div class="result-stat">
        <div class="label">Очки</div>
        <div class="value" id="final-score">0</div>
      </div>
      <div class="result-stat">
        <div class="label">Правильно</div>
        <div class="value" id="final-correct">0/10</div>
      </div>
      <div class="result-stat">
        <div class="label">Время</div>
        <div class="value" id="final-time">00:00</div>
      </div>
    </div>
    <p class="result-message" id="result-message">Отличная работа!</p>
    <div class="result-buttons">
      <button class="btn" id="restart-btn">🔄 Играть снова</button>
      <button class="btn btn-secondary" id="home-btn">🏠 В начало</button>
    </div>
  </div>

</div>

<script>
/* ============================================
   КОНФИГУРАЦИЯ И КОНСТАНТЫ
   ============================================ */
const CONFIG = {
  TOTAL_QUESTIONS: 10,
  GAME_DURATION: 15 * 60, // 15 минут в секундах
  POINTS_PER_CORRECT: 10,
  BONUS_TIME: 5, // бонус за быстрый ответ
  FAST_ANSWER_THRESHOLD: 5, // секунд для бонуса
  STORAGE_KEY: 'shapes_game_best_score'
};

// Палитра цветов для фигур
const COLORS = {
  red: '#FF8A95',
  blue: '#8FC0FF',
  green: '#8FD9B0',
  yellow: '#FFE08A',
  purple: '#C9A8FF',
  pink: '#FFB5C5',
  orange: '#FFB88A'
};

const COLOR_NAMES = {
  red: 'красный',
  blue: 'синий',
  green: 'зелёный',
  yellow: 'жёлтый',
  purple: 'фиолетовый',
  pink: 'розовый',
  orange: 'оранжевый'
};

// Типы фигур
const SHAPES = ['circle', 'square', 'triangle', 'rectangle', 'oval'];

const SHAPE_NAMES = {
  circle: 'круг',
  square: 'квадрат',
  triangle: 'треугольник',
  rectangle: 'прямоугольник',
  oval: 'овал'
};

const SHAPE_PROPERTIES = {
  circle:      { corners: 0, sides: 0,  hasCorners: false },
  square:      { corners: 4, sides: 4,  hasCorners: true },
  triangle:    { corners: 3, sides: 3,  hasCorners: true },
  rectangle:   { corners: 4, sides: 4,  hasCorners: true },
  oval:        { corners: 0, sides: 0,  hasCorners: false }
};

/* ============================================
   СОСТОЯНИЕ ИГРЫ
   ============================================ */
const GameState = {
  currentQuestion: 0,
  score: 0,
  correctAnswers: 0,
  timeLeft: CONFIG.GAME_DURATION,
  timerInterval: null,
  questionStartTime: 0,
  questions: [],
  isAnswering: false,
  startTime: 0
};

/* ============================================
   УТИЛИТЫ
   ============================================ */
const Utils = {
  // Случайный элемент массива
  random(arr) {
    return arr[Math.floor(Math.random() * arr.length)];
  },

  // Случайный элемент, исключая указанный
  randomExcept(arr, excluded) {
    const filtered = arr.filter(x => x !== excluded);
    return filtered[Math.floor(Math.random() * filtered.length)];
  },

  // Перемешать массив
  shuffle(arr) {
    const a = [...arr];
    for (let i = a.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [a[i], a[j]] = [a[j], a[i]];
    }
    return a;
  },

  // Уникальные случайные элементы
  uniqueRandom(arr, count) {
    const shuffled = this.shuffle(arr);
    return shuffled.slice(0, count);
  },

  // Форматирование времени
  formatTime(seconds) {
    const m = Math.floor(seconds / 60);
    const s = seconds % 60;
    return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
  },

  // Случайный цвет
  randomColor() {
    return Utils.random(Object.keys(COLORS));
  },

  // Случайная фигура
  randomShape() {
    return Utils.random(SHAPES);
  },

  // Случайный размер (большой/маленький)
  randomSize() {
    return Math.random() < 0.5 ? 'big' : 'small';
  }
};

/* ============================================
   РЕНДЕРИНГ ФИГУР НА CANVAS
   ============================================ */
const Renderer = {
  // Отрисовка одной фигуры
  drawShape(canvas, shape, color, size = 'big', clickable = false) {
    const ctx = canvas.getContext('2d');
    const w = canvas.width;
    const h = canvas.height;
    const scale = size === 'big' ? 1 : 0.6;

    ctx.clearRect(0, 0, w, h);

    const cx = w / 2;
    const cy = h / 2;
    const baseSize = Math.min(w, h) * 0.35 * scale;

    // Тень
    ctx.shadowColor = 'rgba(74, 74, 106, 0.2)';
    ctx.shadowBlur = 8;
    ctx.shadowOffsetY = 4;

    ctx.fillStyle = COLORS[color];
    ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)';
    ctx.lineWidth = 3;

    ctx.beginPath();

    switch (shape) {
      case 'circle':
        ctx.arc(cx, cy, baseSize, 0, Math.PI * 2);
        break;

      case 'square':
        const s = baseSize * 1.5;
        ctx.rect(cx - s/2, cy - s/2, s, s);
        break;

      case 'rectangle':
        const rw = baseSize * 2;
        const rh = baseSize * 1.3;
        ctx.rect(cx - rw/2, cy - rh/2, rw, rh);
        break;

      case 'triangle':
        const ts = baseSize * 1.7;
        ctx.moveTo(cx, cy - ts/2);
        ctx.lineTo(cx - ts/2, cy + ts/2);
        ctx.lineTo(cx + ts/2, cy + ts/2);
        ctx.closePath();
        break;

      case 'oval':
        ctx.ellipse(cx, cy, baseSize * 1.5, baseSize, 0, 0, Math.PI * 2);
        break;
    }

    ctx.fill();
    ctx.shadowColor = 'transparent';
    ctx.stroke();

    // Блик
    ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
    ctx.beginPath();
    if (shape === 'circle' || shape === 'oval') {
      ctx.ellipse(cx - baseSize * 0.3, cy - baseSize * 0.3, baseSize * 0.3, baseSize * 0.2, 0, 0, Math.PI * 2);
    } else {
      ctx.ellipse(cx - baseSize * 0.3, cy - baseSize * 0.3, baseSize * 0.25, baseSize * 0.15, 0, 0, Math.PI * 2);
    }
    ctx.fill();
  },

  // Создать canvas элемент
  createCanvas(width, height, clickable = false) {
    const canvas = document.createElement('canvas');
    const dpr = window.devicePixelRatio || 1;
    canvas.width = width * dpr;
    canvas.height = height * dpr;
    canvas.style.width = width + 'px';
    canvas.style.height = height + 'px';
    canvas.className = 'shape-canvas';
    if (clickable) canvas.classList.add('clickable');
    const ctx = canvas.getContext('2d');
    ctx.scale(dpr, dpr);
    return canvas;
  }
};

/* ============================================
   ГЕНЕРАТОР ЗАДАНИЙ
   ============================================ */
const QuestionGenerator = {
  // Все типы заданий
  generators: [],

  init() {
    this.generators = [
      this.genNameTheShape.bind(this),      // 1. Назови фигуру
      this.genFindShape.bind(this),         // 2. Найди фигуру
      this.genCountCorners.bind(this),      // 3. Сколько углов?
      this.genFindOdd.bind(this),           // 4. Найди лишнюю
      this.genChooseByProperty.bind(this),  // 5. Выбери по признаку
      this.genCompareShapes.bind(this),     // 6. Сравни фигуры
      this.genFindByDescription.bind(this), // 7. Найди по описанию
      this.genFindPair.bind(this),          // 8. Найди пару
      this.genGuessBySigns.bind(this),      // 9. Угадай по признакам
      this.genCollectAll.bind(this)         // 10. Собери все
    ];
  },

  // 1. Назови фигуру - показывается фигура, нужно выбрать название
  genNameTheShape(difficulty) {
    const shape = Utils.randomShape();
    const color = Utils.randomColor();
    const size = Utils.randomSize();

    // Варианты ответов - название фигуры + 3 других
    const wrongOptions = Utils.uniqueExcept(SHAPES, shape, 3);
    const options = Utils.shuffle([shape, ...wrongOptions]).map(s => SHAPE_NAMES[s]);
    const correct = SHAPE_NAMES[shape];

    return {
      type: 'name',
      helper: `Как называется эта фигура? 🤔`,
      shape: { type: shape, color, size },
      options,
      correct,
      points: 10
    };
  },

  // 2. Найди фигуру - называется фигура, нужно кликнуть
  genFindShape(difficulty) {
    const target = Utils.randomShape();
    const targetColor = Utils.randomColor();

    // Создаём 4 фигуры, одна из них - целевая
    const shapes = [];
    const usedShapes = [target];
    shapes.push({ type: target, color: targetColor, size: 'big', isTarget: true });

    while (shapes.length < 4) {
      const s = Utils.randomShape();
      const c = Utils.randomColor();
      // Чтобы не было слишком просто - хотя бы цвет или тип отличается
      if (s !== target || c !== targetColor) {
        shapes.push({ type: s, color: c, size: Utils.randomSize(), isTarget: false });
      }
    }

    return {
      type: 'find',
      helper: `Найди и нажми на ${SHAPE_NAMES[target]}! 👆`,
      shapes: Utils.shuffle(shapes),
      correctIndex: shapes.findIndex(s => s.isTarget),
      points: 10
    };
  },

  // 3. Сколько углов?
  genCountCorners(difficulty) {
    // Выбираем фигуру с углами (не круг и не овал)
    const shapesWithCorners = ['square', 'triangle', 'rectangle'];
    const shape = Utils.random(shapesWithCorners);
    const color = Utils.randomColor();

    const correct = SHAPE_PROPERTIES[shape].corners;
    const options = Utils.shuffle([0, 3, 4, 5]).map(String);

    return {
      type: 'corners',
      helper: `Сколько углов у этой фигуры? 🔺`,
      shape: { type: shape, color, size: 'big' },
      options,
      correct: String(correct),
      points: 10
    };
  },

  // 4. Найди лишнюю фигуру в ряду
  genFindOdd(difficulty) {
    // Три фигуры одного типа, одна - другого
    const mainType = Utils.randomShape();
    const oddType = Utils.randomExcept(SHAPES, mainType);
    const color = Utils.randomColor();

    const shapes = [];
    const oddIndex = Math.floor(Math.random() * 4);

    for (let i = 0; i < 4; i++) {
      if (i === oddIndex) {
        shapes.push({ type: oddType, color, size: 'big', isOdd: true });
      } else {
        shapes.push({ type: mainType, color, size: 'big', isOdd: false });
      }
    }

    return {
      type: 'odd',
      helper: `Какая фигура здесь лишняя? Она не такая, как остальные! 🔍`,
      shapes,
      correctIndex: oddIndex,
      points: 10
    };
  },

  // 5. Выбери все фигуры с определённым признаком
  genChooseByProperty(difficulty) {
    // Признак: "без углов" (круг и овал) или "с углами"
    const hasCorners = Math.random() < 0.5;
    const description = hasCorners ? 'у которых есть углы' : 'у которых нет углов';

    const shapes = [];
    const correctIndices = [];

    // 6 фигур, некоторые подходят, некоторые нет
    for (let i = 0; i < 6; i++) {
      const type = Utils.randomShape();
      const color = Utils.randomColor();
      const fits = SHAPE_PROPERTIES[type].hasCorners === hasCorners;
      shapes.push({ type, color, size: 'small', fits });
      if (fits) correctIndices.push(i);
    }

    // Гарантируем, что есть хотя бы 1 подходящая и 1 неподходящая
    if (correctIndices.length === 0) {
      const targetType = hasCorners ? 'square' : 'circle';
      shapes[0] = { type: targetType, color: Utils.randomColor(), size: 'small', fits: true };
      correctIndices.push(0);
    }

    return {
      type: 'choose',
      helper: `Выбери все фигуры ${description}! Нажимай на каждую подходящую 👆`,
      shapes,
      correctIndices,
      points: 15
    };
  },

  // 6. Сравни фигуры - у какой больше углов
  genCompareShapes(difficulty) {
    const shapesWithCorners = ['square', 'triangle', 'rectangle'];
    const shape1 = Utils.random(shapesWithCorners);
    let shape2 = Utils.randomExcept(shapesWithCorners, shape1);

    const color1 = Utils.randomColor();
    const color2 = Utils.randomExcept(Object.keys(COLORS), color1);

    const c1 = SHAPE_PROPERTIES[shape1].corners;
    const c2 = SHAPE_PROPERTIES[shape2].corners;

    let correct;
    if (c1 > c2) correct = 'первая';
    else if (c2 > c1) correct = 'вторая';
    else correct = 'одинаково';

    return {
      type: 'compare',
      helper: `У какой фигуры больше углов? 🤔`,
      shapes: [
        { type: shape1, color: color1, size: 'big' },
        { type: shape2, color: color2, size: 'big' }
      ],
      options: ['Первая фигура', 'Вторая фигура', 'Одинаково'],
      correct,
      points: 10
    };
  },

  // 7. Найди фигуру по описанию (цвет + размер + тип)
  genFindByDescription(difficulty) {
    const targetShape = Utils.randomShape();
    const targetColor = Utils.randomColor();
    const targetSize = Utils.randomSize();

    const shapes = [];
    const correctIndex = Math.floor(Math.random() * 4);

    for (let i = 0; i < 4; i++) {
      if (i === correctIndex) {
        shapes.push({ type: targetShape, color: targetColor, size: targetSize, isTarget: true });
      } else {
        // Меняем хотя бы один признак
        const variation = Math.floor(Math.random() * 3);
        let type = targetShape, color = targetColor, size = targetSize;
        if (variation === 0) type = Utils.randomExcept(SHAPES, targetShape);
        else if (variation === 1) color = Utils.randomExcept(Object.keys(COLORS), targetColor);
        else size = targetSize === 'big' ? 'small' : 'big';
        shapes.push({ type, color, size, isTarget: false });
      }
    }

    const sizeText = targetSize === 'big' ? 'большой' : 'маленький';
    const colorText = COLOR_NAMES[targetColor];
    const shapeText = SHAPE_NAMES[targetShape];

    return {
      type: 'findDesc',
      helper: `Найди ${sizeText} ${colorText} ${shapeText}! 🔎`,
      shapes: Utils.shuffle(shapes),
      correctIndex: shapes.findIndex(s => s.isTarget),
      points: 15
    };
  },

  // 8. Найди пару - две одинаковые по форме фигуры
  genFindPair(difficulty) {
    const pairShape = Utils.randomShape();
    const pairColor = Utils.randomColor();

    const shapes = [];
    // 6 фигур: 2 одинаковые (пара), остальные разные
    const positions = [0, 1, 2, 3, 4, 5];
    const pairPositions = Utils.uniqueRandom(positions, 2);

    for (let i = 0; i < 6; i++) {
      if (pairPositions.includes(i)) {
        shapes.push({ type: pairShape, color: pairColor, size: 'small', isPair: true });
      } else {
        shapes.push({
          type: Utils.randomShape(),
          color: Utils.randomColor(),
          size: 'small',
          isPair: false
        });
      }
    }

    return {
      type: 'pair',
      helper: `Найди две одинаковые фигуры (одинаковые по форме и цвету)! 👯`,
      shapes,
      pairPositions,
      points: 15
    };
  },

  // 9. Угадай фигуру по признакам
  genGuessBySigns(difficulty) {
    const shape = Utils.randomShape();
    const props = SHAPE_PROPERTIES[shape];
    const color = Utils.randomColor();

    let description = `У этой фигуры `;
    if (props.hasCorners) {
      description += `${props.corners} угла`;
    } else {
      description += `нет углов`;
    }
    description += `, и она ${COLOR_NAMES[color]}го цвета.`;

    const options = Utils.shuffle([...SHAPES]).map(s => SHAPE_NAMES[s]);
    const correct = SHAPE_NAMES[shape];

    return {
      type: 'guess',
      helper: `Угадай фигуру: ${description} 🧩`,
      options,
      correct,
      points: 15
    };
  },

  // 10. Собери все фигуры определённого типа
  genCollectAll(difficulty) {
    const targetShape = Utils.randomShape();
    const shapes = [];
    const correctIndices = [];

    // 8 фигур, 3 из них - целевые
    const targetCount = 3;
    const targetPositions = Utils.uniqueRandom([0,1,2,3,4,5,6,7], targetCount);

    for (let i = 0; i < 8; i++) {
      if (targetPositions.includes(i)) {
        shapes.push({ type: targetShape, color: Utils.randomColor(), size: 'small', isTarget: true });
        correctIndices.push(i);
      } else {
        shapes.push({ type: Utils.randomExcept(SHAPES, targetShape), color: Utils.randomColor(), size: 'small', isTarget: false });
      }
    }

    return {
      type: 'collect',
      helper: `Собери все ${SHAPE_NAMES[targetShape]}! Нажимай на каждый по очереди 👆`,
      shapes,
      correctIndices,
      points: 20
    };
  },

  // Сгенерировать все вопросы
  generateAll() {
    const questions = [];
    for (let i = 0; i < CONFIG.TOTAL_QUESTIONS; i++) {
      const difficulty = i / CONFIG.TOTAL_QUESTIONS; // от 0 до 1
      const gen = this.generators[i % this.generators.length];
      questions.push(gen(difficulty));
    }
    return Utils.shuffle(questions);
  }
};

// Вспомогательная функция
Utils.uniqueExcept = function(arr, excluded, count) {
  const filtered = arr.filter(x => x !== excluded);
  return this.uniqueRandom(filtered, count);
};

/* ============================================
   МЕНЕДЖЕР ТАЙМЕРА
   ============================================ */
const Timer = {
  start() {
    this.update();
    GameState.timerInterval = setInterval(() => {
      GameState.timeLeft--;
      this.update();

      if (GameState.timeLeft <= 0) {
        this.stop();
        Game.end('time');
      }
    }, 1000);
  },

  stop() {
    if (GameState.timerInterval) {
      clearInterval(GameState.timerInterval);
      GameState.timerInterval = null;
    }
  },

  update() {
    const timerEl = document.getElementById('timer');
    const timerBox = document.getElementById('timer-box');
    timerEl.textContent = Utils.formatTime(GameState.timeLeft);

    if (GameState.timeLeft <= 60) {
      timerBox.classList.add('warning');
    } else {
      timerBox.classList.remove('warning');
    }
  },

  addBonus(seconds) {
    GameState.timeLeft += seconds;
    this.update();
  }
};

/* ============================================
   ХРАНЕНИЕ ДАННЫХ
   ============================================ */
const Storage = {
  getBestScore() {
    try {
      return parseInt(localStorage.getItem(CONFIG.STORAGE_KEY) || '0', 10);
    } catch (e) {
      return 0;
    }
  },

  saveBestScore(score) {
    try {
      const current = this.getBestScore();
      if (score > current) {
        localStorage.setItem(CONFIG.STORAGE_KEY, String(score));
        return true;
      }
      return false;
    } catch (e) {
      return false;
    }
  }
};

/* ============================================
   ЗВУКОВЫЕ ЭФФЕКТЫ (Web Audio API)
   ============================================ */
const Sound = {
  ctx: null,

  init() {
    try {
      this.ctx = new (window.AudioContext || window.webkitAudioContext)();
    } catch (e) {
      this.ctx = null;
    }
  },

  play(type) {
    if (!this.ctx) return;
    try {
      const osc = this.ctx.createOscillator();
      const gain = this.ctx.createGain();
      osc.connect(gain);
      gain.connect(this.ctx.destination);

      const now = this.ctx.currentTime;

      if (type === 'correct') {
        osc.frequency.setValueAtTime(523, now);
        osc.frequency.setValueAtTime(659, now + 0.1);
        osc.frequency.setValueAtTime(784, now + 0.2);
        gain.gain.setValueAtTime(0.15, now);
        gain.gain.exponentialRampToValueAtTime(0.01, now + 0.4);
        osc.start(now);
        osc.stop(now + 0.4);
      } else if (type === 'wrong') {
        osc.frequency.setValueAtTime(200, now);
        osc.frequency.setValueAtTime(150, now + 0.15);
        gain.gain.setValueAtTime(0.15, now);
        gain.gain.exponentialRampToValueAtTime(0.01, now + 0.3);
        osc.start(now);
        osc.stop(now + 0.3);
      } else if (type === 'click') {
        osc.frequency.setValueAtTime(800, now);
        gain.gain.setValueAtTime(0.08, now);
        gain.gain.exponentialRampToValueAtTime(0.01, now + 0.05);
        osc.start(now);
        osc.stop(now + 0.05);
      } else if (type === 'win') {
        [523, 659, 784, 1047].forEach((freq, i) => {
          const o = this.ctx.createOscillator();
          const g = this.ctx.createGain();
          o.connect(g);
          g.connect(this.ctx.destination);
          o.frequency.setValueAtTime(freq, now + i * 0.15);
          g.gain.setValueAtTime(0.15, now + i * 0.15);
          g.gain.exponentialRampToValueAtTime(0.01, now + i * 0.15 + 0.3);
          o.start(now + i * 0.15);
          o.stop(now + i * 0.15 + 0.3);
        });
      }
    } catch (e) {
      // Игнорируем ошибки звука
    }
  }
};

/* ============================================
   ГЛАВНЫЙ ИГРОВОЙ МОДУЛЬ
   ============================================ */
const Game = {
  init() {
    QuestionGenerator.init();
    Sound.init();
    this.bindEvents();
    this.showBestScore();
  },

  bindEvents() {
    document.getElementById('start-btn').addEventListener('click', () => {
      Sound.play('click');
      this.start();
    });

    document.getElementById('restart-btn').addEventListener('click', () => {
      Sound.play('click');
      this.start();
    });

    document.getElementById('home-btn').addEventListener('click', () => {
      Sound.play('click');
      this.showScreen('start-screen');
      this.showBestScore();
    });
  },

  showBestScore() {
    const best = Storage.getBestScore();
    const box = document.getElementById('best-score-box');
    const value = document.getElementById('best-score-value');
    if (best > 0) {
      value.textContent = best;
      box.style.display = 'block';
    } else {
      box.style.display = 'none';
    }
  },

  showScreen(id) {
    document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
    document.getElementById(id).classList.add('active');
  },

  start() {
    // Сброс состояния
    GameState.currentQuestion = 0;
    GameState.score = 0;
    GameState.correctAnswers = 0;
    GameState.timeLeft = CONFIG.GAME_DURATION;
    GameState.questions = QuestionGenerator.generateAll();
    GameState.isAnswering = false;
    GameState.startTime = Date.now();

    this.showScreen('game-screen');
    this.updateUI();
    Timer.start();
    this.showQuestion();
  },

  updateUI() {
    document.getElementById('score').textContent = GameState.score;
    document.getElementById('progress').textContent =
      `${GameState.currentQuestion + 1}/${CONFIG.TOTAL_QUESTIONS}`;

    const progressPercent = (GameState.currentQuestion / CONFIG.TOTAL_QUESTIONS) * 100;
    document.getElementById('progress-fill').style.width = progressPercent + '%';
  },

  showQuestion() {
    if (GameState.currentQuestion >= CONFIG.TOTAL_QUESTIONS) {
      this.end('complete');
      return;
    }

    GameState.isAnswering = false;
    GameState.questionStartTime = Date.now();

    const q = GameState.questions[GameState.currentQuestion];
    const helperText = document.getElementById('helper-text');
    const canvasContainer = document.getElementById('canvas-container');
    const optionsGrid = document.getElementById('options-grid');

    helperText.textContent = q.helper;
    canvasContainer.innerHTML = '';
    optionsGrid.innerHTML = '';

    // Рендерим в зависимости от типа задания
    switch (q.type) {
      case 'name':
      case 'corners':
      case 'guess':
        this.renderSingleShape(q);
        this.renderOptions(q);
        break;

      case 'find':
      case 'findDesc':
        this.renderMultipleShapes(q, 'single');
        break;

      case 'odd':
        this.renderMultipleShapes(q, 'single');
        break;

      case 'choose':
        this.renderMultipleShapes(q, 'multi');
        break;

      case 'compare':
        this.renderCompareShapes(q);
        this.renderOptions(q);
        break;

      case 'pair':
        this.renderMultipleShapes(q, 'pair');
        break;

      case 'collect':
        this.renderMultipleShapes(q, 'collect');
        break;
    }

    this.updateUI();
  },

  // Отобразить одну фигуру
  renderSingleShape(q) {
    const container = document.getElementById('canvas-container');
    const canvas = Renderer.createCanvas(200, 200);
    Renderer.drawShape(canvas, q.shape.type, q.shape.color, q.shape.size);
    container.appendChild(canvas);
  },

  // Отобразить несколько фигур
  renderMultipleShapes(q, mode) {
    const container = document.getElementById('canvas-container');
    const count = q.shapes.length;
    const size = count > 4 ? 90 : (count > 2 ? 110 : 140);

    q.shapes.forEach((shapeData, index) => {
      const canvas = Renderer.createCanvas(size, size, true);
      Renderer.drawShape(canvas, shapeData.type, shapeData.color, shapeData.size);
      canvas.dataset.index = index;

      canvas.addEventListener('click', () => {
        this.handleShapeClick(q, index, canvas, mode);
      });

      container.appendChild(canvas);
    });
  },

  // Отобразить сравнение двух фигур
  renderCompareShapes(q) {
    const container = document.getElementById('canvas-container');
    q.shapes.forEach(shapeData => {
      const canvas = Renderer.createCanvas(150, 150);
      Renderer.drawShape(canvas, shapeData.type, shapeData.color, shapeData.size);
      container.appendChild(canvas);
    });
  },

  // Отобразить варианты ответов
  renderOptions(q) {
    const grid = document.getElementById('options-grid');
    q.options.forEach(option => {
      const btn = document.createElement('button');
      btn.className = 'option-btn';
      btn.textContent = option;
      btn.addEventListener('click', () => this.handleOptionClick(q, option, btn));
      grid.appendChild(btn);
    });
  },

  // Обработка клика по фигуре (одиночный выбор)
  handleShapeClick(q, index, canvas, mode) {
    if (GameState.isAnswering) return;
    Sound.play('click');

    if (mode === 'single') {
      GameState.isAnswering = true;
      const isCorrect = index === q.correctIndex;
      this.processAnswer(isCorrect, q.points, () => {
        canvas.classList.add(isCorrect ? 'selected' : '');
        if (!isCorrect) {
          // Показать правильный ответ
          const canvases = document.querySelectorAll('#canvas-container .shape-canvas');
          canvases[q.correctIndex].classList.add('selected');
        }
      });
    } else if (mode === 'pair') {
      // Режим "найди пару" - нужно кликнуть 2 одинаковые
      if (!q.selected) q.selected = [];
      if (q.selected.includes(index)) return;

      q.selected.push(index);
      canvas.classList.add('selected');

      if (q.selected.length === 2) {
        GameState.isAnswering = true;
        const [a, b] = q.selected;
        const isCorrect = q.pairPositions.includes(a) && q.pairPositions.includes(b);
        this.processAnswer(isCorrect, q.points);
      }
    } else if (mode === 'collect') {
      // Режим "собери все" - нужно кликнуть все правильные
      if (!q.selected) q.selected = [];
      if (q.selected.includes(index)) return;

      q.selected.push(index);
      const isCorrect = q.correctIndices.includes(index);

      if (isCorrect) {
        canvas.classList.add('selected');
        Sound.play('correct');

        // Проверить, все ли выбраны
        const allSelected = q.correctIndices.every(i => q.selected.includes(i));
        if (allSelected) {
          GameState.isAnswering = true;
          this.processAnswer(true, q.points);
        }
      } else {
        // Неправильный выбор - завершаем задание
        GameState.isAnswering = true;
        canvas.style.background = 'var(--error)';
        Sound.play('wrong');
        this.showFeedback('❌');
        setTimeout(() => this.nextQuestion(), 1500);
      }
    } else if (mode === 'multi') {
      // Режим "выбери все по признаку"
      if (!q.selected) q.selected = [];
      if (q.selected.includes(index)) return;

      q.selected.push(index);
      const isCorrectShape = q.correctIndices.includes(index);

      if (isCorrectShape) {
        canvas.classList.add('selected');
        Sound.play('correct');
      } else {
        GameState.isAnswering = true;
        canvas.style.background = 'var(--error)';
        Sound.play('wrong');
        this.showFeedback('❌');
        // Показать все правильные
        const canvases = document.querySelectorAll('#canvas-container .shape-canvas');
        q.correctIndices.forEach(i => canvases[i].classList.add('selected'));
        setTimeout(() => this.nextQuestion(), 1500);
        return;
      }

      // Проверить, все ли правильные выбраны
      const allCorrectSelected = q.correctIndices.every(i => q.selected.includes(i));
      // И нет ли лишних
      const hasWrong = q.selected.some(i => !q.correctIndices.includes(i));

      if (allCorrectSelected && !hasWrong) {
        GameState.isAnswering = true;
        this.processAnswer(true, q.points);
      }
    }
  },

  // Обработка клика по варианту ответа
  handleOptionClick(q, option, btn) {
    if (GameState.isAnswering) return;
    GameState.isAnswering = true;

    const isCorrect = option === q.correct;
    Sound.play(isCorrect ? 'correct' : 'wrong');

    btn.classList.add(isCorrect ? 'correct' : 'wrong');

    // Заблокировать все кнопки
    document.querySelectorAll('.option-btn').forEach(b => {
      b.disabled = true;
      if (b.textContent === q.correct) b.classList.add('correct');
    });

    this.processAnswer(isCorrect, q.points);
  },

  // Обработка ответа
  processAnswer(isCorrect, points, callback) {
    if (isCorrect) {
      GameState.score += points;
      GameState.correctAnswers++;

      // Бонус за быстрый ответ
      const answerTime = (Date.now() - GameState.questionStartTime) / 1000;
      if (answerTime < CONFIG.FAST_ANSWER_THRESHOLD) {
        GameState.score += CONFIG.BONUS_TIME;
        Timer.addBonus(2);
      }

      this.showFeedback('🎉');
      Sound.play('correct');
    } else {
      this.showFeedback('😔');
      Sound.play('wrong');
    }

    if (callback) callback();

    this.updateUI();
    setTimeout(() => this.nextQuestion(), 1800);
  },

  // Показать эмодзи обратной связи
  showFeedback(emoji) {
    const feedback = document.getElementById('feedback');
    feedback.textContent = emoji;
    feedback.classList.remove('show');
    void feedback.offsetWidth; // reflow
    feedback.classList.add('show');
  },

  // Следующий вопрос
  nextQuestion() {
    GameState.currentQuestion++;
    if (GameState.currentQuestion >= CONFIG.TOTAL_QUESTIONS) {
      this.end('complete');
    } else {
      this.showQuestion();
    }
  },

  // Завершение игры
  end(reason) {
    Timer.stop();
    Sound.play('win');

    const timeSpent = Math.floor((Date.now() - GameState.startTime) / 1000);
    const isNewBest = Storage.saveBestScore(GameState.score);

    // Расчёт звёзд
    const starsCount = Math.min(5, Math.ceil(GameState.correctAnswers / 2));

    // Заполняем экран результатов
    document.getElementById('final-score').textContent = GameState.score;
    document.getElementById('final-correct').textContent =
      `${GameState.correctAnswers}/${CONFIG.TOTAL_QUESTIONS}`;
    document.getElementById('final-time').textContent = Utils.formatTime(timeSpent);

    // Заголовок и сообщение в зависимости от результата
    const percent = GameState.correctAnswers / CONFIG.TOTAL_QUESTIONS;
    let title, message, mascot;

    if (percent >= 0.9) {
      title = 'Великолепно! 🏆';
      message = 'Ты настоящий знаток фигур! Мудрая Сова гордится тобой!';
      mascot = '🎉';
    } else if (percent >= 0.7) {
      title = 'Отлично! 🌟';
      message = 'Ты очень хорошо знаешь фигуры! Продолжай в том же духе!';
      mascot = '🎊';
    } else if (percent >= 0.5) {
      title = 'Хорошо! 👍';
      message = 'Неплохой результат! Немного практики и будет идеально!';
      mascot = '😊';
    } else {
      title = 'Молодец, что попробовал! 💪';
      message = 'Фигуры — это интересно! Попробуй ещё раз, и у тебя всё получится!';
      mascot = '🦉';
    }

    if (isNewBest) {
      title += ' Новый рекорд!';
      message += ' 🏆 Ты побил свой рекорд!';
    }

    document.getElementById('result-title').textContent = title;
    document.getElementById('result-message').textContent = message;
    document.getElementById('result-mascot').textContent = mascot;

    // Звёздочки
    const stars = document.querySelectorAll('#stars .star');
    stars.forEach((star, i) => {
      star.classList.remove('earned');
      if (i < starsCount) {
        setTimeout(() => star.classList.add('earned'), i * 200);
      }
    });

    this.showScreen('result-screen');

    // Конфетти при хорошем результате
    if (percent >= 0.7) {
      this.showConfetti();
    }
  },

  // Эффект конфетти
  showConfetti() {
    const colors = ['#FFB5C5', '#B5D8FF', '#B5EBCD', '#E8D5FF', '#FFD5B5', '#FFF3B0'];
    for (let i = 0; i < 50; i++) {
      setTimeout(() => {
        const confetti = document.createElement('div');
        confetti.className = 'confetti';
        confetti.style.left = Math.random() * 100 + 'vw';
        confetti.style.background = Utils.random(colors);
        confetti.style.animationDuration = (2 + Math.random() * 2) + 's';
        confetti.style.animationDelay = Math.random() * 0.5 + 's';

        if (Math.random() < 0.5) {
          confetti.style.borderRadius = '50%';
        } else {
          confetti.style.borderRadius = '2px';
        }

        document.body.appendChild(confetti);
        setTimeout(() => confetti.remove(), 4000);
      }, i * 50);
    }
  }
};

/* ============================================
   ЗАПУСК ПРИЛОЖЕНИЯ
   ============================================ */
document.addEventListener('DOMContentLoaded', () => {
  Game.init();
});

// Инициализация аудио-контекста при первом взаимодействии
document.addEventListener('click', () => {
  if (Sound.ctx && Sound.ctx.state === 'suspended') {
    Sound.ctx.resume();
  }
}, { once: true });
</script>

</body>
</html>
```