Procházet zdrojové kódy

Бурдыко Максим [1]

ypv před 3 dny
rodič
revize
d887aadefd

+ 15 - 3
2026-27/1 сем/46-1.md

@@ -61,7 +61,11 @@ PWA:
 (и) - интерактив, (д) - дизайн, (о) - объем, (зп) - защита проекта
 
 
-Бурдыко Максим []: 
+Бурдыко Максим [1]: 
+у:5;в+;з:   
+[Бурдыко М.](3.2.200_Подавление_опасных_сигналов_акустоэлектрических_преобразований/burdyko.md), 
+[Вопросы](3.2.200_Подавление_опасных_сигналов_акустоэлектрических_преобразований/vaprosi_byrd.md); 
+
 Веденский Даниил []:    
 Гоев Илья []:    
 Горбунов Егор []:   
@@ -85,11 +89,12 @@ PWA:
 Старинцев Леонид Дмитриевич []:     
 Стародубцев Даниил Юрьевич:     
 ТОпальцев Роман Алексеевич:    
-Трофимова Арина [2]: у:3;в+;з: , у:4;в+;з: ,   
+Трофимова Арина [2]: 
+у:3;в+;з: ,   
 [Трофимова А.](2.3.100_Классификация_технических_средств_разведки/trofimova.md), 
+у:4;в+;з: ,  
 [Трофимова А.](2.1.300_Виды_источники_и_носители_защищаемой_информации/trofimova.md), 
 
-
 Цуканов Дмитрий Сергеевич []:     
 
 Интерактивы: 
@@ -129,6 +134,13 @@ http://213.155.192.79:3001/ypv/zamok
 ### Журнал
 
 
