Files
Learn_System/frontend/textbooks/algebra_10_ch1.html
T
Maxim Dolgolyov 55006e691e feat(alg10 W1): Глава 1 §1-§4 (тригонометрический минимум)
Реализованы первые 4 параграфа главы 1 'Тригонометрия':

§1 Единичная окружность. Градусная и радианная мера:
- SVG главный с 12 делениями (0°, 30°, 60°, ..., 330°)
- Интерактив 1: slider угла -720°..+720° с реал-тайм отрисовкой P_α
  + эквивалент в [0°, 360°)
- Интерактив 2: тренажёр перевода град ↔ рад (8 заданий)
- Интерактив 3: четверть угла (6 заданий)
- Босс §1: 5 этапов (углы, четверти, эквивалентные точки)

§2 sin и cos произвольного угла:
- SVG определения через координаты P_α (с подписями sin α и cos α)
- SVG знаков по четвертям с цветными секторами и подписями +/-
- SVG главных углов π/6, π/4, π/3, π/2 на окружности
- Таблица точных значений
- Интерактив 1: знаки sin/cos (8 заданий)
- Интерактив 2: точные значения (6 заданий)
- Интерактив 3: 'может ли так быть?' (6 да/нет)
- Босс §2: 5 этапов

§3 tg и ctg произвольного угла:
- SVG оси тангенсов (касательная x=1, точка A_α)
- SVG оси котангенсов (касательная y=1)
- Таблица знаков по четвертям
- Интерактив 1: 'существует ли?' (6 да/нет)
- Интерактив 2: знаки tg/ctg (6 заданий)
- Босс §3: 5 этапов

§4 Тригонометрические тождества:
- SVG прямоугольного треугольника на окружности → теорема Пифагора
- 3 производных тождества: tg·ctg=1, 1+tg²=1/cos², 1+ctg²=1/sin²
- Алгоритм 'знаю одну → найду все 4'
- Полный пример решения
- Интерактив 1: 'найди cos α по sin' (5 заданий)
- Интерактив 2: 'упрости выражение' (5 заданий)
- Интерактив 3: 'найди tg/ctg' (5 заданий)
- Босс §4: 5 этапов

Инфраструктура главы:
- 13 параграфов в PARAS (4 готовы, §5-§12 + final1 — stub 'в разработке')
- Sidebar с шпаргалкой для §1-§4
- 4 ачивки + ачивка 'Глава 1 пройдена'
- Тёмная тема, прогресс на сервер, XP
- Все SVG используют ALG10.tri.canvas() и связанные хелперы

Используется библиотека alg10_svg.js из Wave 0.
2026-05-29 10:47:44 +03:00

