1
0
ypv 2 недель назад
Родитель
Сommit
c8cf8ba643

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

@@ -1,558 +0,0 @@
-'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);

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

@@ -1,104 +0,0 @@
-<!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>

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

@@ -1,279 +0,0 @@
-: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; }
-}

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


+ 0 - 0
Лекции/ПМ3.2/README.md → Лекции/ПМ3.2/itsfzoi.md