+18Sep26   
+**Отсутств**:  Поддубная, Ласек,    
+
+17Sep26   
+**Отсутств**:  Гузеев, Коваленко, Лайков, Ласек, Маркеев,    
+
+
 16Sep26   
 **Отсутств**:  Ласек, Лайков  
 [Интерференция звука](https://rutube.ru/video/e68711c506cb65709d56bf303649f8cf/), 

+ 558 - 0
2026-27/1 сем/test/app.js

@@ -0,0 +1,558 @@
+'use strict';
+
+// ---------- ДАННЫЕ ----------
+const ACTS = {
+  1: {
+    title: 'Акт I. Идентификация',
+    scenes: [
+      {
+        system: 'Приветствую. Для начала работы мне нужно понять, кто вы. Представьтесь, пожалуйста.',
+        choices: [
+          { text: 'Назвать свое имя: «Я — Алексей Иванов, стажер».', correct: true,
+            feedback: 'Принято. Алексей Иванов. Я зафиксировала ваше имя в журнале посещений.',
+            edu: 'ИДЕНТИФИКАЦИЯ — это присвоение субъекту доступа уникального имени (логина, ID, номера пропуска). Это заявление: «Я — Вася». Важно: На этом этапе система еще не доверяет вам, она просто знает, как вас зовут.'
+          },
+          { text: 'Попытаться пройти молча.', correct: false,
+            feedback: 'Ошибка. Идентификация не пройдена. Я не могу работать с анонимом. Доступ запрещен.'
+          },
+          { text: 'Сказать: «Я — директор банка».', correct: false,
+            feedback: 'Стоп. Вы назвались директором, но в базе нет такого стажера. Это ложная идентификация. Вызов охраны!'
+          }
+        ]
+      }
+    ]
+  },
+  2: {
+    title: 'Акт II. Аутентификация',
+    scenes: [
+      {
+        system: 'Алексей, я вижу ваше имя, но мне нужно убедиться, что вы — это действительно вы. У вас есть 3 попытки. Ваш ключ доступа — это дата вашего рождения + код из СМС.',
+        choices: [
+          { text: 'Ввести пароль, который вы придумали сами (например, «12345»).', correct: false,
+            feedback: 'Проверка... Пароль слишком простой. Взломщик подобрал его за 2 секунды. Это плохая аутентификация. Доступ заблокирован.'
+          },
+          { text: 'Приложить отпечаток пальца к сканеру (Биометрия).', correct: true,
+            feedback: 'Отпечаток совпадает с базой. Отлично! Вы доказали, что вы — это вы. Процесс АУТЕНТИФИКАЦИИ пройден. Теперь я доверяю этому сеансу.',
+            edu: 'АУТЕНТИФИКАЦИЯ — это проверка подлинности. Доказательство того, что субъект действительно тот, за кого себя выдает. Факторы аутентификации: Знание (пароль, PIN). Владение (токен, телефон, смарт-карта). Биометрия (палец, лицо, сетчатка).'
+          },
+          { text: 'Попросить коллегу приложить свой пропуск.', correct: false,
+            feedback: 'Чужой пропуск? Это попытка обмана. Аутентификация не пройдена.'
+          }
+        ]
+      }
+    ]
+  },
+  3: {
+    title: 'Акт III. Авторизация',
+    scenes: [
+      {
+        system: 'Алексей, вы успешно вошли. Но теперь мне нужно определить ваши права. Кто вы в системе?',
+        choices: [
+          { text: 'Администратор (все права).', correct: false,
+            feedback: 'Попытка повышения привилегий! Вы стажер, но пытаетесь получить права админа. Это нарушение политики безопасности. Инцидент записан в лог.'
+          },
+          { text: 'Кассир (доступ к деньгам).', correct: false,
+            feedback: 'Кассир? Ваша роль — стажер. Несоответствие прав. Доступ к деньгам запрещен.'
+          },
+          { text: 'Стажер (только просмотр).', correct: true,
+            feedback: 'Роль подтверждена. Вам разрешен только просмотр курса валют. Кнопка перевода денег для вас неактивна (серая). Это АВТОРИЗАЦИЯ.',
+            edu: 'АВТОРИЗАЦИЯ — это предоставление субъекту прав на доступ к ресурсам и определение разрешенных действий. Простыми словами: Аутентификация говорит «Ты — Вася», а Авторизация говорит «Васе можно только читать, но нельзя писать».'
+          }
+        ]
+      },
+      {
+        system: 'Вдруг экран мигает. Появляется «Тень»: «Я украл логин Алексея (Идентификация) и подобрал пароль (Аутентификация). Теперь я войду в систему!»',
+        choices: [
+          { text: 'Ничего, он войдет.', correct: false,
+            feedback: 'Нет. Даже зная пароль, без второго фактора Хакер не пройдет Аутентификацию.'
+          },
+          { text: 'Двухфакторная аутентификация (2FA).', correct: true,
+            feedback: 'Верно! Даже зная пароль, без второго фактора (СМС или отпечатка) Хакер не пройдет Аутентификацию. А если бы и прошел — Авторизация ограничила бы его права только просмотром, и он не смог бы украсть деньги.',
+            edu: 'Двухфакторная аутентификация (2FA) — метод защиты, при котором для входа требуется два разных фактора: например, пароль (знание) и код из СМС (владение). Это значительно усложняет взлом.'
+          },
+          { text: 'Авторизация.', correct: false,
+            feedback: 'Авторизация происходит после аутентификации. Но без аутентификации хакер не получит даже прав.'
+          }
+        ]
+      }
+    ]
+  }
+};
+
+const QUIZ = [
+  {
+    q: 'Что такое идентификация?',
+    options: [
+      'Присвоение субъекту уникального имени (логина, ID)',
+      'Проверка подлинности субъекта',
+      'Предоставление прав доступа к ресурсам',
+      'Шифрование данных'
+    ],
+    correct: 0,
+    explain: 'Идентификация — это заявление «Я — Вася». Система просто знает, как вас зовут.'
+  },
+  {
+    q: 'Какой из перечисленных факторов НЕ относится к аутентификации?',
+    options: [
+      'Знание (пароль, PIN)',
+      'Владение (токен, телефон)',
+      'Биометрия (отпечаток пальца)',
+      'Роль в системе (стажёр, администратор)'
+    ],
+    correct: 3,
+    explain: 'Роль в системе — это авторизация. Аутентификация проверяет, что вы — это вы.'
+  },
+  {
+    q: 'Что означает авторизация?',
+    options: [
+      'Проверка подлинности пользователя',
+      'Присвоение пользователю уникального имени',
+      'Предоставление прав на доступ к ресурсам и определение разрешённых действий',
+      'Двухфакторная проверка'
+    ],
+    correct: 2,
+    explain: 'Авторизация говорит: «Васе можно только читать, но нельзя писать».'
+  },
+  {
+    q: 'Хакер украл логин и пароль. Что помешает ему войти в систему?',
+    options: [
+      'Ничего, он войдет',
+      'Двухфакторная аутентификация (2FA)',
+      'Авторизация',
+      'Идентификация'
+    ],
+    correct: 1,
+    explain: '2FA требует второй фактор (СМС, отпечаток), которого у хакера нет.'
+  },
+  {
+    q: 'В какой последовательности происходит доступ к защищённой системе?',
+    options: [
+      'Авторизация → Аутентификация → Идентификация',
+      'Идентификация → Аутентификация → Авторизация',
+      'Аутентификация → Идентификация → Авторизация',
+      'Идентификация → Авторизация → Аутентификация'
+    ],
+    correct: 1,
+    explain: 'Сначала система узнаёт имя (идентификация), затем проверяет подлинность (аутентификация), затем даёт права (авторизация).'
+  }
+];
+
+// ---------- СОСТОЯНИЕ ----------
+const state = {
+  screen: 'title',
+  act: 1,
+  scene: 0,
+  actCompleted: { 1: false, 2: false, 3: false },
+  quizAnswers: [],
+  quizIndex: 0,
+  quizScore: 0,
+  quizDone: false,
+  sound: true,
+  voice: true,
+  journal: [],
+  audioCtx: null,
+};
+
+// ---------- DOM ----------
+const $ = (sel, ctx = document) => ctx.querySelector(sel);
+const $$ = (sel, ctx = document) => [...ctx.querySelectorAll(sel)];
+
+const hud = $('#hud');
+const hudAct = $('#hudAct');
+const hudFill = $('#hudFill');
+const tSound = $('#tSound');
+const tVoice = $('#tVoice');
+const tJournal = $('#tJournal');
+const journal = $('#journal');
+const journalBody = $('#journalBody');
+const jClose = $('#jClose');
+const scrim = $('#scrim');
+
+const screens = {
+  title: $('#s-title'),
+  brief: $('#s-brief'),
+  act1: $('#s-act1'),
+  act2: $('#s-act2'),
+  act3: $('#s-act3'),
+  final: $('#s-final'),
+  quiz: $('#s-quiz'),
+  result: $('#s-result'),
+};
+
+// ---------- УТИЛИТЫ ----------
+function showScreen(id) {
+  Object.values(screens).forEach(s => s.classList.remove('is-active'));
+  screens[id].classList.add('is-active');
+  state.screen = id;
+  hud.hidden = (id === 'title');
+  updateHUD();
+  window.scrollTo(0, 0);
+}
+
+function updateHUD() {
+  let label = `АКТ ${state.act}`;
+  if (state.screen === 'quiz') label = 'ТЕСТ';
+  if (state.screen === 'result') label = 'ИТОГ';
+  if (state.screen === 'final') label = 'ФИНАЛ';
+  hudAct.textContent = label;
+
+  const total = 4; // 3 акта + тест
+  let completed = 0;
+  if (state.actCompleted[1]) completed++;
+  if (state.actCompleted[2]) completed++;
+  if (state.actCompleted[3]) completed++;
+  if (state.quizDone) completed++;
+  const pct = (completed / total) * 100;
+  hudFill.style.width = pct + '%';
+}
+
+function logJournal(text, type = 'info') {
+  state.journal.push({ text, type, time: new Date().toLocaleTimeString() });
+  if (state.journal.length > 50) state.journal.shift();
+  renderJournal();
+}
+
+function renderJournal() {
+  if (state.journal.length === 0) {
+    journalBody.innerHTML = '<p class="journal__empty">Записей пока нет.</p>';
+    return;
+  }
+  journalBody.innerHTML = state.journal.map(e => {
+    const cls = e.type === 'ok' ? 'journal__entry--ok' : e.type === 'err' ? 'journal__entry--err' : '';
+    return `<div class="journal__entry ${cls}">[${e.time}] ${e.text}</div>`;
+  }).join('');
+  journalBody.scrollTop = journalBody.scrollHeight;
+}
+
+// ---------- ЗВУК ----------
+function ensureAudio() {
+  if (!state.audioCtx) {
+    state.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+  }
+  if (state.audioCtx.state === 'suspended') state.audioCtx.resume();
+}
+
+function playTone(freq, duration = 0.15, type = 'sine') {
+  if (!state.sound) return;
+  ensureAudio();
+  const ctx = state.audioCtx;
+  const osc = ctx.createOscillator();
+  const gain = ctx.createGain();
+  osc.type = type;
+  osc.frequency.value = freq;
+  gain.gain.setValueAtTime(0.15, ctx.currentTime);
+  gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + duration);
+  osc.connect(gain).connect(ctx.destination);
+  osc.start();
+  osc.stop(ctx.currentTime + duration);
+}
+
+function speak(text) {
+  if (!state.voice || !window.speechSynthesis) return;
+  window.speechSynthesis.cancel();
+  const utter = new SpeechSynthesisUtterance(text);
+  utter.lang = 'ru-RU';
+  utter.rate = 1;
+  utter.pitch = 1;
+  window.speechSynthesis.speak(utter);
+}
+
+// ---------- РЕНДЕР АКТОВ ----------
+function renderActScreen(screenId, actNum, sceneIndex) {
+  const act = ACTS[actNum];
+  const scene = act.scenes[sceneIndex];
+  const screen = screens[`act${actNum}`];
+  screen.innerHTML = `
+    <div class="page">
+      <div class="act-head">
+        <span class="act-num">Акт ${actNum}</span>
+        <h2>${act.title}</h2>
+      </div>
+      <div class="scene">
+        <div class="system-bubble">
+          <div class="system-avatar">С</div>
+          <div class="system-text" id="sysText">${scene.system}</div>
+        </div>
+        <div class="choices" id="choices">
+          ${scene.choices.map((c, i) => `
+            <button class="choice" data-index="${i}">${c.text}</button>
+          `).join('')}
+        </div>
+        <div class="feedback" id="feedback" hidden></div>
+        <div class="edu" id="eduBlock" hidden>
+          <div class="note">
+            <div class="note__head"><span class="note__mark">§</span>Обучающий блок</div>
+            <div class="note__body" id="eduText"></div>
+          </div>
+          <button class="btn" id="continueBtn">Продолжить</button>
+        </div>
+      </div>
+    </div>
+  `;
+
+  screen.querySelectorAll('.choice').forEach(btn => {
+    btn.addEventListener('click', () => {
+      handleChoice(actNum, sceneIndex, parseInt(btn.dataset.index));
+    });
+  });
+
+  speak(scene.system);
+}
+
+function handleChoice(actNum, sceneIndex, choiceIndex) {
+  const act = ACTS[actNum];
+  const scene = act.scenes[sceneIndex];
+  const choice = scene.choices[choiceIndex];
+  const screen = screens[`act${actNum}`];
+  const sysText = screen.querySelector('#sysText');
+  const choicesDiv = screen.querySelector('#choices');
+  const feedbackDiv = screen.querySelector('#feedback');
+  const eduBlock = screen.querySelector('#eduBlock');
+
+  if (choice.correct) {
+    choicesDiv.querySelectorAll('.choice').forEach(b => b.disabled = true);
+    sysText.textContent = choice.feedback;
+    playTone(880, 0.12, 'sine');
+    logJournal(`✅ ${choice.feedback}`, 'ok');
+    if (choice.edu) {
+      eduBlock.hidden = false;
+      screen.querySelector('#eduText').innerHTML = choice.edu;
+      speak(choice.edu);
+      const contBtn = screen.querySelector('#continueBtn');
+      contBtn.addEventListener('click', () => nextScene());
+    } else {
+      feedbackDiv.hidden = false;
+      feedbackDiv.innerHTML = `<button class="btn" id="continueBtn">Продолжить</button>`;
+      feedbackDiv.querySelector('#continueBtn').addEventListener('click', () => nextScene());
+    }
+  } else {
+    playTone(220, 0.25, 'sawtooth');
+    sysText.textContent = choice.feedback;
+    speak(choice.feedback);
+    logJournal(`❌ ${choice.feedback}`, 'err');
+    choicesDiv.querySelectorAll('.choice').forEach(b => b.disabled = true);
+    feedbackDiv.hidden = false;
+    feedbackDiv.innerHTML = `<button class="btn btn--small" id="retryBtn">Попробовать снова</button>`;
+    feedbackDiv.querySelector('#retryBtn').addEventListener('click', () => {
+      renderActScreen(`s-act${actNum}`, actNum, sceneIndex);
+    });
+  }
+}
+
+function nextScene() {
+  const actNum = state.act;
+  const act = ACTS[actNum];
+  if (state.scene < act.scenes.length - 1) {
+    state.scene++;
+    renderActScreen(`s-act${actNum}`, actNum, state.scene);
+  } else {
+    state.actCompleted[actNum] = true;
+    updateHUD();
+    if (actNum < 3) {
+      state.act = actNum + 1;
+      state.scene = 0;
+      showScreen(`act${state.act}`);
+      renderActScreen(`s-act${state.act}`, state.act, 0);
+    } else {
+      showFinal();
+    }
+  }
+}
+
+// ---------- ФИНАЛЬНЫЙ ЭКРАН (ИТОГИ) ----------
+function showFinal() {
+  showScreen('final');
+  screens.final.innerHTML = `
+    <div class="page">
+      <div class="act-head">
+        <span class="act-num">Финал</span>
+        <h2>Цепочка безопасности</h2>
+      </div>
+      <div class="cards">
+        <article class="card">
+          <h3>Идентификация</h3>
+          <p>«Кто ты?» — ввод логина, предъявление имени. Система узнаёт, как вас зовут.</p>
+        </article>
+        <article class="card">
+          <h3>Аутентификация</h3>
+          <p>«Докажи!» — ввод пароля, отпечаток, СМС. Система убеждается, что это действительно вы.</p>
+        </article>
+        <article class="card">
+          <h3>Авторизация</h3>
+          <p>«Что тебе можно?» — права доступа, роль, чтение/запись. Система решает, что вам разрешено.</p>
+        </article>
+      </div>
+      <div class="note">
+        <div class="note__head"><span class="note__mark">§</span>Главный вывод</div>
+        <div class="note__body">
+          <p><b>Безопасность строится на цепочке: Идентификация → Аутентификация → Авторизация.</b> Разрыв любого звена ведёт к уязвимости.</p>
+        </div>
+      </div>
+      <div class="actions">
+        <button class="btn btn--big" id="toQuiz">Итоговое Тестирование</button>
+      </div>
+    </div>
+  `;
+  screens.final.querySelector('#toQuiz').addEventListener('click', startQuiz);
+}
+
+// ---------- ТЕСТ ----------
+function startQuiz() {
+  state.quizAnswers = [];
+  state.quizIndex = 0;
+  state.quizScore = 0;
+  state.quizDone = false;
+  showScreen('quiz');
+  renderQuizQuestion();
+}
+
+function renderQuizQuestion() {
+  const q = QUIZ[state.quizIndex];
+  screens.quiz.innerHTML = `
+    <div class="page">
+      <div class="act-head">
+        <span class="act-num">Тест</span>
+        <h2>Вопрос ${state.quizIndex + 1} из ${QUIZ.length}</h2>
+      </div>
+      <div class="quiz-question">
+        <h3>${q.q}</h3>
+        <div class="quiz-options" id="quizOptions">
+          ${q.options.map((opt, i) => `
+            <button class="quiz-option" data-index="${i}">${opt}</button>
+          `).join('')}
+        </div>
+        <div class="quiz-feedback" id="quizFeedback" hidden></div>
+      </div>
+    </div>
+  `;
+  screens.quiz.querySelectorAll('.quiz-option').forEach(btn => {
+    btn.addEventListener('click', () => handleQuizAnswer(parseInt(btn.dataset.index)));
+  });
+}
+
+function handleQuizAnswer(index) {
+  const q = QUIZ[state.quizIndex];
+  const buttons = screens.quiz.querySelectorAll('.quiz-option');
+  buttons.forEach(b => b.disabled = true);
+  const feedback = screens.quiz.querySelector('#quizFeedback');
+  const isCorrect = index === q.correct;
+  if (isCorrect) {
+    state.quizScore++;
+    playTone(880, 0.12, 'sine');
+    buttons[index].classList.add('correct');
+  } else {
+    playTone(220, 0.2, 'sawtooth');
+    buttons[index].classList.add('wrong');
+    buttons[q.correct].classList.add('correct');
+  }
+  feedback.hidden = false;
+  feedback.innerHTML = `<b>${isCorrect ? 'Верно!' : 'Неверно.'}</b> ${q.explain}`;
+  state.quizAnswers.push({ question: q.q, correct: isCorrect });
+
+  const nextBtn = document.createElement('button');
+  nextBtn.className = 'btn';
+  nextBtn.textContent = state.quizIndex < QUIZ.length - 1 ? 'Следующий вопрос' : 'Показать результат';
+  nextBtn.style.marginTop = '1rem';
+  nextBtn.addEventListener('click', () => {
+    if (state.quizIndex < QUIZ.length - 1) {
+      state.quizIndex++;
+      renderQuizQuestion();
+    } else {
+      showResult();
+    }
+  });
+  feedback.appendChild(nextBtn);
+}
+
+function showResult() {
+  state.quizDone = true;
+  updateHUD();
+  showScreen('result');
+  const score = state.quizScore;
+  const total = QUIZ.length;
+  const percent = Math.round((score / total) * 100);
+  let rank = 'Стажёр';
+  if (percent >= 90) rank = 'Агент безопасности';
+  else if (percent >= 70) rank = 'Сотрудник';
+  else if (percent >= 50) rank = 'Практикант';
+  else rank = 'Требуется переподготовка';
+
+  screens.result.innerHTML = `
+    <div class="page">
+      <div class="act-head">
+        <span class="act-num">Результат</span>
+        <h2>Золотой пропуск</h2>
+      </div>
+      <div class="result-pass">
+        <p>Банк «Кристалл» · отдел информационной безопасности</p>
+        <h3>ПРОПУСК №1957-К</h3>
+        <p>Выдан: Алексею Иванову</p>
+        <p class="chain">Идентификация → Аутентификация → Авторизация</p>
+        <p class="score">${score} из ${total} (${percent}%)</p>
+        <p>Звание: <b>${rank}</b></p>
+      </div>
+      <div class="note">
+        <div class="note__head"><span class="note__mark">§</span>Шпаргалка</div>
+        <div class="note__body">
+          <p><b>Идентификация</b> — «Кто ты?» (Ввод логина, предъявление имени).</p>
+          <p><b>Аутентификация</b> — «Докажи!» (Ввод пароля, отпечаток, СМС).</p>
+          <p><b>Авторизация</b> — «Что тебе можно?» (Права доступа, роль, чтение/запись).</p>
+          <p><b>Главный вывод:</b> Безопасность строится на цепочке: Идентификация → Аутентификация → Авторизация. Разрыв любого звена ведёт к уязвимости.</p>
+        </div>
+      </div>
+      <div class="actions">
+        <button class="btn" id="restartBtn">Пройти заново</button>
+      </div>
+    </div>
+  `;
+  screens.result.querySelector('#restartBtn').addEventListener('click', () => {
+    location.reload();
+  });
+  logJournal(`📋 Тест завершён: ${score}/${total}`, 'ok');
+}
+
+// ---------- ИНИЦИАЛИЗАЦИЯ ----------
+function init() {
+  hud.hidden = true;
+  showScreen('title');
+
+  $('#startBtn').addEventListener('click', () => {
+    ensureAudio();
+    showScreen('brief');
+  });
+
+  $('#toAct1').addEventListener('click', () => {
+    state.act = 1;
+    state.scene = 0;
+    showScreen('act1');
+    renderActScreen('s-act1', 1, 0);
+  });
+
+  tSound.addEventListener('click', () => {
+    state.sound = !state.sound;
+    tSound.setAttribute('aria-pressed', state.sound);
+    tSound.textContent = state.sound ? 'звук' : 'звук ✕';
+  });
+  tVoice.addEventListener('click', () => {
+    state.voice = !state.voice;
+    tVoice.setAttribute('aria-pressed', state.voice);
+    tVoice.textContent = state.voice ? 'голос' : 'голос ✕';
+    if (!state.voice) window.speechSynthesis.cancel();
+  });
+
+  tJournal.addEventListener('click', () => {
+    journal.classList.add('is-open');
+    scrim.hidden = false;
+  });
+  jClose.addEventListener('click', closeJournal);
+  scrim.addEventListener('click', closeJournal);
+
+  function closeJournal() {
+    journal.classList.remove('is-open');
+    scrim.hidden = true;
+  }
+
+  updateHUD();
+}
+
+document.addEventListener('DOMContentLoaded', init);