1345 lines
94 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>Алгебра 10 · Глава 1 · Тригонометрия</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<script src="/js/alg10_svg.js?v=1" defer></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#ecfeff; --card:#fff; --card-soft:#f0fdfa; --text:#0f1a1f; --muted:#4b6671;
--border:#cffafe; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
--pri:#0d9488; --pri2:#0f766e; --pri-soft:#ccfbf1;
--acc:#14b8a6; --acc2:#0d9488; --acc-soft:#99f6e4;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#04181c; --card:#0a2329; --card-soft:#0e2c32; --text:#e0fbf9; --muted:#88aab1; --border:#1d4248}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
button,input,select,textarea{font-family:inherit;font-size:inherit}
button{cursor:pointer;border:0;background:transparent;color:inherit}
a{color:inherit;text-decoration:none}
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
.hdr{position:relative;background:linear-gradient(110deg,#134e4a 0%,#0d9488 55%,#5eead4 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(204,251,241,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 1';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(220,250,250,.12);line-height:1;pointer-events:none;z-index:0}
.hdr-row{position:relative;z-index:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.hdr h1{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:900;letter-spacing:-.01em;line-height:1.3;padding-top:4px}
.hdr-sub{font-size:.85rem;opacity:.88;margin-top:6px;font-weight:500;line-height:1.4}
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.hdr-btn{padding:7px 12px;border-radius:9px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;text-decoration:none}
.hdr-btn:hover{background:rgba(255,255,255,.24)}
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
.col-main{min-width:0}
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
.hero::before{content:'sin α';position:absolute;right:-10px;top:-20px;font-size:clamp(2rem,8vw,5.5rem);font-weight:900;color:var(--pri);opacity:.10;line-height:1;pointer-events:none;font-family:'Unbounded',sans-serif}
.hero h2{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--pri2);margin-bottom:10px}
.hero p{font-size:.95rem;color:var(--text);opacity:.88;margin-bottom:14px;max-width:640px}
.hero-row{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
.btn-primary{padding:11px 22px;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#fff;border-radius:11px;font-weight:700;font-size:.92rem;display:inline-flex;align-items:center;gap:8px;box-shadow:var(--sh2)}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(13,148,136,.32)}
.hero-progress{flex:1;min-width:200px;max-width:280px}
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
.hp-bar{height:8px;background:rgba(13,148,136,.18);border-radius:5px;overflow:hidden}
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;font-family:'Unbounded',sans-serif}
.psel{margin-bottom:24px}
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:12px;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;text-align:left;position:relative}
.psel-card:hover{transform:translateY(-3px);box-shadow:var(--sh2);border-color:var(--pri)}
.psel-card.active{border-color:var(--pri);background:linear-gradient(135deg,var(--pri-soft),var(--card));box-shadow:var(--sh2)}
.psel-card.active::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:13px 13px 0 0}
.psel-num{font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--pri);text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px}
.psel-name{font-size:.82rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:6px}
.psel-prog{height:4px;background:rgba(13,148,136,.10);border-radius:3px;overflow:hidden}
.psel-prog-fill{height:100%;background:var(--pri);width:0%;transition:width .4s}
.psel-card.final{background:linear-gradient(135deg,#fff5e1,#fef3c7)}
.sec[id="sec-p1"] { --sec-acc:#0d9488; --sec-acc-d:#0f766e; --sec-acc-soft:#ccfbf1; }
.sec[id="sec-p2"] { --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.sec[id="sec-p3"] { --sec-acc:#16a34a; --sec-acc-d:#15803d; --sec-acc-soft:#dcfce7; }
.sec[id="sec-p4"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p5"], .sec[id="sec-p6"], .sec[id="sec-p7"], .sec[id="sec-p8"],
.sec[id="sec-p9"], .sec[id="sec-p10"], .sec[id="sec-p11"], .sec[id="sec-p12"],
.sec[id="sec-final1"]{ --sec-acc:#0d9488; --sec-acc-d:#0f766e; --sec-acc-soft:#ccfbf1; }
.sec{display:none;position:relative;animation:fadeIn .35s ease}
.sec.active{display:block}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;z-index:0;opacity:.35}
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));line-height:1.25}
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(13,148,136,.06);position:relative;z-index:1}
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
.card-icon.theory{background:#8b5cf6}.card-icon.rule{background:#ec4899}.card-icon.algo{background:#f59e0b}.card-icon.example{background:#10b981}
.card-icon .ic{width:18px;height:18px}
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
.card-body{font-size:.94rem;line-height:1.65}
.card-body p{margin-bottom:8px}
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s}
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);font-family:'JetBrains Mono',monospace}
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none}
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
.sidecard-row b{color:var(--pri);font-weight:700}
@media(max-width:980px){.col-side{position:static;max-height:none}}
.xp-card{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft));border:1.5px solid var(--acc);border-radius:12px;padding:14px;margin-bottom:14px}
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
.xp-level{font-size:1.1rem;font-weight:900;color:var(--acc2);font-family:'Unbounded',sans-serif}
.xp-bar{height:9px;background:rgba(13,148,136,.18);border-radius:6px;overflow:hidden;margin:7px 0}
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
.spoiler summary::-webkit-details-marker{display:none}
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
.spoiler[open] summary::before{content:'\2212'}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,#0d9488,#14b8a6);color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(13,148,136,.45);z-index:1002;display:none;align-items:center;gap:8px;max-width:340px}
.ach-popup.show{display:flex}
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
.hp-boss{height:14px;background:rgba(220,38,38,.12);border-radius:9px;overflow:hidden;border:1px solid #fecaca;margin:8px 0}
.hp-boss-fill{height:100%;background:linear-gradient(90deg,#dc2626,#f59e0b);border-radius:9px;transition:width .5s cubic-bezier(.4,0,.2,1)}
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none}
.col-side-backdrop.show{display:block}
@media(max-width:980px){
.col-side{position:fixed;top:0;right:0;height:100vh;width:300px;max-width:88vw;background:var(--bg);box-shadow:-12px 0 24px rgba(0,0,0,.18);padding:18px 16px;overflow-y:auto;transform:translateX(100%);transition:transform .25s ease;z-index:9991;max-height:none}
.col-side.open{transform:none}
}
.boss-card{padding:16px;background:var(--card);border-radius:12px;border:2px solid var(--bad,#dc2626);margin-bottom:14px}
.boss-head{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.boss-title{font-family:'Unbounded',sans-serif;font-weight:800;color:#7f1d1d;font-size:1.04rem;flex:1}
.boss-stage{font-size:.85rem;color:var(--muted)}
.boss-q{font-size:1rem;line-height:1.55;padding:11px 13px;background:var(--card-soft);border-radius:8px;margin-bottom:9px;border-left:3px solid var(--bad,#dc2626)}
.svg-host{display:flex;justify-content:center;margin:12px 0}
.svg-host-row{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin:12px 0}
/* Slider control for interactives */
.slider-ctrl{display:flex;align-items:center;gap:10px;background:var(--card);padding:10px 14px;border-radius:10px;border:1px solid var(--border);margin-bottom:12px;flex-wrap:wrap}
.slider-ctrl label{font-size:.86rem;font-weight:700;color:var(--text);min-width:88px}
.slider-ctrl input[type=range]{flex:1;min-width:180px;accent-color:var(--pri)}
.slider-ctrl .val{font-family:'JetBrains Mono',monospace;font-weight:700;color:var(--pri);min-width:80px;text-align:right}
.stub-soon{padding:30px 22px;text-align:center;background:linear-gradient(135deg,var(--pri-soft),var(--acc-soft));border:1.5px dashed var(--pri);border-radius:14px;color:var(--text)}
.stub-soon h3{font-family:'Unbounded',sans-serif;font-size:1.15rem;color:var(--pri2);margin-bottom:8px}
.stub-soon p{font-size:.92rem;color:var(--muted);max-width:520px;margin:0 auto}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Алгебра 10 · Глава 1</h1>
<div class="hdr-sub">Тригонометрия · единичная окружность · функции · уравнения</div>
</div>
<div class="hdr-side">
<a href="/textbook/algebra-10" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К Алгебре 10</a>
<button id="sidebar-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg> Шпаргалка</button>
<button id="theme-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><span id="theme-lab">Тёмная</span></button>
</div>
</div>
</header>
<main class="main">
<div class="col-main">
<section class="hero">
<h2>Тригонометрия начинается с одной окружности</h2>
<p>Единичная окружность с центром в начале координат — фундамент всей тригонометрии. На ней живут <b>sin α</b> и <b>cos α</b> (координаты точки), <b>tg α</b> и <b>ctg α</b> (точки на касательных). Из них вырастают <b>графики</b>, <b>уравнения</b>, <b>формулы</b>.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p1')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 1</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge"></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p1" class="sec" data-watermark="P_α"><div class="sec-header"><span class="sec-num">§ 1</span><h2 class="sec-h">Единичная окружность. Градусная и радианная мера</h2></div><div id="p1-body"></div></section>
<section id="sec-p2" class="sec" data-watermark="sin"><div class="sec-header"><span class="sec-num">§ 2</span><h2 class="sec-h">Синус и косинус произвольного угла</h2></div><div id="p2-body"></div></section>
<section id="sec-p3" class="sec" data-watermark="tg"><div class="sec-header"><span class="sec-num">§ 3</span><h2 class="sec-h">Тангенс и котангенс произвольного угла</h2></div><div id="p3-body"></div></section>
<section id="sec-p4" class="sec" data-watermark="≡"><div class="sec-header"><span class="sec-num">§ 4</span><h2 class="sec-h">Тригонометрические тождества</h2></div><div id="p4-body"></div></section>
<section id="sec-p5" class="sec" data-watermark="∿"><div class="sec-header"><span class="sec-num">§ 5</span><h2 class="sec-h">Функции y = sin x и y = cos x</h2></div><div id="p5-body"></div></section>
<section id="sec-p6" class="sec" data-watermark="∿"><div class="sec-header"><span class="sec-num">§ 6</span><h2 class="sec-h">Функции y = tg x и y = ctg x</h2></div><div id="p6-body"></div></section>
<section id="sec-p7" class="sec" data-watermark="arc"><div class="sec-header"><span class="sec-num">§ 7</span><h2 class="sec-h">Арксинус, арккосинус, арктангенс, арккотангенс</h2></div><div id="p7-body"></div></section>
<section id="sec-p8" class="sec" data-watermark="="><div class="sec-header"><span class="sec-num">§ 8</span><h2 class="sec-h">Тригонометрические уравнения</h2></div><div id="p8-body"></div></section>
<section id="sec-p9" class="sec" data-watermark="↺"><div class="sec-header"><span class="sec-num">§ 9</span><h2 class="sec-h">Формулы приведения</h2></div><div id="p9-body"></div></section>
<section id="sec-p10" class="sec" data-watermark="±"><div class="sec-header"><span class="sec-num">§ 10</span><h2 class="sec-h">Синус, косинус, тангенс суммы и разности</h2></div><div id="p10-body"></div></section>
<section id="sec-p11" class="sec" data-watermark="2α"><div class="sec-header"><span class="sec-num">§ 11</span><h2 class="sec-h">Формулы двойного аргумента</h2></div><div id="p11-body"></div></section>
<section id="sec-p12" class="sec" data-watermark="·"><div class="sec-header"><span class="sec-num">§ 12</span><h2 class="sec-h">Преобразование суммы в произведение</h2></div><div id="p12-body"></div></section>
<section id="sec-final1" class="sec" data-watermark="★"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#0d9488,#5eead4)">Финал главы</span><h2 class="sec-h">Итоги. 6 боссов главы 1</h2></div><div id="final1-body"></div></section>
</div>
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
<div class="col-side-backdrop" id="col-side-backdrop"></div>
</main>
<footer class="foot">Интерактивный учебник «Алгебра — 10 класс» · Глава 1 · Тригонометрия · LearnSpace</footer>
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
<script>
'use strict';
const STATE = { current:'p1', progress:{p1:0,p2:0,p3:0,p4:0,p5:0,p6:0,p7:0,p8:0,p9:0,p10:0,p11:0,p12:0,final1:0}, achievements:new Map(), xp:0, level:1 };
const TOTAL_PARAS = 13;
const _TB_SLUG = 'algebra-10-ch1';
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
function _xpForLevel(lv){ return (lv-1)*(lv-1)*100; }
const ACH_LABELS = {
start:'Начало главы 1!',
p1_done:'Окружность и углы — освоены!',
p2_done:'sin α и cos α — на ладони!',
p3_done:'tg α и ctg α — понимаешь!',
p4_done:'Тождества — твои!',
ch1_done:'Глава 1 — Тригонометрия пройдена!',
};
function loadProgress(){
try{
const s=localStorage.getItem('algebra10_ch1_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
const a=localStorage.getItem('algebra10_ch1_achievements');
if(a){ const p=JSON.parse(a); if(Array.isArray(p)) p.forEach(id=>STATE.achievements.set(id, ACH_LABELS[id]||id)); else if(p&&typeof p==='object'){ for(const[id,t] of Object.entries(p)) STATE.achievements.set(id,(t&&t!==id)?t:(ACH_LABELS[id]||id)); } }
STATE.xp=+(localStorage.getItem('algebra10_xp')||0); STATE.level=calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('algebra10_ch1_progress', JSON.stringify(STATE.progress));
localStorage.setItem('algebra10_ch1_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem('algebra10_xp', String(STATE.xp));
}catch(e){}
}
function bumpProgress(key, delta){
STATE.progress[key]=Math.max(0,Math.min(100,(STATE.progress[key]||0)+delta));
saveProgress(); refreshProgressUI();
if(STATE.progress[key]>=50) markParaRead(key);
if(STATE.progress[key]>=100){
if(key==='p1') achievement('p1_done');
else if(key==='p2') achievement('p2_done');
else if(key==='p3') achievement('p3_done');
else if(key==='p4') achievement('p4_done');
else if(key==='final1') achievement('ch1_done');
}
}
const _markedRead=new Set();
let _pendingProgressBody=null, _progressTimer=null;
function _flushProgress(){
const body=_pendingProgressBody; _pendingProgressBody=null; if(!body) return;
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
fetch('/api/textbooks/'+_TB_SLUG+'/progress',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+tok},body:JSON.stringify(body),keepalive:true}).catch(()=>{});
}
function _queueProgress(patch){ _pendingProgressBody=Object.assign(_pendingProgressBody||{},patch); if(_progressTimer) clearTimeout(_progressTimer); _progressTimer=setTimeout(_flushProgress, 600); }
function markLastPara(id){ _queueProgress({last_para:id}); }
function markParaRead(id){ if(_markedRead.has(id)) return; _markedRead.add(id); _queueProgress({mark_read:id}); }
window.addEventListener('beforeunload', _flushProgress);
function addXp(n,src){
if(!n) return;
const prev=STATE.level; STATE.xp=Math.max(0,(STATE.xp||0)+n); STATE.level=calcLevel(STATE.xp);
saveProgress(); refreshProgressUI();
if(window.LS&&window.LS.xp) window.LS.xp.add(n,'algebra10-ch1-'+(src||'misc'));
if(STATE.level>prev){
const pop=document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),2600); }
}
}
function refreshProgressUI(){
const total=Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0)/TOTAL_PARAS);
const f=document.getElementById('hero-hp-fill'); if(f) f.style.width=total+'%';
const t=document.getElementById('hero-hp-text'); if(t) t.textContent=total+'% пройдено';
document.querySelectorAll('[data-prog-card]').forEach(el=>{ const k=el.dataset.progCard; const fl=el.querySelector('.psel-prog-fill'); if(fl) fl.style.width=(STATE.progress[k]||0)+'%'; });
const xpBadge=document.getElementById('hero-xp-badge');
if(xpBadge){ xpBadge.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:13px;height:13px"><polygon points="12 2 22 20 2 20"/></svg> Ур. '+STATE.level+' \xb7 '+(STATE.xp||0)+' XP'; }
if(STATE.current && document.getElementById('sidebar-content')){ try{ buildSidebar(STATE.current); }catch(e){} }
}
function achievement(id,text){
if(STATE.achievements.has(id)) return;
STATE.achievements.set(id, text||ACH_LABELS[id]||id); saveProgress();
const pop=document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent=text||ACH_LABELS[id]||id; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),3300); }
addXp(20,'ach-'+id);
}
const PARAS = [
{ id:'p1', num:'§ 1', name:'Единичная окружность', sub:'град ↔ рад' },
{ id:'p2', num:'§ 2', name:'sin и cos', sub:'координаты P_α' },
{ id:'p3', num:'§ 3', name:'tg и ctg', sub:'оси тангенсов' },
{ id:'p4', num:'§ 4', name:'Тождества', sub:'sin² + cos² = 1' },
{ id:'p5', num:'§ 5', name:'y = sin x / cos x', sub:'графики' },
{ id:'p6', num:'§ 6', name:'y = tg x / ctg x', sub:'графики' },
{ id:'p7', num:'§ 7', name:'arcsin, arccos, arctg, arcctg', sub:'обратные' },
{ id:'p8', num:'§ 8', name:'Уравнения', sub:'sin x = a и др.' },
{ id:'p9', num:'§ 9', name:'Формулы приведения', sub:'π/2 ± α и др.' },
{ id:'p10',num:'§ 10', name:'Сумма и разность', sub:'sin(α±β)' },
{ id:'p11',num:'§ 11', name:'Двойной аргумент', sub:'sin 2α' },
{ id:'p12',num:'§ 12', name:'Сумма → произведение', sub:'sin α + sin β' },
{ id:'final1', num:'★', name:'Финал главы', sub:'6 боссов', final:true },
];
function buildParaSelector(){
const g=document.getElementById('psel-grid'); g.innerHTML='';
PARAS.forEach(p=>{
const card=document.createElement('div');
card.className='psel-card'+(p.final?' final':'');
card.dataset.id=p.id; card.dataset.progCard=p.id;
card.innerHTML='<div class="psel-num">'+p.num+'</div><div class="psel-name">'+p.name+'</div><div class="psel-prog"><div class="psel-prog-fill"></div></div>';
card.addEventListener('click', ()=>goTo(p.id));
g.appendChild(card);
});
if(window.renderMathInElement) try{ renderMath(g); }catch(e){}
}
const BUILT=new Set();
const BUILDERS = { p1:()=>buildP1(), p2:()=>buildP2(), p3:()=>buildP3(), p4:()=>buildP4(),
p5:()=>buildStub('p5','§5 — Графики функций sin x и cos x'),
p6:()=>buildStub('p6','§6 — Графики функций tg x и ctg x'),
p7:()=>buildStub('p7','§7 — Арксинус, арккосинус, арктангенс, арккотангенс'),
p8:()=>buildStub('p8','§8 — Тригонометрические уравнения'),
p9:()=>buildStub('p9','§9 — Формулы приведения'),
p10:()=>buildStub('p10','§10 — Синус, косинус, тангенс суммы и разности'),
p11:()=>buildStub('p11','§11 — Формулы двойного аргумента'),
p12:()=>buildStub('p12','§12 — Преобразование суммы в произведение'),
final1:()=>buildStub('final1','Финал главы 1 — 6 боссов')
};
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
function goTo(id){
STATE.current=id; ensureBuilt(id);
document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));
const el=document.getElementById('sec-'+id); if(el) el.classList.add('active');
document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active', c.dataset.id===id));
buildSidebar(id);
window.scrollTo({top:0,behavior:'smooth'});
if((STATE.progress[id]||0)<10) bumpProgress(id, 10);
if(window.renderMathInElement) setTimeout(()=>renderMath(el), 0);
markLastPara(id);
}
const SIDEBARS = {
p1:{title:'Шпаргалка §1',rows:[
['Единичная окружн.','центр $(0;0)$, радиус $1$'],
['$P_\\alpha$','поворот $P_0(1;0)$ на $\\alpha$'],
['1 рад','$\\approx 57°$'],
['Перевод','град $\\cdot \\frac{\\pi}{180}$ → рад'],
['Перевод','рад $\\cdot \\frac{180}{\\pi}$ → град'],
['Полный оборот','$360° = 2\\pi$ рад'],
]},
p2:{title:'Шпаргалка §2',rows:[
['Опр.','$\\sin\\alpha = y_\\alpha$, $\\cos\\alpha = x_\\alpha$'],
['Область знач.','$-1 \\le \\sin\\alpha \\le 1$'],
['','$-1 \\le \\cos\\alpha \\le 1$'],
['I четверть','sin $+$, cos $+$'],
['II четверть','sin $+$, cos $-$'],
['III четверть','sin $-$, cos $-$'],
['IV четверть','sin $-$, cos $+$'],
]},
p3:{title:'Шпаргалка §3',rows:[
['Опр.','$\\tg\\alpha = \\frac{\\sin\\alpha}{\\cos\\alpha}$'],
['Опр.','$\\ctg\\alpha = \\frac{\\cos\\alpha}{\\sin\\alpha}$'],
['ОДЗ tg','$\\alpha \\ne \\frac{\\pi}{2} + \\pi n$'],
['ОДЗ ctg','$\\alpha \\ne \\pi n$'],
['Знаки','I: $+$, II: $-$, III: $+$, IV: $-$'],
['Произведение','$\\tg\\alpha \\cdot \\ctg\\alpha = 1$'],
]},
p4:{title:'Шпаргалка §4',rows:[
['Основное','$\\sin^2\\alpha + \\cos^2\\alpha = 1$'],
['Через tg','$1 + \\tg^2\\alpha = \\frac{1}{\\cos^2\\alpha}$'],
['Через ctg','$1 + \\ctg^2\\alpha = \\frac{1}{\\sin^2\\alpha}$'],
['Связь','$\\tg\\alpha \\cdot \\ctg\\alpha = 1$'],
['Знать одно','найти все 4'],
]},
};
const TIPS=[
{sec:'p1',html:'Положительное направление — <b>против часовой</b>. $P_{450°}$ совпадает с $P_{90°}$ — лишний полный оборот.'},
{sec:'p2',html:'Запомни: <b>sin = ордината (y)</b>, <b>cos = абсцисса (x)</b>. Точка $P_\\alpha$ — это и есть пара $(\\cos\\alpha;\\,\\sin\\alpha)$.'},
{sec:'p3',html:'<b>Ось тангенсов</b> — касательная к окружности при $x=1$. Продолжение радиуса $OP_\\alpha$ упирается в эту ось — там и сидит значение tg α.'},
{sec:'p4',html:'Алгоритм «знаю одну → найду все»: 1) основное тождество даёт пару sin/cos; 2) знак выбираем по четверти; 3) tg = sin/cos; 4) ctg = 1/tg.'},
];
function buildSidebar(id){
const box=document.getElementById('sidebar-content');
const sb=SIDEBARS[id]||SIDEBARS.p1;
let html='';
const xpForLv=_xpForLevel(STATE.level), xpNext=_xpForLevel(STATE.level+1);
const xpInLv=STATE.xp-xpForLv, xpRange=xpNext-xpForLv;
const xpPct=xpRange>0?Math.round(xpInLv/xpRange*100):100;
html+='<div class="xp-card"><div class="xp-card-title"><span>XP-прогресс</span><span class="xp-level">Ур. '+STATE.level+'</span></div><div class="xp-bar"><div class="xp-fill" style="width:'+xpPct+'%"></div></div><div class="xp-nums"><span>'+STATE.xp+' XP</span><span>'+xpNext+' XP</span></div></div>';
html+='<div class="sidecard"><h4>'+sb.title+'</h4>';
sb.rows.forEach(([k,v])=>{ html+='<div class="sidecard-row"><b>'+k+'</b>'+(v?' — '+v:'')+'</div>'; });
html+='</div>';
const tip=TIPS.find(t=>t.sec===id)||TIPS[0];
html+='<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#92400e">Подсказка</h4><div class="sidecard-row" style="font-size:.84rem;line-height:1.55">'+tip.html+'</div></div>';
if(STATE.achievements.size>0){
html+='<div class="sidecard"><h4>Достижения</h4>';
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html+='<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">✓ '+text+'</div>'; });
html+='</div>';
}
box.innerHTML=html;
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
}
function initTheme(){
const t=localStorage.getItem('algebra10_ch1_theme')||'light';
if(t==='dark') document.documentElement.classList.add('dark');
document.getElementById('theme-lab').textContent=t==='dark'?'Светлая':'Тёмная';
document.getElementById('theme-btn').addEventListener('click', ()=>{
document.documentElement.classList.toggle('dark');
const dark=document.documentElement.classList.contains('dark');
localStorage.setItem('algebra10_ch1_theme', dark?'dark':'light');
document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';
});
}
function renderMath(root){ if(window.renderMathInElement){ try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){} } }
function feedback(elm, ok, text){ if(!elm) return; elm.className='feedback '+(ok?'ok':'fail'); elm.innerHTML=text||(ok?'&#10003; Верно!':'&#10007; Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
const ICONS = {
theory:'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>',
algo:'<svg class="ic" viewBox="0 0 24 24"><polyline points="17 11 21 7 17 3"/><line x1="21" y1="7" x2="9" y2="7"/><polyline points="7 13 3 17 7 21"/><line x1="3" y1="17" x2="15" y2="17"/></svg>',
rule:'<svg class="ic" viewBox="0 0 24 24"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>',
example:'<svg class="ic" viewBox="0 0 24 24"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 13c1 1 2 2 2 4h4c0-2 1-3 2-4a7 7 0 0 0-4-13z"/></svg>',
};
function makeCard(kind, title, num, body){
const labels={theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример'};
return '<div class="card"><div class="card-header"><div class="card-icon '+kind+'">'+ICONS[kind]+'</div><div class="card-title">'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'</div>'+(num?'<div class="card-num">'+num+'</div>':'')+'</div><div class="card-body">'+body+'</div></div>';
}
function secNav(prev, next){
const NAMES={p1:'§1',p2:'§2',p3:'§3',p4:'§4',p5:'§5',p6:'§6',p7:'§7',p8:'§8',p9:'§9',p10:'§10',p11:'§11',p12:'§12',final1:'Финал'};
let h='<div class="sec-nav">';
h+=prev?'<button class="btn" onclick="goTo(\''+prev+'\')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> '+NAMES[prev]+'</button>':'<span></span>';
h+=next?'<button class="btn primary" onclick="goTo(\''+next+'\')">'+NAMES[next]+' <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>':'<span></span>';
h+='</div>'; return h;
}
function makeTrainer(opts){
let i=0, score=0;
const Q=opts.questions;
const parser = opts.parser || (v => parseFloat(String(v).replace(',','.')));
function show(){
if(i >= Q.length){
document.getElementById(opts.idPrefix+'-q').innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
if(opts.onComplete) opts.onComplete(score, Q.length);
return;
}
document.getElementById(opts.idPrefix+'-i').textContent = (i+1);
document.getElementById(opts.idPrefix+'-s').textContent = score;
document.getElementById(opts.idPrefix+'-q').innerHTML = Q[i].q;
document.getElementById(opts.idPrefix+'-ans').value = '';
renderMath(document.getElementById(opts.idPrefix+'-q'));
document.getElementById(opts.idPrefix+'-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById(opts.idPrefix+'-fb');
const raw = document.getElementById(opts.idPrefix+'-ans').value.trim();
if(raw === ''){ feedback(fb, false, '&#10007; Введи ответ.'); return; }
const expected = Q[i].a;
let ok = false;
if(typeof expected === 'function') ok = expected(raw);
else { const got = parser(raw); ok = !isNaN(got) && Math.abs(got - expected) < 1e-6; }
if(ok){ score++; feedback(fb, true, '&#10003; Верно! Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Правильно: <b>'+(Q[i].show||expected)+'</b>. Дальше ▶');
document.getElementById(opts.idPrefix+'-s').textContent = score;
i++; setTimeout(show, 1100);
}
document.getElementById(opts.idPrefix+'-go').addEventListener('click', go);
document.getElementById(opts.idPrefix+'-ans').addEventListener('keydown', e=>{ if(e.key==='Enter') go(); });
const restart = document.getElementById(opts.idPrefix+'-start');
if(restart) restart.addEventListener('click', ()=>{ i=0; score=0; show(); });
show();
}
function trainerHTML(idPrefix, total, placeholder){
return '<div class="score-display"><span>Задача <b id="'+idPrefix+'-i">1</b> / '+total+'</span><span>Очки: <b id="'+idPrefix+'-s">0</b> / '+total+'</span></div>'
+'<div id="'+idPrefix+'-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center"></div>'
+'<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">'
+'<input type="text" id="'+idPrefix+'-ans" class="tinp" placeholder="'+(placeholder||'Ответ')+'" style="width:140px;text-align:center">'
+'<button class="btn primary" id="'+idPrefix+'-go">Проверить</button>'
+'<button class="btn" id="'+idPrefix+'-start">Заново</button>'
+'</div><div class="feedback" id="'+idPrefix+'-fb"></div>';
}
function makeBoss(paraId, bossDef){
/* bossDef: { color, title, steps:[{q, verify, hint}] } */
const idP = paraId+'-boss';
const SKEY = 'algebra10_ch1_'+paraId+'_boss';
let st = {stage:0, defeated:false};
try{ const s=localStorage.getItem(SKEY); if(s) st=JSON.parse(s); }catch(e){}
const html = '<div class="boss-card" style="border-color:'+bossDef.color+'">'
+'<div class="boss-head">'
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+bossDef.color+'" stroke-width="2.2" style="width:28px;height:28px"><polygon points="12,2 22,20 2,20"/></svg>'
+'<div class="boss-title" style="color:'+bossDef.color+'">'+bossDef.title+'</div>'
+'<div class="boss-stage" id="'+idP+'-stage">Этап 1 / '+bossDef.steps.length+'</div>'
+'</div>'
+'<div class="hp-boss" style="border-color:'+bossDef.color+'66;background:'+bossDef.color+'1a"><div class="hp-boss-fill" id="'+idP+'-fill" style="width:0%;background:linear-gradient(90deg,'+bossDef.color+',#f59e0b)"></div></div>'
+'<div class="boss-q" id="'+idP+'-q" style="border-color:'+bossDef.color+'"></div>'
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
+'<input type="text" id="'+idP+'-input" class="tinp" placeholder="Ответ" style="width:160px;text-align:center">'
+'<button class="btn primary" id="'+idP+'-go" style="background:'+bossDef.color+';border-color:'+bossDef.color+'">Атака</button>'
+'<button class="btn" id="'+idP+'-hint">Подсказка</button>'
+'<button class="btn" id="'+idP+'-restart">↻</button>'
+'</div>'
+'<div class="feedback" id="'+idP+'-fb"></div>'
+'</div>';
setTimeout(()=>{
function save(){ try{ localStorage.setItem(SKEY, JSON.stringify(st)); }catch(e){} }
function show(){
const stageEl=document.getElementById(idP+'-stage');
const fill=document.getElementById(idP+'-fill');
const q=document.getElementById(idP+'-q');
const fb=document.getElementById(idP+'-fb');
if(st.defeated){
stageEl.textContent='✓ Побеждён'; fill.style.width='100%';
q.innerHTML='<b style="color:'+bossDef.color+'">Босс повержен!</b>';
document.getElementById(idP+'-go').disabled=true;
document.getElementById(idP+'-go').style.opacity=.5;
return;
}
stageEl.textContent='Этап '+(st.stage+1)+' / '+bossDef.steps.length;
fill.style.width=(st.stage*100/bossDef.steps.length)+'%';
q.innerHTML=bossDef.steps[st.stage].q;
document.getElementById(idP+'-input').value='';
fb.style.display='none';
renderMath(q);
}
document.getElementById(idP+'-go').addEventListener('click',()=>{
if(st.defeated) return;
const step=bossDef.steps[st.stage];
const val=document.getElementById(idP+'-input').value;
const fb=document.getElementById(idP+'-fb');
if(!val.trim()){ feedback(fb,false,'&#10007; Введи ответ.'); return; }
if(step.verify(val)){
st.stage++;
if(st.stage>=bossDef.steps.length){
st.defeated=true; save();
feedback(fb,true,'&#10003; Босс повержен! +20 XP');
addXp(20,paraId+'-boss'); bumpProgress(paraId, 30);
setTimeout(show,1400);
}else{
save(); feedback(fb,true,'&#10003; Верно! +3 XP'); addXp(3,paraId+'-boss-step'); setTimeout(show,1100);
}
}else{ feedback(fb,false,'&#10007; Промах.'); }
});
document.getElementById(idP+'-hint').addEventListener('click',()=>{
if(st.defeated) return;
const fb=document.getElementById(idP+'-fb');
fb.className='feedback ok';
fb.innerHTML='<span style="color:#92400e">\u{1F4A1} Подсказка:</span> '+bossDef.steps[st.stage].hint;
fb.style.display='block';
fb.style.background='var(--warn-bg)'; fb.style.color='#92400e'; fb.style.borderLeftColor='var(--warn)';
renderMath(fb);
});
document.getElementById(idP+'-restart').addEventListener('click',()=>{
st={stage:0,defeated:false}; save();
document.getElementById(idP+'-go').disabled=false;
document.getElementById(idP+'-go').style.opacity=1;
show();
});
show();
}, 0);
return html;
}
function readButton(paraId){
return '<div style="margin-top:18px;display:flex;justify-content:center">'
+'<button class="btn primary" id="'+paraId+'-read-btn">'
+'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>'
+' Я прочитал §'+paraId.replace('p','').replace('final','финал ')+' (+10 XP)'
+'</button></div>';
}
function wireReadBtn(paraId){
document.getElementById(paraId+'-read-btn').addEventListener('click', ()=>{
addXp(10, paraId+'-read'); bumpProgress(paraId, 30);
const b=document.getElementById(paraId+'-read-btn'); b.textContent='Прочитано! +10 XP'; b.disabled=true; b.style.opacity=.6;
});
}
function initSidebarToggle(){
const side=document.getElementById('col-side'),back=document.getElementById('col-side-backdrop'),btn=document.getElementById('sidebar-btn');
if(!side||!btn) return;
function open(){ side.classList.add('open'); back.classList.add('show'); }
function close(){ side.classList.remove('open'); back.classList.remove('show'); }
btn.addEventListener('click',()=>{ if(side.classList.contains('open')) close(); else open(); });
back.addEventListener('click',close);
document.addEventListener('keydown',e=>{ if(e.key==='Escape') close(); });
}
function init(){
loadProgress(); initTheme(); initSidebarToggle();
buildParaSelector(); refreshProgressUI(); goTo('p1');
setTimeout(()=>achievement('start','Начало главы 1!'), 600);
}
document.addEventListener('DOMContentLoaded', init);
/* ============================================================
Заглушка для §5-§12 и финала
============================================================ */
function buildStub(paraId, title){
const box = document.getElementById(paraId+'-body');
box.innerHTML = '<div class="stub-soon">'
+'<h3>'+title+'</h3>'
+'<p>Этот параграф появится в одной из ближайших волн реализации. Сейчас доступны §1–§4 — базовый блок «единичная окружность + sin/cos + tg/ctg + тождества».</p>'
+'</div>'
+ secNav(paraId==='p5'?'p4':(paraId==='final1'?'p12':PARAS[PARAS.findIndex(p=>p.id===paraId)-1].id),
paraId==='final1'?null:PARAS[PARAS.findIndex(p=>p.id===paraId)+1].id);
}
/* ============================================================
§ 1 — Единичная окружность. Градусная и радианная мера
============================================================ */
function buildP1(){
const box = document.getElementById('p1-body');
const A = window.ALG10;
let html = '';
/* === Главный SVG: единичная окружность с делениями === */
let svgBase = '';
if(A){
const c = A.tri.canvas({id:'p1-base', W:340, H:340, R:130, bg:'#fff'});
let s = c.open
+ c.axes()
+ c.circle({width:2.5})
+ c.degreeMark(0) + c.degreeMark(30) + c.degreeMark(60) + c.degreeMark(90)
+ c.degreeMark(120)+ c.degreeMark(150) + c.degreeMark(180) + c.degreeMark(210)
+ c.degreeMark(240)+ c.degreeMark(270) + c.degreeMark(300) + c.degreeMark(330)
+ c.point(0, {label:'P_0', color:'#1e293b', labelOffset:18, fontSize:11})
+ c.point(Math.PI/3, {label:'P_{60°}', color:'#dc2626', labelOffset:18, fontSize:11})
+ c.radius(Math.PI/3, {color:'#dc2626'})
+ c.rotationArrow(Math.PI/3, {color:'#16a34a', r:35})
+ c.close;
svgBase = s;
}
/* === Slider-эксперимент: точка P_α на окружности === */
let svgSlider = '';
if(A){
const c = A.tri.canvas({id:'p1-sl', W:340, H:340, R:130, bg:'#fff'});
/* Заглушка — отрисует с углом 60°, динамика добавится при инициализации */
let s = c.open
+ c.axes()
+ c.circle({width:2})
+ c.point(0, {label:'P_0', color:'#94a3b8', labelOffset:18, fontSize:11})
+ '<g id="p1-sl-dyn"></g>'
+ c.close;
svgSlider = s;
}
html += makeCard('theory', 'Единичная окружность', '1.1', `
<p><b>Единичной (тригонометрической) окружностью</b> называется окружность с центром в начале координат и радиусом $R = 1$.</p>
<p>Начало отсчёта — точка $P_0(1;\\,0)$. <b>Положительное направление</b> движения по окружности — против часовой стрелки.</p>
<div class="svg-host">${svgBase}</div>
<p>Точка $P_\\alpha$ получается поворотом точки $P_0$ вокруг начала координат на угол $\\alpha$ — это базовая операция тригонометрии.</p>`);
html += makeCard('rule', 'Углы любой величины', '1.2', `
<p>В отличие от планиметрии, здесь угол может быть <b>любым действительным числом</b>:</p>
<ul style="padding-left:22px;line-height:1.85">
<li><b>$\\alpha > 0$</b> — поворот <b>против часовой</b>;</li>
<li><b>$\\alpha < 0$</b> — поворот <b>по часовой</b>;</li>
<li><b>$|\\alpha| > 360°$</b> — несколько полных оборотов плюс остаток.</li>
</ul>
<p>Например, $P_{450°} = P_{360° + 90°} = P_{90°}$ — лишний полный оборот не изменяет положения.</p>
<p>Общая формула: $P_\\alpha = P_{\\alpha + 360° \\cdot n}$ для любого целого $n$.</p>`);
html += makeCard('rule', 'Радианная мера', '1.3', `
<p>На единичной окружности длина дуги <b>равна</b> радианной мере соответствующего угла.</p>
<p>$1$ радиан — центральный угол, опирающийся на дугу длиной $1$ (то есть равную радиусу).</p>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>$2\\pi$ рад $= 360°$</b>, поэтому <b>$1$ рад $= \\frac{180°}{\\pi} \\approx 57°$</b>.</p>
<p><b>Перевод:</b></p>
<ul style="padding-left:22px;line-height:1.9">
<li>град $\\to$ рад: <b>умножить на $\\dfrac{\\pi}{180}$</b>;</li>
<li>рад $\\to$ град: <b>умножить на $\\dfrac{180}{\\pi}$</b>.</li>
</ul>
<p>Примеры: $30° = \\dfrac{\\pi}{6}$, $45° = \\dfrac{\\pi}{4}$, $60° = \\dfrac{\\pi}{3}$, $90° = \\dfrac{\\pi}{2}$.</p>`);
html += makeCard('algo', 'Четверть угла', '1.4', `
<p>Чтобы определить, в какой четверти лежит угол $\\alpha$:</p>
<ol style="padding-left:22px;line-height:1.9">
<li>Если $\\alpha$ в радианах — переведи в градусы (по желанию).</li>
<li>Приведи к промежутку $[0°;\\,360°)$, отнимая или прибавляя нужное число оборотов.</li>
<li>Сравни:
<ul style="padding-left:18px;line-height:1.75">
<li>$[0°;\\,90°)$ — <b>I</b>;</li>
<li>$[90°;\\,180°)$ — <b>II</b>;</li>
<li>$[180°;\\,270°)$ — <b>III</b>;</li>
<li>$[270°;\\,360°)$ — <b>IV</b>.</li>
</ul>
</li>
</ol>
<p><b>Пример.</b> $\\alpha = 1300°$ → $1300° - 3 \\cdot 360° = 220°$ → III четверть.</p>`);
/* === ИНТЕРАКТИВ 1: Slider угла === */
html += '<div class="wg" id="p1-iv1">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Точка $P_\\alpha$ — крути угол</div></div>'
+'<div class="wg-help">Двигай ползунок угла от <b>-720°</b> до <b>+720°</b> и наблюдай, как точка $P_\\alpha$ перемещается по окружности.</div>'
+'<div class="slider-ctrl"><label>Угол α:</label><input type="range" id="p1-iv1-r" min="-720" max="720" step="5" value="60"><span class="val" id="p1-iv1-v">60°</span></div>'
+'<div class="svg-host">'+svgSlider+'</div>'
+'<div style="font-size:.86rem;color:var(--muted);text-align:center">Эквивалент в $[0°;\\,360°)$: <b id="p1-iv1-eq" style="color:var(--pri)">60°</b></div>'
+'</div>';
/* === ИНТЕРАКТИВ 2: Перевод град ↔ рад === */
html += '<div class="wg" id="p1-iv2">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Градусы ↔ радианы</div></div>'
+'<div class="wg-help">Введи ответ в виде дроби (например <b>5pi/6</b>) или числа (для градусов — без °).</div>'
+trainerHTML('p1-iv2', 8, 'ответ')
+'</div>';
/* === ИНТЕРАКТИВ 3: Четверть угла === */
html += '<div class="wg" id="p1-iv3">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">В какой четверти?</div></div>'
+'<div class="wg-help">Определи номер четверти (1, 2, 3, 4), в которой лежит угол.</div>'
+trainerHTML('p1-iv3', 6, 'номер')
+'</div>';
/* === БОСС === */
html += '<h3 style="font-family:Unbounded,sans-serif;font-size:1.05rem;color:var(--pri2);margin:20px 0 12px">Босс §1 — Окружность и углы</h3>';
html += makeBoss('p1', {
color:'#0d9488',
title:'Босс §1 — Окружность и углы',
steps:[
{ q:'Сколько градусов в $\\pi$ радиан?', verify:(v)=>+v===180, hint:'$\\pi$ рад $= 180°$.' },
{ q:'Преобразуй $240°$ в радианы (введи как <b>4pi/3</b>).', verify:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='4pi/3' || s==='4π/3';}, hint:'$240° \\cdot \\dfrac{\\pi}{180} = \\dfrac{4\\pi}{3}$.' },
{ q:'В какой четверти лежит угол $1300°$?', verify:(v)=>+v===3, hint:'$1300° - 3 \\cdot 360° = 220°$ — III четверть.' },
{ q:'Сколько различных точек $P_\\alpha$ дадут углы $\\frac{\\pi}{3}$, $-\\frac{\\pi}{3}$, $\\frac{7\\pi}{3}$, $-\\frac{5\\pi}{3}$?', verify:(v)=>+v===2, hint:'$\\frac{\\pi}{3} = \\frac{7\\pi}{3}$ (отличие $2\\pi$) и $-\\frac{\\pi}{3} = -\\frac{5\\pi}{3} + \\frac{2\\pi} \\cdot? $ — на самом деле $-\\frac{5\\pi}{3} = \\frac{\\pi}{3}$. Итого 2.' },
{ q:'Радианная мера двух углов треугольника — $\\frac{2\\pi}{5}$ и $\\frac{3\\pi}{10}$. Найди третий угол в градусах.', verify:(v)=>+v===54, hint:'Сумма $= \\pi$, третий $= \\pi - \\frac{2\\pi}{5} - \\frac{3\\pi}{10} = \\frac{3\\pi}{10}$ — это $54°$.' },
]
});
html += secNav(null, 'p2') + readButton('p1');
box.innerHTML = html; renderMath(box);
/* === Логика интерактивов === */
/* IV1: slider угла */
(function(){
const r = document.getElementById('p1-iv1-r');
const v = document.getElementById('p1-iv1-v');
const eq = document.getElementById('p1-iv1-eq');
const dyn = document.getElementById('p1-sl-dyn');
function redraw(deg){
v.textContent = deg + '°';
let mod = ((deg % 360) + 360) % 360;
eq.textContent = mod + '°';
if(!A || !dyn) return;
const c = A.tri.canvas({id:'p1-sl-h', W:340, H:340, R:130});
const rad = deg * Math.PI / 180;
dyn.innerHTML = c.radius(rad, {color:'#dc2626'})
+ c.point(rad, {label:'P_α', color:'#dc2626', labelOffset:18, fontSize:11})
+ c.rotationArrow(rad, {r:30});
}
r.addEventListener('input', e=>redraw(+e.target.value));
redraw(60);
})();
/* IV2: тренажёр перевода */
makeTrainer({
idPrefix:'p1-iv2',
parser:(v)=>v,
questions:[
{ q:'Преобразуй $30°$ в радианы. Введи как pi/n.', a:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='pi/6'||s==='π/6';}, show:'$\\pi/6$' },
{ q:'Преобразуй $90°$ в радианы.', a:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='pi/2'||s==='π/2';}, show:'$\\pi/2$' },
{ q:'Преобразуй $150°$ в радианы.', a:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='5pi/6'||s==='5π/6';}, show:'$5\\pi/6$' },
{ q:'Преобразуй $\\dfrac{\\pi}{4}$ в градусы.', a:(v)=>+v===45, show:'$45°$' },
{ q:'Преобразуй $\\dfrac{2\\pi}{3}$ в градусы.', a:(v)=>+v===120, show:'$120°$' },
{ q:'Преобразуй $\\dfrac{7\\pi}{6}$ в градусы.', a:(v)=>+v===210, show:'$210°$' },
{ q:'Преобразуй $-80°$ в радианы. Введи как -np/m.', a:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='-4pi/9'||s==='-4π/9';}, show:'$-4\\pi/9$' },
{ q:'Сколько градусов в $4$ радианах? (округли)', a:(v)=>+v===229, show:'$\\approx 229°$' },
],
onComplete:(s,n)=>{ if(s===n){addXp(18,'p1-iv2');bumpProgress('p1',30);} else if(s>=5){addXp(8,'p1-iv2');bumpProgress('p1',15);} }
});
/* IV3: четверть угла */
makeTrainer({
idPrefix:'p1-iv3',
questions:[
{ q:'$\\alpha = 126°$. Какая четверть?', a:2 },
{ q:'$\\alpha = 220°$. Какая четверть?', a:3 },
{ q:'$\\alpha = 7\\pi/10$. Какая четверть?', a:2 },
{ q:'$\\alpha = -\\pi/3$. Какая четверть?', a:4 },
{ q:'$\\alpha = 722°$. Какая четверть?', a:1 },
{ q:'$\\alpha = -189°$. Какая четверть?', a:2 },
],
onComplete:(s,n)=>{ if(s===n){addXp(15,'p1-iv3');bumpProgress('p1',25);} else if(s>=4){addXp(8,'p1-iv3');bumpProgress('p1',12);} }
});
wireReadBtn('p1');
}
/* ============================================================
§ 2 — Синус и косинус произвольного угла
============================================================ */
function buildP2(){
const box = document.getElementById('p2-body');
const A = window.ALG10;
let html = '';
/* === SVG 1: определение через координаты P_α === */
let svgDef = '';
if(A){
const c = A.tri.canvas({id:'p2-def', W:320, H:320, R:120});
const angle = 60 * Math.PI / 180;
let s = c.open
+ c.axes()
+ c.circle({width:2})
+ c.radius(angle, {color:'#dc2626'})
+ c.sinSegment(angle, {color:'#dc2626', label:'sin α', dash:'0', width:2.5})
+ c.cosSegment(angle, {color:'#2563eb', label:'cos α', dash:'0', width:2.5})
+ c.point(angle, {label:'P_α (cos α; sin α)', color:'#dc2626', labelOffset:20, fontSize:10})
+ c.close;
svgDef = s;
}
/* === SVG 2: знаки по четвертям === */
let svgSigns = '';
if(A){
const c = A.tri.canvas({id:'p2-signs', W:320, H:320, R:120});
let s = c.open
+ c.quadrant(1, {fill:'rgba(16,185,129,.15)'})
+ c.quadrant(2, {fill:'rgba(245,158,11,.15)'})
+ c.quadrant(3, {fill:'rgba(239,68,68,.15)'})
+ c.quadrant(4, {fill:'rgba(124,58,237,.15)'})
+ c.axes()
+ c.circle({width:2})
+ c.quadrantLabels({color:'#1e293b'})
/* Подписи знаков */
+ '<text x="220" y="115" text-anchor="middle" font-size="13" font-weight="800" fill="#065f46">sin +</text>'
+ '<text x="220" y="135" text-anchor="middle" font-size="13" font-weight="800" fill="#065f46">cos +</text>'
+ '<text x="100" y="115" text-anchor="middle" font-size="13" font-weight="800" fill="#92400e">sin +</text>'
+ '<text x="100" y="135" text-anchor="middle" font-size="13" font-weight="800" fill="#dc2626">cos </text>'
+ '<text x="100" y="225" text-anchor="middle" font-size="13" font-weight="800" fill="#dc2626">sin </text>'
+ '<text x="100" y="245" text-anchor="middle" font-size="13" font-weight="800" fill="#dc2626">cos </text>'
+ '<text x="220" y="225" text-anchor="middle" font-size="13" font-weight="800" fill="#dc2626">sin </text>'
+ '<text x="220" y="245" text-anchor="middle" font-size="13" font-weight="800" fill="#065f46">cos +</text>'
+ c.close;
svgSigns = s;
}
/* === SVG 3: точные значения для главных углов === */
let svgTable = '';
if(A){
const c = A.tri.canvas({id:'p2-tbl', W:340, H:340, R:130});
let s = c.open + c.axes() + c.circle({width:2});
const marks = [
{a:Math.PI/6, lab:'π/6', c:'#dc2626'},
{a:Math.PI/4, lab:'π/4', c:'#16a34a'},
{a:Math.PI/3, lab:'π/3', c:'#2563eb'},
{a:Math.PI/2, lab:'π/2', c:'#7c3aed'},
];
for(const m of marks){
s += c.radius(m.a, {color:m.c, width:1.8});
s += c.point(m.a, {color:m.c, label:m.lab, labelOffset:18, fontSize:11, r:3.5});
}
s += c.close;
svgTable = s;
}
html += makeCard('theory', 'Определение sin и cos', '2.1', `
<p>В планиметрии $\\sin\\alpha$ и $\\cos\\alpha$ были определены только для острого угла прямоугольного треугольника. Теперь обобщаем на любой угол.</p>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Определение.</b> Для любого угла $\\alpha$ точка $P_\\alpha$ единичной окружности имеет координаты $(\\cos\\alpha;\\,\\sin\\alpha)$:</p>
<p style="text-align:center;font-size:1.1rem;margin:10px 0">$\\sin\\alpha = y_\\alpha, \\quad \\cos\\alpha = x_\\alpha$</p>
<div class="svg-host">${svgDef}</div>
<p>Запомни: <b>sin — ордината (y)</b>, <b>cos — абсцисса (x)</b>.</p>`);
html += makeCard('rule', 'Область значений', '2.2', `
<p>Координаты любой точки единичной окружности лежат в $[-1;\\,1]$, поэтому:</p>
<p style="text-align:center;font-size:1.1rem;margin:10px 0">$-1 \\le \\sin\\alpha \\le 1, \\quad -1 \\le \\cos\\alpha \\le 1$</p>
<p>Значения, которые <b>не могут</b> быть синусами или косинусами: $\\sqrt{3}$, $-2$, $1{,}5$ — они вне отрезка $[-1;\\,1]$.</p>`);
html += makeCard('rule', 'Знаки sin/cos по четвертям', '2.3', `
<p>Знак $\\sin\\alpha$ совпадает со знаком ординаты точки $P_\\alpha$, а $\\cos\\alpha$ — со знаком абсциссы.</p>
<div class="svg-host">${svgSigns}</div>
<p><b>Мнемоника</b> для $\\sin\\alpha$: «положителен сверху от оси x» — то есть в I и II четвертях.</p>
<p><b>Для $\\cos\\alpha$:</b> «положителен справа от оси y» — I и IV четверти.</p>`);
html += makeCard('example', 'Значения для главных углов', '2.4', `
<p>Запомнить нужно только эти три:</p>
<table style="width:100%;border-collapse:collapse;margin:10px 0;font-size:.94rem">
<tr style="background:var(--sec-acc-soft)"><th style="padding:8px;border:1px solid var(--border)">α</th><th style="padding:8px;border:1px solid var(--border)">$\\pi/6$ ($30°$)</th><th style="padding:8px;border:1px solid var(--border)">$\\pi/4$ ($45°$)</th><th style="padding:8px;border:1px solid var(--border)">$\\pi/3$ ($60°$)</th></tr>
<tr><td style="padding:8px;border:1px solid var(--border);text-align:center"><b>sin α</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center">$\\frac{1}{2}$</td><td style="padding:8px;border:1px solid var(--border);text-align:center">$\\frac{\\sqrt{2}}{2}$</td><td style="padding:8px;border:1px solid var(--border);text-align:center">$\\frac{\\sqrt{3}}{2}$</td></tr>
<tr><td style="padding:8px;border:1px solid var(--border);text-align:center"><b>cos α</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center">$\\frac{\\sqrt{3}}{2}$</td><td style="padding:8px;border:1px solid var(--border);text-align:center">$\\frac{\\sqrt{2}}{2}$</td><td style="padding:8px;border:1px solid var(--border);text-align:center">$\\frac{1}{2}$</td></tr>
</table>
<p>Остальные значения находим через симметрии:</p>
<ul style="padding-left:22px;line-height:1.85">
<li>Через ось $x$: $\\sin(-\\alpha) = -\\sin\\alpha$, $\\cos(-\\alpha) = \\cos\\alpha$.</li>
<li>Через ось $y$: $\\sin(\\pi - \\alpha) = \\sin\\alpha$, $\\cos(\\pi - \\alpha) = -\\cos\\alpha$.</li>
<li>Через начало: $\\sin(\\alpha + \\pi) = -\\sin\\alpha$, $\\cos(\\alpha + \\pi) = -\\cos\\alpha$.</li>
</ul>
<div class="svg-host">${svgTable}</div>`);
/* === ИНТЕРАКТИВ 1: Знак sin/cos === */
html += '<div class="wg" id="p2-iv1">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Знаки sin и cos</div></div>'
+'<div class="wg-help">Введи знак: <b>+</b>, <b>-</b> (или <b>0</b> если значение равно нулю).</div>'
+trainerHTML('p2-iv1', 8, '+ или -')
+'</div>';
/* === ИНТЕРАКТИВ 2: Значение === */
html += '<div class="wg" id="p2-iv2">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Точные значения</div></div>'
+'<div class="wg-help">Введи ответ в виде дроби: <b>1/2</b>, <b>sqrt2/2</b>, <b>sqrt3/2</b>, <b>-1/2</b> и т.п. Знак минус — впереди.</div>'
+trainerHTML('p2-iv2', 6, 'значение')
+'</div>';
/* === ИНТЕРАКТИВ 3: Возможно ли? === */
html += '<div class="wg" id="p2-iv3">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Может ли так быть?</div></div>'
+'<div class="wg-help">Проверяй: $-1 \\le \\sin\\alpha \\le 1$ и $-1 \\le \\cos\\alpha \\le 1$. И $\\sin^2 + \\cos^2 = 1$.</div>'
+'<div class="score-display"><span>Задача <b id="p2-iv3-i">1</b> / 6</span><span>Очки: <b id="p2-iv3-s">0</b> / 6</span></div>'
+'<div id="p2-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1rem;text-align:center;margin-bottom:10px"></div>'
+'<div style="display:flex;gap:8px;justify-content:center"><button class="btn primary" id="p2-iv3-y" style="background:#10b981;border-color:#10b981">Да</button><button class="btn primary" id="p2-iv3-n" style="background:#dc2626;border-color:#dc2626">Нет</button></div>'
+'<div class="feedback" id="p2-iv3-fb"></div></div>';
/* === БОСС === */
html += '<h3 style="font-family:Unbounded,sans-serif;font-size:1.05rem;color:var(--pri2);margin:20px 0 12px">Босс §2 — sin и cos</h3>';
html += makeBoss('p2', {
color:'#0891b2',
title:'Босс §2 — sin и cos',
steps:[
{ q:'$\\sin\\dfrac{\\pi}{2} = ?$', verify:(v)=>+v===1, hint:'Точка $P_{\\pi/2} = (0;1)$, ордината $= 1$.' },
{ q:'$\\cos 180° = ?$', verify:(v)=>+v===-1, hint:'Точка $P_\\pi = (-1;0)$, абсцисса $= -1$.' },
{ q:'Точка $P_\\alpha = (3/5;\\,-4/5)$. Найди $\\sin\\alpha$ как дробь (введи -4/5).', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='-4/5'||+v===-0.8;}, hint:'sin = ордината.' },
{ q:'В каких четвертях $\\sin\\alpha > 0$? Введи через запятую: 1,2 или 3,4.', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='1,2'||s==='2,1';}, hint:'Ордината положительна сверху от оси x.' },
{ q:'Может ли $\\sin\\alpha = \\sqrt{3}$? «да» или «нет»', verify:(v)=>String(v).trim().toLowerCase().startsWith('н'), hint:'$\\sqrt{3} \\approx 1{,}73 > 1$, а $\\sin\\alpha \\le 1$.' },
]
});
html += secNav('p1', 'p3') + readButton('p2');
box.innerHTML = html; renderMath(box);
/* IV1: знаки */
makeTrainer({
idPrefix:'p2-iv1',
parser:(v)=>v,
questions:[
{ q:'$\\sin 130°$ — какой знак?', a:(v)=>String(v).trim().startsWith('+'), show:'+ (II четв.)' },
{ q:'$\\cos 258°$ — какой знак?', a:(v)=>String(v).trim().startsWith('-')||String(v).trim()==='', show:' (III четв.)' },
{ q:'$\\sin(-150°)$ — какой знак?', a:(v)=>String(v).trim().startsWith('-')||String(v).trim()==='', show:' (III четв.)' },
{ q:'$\\cos(-340°)$ — какой знак?', a:(v)=>String(v).trim().startsWith('+'), show:'+ (I четв.)' },
{ q:'$\\sin 90°$ — какой знак?', a:(v)=>String(v).trim().startsWith('+'), show:'+ (равно 1)' },
{ q:'$\\cos 180°$ — какой знак?', a:(v)=>String(v).trim().startsWith('-')||String(v).trim()==='', show:' (равно 1)' },
{ q:'$\\sin\\pi$ — какой знак?', a:(v)=>+v===0||String(v).trim()==='0', show:'0' },
{ q:'$\\cos\\dfrac{3\\pi}{2}$ — какой знак?', a:(v)=>+v===0||String(v).trim()==='0', show:'0' },
],
onComplete:(s,n)=>{ if(s===n){addXp(15,'p2-iv1');bumpProgress('p2',25);} else if(s>=5){addXp(8,'p2-iv1');bumpProgress('p2',12);} }
});
/* IV2: точные значения */
makeTrainer({
idPrefix:'p2-iv2',
parser:(v)=>v,
questions:[
{ q:'$\\sin\\dfrac{\\pi}{6} = ?$', a:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='1/2'||+v===0.5;}, show:'$\\frac{1}{2}$' },
{ q:'$\\cos\\dfrac{\\pi}{3} = ?$', a:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='1/2'||+v===0.5;}, show:'$\\frac{1}{2}$' },
{ q:'$\\sin 90° = ?$', a:(v)=>+v===1, show:'1' },
{ q:'$\\cos 180° = ?$', a:(v)=>+v===-1, show:'$-1$' },
{ q:'$\\sin(-60°) = ?$ (введи -sqrt3/2 как ответ)', a:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='-sqrt3/2'||s==='-√3/2';}, show:'$-\\frac{\\sqrt{3}}{2}$' },
{ q:'$\\cos 120° = ?$ (введи -1/2)', a:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='-1/2'||+v===-0.5;}, show:'$-\\frac{1}{2}$' },
],
onComplete:(s,n)=>{ if(s===n){addXp(18,'p2-iv2');bumpProgress('p2',30);} else if(s>=3){addXp(9,'p2-iv2');bumpProgress('p2',15);} }
});
/* IV3: возможно ли */
(function(){
const Q=[
{ e:'$\\sin\\alpha = 5/13$ и $\\cos\\alpha = 12/13$ одновременно', ok:true, why:'$(5/13)^2 + (12/13)^2 = 169/169 = 1$ ✓' },
{ e:'$\\sin\\alpha = -0{,}3$ и $\\cos\\alpha = 0{,}4$ одновременно', ok:false, why:'$0{,}09 + 0{,}16 = 0{,}25 \\ne 1$.' },
{ e:'$\\sin\\alpha = 0{,}8$ и $\\cos\\alpha = 0{,}6$ одновременно', ok:true, why:'$0{,}64 + 0{,}36 = 1$ ✓' },
{ e:'$\\sin\\alpha = \\sqrt{3}$', ok:false, why:'$\\sqrt{3} \\approx 1{,}73 > 1$.' },
{ e:'$\\cos\\alpha = -1$', ok:true, why:'$\\cos\\pi = -1$.' },
{ e:'$\\sin\\alpha = 2$', ok:false, why:'$|\\sin\\alpha| \\le 1$.' },
];
let i=0,score=0;
function show(){
if(i>=Q.length){
document.getElementById('p2-iv3-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length;
if(score===Q.length){addXp(15,'p2-iv3');bumpProgress('p2',25);} else if(score>=4){addXp(8,'p2-iv3');bumpProgress('p2',12);}
return;
}
document.getElementById('p2-iv3-i').textContent=(i+1);
document.getElementById('p2-iv3-s').textContent=score;
document.getElementById('p2-iv3-q').innerHTML=Q[i].e;
renderMath(document.getElementById('p2-iv3-q'));
document.getElementById('p2-iv3-fb').style.display='none';
}
function ans(yes){
if(i>=Q.length) return;
const fb=document.getElementById('p2-iv3-fb');
if(yes===Q[i].ok){ score++; feedback(fb,true,'&#10003; Верно! '+Q[i].why); }
else feedback(fb,false,'&#10007; '+(Q[i].ok?'Да: ':'Нет: ')+Q[i].why);
document.getElementById('p2-iv3-s').textContent=score;
i++; setTimeout(show,1400);
}
document.getElementById('p2-iv3-y').addEventListener('click',()=>ans(true));
document.getElementById('p2-iv3-n').addEventListener('click',()=>ans(false));
show();
})();
wireReadBtn('p2');
}
/* ============================================================
§ 3 — Тангенс и котангенс произвольного угла
============================================================ */
function buildP3(){
const box = document.getElementById('p3-body');
const A = window.ALG10;
let html = '';
/* === SVG 1: ось тангенсов === */
let svgTg = '';
if(A){
const c = A.tri.canvas({id:'p3-tg', W:340, H:340, R:120});
const angle = 30 * Math.PI / 180;
let s = c.open
+ c.axes({xExt:c.R+40})
+ c.circle({width:2})
+ c.tgAxis()
+ c.radius(angle, {color:'#dc2626'})
+ c.point(angle, {label:'P_α', color:'#dc2626', labelOffset:14, fontSize:11})
+ c.tgValue(angle, {color:'#16a34a'})
+ c.close;
svgTg = s;
}
/* === SVG 2: ось котангенсов === */
let svgCtg = '';
if(A){
const c = A.tri.canvas({id:'p3-ctg', W:340, H:340, R:120});
const angle = 60 * Math.PI / 180;
let s = c.open
+ c.axes({yExt:c.R+40})
+ c.circle({width:2})
+ c.ctgAxis()
+ c.radius(angle, {color:'#dc2626'})
+ c.point(angle, {label:'P_α', color:'#dc2626', labelOffset:14, fontSize:11})
+ c.ctgValue(angle, {color:'#7c3aed'})
+ c.close;
svgCtg = s;
}
html += makeCard('theory', 'Определения', '3.1', `
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Определение.</b> Тангенс и котангенс угла $\\alpha$:</p>
<p style="text-align:center;font-size:1.1rem;margin:10px 0">$\\tg\\alpha = \\dfrac{\\sin\\alpha}{\\cos\\alpha}, \\quad \\ctg\\alpha = \\dfrac{\\cos\\alpha}{\\sin\\alpha}$</p>
<p><b>ОДЗ:</b></p>
<ul style="padding-left:22px;line-height:1.85">
<li>$\\tg\\alpha$ существует, если $\\cos\\alpha \\ne 0$, то есть $\\alpha \\ne \\dfrac{\\pi}{2} + \\pi n$, $n \\in \\mathbb{Z}$.</li>
<li>$\\ctg\\alpha$ существует, если $\\sin\\alpha \\ne 0$, то есть $\\alpha \\ne \\pi n$, $n \\in \\mathbb{Z}$.</li>
</ul>
<p>Между ними простая связь: $\\tg\\alpha \\cdot \\ctg\\alpha = 1$ (если оба определены).</p>`);
html += makeCard('rule', 'Ось тангенсов', '3.2', `
<p>Геометрически tg α можно «увидеть»: проведём <b>касательную к окружности в точке $P_0(1;\\,0)$</b> — это прямая $x = 1$. Назовём её <b>осью тангенсов</b>.</p>
<p>Прямая $OP_\\alpha$ (или её продолжение) пересекает ось тангенсов в точке $A_\\alpha$. Координата $y$ этой точки и есть $\\tg\\alpha$.</p>
<div class="svg-host">${svgTg}</div>
<p>Если $P_\\alpha$ в I или IV четверти — $A_\\alpha$ строится через продолжение радиуса прямо; если во II или III — через продолжение радиуса в противоположную сторону.</p>`);
html += makeCard('rule', 'Ось котангенсов', '3.3', `
<p>Аналогично: касательная в точке $P_{\\pi/2}(0;\\,1)$ — это прямая $y = 1$. На ней «живёт» $\\ctg\\alpha$.</p>
<div class="svg-host">${svgCtg}</div>
<p>Координата $x$ точки пересечения — это $\\ctg\\alpha$.</p>`);
html += makeCard('algo', 'Знаки tg и ctg по четвертям', '3.4', `
<p>Так как $\\tg\\alpha = \\dfrac{\\sin\\alpha}{\\cos\\alpha}$, знак — произведение знаков sin и cos:</p>
<table style="width:100%;border-collapse:collapse;margin:10px 0;font-size:.94rem">
<tr style="background:var(--sec-acc-soft)"><th style="padding:8px;border:1px solid var(--border)">Четверть</th><th style="padding:8px;border:1px solid var(--border)">I</th><th style="padding:8px;border:1px solid var(--border)">II</th><th style="padding:8px;border:1px solid var(--border)">III</th><th style="padding:8px;border:1px solid var(--border)">IV</th></tr>
<tr><td style="padding:8px;border:1px solid var(--border);text-align:center"><b>sin</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#065f46">+</td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#065f46">+</td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#dc2626"></td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#dc2626"></td></tr>
<tr><td style="padding:8px;border:1px solid var(--border);text-align:center"><b>cos</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#065f46">+</td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#dc2626"></td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#dc2626"></td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#065f46">+</td></tr>
<tr style="background:var(--sec-acc-soft)"><td style="padding:8px;border:1px solid var(--border);text-align:center"><b>tg, ctg</b></td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#065f46">+</td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#dc2626"></td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#065f46">+</td><td style="padding:8px;border:1px solid var(--border);text-align:center;color:#dc2626"></td></tr>
</table>
<p><b>Запоминалка:</b> tg/ctg положительны в I и III четвертях, отрицательны во II и IV.</p>`);
/* === ИНТЕРАКТИВ 1: Существует ли? === */
html += '<div class="wg" id="p3-iv1">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Существует ли?</div></div>'
+'<div class="wg-help">tg α не существует, если cos α = 0 (то есть α = π/2 + πn). ctg α не существует, если sin α = 0 (α = πn).</div>'
+'<div class="score-display"><span>Задача <b id="p3-iv1-i">1</b> / 6</span><span>Очки: <b id="p3-iv1-s">0</b> / 6</span></div>'
+'<div id="p3-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1rem;text-align:center;margin-bottom:10px"></div>'
+'<div style="display:flex;gap:8px;justify-content:center"><button class="btn primary" id="p3-iv1-y" style="background:#10b981;border-color:#10b981">Существует</button><button class="btn primary" id="p3-iv1-n" style="background:#dc2626;border-color:#dc2626">Не существует</button></div>'
+'<div class="feedback" id="p3-iv1-fb"></div></div>';
/* === ИНТЕРАКТИВ 2: Знак tg/ctg === */
html += '<div class="wg" id="p3-iv2">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Знак tg / ctg</div></div>'
+'<div class="wg-help">Введи знак: <b>+</b> или <b>-</b>.</div>'
+trainerHTML('p3-iv2', 6, '+ или -')
+'</div>';
/* === БОСС === */
html += '<h3 style="font-family:Unbounded,sans-serif;font-size:1.05rem;color:var(--pri2);margin:20px 0 12px">Босс §3 — tg и ctg</h3>';
html += makeBoss('p3', {
color:'#16a34a',
title:'Босс §3 — tg и ctg',
steps:[
{ q:'$\\tg\\pi = ?$', verify:(v)=>+v===0, hint:'$\\sin\\pi = 0$, $\\cos\\pi = -1$, $0/(-1) = 0$.' },
{ q:'Существует ли $\\tg\\dfrac{\\pi}{2}$? «да» или «нет»', verify:(v)=>String(v).trim().toLowerCase().startsWith('н'), hint:'$\\cos\\frac{\\pi}{2} = 0$, делить нельзя.' },
{ q:'$\\ctg\\dfrac{\\pi}{4} = ?$', verify:(v)=>+v===1, hint:'$\\cos\\frac{\\pi}{4} / \\sin\\frac{\\pi}{4} = 1$.' },
{ q:'Точка $P_\\alpha = (5/13;\\,-12/13)$. Найди $\\tg\\alpha$ (введи в виде десятичной дроби, например -2.4).', verify:(v)=>Math.abs(+v - (-12/5)) < 0.05, hint:'$\\tg\\alpha = -12/5 = -2{,}4$.' },
{ q:'В каких четвертях $\\tg\\alpha > 0$? Введи: 1,2 / 1,3 / 2,4 / 2,3.', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='1,3'||s==='3,1';}, hint:'sin и cos одного знака — I и III.' },
]
});
html += secNav('p2', 'p4') + readButton('p3');
box.innerHTML = html; renderMath(box);
/* IV1: существует ли */
(function(){
const Q=[
{ e:'$\\tg 0$', ok:true, why:'$\\sin 0 / \\cos 0 = 0/1 = 0$.' },
{ e:'$\\tg \\dfrac{\\pi}{2}$', ok:false, why:'$\\cos\\frac{\\pi}{2} = 0$.' },
{ e:'$\\ctg 0$', ok:false, why:'$\\sin 0 = 0$.' },
{ e:'$\\ctg 2\\pi$', ok:false, why:'$\\sin 2\\pi = 0$.' },
{ e:'$\\tg\\left(-\\dfrac{5\\pi}{2}\\right)$', ok:false, why:'$-5\\pi/2 = -2\\pi - \\pi/2$, эквивалентен $-\\pi/2$, $\\cos = 0$.' },
{ e:'$\\ctg(-3\\pi)$', ok:false, why:'$-3\\pi = -2\\pi - \\pi$, эквивалентен $\\pi$, $\\sin\\pi = 0$.' },
];
let i=0,score=0;
function show(){
if(i>=Q.length){ document.getElementById('p3-iv1-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(15,'p3-iv1');bumpProgress('p3',25);} else if(score>=4){addXp(8,'p3-iv1');bumpProgress('p3',12);} return; }
document.getElementById('p3-iv1-i').textContent=(i+1);
document.getElementById('p3-iv1-s').textContent=score;
document.getElementById('p3-iv1-q').innerHTML=Q[i].e;
renderMath(document.getElementById('p3-iv1-q'));
document.getElementById('p3-iv1-fb').style.display='none';
}
function ans(yes){
if(i>=Q.length) return;
const fb=document.getElementById('p3-iv1-fb');
if(yes===Q[i].ok){ score++; feedback(fb,true,'&#10003; Верно! '+Q[i].why); }
else feedback(fb,false,'&#10007; '+(Q[i].ok?'Существует: ':'Не существует: ')+Q[i].why);
document.getElementById('p3-iv1-s').textContent=score;
i++; setTimeout(show,1500);
}
document.getElementById('p3-iv1-y').addEventListener('click',()=>ans(true));
document.getElementById('p3-iv1-n').addEventListener('click',()=>ans(false));
show();
})();
/* IV2: знаки tg/ctg */
makeTrainer({
idPrefix:'p3-iv2',
parser:(v)=>v,
questions:[
{ q:'$\\tg 118°$ — какой знак?', a:(v)=>String(v).trim().startsWith('-')||String(v).trim()==='', show:' (II четв.)' },
{ q:'$\\ctg 200°$ — какой знак?', a:(v)=>String(v).trim().startsWith('+'), show:'+ (III четв.)' },
{ q:'$\\tg(-50°)$ — какой знак?', a:(v)=>String(v).trim().startsWith('-')||String(v).trim()==='', show:' (IV четв.)' },
{ q:'$\\ctg 330°$ — какой знак?', a:(v)=>String(v).trim().startsWith('-')||String(v).trim()==='', show:' (IV четв.)' },
{ q:'$\\tg 240°$ — какой знак?', a:(v)=>String(v).trim().startsWith('+'), show:'+ (III четв.)' },
{ q:'$\\ctg(-100°)$ — какой знак?', a:(v)=>String(v).trim().startsWith('+'), show:'+ (III четв., $-100°+360° = 260°$)' },
],
onComplete:(s,n)=>{ if(s===n){addXp(15,'p3-iv2');bumpProgress('p3',25);} else if(s>=4){addXp(8,'p3-iv2');bumpProgress('p3',12);} }
});
wireReadBtn('p3');
}
/* ============================================================
§ 4 — Тригонометрические тождества
============================================================ */
function buildP4(){
const box = document.getElementById('p4-body');
const A = window.ALG10;
let html = '';
/* === SVG 1: теорема Пифагора → основное тождество === */
let svgMain = '';
if(A){
const c = A.tri.canvas({id:'p4-main', W:320, H:320, R:120});
const angle = 50 * Math.PI / 180;
let s = c.open
+ c.axes()
+ c.circle({width:2})
+ c.radius(angle, {color:'#dc2626'})
+ c.sinSegment(angle, {color:'#dc2626', dash:'0', width:2.5})
+ c.cosSegment(angle, {color:'#2563eb', dash:'0', width:2.5})
+ c.point(angle, {color:'#dc2626', label:'P_α(x; y)', labelOffset:18, fontSize:10})
/* Подпись внизу */
+ '<text x="160" y="305" text-anchor="middle" font-size="12" font-family="JetBrains Mono,monospace" font-weight="700" fill="#0d9488">x² + y² = 1 ⇒ cos²α + sin²α = 1</text>'
+ c.close;
svgMain = s;
}
html += makeCard('rule', 'Основное тригонометрическое тождество', '4.1', `
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Теорема.</b> Для любого угла $\\alpha$:</p>
<p style="text-align:center;font-size:1.2rem;margin:10px 0">$\\sin^2\\alpha + \\cos^2\\alpha = 1$</p>
<div class="svg-host">${svgMain}</div>
<p><b>Доказательство.</b> Точка $P_\\alpha = (\\cos\\alpha;\\,\\sin\\alpha)$ лежит на единичной окружности, заданной уравнением $x^2 + y^2 = 1$. Подставляем $x = \\cos\\alpha$, $y = \\sin\\alpha$ — получаем тождество. ■</p>`);
html += makeCard('rule', 'Производные тождества', '4.2', `
<p>Из основного тождества выводим ещё три:</p>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px;text-align:center">$\\tg\\alpha \\cdot \\ctg\\alpha = 1$ (если оба определены)</p>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px;text-align:center">$1 + \\tg^2\\alpha = \\dfrac{1}{\\cos^2\\alpha}$ (при $\\cos\\alpha \\ne 0$)</p>
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px;text-align:center">$1 + \\ctg^2\\alpha = \\dfrac{1}{\\sin^2\\alpha}$ (при $\\sin\\alpha \\ne 0$)</p>
<details class="spoiler"><summary>Доказательство для $1 + \\tg^2 = 1/\\cos^2$</summary>
<div class="spoiler-body">
<p>Разделим основное тождество $\\sin^2\\alpha + \\cos^2\\alpha = 1$ на $\\cos^2\\alpha$:</p>
<p>$\\dfrac{\\sin^2\\alpha}{\\cos^2\\alpha} + 1 = \\dfrac{1}{\\cos^2\\alpha}$ ⇒ $\\tg^2\\alpha + 1 = \\dfrac{1}{\\cos^2\\alpha}$. ■</p>
</div></details>`);
html += makeCard('algo', 'Алгоритм «знаю одну → найду все»', '4.3', `
<p>Если известно одно тригонометрическое значение угла $\\alpha$ и четверть, в которой он лежит, найди остальные три.</p>
<ol style="padding-left:22px;line-height:1.95">
<li>Из основного тождества вырази недостающее (sin или cos).</li>
<li>Знак выбери по четверти.</li>
<li>$\\tg\\alpha = \\dfrac{\\sin\\alpha}{\\cos\\alpha}$.</li>
<li>$\\ctg\\alpha = \\dfrac{1}{\\tg\\alpha}$ (или $\\dfrac{\\cos\\alpha}{\\sin\\alpha}$).</li>
</ol>`);
html += makeCard('example', 'Пример', '4.4', `
<p><b>Задача.</b> $\\sin\\alpha = \\dfrac{3}{5}$, $\\alpha \\in \\left(\\dfrac{\\pi}{2};\\,\\pi\\right)$. Найди $\\cos\\alpha$, $\\tg\\alpha$, $\\ctg\\alpha$.</p>
<p><b>Решение.</b></p>
<ol style="padding-left:22px;line-height:1.95">
<li>$\\cos^2\\alpha = 1 - \\sin^2\\alpha = 1 - \\dfrac{9}{25} = \\dfrac{16}{25}$ ⇒ $\\cos\\alpha = \\pm \\dfrac{4}{5}$.</li>
<li>$\\alpha \\in (\\pi/2;\\pi)$ — II четверть, $\\cos\\alpha < 0$, значит $\\cos\\alpha = -\\dfrac{4}{5}$.</li>
<li>$\\tg\\alpha = \\dfrac{3/5}{-4/5} = -\\dfrac{3}{4}$.</li>
<li>$\\ctg\\alpha = -\\dfrac{4}{3}$.</li>
</ol>`);
/* === ИНТЕРАКТИВ 1: Найди недостающее === */
html += '<div class="wg" id="p4-iv1">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Найди cos α</div></div>'
+'<div class="wg-help">Известны sin α и четверть. Найди cos α как дробь или с десятичной точкой.</div>'
+trainerHTML('p4-iv1', 5, 'значение')
+'</div>';
/* === ИНТЕРАКТИВ 2: Упрости === */
html += '<div class="wg" id="p4-iv2">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Упрости выражение</div></div>'
+'<div class="wg-help">Используй $\\sin^2 + \\cos^2 = 1$ и $\\tg \\cdot \\ctg = 1$. Введи итоговое значение или выражение.</div>'
+trainerHTML('p4-iv2', 5, 'итог')
+'</div>';
/* === ИНТЕРАКТИВ 3: Найди tg, ctg === */
html += '<div class="wg" id="p4-iv3">'
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Найди tg α или ctg α</div></div>'
+'<div class="wg-help">$\\tg\\alpha = \\sin\\alpha / \\cos\\alpha$, $\\ctg\\alpha = 1 / \\tg\\alpha$.</div>'
+trainerHTML('p4-iv3', 5, 'значение')
+'</div>';
/* === БОСС === */
html += '<h3 style="font-family:Unbounded,sans-serif;font-size:1.05rem;color:var(--pri2);margin:20px 0 12px">Босс §4 — Тождества</h3>';
html += makeBoss('p4', {
color:'#7c3aed',
title:'Босс §4 — Тождества',
steps:[
{ q:'Чему равно $\\sin^2 30° + \\cos^2 30°$?', verify:(v)=>+v===1, hint:'Основное тождество, всегда равно 1.' },
{ q:'$\\sin\\alpha = 5/13$ и $\\cos\\alpha = 12/13$. Возможно? «да»/«нет»', verify:(v)=>String(v).trim().toLowerCase().startsWith('д'), hint:'$25/169 + 144/169 = 1$ ✓.' },
{ q:'$\\sin\\alpha = -3/5$, $\\alpha \\in (\\pi; 3\\pi/2)$. Найди $\\cos\\alpha$ как дробь (введи -4/5).', verify:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='-4/5'||+v===-0.8;}, hint:'$\\cos^2 = 16/25$, в III четверти $\\cos < 0$.' },
{ q:'Если $\\tg\\alpha = 0{,}75$, то $\\ctg\\alpha = ?$ (введи в виде десятичной дроби).', verify:(v)=>Math.abs(+v - 4/3) < 0.01, hint:'$\\ctg = 1/\\tg = 1/0{,}75 = 4/3 \\approx 1{,}33$.' },
{ q:'Упрости: $(1 + \\tg^2\\alpha) \\cdot \\cos^2\\alpha$. Чему равно?', verify:(v)=>+v===1, hint:'$1 + \\tg^2 = 1/\\cos^2$, значит произведение $= 1$.' },
]
});
html += secNav('p3', 'p5') + readButton('p4');
box.innerHTML = html; renderMath(box);
/* IV1: Найди cos α */
makeTrainer({
idPrefix:'p4-iv1',
parser:(v)=>v,
questions:[
{ q:'$\\sin\\alpha = 3/5$, $\\alpha \\in (0;\\pi/2)$. Найди $\\cos\\alpha$ (введи 4/5 или 0.8).', a:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='4/5'||+v===0.8;}, show:'$4/5 = 0{,}8$' },
{ q:'$\\sin\\alpha = 3/5$, $\\alpha \\in (\\pi/2;\\pi)$. Найди $\\cos\\alpha$.', a:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='-4/5'||+v===-0.8;}, show:'$-4/5$' },
{ q:'$\\sin\\alpha = -5/13$, $\\alpha \\in (3\\pi/2;\\,2\\pi)$. Найди $\\cos\\alpha$.', a:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='12/13'||Math.abs(+v - 12/13)<0.01;}, show:'$12/13$' },
{ q:'$\\cos\\alpha = -4/5$, $\\alpha \\in (\\pi;3\\pi/2)$. Найди $\\sin\\alpha$.', a:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='-3/5'||+v===-0.6;}, show:'$-3/5$' },
{ q:'$\\sin\\alpha = 0{,}8$, $\\alpha$ во II четв. Найди $\\cos\\alpha$.', a:(v)=>Math.abs(+v - (-0.6))<0.01, show:'$-0{,}6$' },
],
onComplete:(s,n)=>{ if(s===n){addXp(18,'p4-iv1');bumpProgress('p4',30);} else if(s>=3){addXp(9,'p4-iv1');bumpProgress('p4',15);} }
});
/* IV2: Упрости */
makeTrainer({
idPrefix:'p4-iv2',
parser:(v)=>v,
questions:[
{ q:'$3 - \\sin^2\\alpha - \\cos^2\\alpha = ?$', a:(v)=>+v===2, show:'2' },
{ q:'$\\cos^2\\alpha + \\sin^2\\alpha - 7 = ?$', a:(v)=>+v===-6, show:'$-6$' },
{ q:'$\\sin\\alpha \\cdot \\ctg\\alpha + \\cos\\alpha = ?$ (умножь на 1, выр-же $= 2\\cos\\alpha$, введи коэф-т)', a:(v)=>+v===2, show:'$2\\cos\\alpha$, коэф 2' },
{ q:'$(\\tg\\alpha + \\ctg\\alpha)^2 - (\\tg\\alpha - \\ctg\\alpha)^2 = ?$', a:(v)=>+v===4, show:'4' },
{ q:'$(1+\\ctg^2\\alpha)\\sin^2\\alpha = ?$', a:(v)=>+v===1, show:'1' },
],
onComplete:(s,n)=>{ if(s===n){addXp(18,'p4-iv2');bumpProgress('p4',30);} else if(s>=3){addXp(9,'p4-iv2');bumpProgress('p4',14);} }
});
/* IV3: tg, ctg */
makeTrainer({
idPrefix:'p4-iv3',
parser:(v)=>v,
questions:[
{ q:'$\\sin\\alpha = 3/5$, $\\cos\\alpha = -4/5$. Найди $\\tg\\alpha$ (как дробь).', a:(v)=>{const s=String(v).replace(/\\s/g,''); return s==='-3/4'||+v===-0.75;}, show:'$-3/4$' },
{ q:'$\\tg\\alpha = 2$. Найди $\\ctg\\alpha$.', a:(v)=>+v===0.5, show:'$0{,}5$' },
{ q:'$\\sin\\alpha = 1/2$, $\\cos\\alpha = -\\sqrt{3}/2$. Найди $\\ctg\\alpha$ (введи как -sqrt3 или -1.73).', a:(v)=>{const s=String(v).replace(/\\s/g,'').toLowerCase(); return s==='-sqrt3'||s==='-√3'||Math.abs(+v - (-Math.sqrt(3)))<0.05;}, show:'$-\\sqrt{3}$' },
{ q:'$\\tg\\alpha = 5$. Найди $\\dfrac{3\\sin\\alpha - \\cos\\alpha}{\\sin\\alpha + 2\\cos\\alpha}$.', a:(v)=>+v===2, show:'2 (разделить числитель и знаменатель на $\\cos\\alpha$)' },
{ q:'$\\ctg\\alpha = 1/2$. Найди $\\tg\\alpha$.', a:(v)=>+v===2, show:'2' },
],
onComplete:(s,n)=>{ if(s===n){addXp(18,'p4-iv3');bumpProgress('p4',30);} else if(s>=3){addXp(9,'p4-iv3');bumpProgress('p4',14);} }
});
wireReadBtn('p4');
}
</script>
</body>
</html>