+ 104 - 0
2026-27/1 сем/test/index.html

@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html lang="ru">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+<title>Цифровой пропуск — Взлом невидимого банка</title>
+<meta name="description" content="Интерактивный квест-детектив об идентификации, аутентификации и авторизации.">
+<meta name="theme-color" content="#241f18">
+<link rel="stylesheet" href="style.css">
+</head>
+<body>
+<div class="grain" aria-hidden="true"></div>
+
+<header class="hud" id="hud" hidden>
+  <div class="hud__left">
+    <span class="hud__badge" id="hudAct">АКТ I</span>
+    <span class="hud__name">Цифровой пропуск</span>
+  </div>
+  <div class="hud__track"><span class="hud__fill" id="hudFill"></span></div>
+  <div class="hud__tools">
+    <button class="tool" id="tSound" aria-pressed="true" title="Звук">звук</button>
+    <button class="tool" id="tVoice" aria-pressed="true" title="Голос Системы">голос</button>
+    <button class="tool" id="tJournal" title="Журнал системы">журнал</button>
+  </div>
+</header>
+
+<main id="stage">
+
+  <!-- ================= ТИТУЛ ================= -->
+  <section class="screen is-active" id="s-title">
+    <div class="cover">
+      <div class="cover__series">Научно-популярная библиотека · выпуск VII</div>
+      <div class="cover__rule"><span>★</span></div>
+      <h1 class="cover__title">Цифровой<br>пропуск</h1>
+      <div class="cover__sub">Взлом невидимого банка</div>
+      <p class="cover__lead">
+        Интерактивный квест-детектив в трёх актах о том, как машина узнаёт человека:
+        <b>идентификация</b>, <b>аутентификация</b>, <b>авторизация</b>.
+      </p>
+      <div class="cover__meta">
+        <span>3 акта</span><span>итоговый тест</span><span>15–20 минут</span>
+      </div>
+      <button class="btn btn--big" id="startBtn">Открыть дело</button>
+      <p class="cover__foot">Банк «Кристалл» · отдел информационной безопасности · дело №1957-К</p>
+    </div>
+  </section>
+
+  <!-- ================= ДОСЬЕ ================= -->
+  <section class="screen" id="s-brief">
+    <div class="page">
+      <div class="act-head">
+        <span class="act-num">Досье</span>
+        <h2>Участники дела</h2>
+      </div>
+      <div class="cards">
+        <article class="card">
+          <h3>Вы</h3>
+          <p>Алексей Иванов, стажёр службы безопасности банка «Кристалл». Первый рабочий день. Постоянный пропуск ещё не выдан.</p>
+        </article>
+        <article class="card">
+          <h3>Система</h3>
+          <p>Голосовой помощник банка. Говорит коротко, ведёт журнал и ничего не забывает. Именно она решает, пустить вас или нет.</p>
+        </article>
+        <article class="card card--dark">
+          <h3>Тень</h3>
+          <p>Злоумышленник. Уже внутри периметра. Ждёт, когда вы ошибётесь, чтобы пройти по вашим следам.</p>
+        </article>
+      </div>
+      <div class="note note--flat">
+        <div class="note__head"><span class="note__mark">§</span>Как устроено дело</div>
+        <div class="note__body">
+          <p>Три акта — три ситуации. В каждой нужно выбрать верное действие. Ошибка не заканчивает игру: Система объяснит, что пошло не так, и даст попробовать снова. В конце — <b>итоговое тестирование</b>.</p>
+        </div>
+      </div>
+      <div class="actions">
+        <button class="btn" id="toAct1">Акт I. Идентификация</button>
+      </div>
+    </div>
+  </section>
+
+  <!-- ================= АКТЫ ================= -->
+  <section class="screen" id="s-act1"></section>
+  <section class="screen" id="s-act2"></section>
+  <section class="screen" id="s-act3"></section>
+  <section class="screen" id="s-final"></section>
+  <section class="screen" id="s-quiz"></section>
+  <section class="screen" id="s-result"></section>
+
+</main>
+
+<aside class="journal" id="journal" aria-hidden="true">
+  <div class="journal__head">
+    <span>Журнал системы</span>
+    <button class="tool" id="jClose">закрыть</button>
+  </div>
+  <div class="journal__body" id="journalBody">
+    <p class="journal__empty">Записей пока нет.</p>
+  </div>
+</aside>
+<div class="scrim" id="scrim" hidden></div>
+
+<script src="app.js"></script>
+</body>
+</html>

+ 279 - 0
2026-27/1 сем/test/style.css

@@ -0,0 +1,279 @@
+:root {
+  --paper: #efe8d8;
+  --paper-dark: #e0d6c2;
+  --ink: #2b241c;
+  --ink-soft: #5a4f42;
+  --accent: #a33b2a;
+  --accent-dark: #7a2a1e;
+  --rule: #c9bfae;
+  --shadow: rgba(43,36,28,0.15);
+  --font-serif: 'Georgia', 'Times New Roman', 'PT Serif', serif;
+  --font-mono: 'Courier New', monospace;
+}
+
+* { box-sizing: border-box; }
+
+body {
+  margin: 0;
+  background: var(--paper);
+  color: var(--ink);
+  font-family: var(--font-serif);
+  font-size: 18px;
+  line-height: 1.6;
+  min-height: 100vh;
+  overflow-x: hidden;
+}
+
+.grain {
+  position: fixed;
+  inset: 0;
+  pointer-events: none;
+  z-index: 9999;
+  opacity: 0.12;
+  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
+}
+
+.hud {
+  position: sticky;
+  top: 0;
+  z-index: 100;
+  display: flex;
+  align-items: center;
+  gap: 1rem;
+  padding: 0.6rem 1.2rem;
+  background: var(--ink);
+  color: var(--paper);
+  border-bottom: 3px double var(--accent);
+}
+.hud__left { display: flex; align-items: center; gap: 0.8rem; }
+.hud__badge {
+  background: var(--accent);
+  color: var(--paper);
+  font-weight: bold;
+  font-size: 0.8rem;
+  letter-spacing: 0.08em;
+  padding: 0.2rem 0.6rem;
+  text-transform: uppercase;
+}
+.hud__name { font-weight: bold; letter-spacing: 0.05em; }
+.hud__track {
+  flex: 1;
+  height: 8px;
+  background: rgba(255,255,255,0.2);
+  border: 1px solid rgba(255,255,255,0.3);
+}
+.hud__fill {
+  display: block;
+  height: 100%;
+  width: 0%;
+  background: var(--accent);
+  transition: width 0.4s ease;
+}
+.hud__tools { display: flex; gap: 0.4rem; }
+.tool {
+  background: transparent;
+  border: 1px solid rgba(255,255,255,0.4);
+  color: var(--paper);
+  font-family: var(--font-mono);
+  font-size: 0.7rem;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  padding: 0.3rem 0.5rem;
+  cursor: pointer;
+}
+.tool[aria-pressed="false"] { opacity: 0.5; text-decoration: line-through; }
+.tool:hover { background: rgba(255,255,255,0.15); }
+
+.screen { display: none; }
+.screen.is-active { display: block; }
+
+.page {
+  max-width: 820px;
+  margin: 0 auto;
+  padding: 2rem 1.5rem 4rem;
+  animation: fadeIn 0.5s ease;
+}
+@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
+
+/* Обложка */
+.cover {
+  max-width: 620px;
+  margin: 6vh auto;
+  padding: 2.5rem 2rem;
+  border: 4px double var(--ink);
+  background: var(--paper);
+  box-shadow: 12px 12px 0 var(--paper-dark);
+  text-align: center;
+}
+.cover__series { font-size: 0.8rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-soft); }
+.cover__rule { margin: 1rem 0; font-size: 1.4rem; color: var(--accent); }
+.cover__title {
+  font-size: clamp(2.4rem, 8vw, 3.8rem);
+  line-height: 1.05;
+  margin: 0.4rem 0;
+  text-transform: uppercase;
+  letter-spacing: 0.04em;
+}
+.cover__sub { font-size: 1.3rem; font-style: italic; color: var(--accent-dark); margin-bottom: 1.5rem; }
+.cover__lead { text-align: left; font-size: 1rem; margin: 1.5rem 0; }
+.cover__meta { display: flex; justify-content: center; gap: 1.5rem; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink-soft); margin: 1.5rem 0; }
+.cover__foot { font-size: 0.75rem; color: var(--ink-soft); margin-top: 2rem; }
+
+.btn {
+  display: inline-block;
+  background: var(--paper);
+  color: var(--ink);
+  border: 2px solid var(--ink);
+  padding: 0.7rem 1.4rem;
+  font-family: var(--font-serif);
+  font-size: 1rem;
+  font-weight: bold;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  cursor: pointer;
+  box-shadow: 4px 4px 0 var(--ink);
+  transition: all 0.15s;
+}
+.btn:hover { background: var(--ink); color: var(--paper); box-shadow: 2px 2px 0 var(--accent); }
+.btn:active { transform: translate(2px,2px); box-shadow: none; }
+.btn--big { font-size: 1.2rem; padding: 1rem 2rem; }
+.btn--small { font-size: 0.8rem; padding: 0.4rem 0.8rem; }
+
+/* Акт */
+.act-head { border-bottom: 3px double var(--ink); margin-bottom: 2rem; padding-bottom: 0.5rem; }
+.act-num { font-size: 0.8rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--accent); font-weight: bold; }
+.act-head h2 { margin: 0.2rem 0 0; font-size: 2rem; text-transform: uppercase; }
+
+.scene { display: flex; flex-direction: column; gap: 1.5rem; }
+
+.system-bubble {
+  display: flex;
+  gap: 1rem;
+  background: var(--paper-dark);
+  border: 2px solid var(--ink);
+  padding: 1.2rem;
+  box-shadow: 6px 6px 0 var(--paper);
+}
+.system-avatar {
+  flex: 0 0 48px;
+  height: 48px;
+  background: var(--ink);
+  color: var(--paper);
+  display: grid;
+  place-items: center;
+  font-size: 1.4rem;
+  font-weight: bold;
+  border-radius: 50%;
+}
+.system-text { font-size: 1.05rem; }
+
+.choices { display: flex; flex-direction: column; gap: 0.8rem; }
+.choice {
+  text-align: left;
+  background: var(--paper);
+  border: 2px solid var(--ink);
+  padding: 0.9rem 1.2rem;
+  font-family: var(--font-serif);
+  font-size: 1rem;
+  cursor: pointer;
+  box-shadow: 4px 4px 0 var(--paper-dark);
+  transition: all 0.15s;
+}
+.choice:hover:not(:disabled) { background: var(--ink); color: var(--paper); box-shadow: 2px 2px 0 var(--accent); }
+.choice:disabled { opacity: 0.5; cursor: not-allowed; }
+
+.feedback { padding: 1rem; border-left: 6px solid var(--accent); background: rgba(163,59,42,0.08); }
+
+.note {
+  border: 2px solid var(--ink);
+  background: var(--paper);
+  box-shadow: 6px 6px 0 var(--paper-dark);
+  margin: 1rem 0;
+}
+.note__head {
+  background: var(--ink);
+  color: var(--paper);
+  padding: 0.4rem 1rem;
+  font-size: 0.85rem;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+}
+.note__mark { color: var(--accent); margin-right: 0.4rem; }
+.note__body { padding: 1rem 1.2rem; font-size: 0.95rem; }
+.note--flat { box-shadow: none; }
+
+.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px,1fr)); gap: 1rem; margin: 1.5rem 0; }
+.card { border: 2px solid var(--ink); padding: 1rem; background: var(--paper); box-shadow: 4px 4px 0 var(--paper-dark); }
+.card--dark { background: var(--ink); color: var(--paper); }
+.card--dark h3 { color: var(--accent); }
+.card h3 { margin-top: 0; text-transform: uppercase; font-size: 1rem; letter-spacing: 0.08em; }
+
+/* Журнал */
+.journal {
+  position: fixed;
+  top: 0;
+  right: 0;
+  width: min(420px, 90vw);
+  height: 100vh;
+  background: var(--ink);
+  color: var(--paper);
+  z-index: 200;
+  transform: translateX(100%);
+  transition: transform 0.3s ease;
+  display: flex;
+  flex-direction: column;
+  border-left: 4px solid var(--accent);
+}
+.journal.is-open { transform: none; }
+.journal__head { display: flex; justify-content: space-between; align-items: center; padding: 1rem; border-bottom: 2px solid var(--accent); font-weight: bold; text-transform: uppercase; letter-spacing: 0.1em; }
+.journal__body { flex: 1; overflow-y: auto; padding: 1rem; font-family: var(--font-mono); font-size: 0.8rem; }
+.journal__empty { color: rgba(255,255,255,0.5); font-style: italic; }
+.journal__entry { border-bottom: 1px solid rgba(255,255,255,0.15); padding: 0.5rem 0; }
+.journal__entry--ok { color: #8fdf8f; }
+.journal__entry--err { color: #ff9a8a; }
+
+.scrim { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 150; }
+
+/* Тест */
+.quiz-question { margin-bottom: 2rem; }
+.quiz-question h3 { font-size: 1.2rem; margin-bottom: 1rem; }
+.quiz-options { display: flex; flex-direction: column; gap: 0.6rem; }
+.quiz-option {
+  text-align: left;
+  background: var(--paper);
+  border: 2px solid var(--ink);
+  padding: 0.8rem 1rem;
+  font-family: var(--font-serif);
+  font-size: 1rem;
+  cursor: pointer;
+  transition: all 0.15s;
+}
+.quiz-option:hover:not(:disabled) { background: var(--paper-dark); }
+.quiz-option.correct { background: #d4edda; border-color: #2e7d32; }
+.quiz-option.wrong { background: #f8d7da; border-color: #c62828; }
+.quiz-option:disabled { cursor: default; }
+.quiz-feedback { margin-top: 1rem; padding: 0.8rem; border-left: 4px solid var(--accent); background: rgba(163,59,42,0.08); }
+
+.result-pass {
+  border: 4px double var(--accent);
+  padding: 2rem;
+  text-align: center;
+  background: #fdf8e8;
+  box-shadow: 8px 8px 0 var(--paper-dark);
+  margin: 2rem 0;
+}
+.result-pass h3 { font-size: 2rem; text-transform: uppercase; margin: 0.5rem 0; color: var(--accent-dark); }
+.result-pass .chain { font-size: 1.1rem; margin: 1rem 0; }
+.result-pass .score { font-size: 1.5rem; font-weight: bold; }
+
+.actions { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 2rem; }
+
+@media (max-width: 600px) {
+  body { font-size: 16px; }
+  .page { padding: 1.2rem 1rem 3rem; }
+  .cover { margin: 2vh auto; padding: 1.5rem 1rem; }
+  .hud { flex-wrap: wrap; gap: 0.5rem; }
+  .hud__track { order: 3; flex-basis: 100%; }
+  .system-bubble { flex-direction: column; }
+  .system-avatar { align-self: flex-start; }
+}

+ 1 - 1
Лекции/plagiat_1.v3.py

@@ -21,7 +21,7 @@ BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
 LECTION_DIR = os.path.join("Лекции", "ПМ3.1")
 
 # ссылка для проверки
-url = "http://213.155.192.79:3001/u23-27trofimova/TZI/src/ed1c56af34c71449f924b8a0efaebd7842c66e39/%d0%9b%d0%b5%d0%ba%d1%86%d0%b8%d0%b8/%d0%9f%d0%9c3.1/2.1.300_%d0%92%d0%b8%d0%b4%d1%8b_%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8_%d0%b8_%d0%bd%d0%be%d1%81%d0%b8%d1%82%d0%b5%d0%bb%d0%b8_%d0%b7%d0%b0%d1%89%d0%b8%d1%89%d0%b0%d0%b5%d0%bc%d0%be%d0%b9_%d0%b8%d0%bd%d1%84%d0%be%d1%80%d0%bc%d0%b0%d1%86%d0%b8%d0%b8/trofimova.md"
+url = "http://213.155.192.79:3001/u23-27burdyko/TZI/src/9393b81c083a6f84fb5cddf9473b72552fcf260d/%d0%9b%d0%b5%d0%ba%d1%86%d0%b8%d0%b8/%d0%9f%d0%9c3.1/3.2.200_%d0%9f%d0%be%d0%b4%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5_%d0%be%d0%bf%d0%b0%d1%81%d0%bd%d1%8b%d1%85_%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2_%d0%b0%d0%ba%d1%83%d1%81%d1%82%d0%be%d1%8d%d0%bb%d0%b5%d0%ba%d1%82%d1%80%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d1%85_%d0%bf%d1%80%d0%b5%d0%be%d0%b1%d1%80%d0%b0%d0%b7%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b9/burdyko.md"
 
 # ------------------------------- / НАСТРОЙКИ ------------
 url = url.replace("src", "raw")

+ 4 - 0
Лекции/ПМ3.1/README.md

@@ -466,6 +466,10 @@
 [Вопросы](3.2.200_Подавление_опасных_сигналов_акустоэлектрических_преобразований/вопросы.md); 
 [Ермоленко А.](3.2.200_Подавление_опасных_сигналов_акустоэлектрических_преобразований/Ермоленко.md), 
 [Вопросы](3.2.200_Подавление_опасных_сигналов_акустоэлектрических_преобразований/Ермоленко_вопросы.md); 
+[Бурдыко М.](3.2.200_Подавление_опасных_сигналов_акустоэлектрических_преобразований/burdyko.md), 
+[Вопросы](3.2.200_Подавление_опасных_сигналов_акустоэлектрических_преобразований/vaprosi_byrd.md); 
+
+
 
 - радиоэлектронный канал;
 3.2.250 Модуляция.