Files
Learn_System/frontend/textbooks/geometry_9_ch1.html
T

1875 lines
142 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>Геометрия 9 · Глава 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>
<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:#fafafa; --card:#fff; --card-soft:#f8fafc; --text:#0f172a; --ink:#0f172a; --muted:#64748b;
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
--pri:#d97706; --pri2:#b45309; --pri-soft:#fef3c7;
--acc:#fbbf24; --acc2:#d97706; --acc-soft:#fffbeb;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#1c1208; --card:#251a0a; --card-soft:#2b1f0d; --text:#fef3c7; --ink:#fef3c7; --muted:#a89070; --border:#3d2d18}
*{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,#92400e 0%,#d97706 55%,#fbbf24 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(254,243,199,.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(254,243,199,.12);line-height:1;pointer-events:none;user-select: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:0;top:-30px;font-size:clamp(2rem,12vw,8rem);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;letter-spacing:-.01em}
.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);transition:transform .15s,box-shadow .15s}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(0,0,0,.18)}
.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(0,0,0,.12);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;letter-spacing:.02em;box-shadow:0 4px 12px rgba(0,0,0,.18);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(180px,1fr));gap:10px}
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:14px;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:5px}
.psel-name{font-size:.86rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
.psel-prog{height:4px;background:rgba(0,0,0,.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)}
.psel-card.final .psel-num{color:var(--warn)}
.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(--pri-soft);line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--pri-soft);position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--pri),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.6rem;font-weight:800;color:var(--pri2);letter-spacing:-.01em;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(0,0,0,.04);position:relative;z-index:1;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s}
.card:hover{transform:translateY(-2px);box-shadow:0 4px 10px rgba(0,0,0,.06),0 16px 36px rgba(0,0,0,.08)}
.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.repeat{background:#0ea5e9}.card-icon.theory{background:#8b5cf6}.card-icon.algo{background:#f59e0b}.card-icon.rule{background:#ec4899}.card-icon.example{background:#10b981}.card-icon.oral{background:#06b6d4}
.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(--pri-soft);padding:3px 7px;border-radius:5px}
.card-body{font-size:.94rem;line-height:1.65}
.card-body p{margin-bottom:8px}
.card-body p:last-child{margin-bottom:0}
.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,transform .1s}
.btn:hover{background:var(--pri-soft);border-color:var(--pri)}
.btn:active{transform:scale(.96)}
.btn.primary{background:var(--pri);color:#fff;border-color:var(--pri)}
.btn.primary:hover{background:var(--pri2);border-color:var(--pri2)}
.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)}
.wg{background:linear-gradient(135deg,var(--card),var(--pri-soft));border:1.5px solid 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(--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(--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),var(--pri-soft));border-left:4px solid var(--warn);padding:9px 14px;border-radius:9px}
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace}
.tinp:focus{outline:0;border-color:var(--pri);box-shadow:0 0 0 3px var(--pri-soft)}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--pri2);margin-left:4px}
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--pri)}
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--pri-soft);border-radius:10px;margin-bottom:12px}
.score-display b{color:var(--pri2);font-size:1.15rem}
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
.spoiler summary{padding:8px 14px;background:var(--pri-soft);font-weight:700;cursor:pointer;font-size:.88rem;color: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(--pri);width:18px}
.spoiler[open] summary::before{content:'\2212'}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
.dnd-pool.over{border-color:var(--pri);background:var(--pri-soft);border-style:solid}
.dnd-pool.col{flex-direction:column;align-items:stretch}
.dnd-pool.col .dnd-chip{width:auto}
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--pri);box-shadow:var(--sh)}
.dnd-chip:active{cursor:grabbing}
.dnd-chip.armed{border-color:var(--pri);background:var(--pri-soft);box-shadow:0 0 0 3px rgba(217,119,6,.22);transform:translateY(-1px)}
.dnd-chip.dragging{opacity:.28}
.dnd-chip.placed{background:var(--pri-soft);border-color:var(--pri)}
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
.dnd-chip .dnd-x:hover{color:var(--bad);background:var(--fail-bg)}
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
.drop-box:hover{border-color:var(--pri);background:var(--pri-soft)}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--pri2);margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--pri);background:var(--pri-soft);border-style:solid;transform:scale(1.015)}
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
.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}
.sidecard-row:last-child{margin-bottom:0}
@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(0,0,0,.10);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}
.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,var(--pri),var(--acc));color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(0,0,0,.32);z-index:1002;display:none;align-items:center;gap:8px;max-width:340px}
.ach-popup.show{display:flex}
.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}
}
.search-modal{position:fixed;inset:0;background:rgba(15,23,42,.55);backdrop-filter:blur(4px);z-index:9993;display:none;align-items:flex-start;justify-content:center;padding-top:14vh}
.search-modal.show{display:flex}
.search-box{background:var(--bg);border:1px solid var(--border);border-radius:14px;width:560px;max-width:92vw;max-height:70vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.4)}
.search-input{padding:14px 16px;font-size:1rem;border:0;border-bottom:1px solid var(--border);background:transparent;color:var(--text);outline:none}
.search-results{flex:1;overflow-y:auto;padding:6px 0}
.search-row{display:block;padding:8px 16px;cursor:pointer;border-bottom:1px solid var(--border);text-align:left;background:transparent;border:0;width:100%;color:var(--text)}
.search-row:hover,.search-row.active{background:var(--pri-soft)}
.search-row .sr-kind{font-size:.7rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
.search-row .sr-title{font-weight:700;font-size:.92rem;color:var(--text)}
.search-row .sr-desc{font-size:.8rem;color:var(--muted);margin-top:2px}
.search-empty{padding:20px;text-align:center;color:var(--muted);font-size:.88rem}
.search-foot{padding:8px 14px;border-top:1px solid var(--border);font-size:.74rem;color:var(--muted);display:flex;gap:14px}
.search-foot kbd{padding:2px 6px;background:var(--card);border:1px solid var(--border);border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:.72rem}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Геометрия 9 · Глава 1</h1>
<div class="hdr-sub">Прямоугольный треугольник · тригонометрия · площадь</div>
</div>
<div class="hdr-side">
<a href="/textbook/geometry-9" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К геометрии 9</a>
<button id="search-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg> Поиск</button>
<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> острого и тупого угла, решаем прямоугольный треугольник, выводим формулы площади $S = \tfrac{1}{2}ab\sin C$ и находим высоту к гипотенузе как среднее геометрическое $h^2 = a_1 b_1$.</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="sin"><div class="sec-header"><span class="sec-num">§ 1</span><h2 class="sec-h">sin, cos, tg, ctg острого угла</h2></div><div id="p1-body"></div></section>
<section id="sec-p2" class="sec" data-watermark="△"><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="sin²"><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">sin, cos, tg, ctg тупого угла</h2></div><div id="p4-body"></div></section>
<section id="sec-p5" class="sec" data-watermark="S"><div class="sec-header"><span class="sec-num">§ 5</span><h2 class="sec-h">Формулы площади</h2></div><div id="p5-body"></div></section>
<section id="sec-p6" class="sec" data-watermark="h²"><div class="sec-header"><span class="sec-num">§ 6</span><h2 class="sec-h">Среднее геометрическое</h2></div><div id="p6-body"></div></section>
<section id="sec-final1" class="sec" data-watermark="★"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#d97706,#fbbf24)">Финал главы</span><h2 class="sec-h">Итоги главы 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">Интерактивный учебник «Геометрия 9» · Глава 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>
<div id="search-modal" class="search-modal" role="dialog">
<div class="search-box">
<input type="text" id="search-input" class="search-input" placeholder="Поиск…" autocomplete="off">
<div id="search-results" class="search-results"></div>
<div class="search-foot"><span><kbd>↑↓</kbd> навигация</span><span><kbd>Enter</kbd> открыть</span><span><kbd>Esc</kbd> закрыть</span></div>
</div>
</div>
<script>
'use strict';
const STATE = { current:'p1', progress:{p1:0,p2:0,p3:0,p4:0,p5:0,p6:0,final1:0}, achievements:new Map(), xp:0, level:1 };
const TOTAL_PARAS = 7;
const _TB_SLUG = 'geometry-9-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:'sin, cos, tg, ctg острого угла освоены!',
p3_done:'Тригонометрические формулы освоены!',
p5_done:'Формулы площади освоены!',
ch1_done:'Глава 1 пройдена! Прямоугольный треугольник — финал!'
};
function loadProgress(){
try{
const s=localStorage.getItem('geometry9_ch1_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
const a=localStorage.getItem('geometry9_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('geometry9_xp')||0); STATE.level=calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('geometry9_ch1_progress', JSON.stringify(STATE.progress));
localStorage.setItem('geometry9_ch1_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem('geometry9_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);
}
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 loadServerReadState(){
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
fetch('/api/textbooks/'+_TB_SLUG,{headers:{'Authorization':'Bearer '+tok}}).then(r=>r.ok?r.json():null).then(d=>{
if(!d||!d.progress) return;
(d.progress.read||[]).forEach(k=>{_markedRead.add(k); if((STATE.progress[k]||0)<50) STATE.progress[k]=100;});
saveProgress(); refreshProgressUI();
}).catch(()=>{});
}
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,'geometry9-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:'sin, cos, tg, ctg острого угла', sub:'$\\sin\\alpha = \\tfrac{a}{c}$' },
{ id:'p2', num:'§ 2', name:'Решение прямоугольного треугольника', sub:'$a, b, c, \\alpha$' },
{ id:'p3', num:'§ 3', name:'Тригонометрические формулы', sub:'$\\sin^2 + \\cos^2 = 1$' },
{ id:'p4', num:'§ 4', name:'sin, cos, tg, ctg тупого угла', sub:'$\\sin(180^\\circ - \\alpha)$' },
{ id:'p5', num:'§ 5', name:'Формулы площади', sub:'$S = \\tfrac{1}{2}ab\\sin C$' },
{ id:'p6', num:'§ 6', name:'Среднее геометрическое', sub:'$h^2 = a_1 b_1$' },
{ id:'final1', num:'★', name:'Финал главы', sub:'Итоги главы 1', 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);
});
}
const BUILT=new Set();
const BUILDERS = { p1:()=>buildP1(), p2:()=>buildP2(), p3:()=>buildP3(), p4:()=>buildP4(), p5:()=>buildP5(), p6:()=>buildP6(), final1:()=>buildFinal1() };
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:'Шпаргалка \xA71',rows:[['sin α','противолежащий / гипотенуза'],['cos α','прилежащий / гипотенуза'],['tg α','sin α / cos α']]},
p2:{title:'Шпаргалка \xA72',rows:[['Дано 2 элемента','найти остальные'],['Гипотенуза','$c = \\sqrt{a^2 + b^2}$'],['Угол','$\\alpha + \\beta = 90^\\circ$']]},
p3:{title:'Шпаргалка \xA73',rows:[['Основное','$\\sin^2\\alpha + \\cos^2\\alpha = 1$'],['30°','$\\sin = \\tfrac{1}{2}$, $\\cos = \\tfrac{\\sqrt3}{2}$'],['45°','$\\sin = \\cos = \\tfrac{\\sqrt2}{2}$'],['60°','$\\sin = \\tfrac{\\sqrt3}{2}$, $\\cos = \\tfrac{1}{2}$']]},
p4:{title:'Шпаргалка \xA74',rows:[['Тупой угол','$90^\\circ < \\alpha < 180^\\circ$'],['Симметрия','$\\sin(180^\\circ - \\alpha) = \\sin\\alpha$'],['Знак','$\\cos(180^\\circ - \\alpha) = -\\cos\\alpha$']]},
p5:{title:'Шпаргалка \xA75',rows:[['Через сторону и угол','$S = \\tfrac{1}{2}ab\\sin C$'],['Через высоту','$S = \\tfrac{1}{2}ah$']]},
p6:{title:'Шпаргалка \xA76',rows:[['Среднее геом.','$\\sqrt{ab}$'],['Высота','$h = \\sqrt{a_1 \\cdot b_1}$'],['Катет','$a^2 = a_1 \\cdot c$']]},
final1:{title:'Финал главы',rows:[['§§16','теория главы 1'],['Награда','XP за прочтение'],['Дальше','глава 2 — окружности']]}
};
const TIPS=[
{sec:'p1',html:'$\\sin\\alpha = \\dfrac{\\text{противолежащий}}{\\text{гипотенуза}}$, $\\cos\\alpha = \\dfrac{\\text{прилежащий}}{\\text{гипотенуза}}$.'},
{sec:'p2',html:'Решить треугольник — найти все стороны и углы по двум данным элементам.'},
{sec:'p3',html:'$\\sin^2\\alpha + \\cos^2\\alpha = 1$ — следствие теоремы Пифагора.'},
{sec:'p4',html:'Для тупого угла $\\alpha$: $\\sin\\alpha = \\sin(180^\\circ - \\alpha)$, $\\cos\\alpha = -\\cos(180^\\circ - \\alpha)$.'},
{sec:'p5',html:'$S = \\dfrac{1}{2}ab\\sin C$ — площадь по двум сторонам и углу между ними.'},
{sec:'p6',html:'В прямоугольном треугольнике высота к гипотенузе равна $h = \\sqrt{a_1 b_1}$.'},
{sec:'final1',html:'Главные результаты главы 1: тригонометрические функции, формулы площади и среднее геометрическое.'}
];
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];
if(tip){
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;display:flex;align-items:center;gap:6px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><polygon points="12,2 22,20 2,20"/></svg>Подсказка</h4><div class="sidecard-row" style="margin-bottom:0;font-size:.84rem;line-height:1.55">'+tip.html+'</div></div>';
}
if(STATE.achievements.size>0){
html+='<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">'+STATE.achievements.size+'</span></h4>';
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html+='<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">&#10003; '+text+'</div>'; });
html+='</div>';
}
box.innerHTML=html;
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
}
function initTheme(){
const t=localStorage.getItem('geometry9_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('geometry9_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){} }
function fmt(n){ if(!isFinite(n)) return '?'; if(Number.isInteger(n)) return String(n); return Math.abs(n-Math.round(n))<1e-9?String(Math.round(n)):(+n.toFixed(6)).toString(); }
const ICONS = {
repeat:'<svg class="ic" viewBox="0 0 24 24"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>',
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>',
oral:'<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
};
/* ===== Геометрические SVG-хелперы (используются во всей главе) ===== */
// L-маркер прямого угла (polyline ВНУТРЬ угла).
// V — вершина прямого угла; uIn, wIn — единичные векторы в SVG-координатах вдоль двух катетов.
function rightAngleMark(V, uIn, wIn, s){
s = s || 9;
const p1 = {x: V.x + s*uIn.x, y: V.y + s*uIn.y};
const c = {x: p1.x + s*wIn.x, y: p1.y + s*wIn.y};
const p2 = {x: V.x + s*wIn.x, y: V.y + s*wIn.y};
return p1.x+','+p1.y+' '+c.x+','+c.y+' '+p2.x+','+p2.y;
}
// Дуга угла с автовыбором sweep (через cross product).
// V — вершина, uA/uB — единичные векторы вдоль сторон угла (в SVG-координатах), R — радиус.
function angleArcAuto(V, uA, uB, R){
const sA = {x: V.x + R*uA.x, y: V.y + R*uA.y};
const eB = {x: V.x + R*uB.x, y: V.y + R*uB.y};
const cross = uA.x*uB.y - uA.y*uB.x;
const sweep = cross > 0 ? 1 : 0;
return 'M'+sA.x+','+sA.y+' A'+R+','+R+' 0 0,'+sweep+' '+eB.x+','+eB.y;
}
// Нормализованный вектор от p1 к p2 (в координатах того же пространства).
function unitVec(p1, p2){
const dx = p2.x - p1.x, dy = p2.y - p1.y;
const len = Math.sqrt(dx*dx + dy*dy) || 1;
return {x: dx/len, y: dy/len};
}
function deg2rad(d){ return d * Math.PI / 180; }
function gcd(a,b){ a=Math.abs(a|0); b=Math.abs(b|0); while(b){ const t=b; b=a%b; a=t; } return a||1; }
/* ===== DnD сортер ===== */
function setupSorter(cfg){
const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector);
if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}};
pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col');
let armed = null;
function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; attach(e,it.id); return e; }
function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); }
function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); }
function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} }
attachBoxTaps(); render();
return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }};
}
function makeCard(kind, title, num, body){
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'};
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:'\xA71',p2:'\xA72',p3:'\xA73',p4:'\xA74',p5:'\xA75',p6:'\xA76',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 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.startsWith('final')?'финал':'\xA7'+paraId.replace('p',''))+' (+10 XP)'
+'</button></div>';
}
function wireReadBtn(paraId){
const btn = document.getElementById(paraId+'-read-btn'); if(!btn) return;
btn.addEventListener('click', ()=>{
addXp(10, paraId+'-read'); bumpProgress(paraId, 100);
btn.textContent='Прочитано! +10 XP'; btn.disabled=true; btn.style.opacity=.6;
if(paraId==='final1') achievement('ch1_done');
});
}
/* ===== STUB BUILDERS — наполнение в Phase 7+ ===== */
function _stubBuilder(paraId, num, name, prev, next){
const body = document.getElementById(paraId+'-body');
let html = '';
html += makeCard('theory', 'В разработке', num, `
<p>Содержание параграфа <b>«${name}»</b> будет добавлено в следующих обновлениях.</p>
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 7.</p>`);
html += readButton(paraId);
html += secNav(prev, next);
body.innerHTML = html;
wireReadBtn(paraId);
if(window.renderMathInElement) renderMath(body);
}
/* ===== §1 sin, cos, tg, ctg острого угла ===== */
function buildP1(){
const box = document.getElementById('p1-body');
let html = '';
html += makeCard('theory', 'Определения', '1.1', `
<p>Рассмотрим прямоугольный треугольник с острым углом $\\alpha$. Относительно $\\alpha$ катеты делятся на <b>противолежащий</b> (лежит напротив $\\alpha$) и <b>прилежащий</b> (образует $\\alpha$ вместе с гипотенузой).</p>
<p>Тригонометрические функции острого угла:</p>
<ul style="padding-left:22px;line-height:1.95">
<li>$\\sin \\alpha = \\dfrac{\\text{противолежащий катет}}{\\text{гипотенуза}}$</li>
<li>$\\cos \\alpha = \\dfrac{\\text{прилежащий катет}}{\\text{гипотенуза}}$</li>
<li>$\\tan \\alpha = \\dfrac{\\sin \\alpha}{\\cos \\alpha} = \\dfrac{\\text{противолежащий}}{\\text{прилежащий}}$</li>
<li>$\\cot \\alpha = \\dfrac{\\cos \\alpha}{\\sin \\alpha} = \\dfrac{1}{\\tan \\alpha} = \\dfrac{\\text{прилежащий}}{\\text{противолежащий}}$</li>
</ul>
<details class="spoiler"><summary>Почему отношения, а не «длины»?</summary><div class="spoiler-body">
Все прямоугольные треугольники с одним и тем же острым углом $\\alpha$ <b>подобны</b>. У подобных треугольников <b>отношения сторон</b> совпадают. Поэтому $\\sin \\alpha$, $\\cos \\alpha$ и $\\tan \\alpha$ зависят только от угла, а не от размера треугольника.
</div></details>`);
html += makeCard('rule', 'Свойства тригонометрических функций', '1.2', `
<p>Для острого угла $\\alpha$ ($0^\\circ < \\alpha < 90^\\circ$):</p>
<ul style="padding-left:22px;line-height:1.95">
<li><b>Ограничены единицей:</b> $0 < \\sin \\alpha < 1$ и $0 < \\cos \\alpha < 1$ (катет меньше гипотенузы).</li>
<li><b>Косинус дополнительного угла:</b> $\\sin \\alpha = \\cos(90^\\circ - \\alpha)$ и $\\cos \\alpha = \\sin(90^\\circ - \\alpha)$.</li>
<li><b>Произведение:</b> $\\tan \\alpha \\cdot \\cot \\alpha = 1$.</li>
<li><b>Основное тригонометрическое тождество:</b> $\\sin^2 \\alpha + \\cos^2 \\alpha = 1$.</li>
</ul>
<details class="spoiler"><summary>Откуда тождество $\\sin^2 + \\cos^2 = 1$?</summary><div class="spoiler-body">
В прямоугольном треугольнике катеты $a, b$ и гипотенуза $c$. По Пифагору $a^2 + b^2 = c^2$. Поделим на $c^2$: $\\left(\\dfrac{a}{c}\\right)^2 + \\left(\\dfrac{b}{c}\\right)^2 = 1$, то есть $\\sin^2 \\alpha + \\cos^2 \\alpha = 1$.
</div></details>`);
html += makeCard('example', 'Эталонные значения 30°, 45°, 60°', '1.3', `
<p>Запомни эту таблицу — она встречается во всех задачах:</p>
<div style="overflow-x:auto;margin:10px 0">
<table style="width:100%;border-collapse:collapse;font-size:.95rem;background:var(--card);border:1.5px solid var(--border);border-radius:9px;overflow:hidden">
<thead><tr style="background:var(--pri-soft)">
<th style="padding:8px;border-bottom:1px solid var(--border);text-align:left">Угол</th>
<th style="padding:8px;border-bottom:1px solid var(--border)">$\\sin$</th>
<th style="padding:8px;border-bottom:1px solid var(--border)">$\\cos$</th>
<th style="padding:8px;border-bottom:1px solid var(--border)">$\\tan$</th>
<th style="padding:8px;border-bottom:1px solid var(--border)">$\\cot$</th>
</tr></thead>
<tbody>
<tr><td style="padding:8px;border-bottom:1px solid var(--border)"><b>30°</b></td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{1}{2}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{\\sqrt{3}}{2}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{1}{\\sqrt{3}}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\sqrt{3}$</td></tr>
<tr><td style="padding:8px;border-bottom:1px solid var(--border)"><b>45°</b></td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{\\sqrt{2}}{2}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{\\sqrt{2}}{2}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$1$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$1$</td></tr>
<tr><td style="padding:8px"><b>60°</b></td><td style="padding:8px;text-align:center">$\\tfrac{\\sqrt{3}}{2}$</td><td style="padding:8px;text-align:center">$\\tfrac{1}{2}$</td><td style="padding:8px;text-align:center">$\\sqrt{3}$</td><td style="padding:8px;text-align:center">$\\tfrac{1}{\\sqrt{3}}$</td></tr>
</tbody>
</table>
</div>
<p><b>Заметь:</b> $\\sin 30^\\circ = \\cos 60^\\circ$, $\\sin 45^\\circ = \\cos 45^\\circ$ — это и есть «косинус дополнительного угла» из §1.2.</p>`);
/* IV1 — Конструктор прямоугольного треугольника */
html += `<div class="wg" id="p1-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор прямоугольного треугольника</div></div>
<div class="wg-help">Меняй угол $\\alpha$ ползунком — катеты и гипотенуза перестроятся, а внизу появятся значения $\\sin \\alpha$, $\\cos \\alpha$, $\\tan \\alpha$, $\\cot \\alpha$.</div>
<div class="sliders">
<label>Угол $\\alpha$, °<b id="p1-iv1-aval">40</b><input type="range" id="p1-iv1-a" min="10" max="80" step="1" value="40"></label>
</div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p1-iv1-svg" viewBox="0 0 400 320" style="width:100%;min-width:320px;height:auto;display:block"></svg>
</div>
<div id="p1-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--pri-soft);border-radius:9px;font-size:.95rem;text-align:center;line-height:1.9"></div>
</div>`;
/* IV2 — Калькулятор сторон */
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">Введи угол $\\alpha$ и гипотенузу $c$ — посчитаем оба катета: $a = c \\sin \\alpha$ (противолежащий) и $b = c \\cos \\alpha$ (прилежащий).</div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
<span style="font-family:'JetBrains Mono',monospace">$\\alpha$ =</span>
<input type="number" id="p1-iv2-a" class="tinp" style="width:90px;text-align:center" value="30" min="10" max="80">
<span style="font-family:'JetBrains Mono',monospace">°,&nbsp; $c$ =</span>
<input type="number" id="p1-iv2-c" class="tinp" style="width:90px;text-align:center" value="10" min="0.1" step="0.1">
<button class="btn primary" id="p1-iv2-go">Найти катеты</button>
</div>
<div id="p1-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;text-align:center;font-size:1rem;min-height:50px"></div>
<div class="feedback" id="p1-iv2-fb"></div>
</div>`;
/* IV3 — Quickfire «Какое отношение?» */
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">Прямоугольный треугольник с гипотенузой $c$, катетами $a$ (противолежащий $\\alpha$) и $b$ (прилежащий $\\alpha$). Какое из четырёх отношений равно указанному выражению?</div>
<div class="score-display"><span>Задача <b id="p1-iv3-i">1</b> / 8</span><span>Очки: <b id="p1-iv3-s">0</b> / 8</span></div>
<div id="p1-iv3-q" style="padding:14px;background:var(--pri-soft);border-radius:10px;font-size:1.15rem;text-align:center;margin-bottom:10px"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:8px">
<button class="btn primary" data-ans="ac" id="p1-iv3-ac">$a / c$</button>
<button class="btn primary" data-ans="bc" id="p1-iv3-bc">$b / c$</button>
<button class="btn primary" data-ans="ab" id="p1-iv3-ab">$a / b$</button>
<button class="btn primary" data-ans="ba" id="p1-iv3-ba">$b / a$</button>
</div>
<div class="feedback" id="p1-iv3-fb"></div>
</div>`;
/* IV4 — Тренажёр */
html += `<div class="wg" id="p1-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр</div></div>
<div class="wg-help">Реши задачу и введи число (округли до 2 знаков, если получается дробное).</div>
<div class="score-display"><span>Задача <b id="p1-iv4-i">1</b> / 6</span><span>Очки: <b id="p1-iv4-s">0</b> / 6</span></div>
<div id="p1-iv4-q" style="padding:14px;background:var(--pri-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">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p1-iv4-ans" class="tinp" style="width:110px;text-align:center" step="0.01">
<button class="btn primary" id="p1-iv4-go">Проверить</button>
<button class="btn" id="p1-iv4-start">Заново</button>
</div>
<div class="feedback" id="p1-iv4-fb"></div>
</div>`;
html += secNav(null, 'p2');
html += readButton('p1');
box.innerHTML = html;
renderMath(box);
/* IV1 — слайдер + SVG */
(function(){
const sl = document.getElementById('p1-iv1-a');
const lab = document.getElementById('p1-iv1-aval');
const svg = document.getElementById('p1-iv1-svg');
const out = document.getElementById('p1-iv1-out');
const seen = new Set();
function draw(){
const aDeg = +sl.value;
lab.textContent = aDeg;
const aRad = deg2rad(aDeg);
const c = 220; // гипотенуза в пикселях
// Геометрические вершины: A (внизу слева), B (верх — прямой угол), C (внизу справа).
// Прямой угол при B. Угол α при C.
// BC = c·cos α (горизонтальный катет, прилежащий к α)
// AB = c·sin α (вертикальный катет, противолежащий α)
const BCpx = c * Math.cos(aRad);
const ABpx = c * Math.sin(aRad);
const cx = 60, cyBase = 270; // позиция A
const A = {x: cx, y: cyBase};
const C = {x: cx + BCpx, y: cyBase};
const B = {x: cx, y: cyBase - ABpx};
// Внутренние векторы в B (прямой угол): по BA — вниз, по BC — вправо-вниз
const uBA = unitVec(B, A);
const uBC = unitVec(B, C);
const uCA = unitVec(C, A); // вдоль гипотенузы из C
const uCB = unitVec(C, B); // вдоль катета из C
let s = '';
// фон
s += '<rect x="0" y="0" width="400" height="320" fill="none"/>';
// треугольник
s += '<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="rgba(217,119,6,.08)" stroke="#b45309" stroke-width="2.2" stroke-linejoin="round"/>';
// маркер прямого угла в B (внутренние векторы — uBA и uBC)
s += '<polyline points="'+rightAngleMark(B, uBA, uBC, 12)+'" fill="none" stroke="#0f172a" stroke-width="1.8"/>';
// дуга угла α при C (от CA к CB)
s += '<path d="'+angleArcAuto(C, uCA, uCB, 30)+'" fill="none" stroke="#dc2626" stroke-width="2"/>';
// подпись α
const aMid = {x: C.x + 44*Math.cos(Math.atan2((uCA.y+uCB.y)/2,(uCA.x+uCB.x)/2)), y: C.y + 44*Math.sin(Math.atan2((uCA.y+uCB.y)/2,(uCA.x+uCB.x)/2))};
s += '<text x="'+aMid.x+'" y="'+aMid.y+'" text-anchor="middle" dominant-baseline="middle" font-family="Inter,sans-serif" font-size="15" font-weight="700" fill="#dc2626">α</text>';
// вершины
s += '<circle cx="'+A.x+'" cy="'+A.y+'" r="4" fill="#0f172a"/>';
s += '<circle cx="'+B.x+'" cy="'+B.y+'" r="4" fill="#0f172a"/>';
s += '<circle cx="'+C.x+'" cy="'+C.y+'" r="4" fill="#0f172a"/>';
s += '<text x="'+(A.x-12)+'" y="'+(A.y+18)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="16" font-weight="700" fill="#0f172a">A</text>';
s += '<text x="'+(B.x-12)+'" y="'+(B.y-4)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="16" font-weight="700" fill="#0f172a">B</text>';
s += '<text x="'+(C.x+12)+'" y="'+(C.y+18)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="16" font-weight="700" fill="#0f172a">C</text>';
// подписи сторон
const labBC = 'BC='+(BCpx/22).toFixed(2);
const labAB = 'AB='+(ABpx/22).toFixed(2);
const labAC = 'AC='+(c/22).toFixed(2);
s += '<text x="'+((B.x+C.x)/2)+'" y="'+(cyBase+34)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">'+labBC+'</text>';
s += '<text x="'+(B.x-32)+'" y="'+((A.y+B.y)/2)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">'+labAB+'</text>';
// подпись гипотенузы — поднимем над линией AC
const midAC = {x:(A.x+C.x)/2, y:(A.y+C.y)/2};
const nAC = {x:-(C.y-A.y), y:(C.x-A.x)};
const nL = Math.sqrt(nAC.x*nAC.x+nAC.y*nAC.y)||1;
const labP = {x: midAC.x + 16*nAC.x/nL, y: midAC.y + 16*nAC.y/nL};
s += '<text x="'+labP.x+'" y="'+labP.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">'+labAC+'</text>';
svg.innerHTML = s;
// числовые значения
const sn = Math.sin(aRad), cs = Math.cos(aRad), tn = Math.tan(aRad), ct = 1/Math.tan(aRad);
out.innerHTML = '$\\sin '+aDeg+'^\\circ \\approx '+sn.toFixed(3)+'$ &nbsp;·&nbsp; $\\cos '+aDeg+'^\\circ \\approx '+cs.toFixed(3)+'$<br>'
+ '$\\tan '+aDeg+'^\\circ \\approx '+tn.toFixed(3)+'$ &nbsp;·&nbsp; $\\cot '+aDeg+'^\\circ \\approx '+ct.toFixed(3)+'$';
renderMath(out);
seen.add(aDeg);
if(seen.size >= 6 && !seen.has('done')){ addXp(10,'p1-iv1'); bumpProgress('p1', 15); seen.add('done'); }
}
sl.addEventListener('input', draw);
draw();
})();
/* IV2 — калькулятор сторон */
(function(){
const aI = document.getElementById('p1-iv2-a');
const cI = document.getElementById('p1-iv2-c');
const go = document.getElementById('p1-iv2-go');
const out= document.getElementById('p1-iv2-out');
const fb = document.getElementById('p1-iv2-fb');
let solved = 0;
function calc(){
const aDeg = parseFloat(aI.value), c = parseFloat(cI.value);
if(isNaN(aDeg) || isNaN(c)){ feedback(fb, false, '&#10007; Введи число для $\\alpha$ и $c$.'); return; }
if(aDeg<=0 || aDeg>=90){ feedback(fb, false, '&#10007; Угол должен быть в диапазоне (0°; 90°). Лучше 10..80.'); return; }
if(c<=0){ feedback(fb, false, '&#10007; Гипотенуза должна быть положительной.'); return; }
const r = deg2rad(aDeg);
const a = c * Math.sin(r);
const b = c * Math.cos(r);
out.innerHTML = '<b>$a = c \\sin \\alpha = '+c+' \\cdot \\sin '+aDeg+'^\\circ \\approx '+a.toFixed(2)+'$</b> (противолежащий)<br>'
+ '<b>$b = c \\cos \\alpha = '+c+' \\cdot \\cos '+aDeg+'^\\circ \\approx '+b.toFixed(2)+'$</b> (прилежащий)';
renderMath(out);
feedback(fb, true, '&#10003; Катеты найдены.');
solved++;
if(solved === 1){ addXp(10,'p1-iv2'); bumpProgress('p1', 15); }
}
go.addEventListener('click', calc);
calc();
})();
/* IV3 — Какое отношение? */
(function(){
const Q = [
{ expr:'$\\sin \\alpha$', ans:'ac', why:'противолежащий $a$ к гипотенузе $c$' },
{ expr:'$\\cos \\alpha$', ans:'bc', why:'прилежащий $b$ к гипотенузе $c$' },
{ expr:'$\\tan \\alpha$', ans:'ab', why:'противолежащий $a$ к прилежащему $b$' },
{ expr:'$\\cot \\alpha$', ans:'ba', why:'прилежащий $b$ к противолежащему $a$' },
{ expr:'$\\cos(90^\\circ - \\alpha)$', ans:'ac', why:'$= \\sin \\alpha = a/c$' },
{ expr:'$\\sin(90^\\circ - \\alpha)$', ans:'bc', why:'$= \\cos \\alpha = b/c$' },
{ expr:'$\\dfrac{1}{\\cot \\alpha}$', ans:'ab', why:'$= \\tan \\alpha = a/b$' },
{ expr:'$\\dfrac{\\sin \\alpha \\cdot \\cos \\alpha}{\\sin \\alpha}$', ans:'bc', why:'$= \\cos \\alpha = b/c$' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p1-iv3-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p1-iv3'); bumpProgress('p1', 25); }
else if(score >= Q.length - 2){ addXp(8,'p1-iv3'); bumpProgress('p1', 15); }
return;
}
document.getElementById('p1-iv3-i').textContent = (i+1);
document.getElementById('p1-iv3-s').textContent = score;
document.getElementById('p1-iv3-q').innerHTML = Q[i].expr;
renderMath(document.getElementById('p1-iv3-q'));
document.getElementById('p1-iv3-fb').style.display = 'none';
}
function answer(a){
if(i >= Q.length) return;
const fb = document.getElementById('p1-iv3-fb');
if(a === Q[i].ans){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].why+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Нет. '+Q[i].why+'. Дальше ▶');
document.getElementById('p1-iv3-s').textContent = score;
i++;
setTimeout(show, 1100);
}
['ac','bc','ab','ba'].forEach(k=>{
const b = document.getElementById('p1-iv3-'+k); if(b) b.addEventListener('click', ()=>answer(k));
});
show();
})();
/* IV4 — Тренажёр */
(function(){
const Q = [
{ q:'$\\sin 30^\\circ = ?$', ans:0.5, tol:0.02, hint:'эталон: $\\sin 30^\\circ = 1/2$' },
{ q:'$\\cos 60^\\circ = ?$', ans:0.5, tol:0.02, hint:'эталон: $\\cos 60^\\circ = 1/2$' },
{ q:'В прямоуг. треугольнике катет $a = 3$, гипотенуза $c = 5$. Найди $\\sin \\alpha$ ($\\alpha$ — угол, противолежащий $a$).', ans:0.6, tol:0.02, hint:'$\\sin \\alpha = a/c = 3/5 = 0{,}6$' },
{ q:'В прямоуг. треугольнике катеты 3 и 4. Чему равна гипотенуза?', ans:5, tol:0.05, hint:'$c = \\sqrt{9+16} = \\sqrt{25} = 5$' },
{ q:'$\\tan 45^\\circ = ?$', ans:1, tol:0.02, hint:'эталон: $\\tan 45^\\circ = 1$' },
{ q:'$c = 10$, $\\alpha = 30^\\circ$. Найди катет, противолежащий $\\alpha$.', ans:5, tol:0.05, hint:'$a = c \\sin \\alpha = 10 \\cdot 0{,}5 = 5$' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p1-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p1-iv4'); bumpProgress('p1', 25); }
else if(score >= 4){ addXp(8,'p1-iv4'); bumpProgress('p1', 15); }
return;
}
document.getElementById('p1-iv4-i').textContent = (i+1);
document.getElementById('p1-iv4-s').textContent = score;
document.getElementById('p1-iv4-q').innerHTML = Q[i].q;
document.getElementById('p1-iv4-ans').value = '';
renderMath(document.getElementById('p1-iv4-q'));
document.getElementById('p1-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p1-iv4-fb');
const ans = parseFloat(document.getElementById('p1-iv4-ans').value);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: '+Q[i].ans+' ('+Q[i].hint+'). Дальше ▶');
document.getElementById('p1-iv4-s').textContent = score;
i++;
setTimeout(show, 1300);
}
document.getElementById('p1-iv4-go').addEventListener('click', go);
document.getElementById('p1-iv4-ans').addEventListener('keydown', e=>{ if(e.key==='Enter') go(); });
document.getElementById('p1-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
show();
})();
wireReadBtn('p1');
}
/* ===== §2 Решение прямоугольного треугольника ===== */
function buildP2(){
const box = document.getElementById('p2-body');
let html = '';
html += makeCard('theory', 'Что значит «решить треугольник»', '2.1', `
<p><b>Решить треугольник</b> — найти все его неизвестные элементы (стороны и углы) по заданным.</p>
<p>В прямоугольном треугольнике один угол всегда равен $90^\\circ$. Поэтому, чтобы найти все остальные элементы, достаточно знать <b>любые два из них</b> (кроме случая «два угла» — там стороны определяются с точностью до подобия):</p>
<ul style="padding-left:22px;line-height:1.95">
<li>две стороны (два катета; катет и гипотенузу), <b>или</b></li>
<li>одну сторону и один из острых углов.</li>
</ul>
<p>Обозначения: пусть $A, B, C$ — вершины (угол $C = 90^\\circ$), $a = BC$ — катет, лежащий напротив $A$, $b = AC$ — катет, лежащий напротив $B$, $c = AB$ — гипотенуза. Тогда $A + B = 90^\\circ$.</p>`);
html += makeCard('rule', 'Четыре случая решения', '2.2', `
<p>В каждом случае схема одна: применяем теорему Пифагора или тригонометрическую функцию.</p>
<p><b>Случай 1.</b> Даны два катета $a, b$.<br>
$c = \\sqrt{a^2 + b^2}$; &nbsp; $\\tan A = \\dfrac{a}{b} \\Rightarrow A$; &nbsp; $B = 90^\\circ - A$.</p>
<p><b>Случай 2.</b> Даны катет $a$ и гипотенуза $c$.<br>
$b = \\sqrt{c^2 - a^2}$; &nbsp; $\\sin A = \\dfrac{a}{c} \\Rightarrow A$; &nbsp; $B = 90^\\circ - A$.</p>
<p><b>Случай 3.</b> Даны катет $a$ и противолежащий угол $A$.<br>
$c = \\dfrac{a}{\\sin A}$; &nbsp; $b = \\dfrac{a}{\\tan A}$; &nbsp; $B = 90^\\circ - A$.</p>
<p><b>Случай 4.</b> Даны гипотенуза $c$ и угол $A$.<br>
$a = c \\sin A$; &nbsp; $b = c \\cos A$; &nbsp; $B = 90^\\circ - A$.</p>
<details class="spoiler"><summary>Совет по выбору формулы</summary><div class="spoiler-body">
<ul style="padding-left:18px;line-height:1.85;margin:0">
<li>Есть <b>обе</b> стороны без угла — теорема Пифагора + $\\tan$.</li>
<li>Есть <b>гипотенуза и катет</b> — Пифагор для второго катета, $\\sin$ для угла.</li>
<li>Есть <b>сторона и угол</b> — выбирай функцию так, чтобы данный отрезок встал в числитель или знаменатель: $\\sin$ для пары «противолежащий–гипотенуза», $\\cos$ — «прилежащий–гипотенуза», $\\tan$ — два катета.</li>
</ul>
</div></details>`);
html += makeCard('example', 'Пример: даны два катета', '2.3', `
<p><b>Дано:</b> $a = 6$, $b = 8$. <b>Найти:</b> $c$, $A$, $B$.</p>
<p><b>1.</b> $c = \\sqrt{a^2 + b^2} = \\sqrt{36 + 64} = \\sqrt{100} = 10$.</p>
<p><b>2.</b> $\\tan A = \\dfrac{a}{b} = \\dfrac{6}{8} = 0{,}75 \\Rightarrow A \\approx 36{,}87^\\circ$.</p>
<p><b>3.</b> $B = 90^\\circ - A \\approx 53{,}13^\\circ$.</p>
<p><b>Ответ:</b> $a = 6$, $b = 8$, $c = 10$, $A \\approx 36{,}87^\\circ$, $B \\approx 53{,}13^\\circ$.</p>
<details class="spoiler"><summary>Проверка через $\\sin$</summary><div class="spoiler-body">
$\\sin A = a / c = 6/10 = 0{,}6 \\Rightarrow A = \\arcsin 0{,}6 \\approx 36{,}87^\\circ$ — совпадает.
</div></details>`);
/* IV1 — Универсальный решатель */
html += `<div class="wg" id="p2-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Универсальный решатель</div></div>
<div class="wg-help">Выбери тип задачи и введи данные — найдём все остальные элементы с подстановкой формул.</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px" id="p2-iv1-cases">
<button class="btn primary" data-c="1">Случай 1: $a, b$</button>
<button class="btn" data-c="2">Случай 2: $a, c$</button>
<button class="btn" data-c="3">Случай 3: $a, A$</button>
<button class="btn" data-c="4">Случай 4: $c, A$</button>
</div>
<div id="p2-iv1-inputs" style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:10px"></div>
<div style="text-align:center;margin-bottom:10px"><button class="btn primary" id="p2-iv1-go">Решить</button></div>
<div id="p2-iv1-out" style="padding:12px 14px;background:var(--card);border-radius:9px;font-size:.96rem;line-height:1.75;min-height:50px"></div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto;margin-top:10px">
<svg id="p2-iv1-svg" viewBox="0 0 400 300" style="width:100%;min-width:320px;height:auto;display:block"></svg>
</div>
<div class="feedback" id="p2-iv1-fb"></div>
</div>`;
/* IV2 — DnD сортер «Подбор формулы» */
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">Распредели 6 формул по четырём случаям решения. Тапни карточку, потом — нужный ящик (или перетащи).</div>
<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v20"/><path d="M5 12h14"/></svg> 6 формул — 4 ящика</div>
<div id="p2-iv2-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="c1">Случай 1 ($a, b$)</h5><div class="drop-items" data-cat="c1"></div></div>
<div class="drop-box"><h5 data-cat="c2">Случай 2 ($a, c$)</h5><div class="drop-items" data-cat="c2"></div></div>
<div class="drop-box"><h5 data-cat="c3">Случай 3 ($a, A$)</h5><div class="drop-items" data-cat="c3"></div></div>
<div class="drop-box"><h5 data-cat="c4">Случай 4 ($c, A$)</h5><div class="drop-items" data-cat="c4"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p2-iv2-check">Проверить</button><button class="btn" id="p2-iv2-reset">Сначала</button></div>
<div class="feedback" id="p2-iv2-fb"></div>
</div>`;
/* IV3 — Какой угол найдём? */
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">Дано — нужно выбрать тригонометрическую функцию, через которую быстрее всего найти угол $A$.</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(--pri-soft);border-radius:10px;font-size:1.05rem;text-align:center;margin-bottom:10px"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:8px">
<button class="btn primary" data-ans="sin" id="p2-iv3-sin">через $\\sin$</button>
<button class="btn primary" data-ans="cos" id="p2-iv3-cos">через $\\cos$</button>
<button class="btn primary" data-ans="tan" id="p2-iv3-tan">через $\\tan$</button>
</div>
<div class="feedback" id="p2-iv3-fb"></div>
</div>`;
/* IV4 — Тренажёр решения */
html += `<div class="wg" id="p2-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр решения треугольника</div></div>
<div class="wg-help">Реши задачу и введи число (округли до 1 знака, если получается дробное).</div>
<div class="score-display"><span>Задача <b id="p2-iv4-i">1</b> / 5</span><span>Очки: <b id="p2-iv4-s">0</b> / 5</span></div>
<div id="p2-iv4-q" style="padding:14px;background:var(--pri-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">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p2-iv4-ans" class="tinp" style="width:110px;text-align:center" step="0.1">
<button class="btn primary" id="p2-iv4-go">Проверить</button>
<button class="btn" id="p2-iv4-start">Заново</button>
</div>
<div class="feedback" id="p2-iv4-fb"></div>
</div>`;
html += secNav('p1', 'p3');
html += readButton('p2');
box.innerHTML = html;
renderMath(box);
/* IV1 — универсальный решатель */
(function(){
let curCase = 1;
const inputsBox = document.getElementById('p2-iv1-inputs');
const out = document.getElementById('p2-iv1-out');
const fb = document.getElementById('p2-iv1-fb');
const svg = document.getElementById('p2-iv1-svg');
const caseBtns = document.querySelectorAll('#p2-iv1-cases button');
let solved = 0;
function setInputs(){
let h = '';
if(curCase === 1){ h = '$a$ = <input type="number" id="p2-iv1-i1" class="tinp" style="width:80px;text-align:center" value="6">&nbsp; $b$ = <input type="number" id="p2-iv1-i2" class="tinp" style="width:80px;text-align:center" value="8">'; }
else if(curCase === 2){ h = '$a$ = <input type="number" id="p2-iv1-i1" class="tinp" style="width:80px;text-align:center" value="6">&nbsp; $c$ = <input type="number" id="p2-iv1-i2" class="tinp" style="width:80px;text-align:center" value="10">'; }
else if(curCase === 3){ h = '$a$ = <input type="number" id="p2-iv1-i1" class="tinp" style="width:80px;text-align:center" value="5">&nbsp; $A$ = <input type="number" id="p2-iv1-i2" class="tinp" style="width:80px;text-align:center" value="30">°'; }
else { h = '$c$ = <input type="number" id="p2-iv1-i1" class="tinp" style="width:80px;text-align:center" value="10">&nbsp; $A$ = <input type="number" id="p2-iv1-i2" class="tinp" style="width:80px;text-align:center" value="30">°'; }
inputsBox.innerHTML = h;
renderMath(inputsBox);
}
function drawTri(a, b, c, ADeg){
const aR = deg2rad(ADeg);
// нарисуем по c и углу A: BC=a (вертикаль), AC=b (горизонталь), угол A внизу слева
const scale = Math.min(200/Math.max(b,1), 160/Math.max(a,1), 22);
const Bx = 80, By = 250; // вершина B (прямой угол) внизу слева
const Ax = Bx, Ay = By; // ...A совпадёт ниже
// ставим: B (низ-лев, прямой угол), C (низ-прав, угол A?). Свяжем по стандарту: C=90°.
// На этот раз: C = (low-right) — прямой угол; A = (low-left); B = (top-right).
// a = BC (противолежащий A) — вертикальный; b = AC — горизонтальный.
const Cx = Bx + b*scale, Cy = By;
const A2 = {x: Bx, y: By};
const C2 = {x: Cx, y: Cy};
const B2 = {x: Cx, y: Cy - a*scale};
const uCA = unitVec(C2, A2);
const uCB = unitVec(C2, B2);
const uAB = unitVec(A2, B2);
const uAC = unitVec(A2, C2);
let s = '';
s += '<polygon points="'+A2.x+','+A2.y+' '+B2.x+','+B2.y+' '+C2.x+','+C2.y+'" fill="rgba(217,119,6,.08)" stroke="#b45309" stroke-width="2.2" stroke-linejoin="round"/>';
// прямой угол в C
s += '<polyline points="'+rightAngleMark(C2, uCA, uCB, 12)+'" fill="none" stroke="#0f172a" stroke-width="1.8"/>';
// угол A (от AC к AB)
s += '<path d="'+angleArcAuto(A2, uAC, uAB, 28)+'" fill="none" stroke="#dc2626" stroke-width="2"/>';
// вершины
s += '<circle cx="'+A2.x+'" cy="'+A2.y+'" r="4" fill="#0f172a"/>';
s += '<circle cx="'+B2.x+'" cy="'+B2.y+'" r="4" fill="#0f172a"/>';
s += '<circle cx="'+C2.x+'" cy="'+C2.y+'" r="4" fill="#0f172a"/>';
s += '<text x="'+(A2.x-12)+'" y="'+(A2.y+18)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="16" font-weight="700">A</text>';
s += '<text x="'+(B2.x+12)+'" y="'+(B2.y-4)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="16" font-weight="700">B</text>';
s += '<text x="'+(C2.x+12)+'" y="'+(C2.y+18)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="16" font-weight="700">C</text>';
// подписи сторон
s += '<text x="'+((A2.x+C2.x)/2)+'" y="'+(C2.y+22)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">b='+b.toFixed(2)+'</text>';
s += '<text x="'+(C2.x+22)+'" y="'+((B2.y+C2.y)/2)+'" text-anchor="start" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">a='+a.toFixed(2)+'</text>';
const midAB = {x:(A2.x+B2.x)/2, y:(A2.y+B2.y)/2};
const nAB = {x:-(B2.y-A2.y), y:(B2.x-A2.x)};
const nL = Math.sqrt(nAB.x*nAB.x+nAB.y*nAB.y)||1;
const labP = {x:midAB.x - 18*nAB.x/nL, y:midAB.y - 18*nAB.y/nL};
s += '<text x="'+labP.x+'" y="'+labP.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">c='+c.toFixed(2)+'</text>';
s += '<text x="'+(A2.x+24)+'" y="'+(A2.y-10)+'" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#dc2626">A='+ADeg.toFixed(1)+'°</text>';
svg.innerHTML = s;
}
function solve(){
const v1 = parseFloat(document.getElementById('p2-iv1-i1').value);
const v2 = parseFloat(document.getElementById('p2-iv1-i2').value);
if(isNaN(v1) || isNaN(v2)){ feedback(fb, false, '&#10007; Введи оба числа.'); return; }
let a, b, c, ADeg, BDeg, html;
if(curCase === 1){
a = v1; b = v2;
if(a<=0||b<=0){ feedback(fb,false,'&#10007; Стороны должны быть положительны.'); return; }
c = Math.sqrt(a*a+b*b);
ADeg = Math.atan2(a,b) * 180 / Math.PI;
BDeg = 90 - ADeg;
html = '<b>$c = \\sqrt{a^2+b^2} = \\sqrt{'+(a*a)+'+'+(b*b)+'} = \\sqrt{'+(a*a+b*b)+'} \\approx '+c.toFixed(2)+'$</b><br>'
+ '$\\tan A = a/b = '+a+'/'+b+' \\approx '+(a/b).toFixed(3)+' \\Rightarrow A \\approx '+ADeg.toFixed(2)+'^\\circ$<br>'
+ '$B = 90^\\circ - A \\approx '+BDeg.toFixed(2)+'^\\circ$';
} else if(curCase === 2){
a = v1; c = v2;
if(a<=0||c<=0||a>=c){ feedback(fb,false,'&#10007; Нужно $0 < a < c$.'); return; }
b = Math.sqrt(c*c-a*a);
ADeg = Math.asin(a/c) * 180 / Math.PI;
BDeg = 90 - ADeg;
html = '<b>$b = \\sqrt{c^2-a^2} = \\sqrt{'+(c*c)+'-'+(a*a)+'} = \\sqrt{'+(c*c-a*a)+'} \\approx '+b.toFixed(2)+'$</b><br>'
+ '$\\sin A = a/c = '+a+'/'+c+' \\approx '+(a/c).toFixed(3)+' \\Rightarrow A \\approx '+ADeg.toFixed(2)+'^\\circ$<br>'
+ '$B = 90^\\circ - A \\approx '+BDeg.toFixed(2)+'^\\circ$';
} else if(curCase === 3){
a = v1; ADeg = v2;
if(a<=0||ADeg<=0||ADeg>=90){ feedback(fb,false,'&#10007; Нужно $a > 0$ и $0 < A < 90°$.'); return; }
const r = deg2rad(ADeg);
c = a / Math.sin(r);
b = a / Math.tan(r);
BDeg = 90 - ADeg;
html = '<b>$c = a/\\sin A = '+a+'/\\sin '+ADeg+'^\\circ \\approx '+c.toFixed(2)+'$</b><br>'
+ '<b>$b = a/\\tan A = '+a+'/\\tan '+ADeg+'^\\circ \\approx '+b.toFixed(2)+'$</b><br>'
+ '$B = 90^\\circ - A = '+BDeg.toFixed(2)+'^\\circ$';
} else {
c = v1; ADeg = v2;
if(c<=0||ADeg<=0||ADeg>=90){ feedback(fb,false,'&#10007; Нужно $c > 0$ и $0 < A < 90°$.'); return; }
const r = deg2rad(ADeg);
a = c * Math.sin(r);
b = c * Math.cos(r);
BDeg = 90 - ADeg;
html = '<b>$a = c \\sin A = '+c+' \\cdot \\sin '+ADeg+'^\\circ \\approx '+a.toFixed(2)+'$</b><br>'
+ '<b>$b = c \\cos A = '+c+' \\cdot \\cos '+ADeg+'^\\circ \\approx '+b.toFixed(2)+'$</b><br>'
+ '$B = 90^\\circ - A = '+BDeg.toFixed(2)+'^\\circ$';
}
out.innerHTML = html;
renderMath(out);
drawTri(a, b, c, ADeg);
feedback(fb, true, '&#10003; Треугольник решён.');
solved++;
if(solved === 1){ addXp(10,'p2-iv1'); bumpProgress('p2', 15); }
}
caseBtns.forEach(b=>{
b.addEventListener('click', ()=>{
curCase = +b.dataset.c;
caseBtns.forEach(x=>{ x.classList.remove('primary'); x.classList.add('btn'); });
b.classList.remove('btn'); b.classList.add('btn','primary');
setInputs();
out.innerHTML = ''; svg.innerHTML='';
fb.style.display = 'none';
});
});
document.getElementById('p2-iv1-go').addEventListener('click', solve);
setInputs();
solve();
})();
/* IV2 — DnD сортер «Подбор формулы» */
(function(){
const items = [
{ id:'f1', cat:'c1', html:'$c = \\sqrt{a^2 + b^2}$' },
{ id:'f2', cat:'c1', html:'$\\tan A = a / b$' },
{ id:'f3', cat:'c2', html:'$b = \\sqrt{c^2 - a^2}$' },
{ id:'f4', cat:'c3', html:'$c = a / \\sin A$' },
{ id:'f5', cat:'c4', html:'$a = c \\sin A$' },
{ id:'f6', cat:'c4', html:'$b = c \\cos A$' },
];
const sorter = setupSorter({
poolId:'p2-iv2-pool',
scopeSelector:'#p2-iv2',
items: items,
cats:['c1','c2','c3','c4'],
columnLayout:false,
});
document.getElementById('p2-iv2-check').addEventListener('click', ()=>{
const fb = document.getElementById('p2-iv2-fb');
const placedCount = items.filter(it => sorter.placed[it.id]).length;
const correct = items.filter(it => sorter.placed[it.id] === it.cat).length;
if(placedCount < items.length){ feedback(fb, false, '&#10007; Размести все 6 формул.'); return; }
if(correct === items.length){ feedback(fb, true, '&#10003; Все 6 на месте! +10 XP'); addXp(10,'p2-iv2'); bumpProgress('p2', 15); }
else feedback(fb, false, '&#10007; Правильно ' + correct + ' из 6. Попробуй ещё.');
});
document.getElementById('p2-iv2-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p2-iv2-fb').style.display = 'none'; });
})();
/* IV3 — Какой функцией найдём угол */
(function(){
const Q = [
{ expr:'Даны $a$ (противолежащий $A$) и гипотенуза $c$.', ans:'sin', why:'$\\sin A = a/c$' },
{ expr:'Даны $b$ (прилежащий $A$) и гипотенуза $c$.', ans:'cos', why:'$\\cos A = b/c$' },
{ expr:'Даны оба катета $a$ и $b$.', ans:'tan', why:'$\\tan A = a/b$' },
{ expr:'Известно произведение $c \\sin A$ и значение $c$. Найти $A$.', ans:'sin', why:'$\\sin A = (c \\sin A)/c$' },
{ expr:'Даны два катета (противолежащий и прилежащий).', ans:'tan', why:'$\\tan A$ — отношение катетов' },
{ expr:'Даны противолежащий катет и гипотенуза.', ans:'sin', why:'$\\sin A = \\text{противолежащий}/\\text{гипотенуза}$' },
];
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 >= Q.length - 1){ addXp(8,'p2-iv3'); bumpProgress('p2', 15); }
return;
}
document.getElementById('p2-iv3-i').textContent = (i+1);
document.getElementById('p2-iv3-s').textContent = score;
document.getElementById('p2-iv3-q').innerHTML = Q[i].expr;
renderMath(document.getElementById('p2-iv3-q'));
document.getElementById('p2-iv3-fb').style.display = 'none';
}
function answer(a){
if(i >= Q.length) return;
const fb = document.getElementById('p2-iv3-fb');
if(a === Q[i].ans){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].why+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Нет. Нужно: '+Q[i].why+'. Дальше ▶');
document.getElementById('p2-iv3-s').textContent = score;
i++;
setTimeout(show, 1100);
}
['sin','cos','tan'].forEach(k=>{
const b = document.getElementById('p2-iv3-'+k); if(b) b.addEventListener('click', ()=>answer(k));
});
show();
})();
/* IV4 — Тренажёр решения */
(function(){
const Q = [
{ q:'Катеты 3 и 4. Чему равна гипотенуза?', ans:5, tol:0.1, hint:'$c = \\sqrt{9+16} = 5$' },
{ q:'Катет $5$, гипотенуза $13$. Найди второй катет.', ans:12, tol:0.1, hint:'$b = \\sqrt{169-25} = \\sqrt{144} = 12$' },
{ q:'В прямоуг. треуг. $c = 20$, $A = 30^\\circ$. Найди катет $a$ (противолежащий $A$).', ans:10, tol:0.1, hint:'$a = c \\sin A = 20 \\cdot 0{,}5 = 10$' },
{ q:'$c = 10$, $a = 6$. Найди угол $A$ в градусах (округли до целых).', ans:37, tol:1, hint:'$\\sin A = 0{,}6 \\Rightarrow A \\approx 36{,}87^\\circ \\approx 37^\\circ$' },
{ q:'Катет $a = 3$, угол $A = 45^\\circ$. Чему равна гипотенуза?', ans:4.2, tol:0.2, hint:'$c = a/\\sin 45^\\circ = 3\\sqrt{2} \\approx 4{,}24$' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p2-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p2-iv4'); bumpProgress('p2', 25); }
else if(score >= 3){ addXp(8,'p2-iv4'); bumpProgress('p2', 15); }
return;
}
document.getElementById('p2-iv4-i').textContent = (i+1);
document.getElementById('p2-iv4-s').textContent = score;
document.getElementById('p2-iv4-q').innerHTML = Q[i].q;
document.getElementById('p2-iv4-ans').value = '';
renderMath(document.getElementById('p2-iv4-q'));
document.getElementById('p2-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p2-iv4-fb');
const ans = parseFloat(document.getElementById('p2-iv4-ans').value);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: '+Q[i].ans+' ('+Q[i].hint+'). Дальше ▶');
document.getElementById('p2-iv4-s').textContent = score;
i++;
setTimeout(show, 1300);
}
document.getElementById('p2-iv4-go').addEventListener('click', go);
document.getElementById('p2-iv4-ans').addEventListener('keydown', e=>{ if(e.key==='Enter') go(); });
document.getElementById('p2-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
show();
})();
wireReadBtn('p2');
}
/* ===== §3 Тригонометрические формулы ===== */
function buildP3(){
const box = document.getElementById('p3-body');
let html = '';
html += makeCard('theory', 'Основное тригонометрическое тождество', '3.1', `
<p>Возьмём прямоугольный треугольник с острым углом $\\alpha$, противолежащим катетом $a$, прилежащим катетом $b$ и гипотенузой $c$. По определению $\\sin \\alpha = a/c$, $\\cos \\alpha = b/c$.</p>
<p>По теореме Пифагора $a^2 + b^2 = c^2$. Поделим обе части на $c^2$:</p>
<p style="text-align:center">$\\dfrac{a^2}{c^2} + \\dfrac{b^2}{c^2} = 1 \\;\\Rightarrow\\; \\left(\\dfrac{a}{c}\\right)^2 + \\left(\\dfrac{b}{c}\\right)^2 = 1.$</p>
<p style="text-align:center;font-size:1.1rem"><b>$\\sin^2 \\alpha + \\cos^2 \\alpha = 1$</b></p>
<p><b>Следствия</b> (для острого $\\alpha$):</p>
<ul style="padding-left:22px;line-height:1.95">
<li>$\\sin \\alpha = \\sqrt{1 - \\cos^2 \\alpha}$</li>
<li>$\\cos \\alpha = \\sqrt{1 - \\sin^2 \\alpha}$</li>
</ul>
<details class="spoiler"><summary>Зачем это нужно?</summary><div class="spoiler-body">
Если известно одно из значений ($\\sin \\alpha$ или $\\cos \\alpha$), второе мгновенно вычисляется без чертежа треугольника. Это первое настоящее тригонометрическое <b>тождество</b> — равенство, верное для любого допустимого угла.
</div></details>`);
html += makeCard('rule', 'Связь tg и ctg с sin и cos', '3.2', `
<p>Из определений $\\tan \\alpha = \\dfrac{a}{b}$ и $\\sin \\alpha / \\cos \\alpha = \\dfrac{a/c}{b/c} = \\dfrac{a}{b}$ получаем:</p>
<ul style="padding-left:22px;line-height:1.95">
<li>$\\tan \\alpha = \\dfrac{\\sin \\alpha}{\\cos \\alpha}$</li>
<li>$\\cot \\alpha = \\dfrac{\\cos \\alpha}{\\sin \\alpha}$</li>
<li>$\\tan \\alpha \\cdot \\cot \\alpha = 1$ &nbsp;(значит, $\\cot \\alpha = 1/\\tan \\alpha$)</li>
</ul>
<p>Поделим основное тождество $\\sin^2 \\alpha + \\cos^2 \\alpha = 1$ на $\\cos^2 \\alpha$:</p>
<p style="text-align:center">$\\dfrac{\\sin^2 \\alpha}{\\cos^2 \\alpha} + 1 = \\dfrac{1}{\\cos^2 \\alpha} \\;\\Rightarrow\\; \\boxed{\\,1 + \\tan^2 \\alpha = \\dfrac{1}{\\cos^2 \\alpha}\\,}$</p>
<p>Аналогично, поделив на $\\sin^2 \\alpha$:</p>
<p style="text-align:center">$\\boxed{\\,1 + \\cot^2 \\alpha = \\dfrac{1}{\\sin^2 \\alpha}\\,}$</p>`);
html += makeCard('example', 'Откуда берутся 30°, 45°, 60°', '3.3', `
<p><b>1) Треугольник 45°-45°-90°</b> — равнобедренный прямоугольный. Возьмём оба катета равными $1$. Тогда гипотенуза $\\sqrt{1^2 + 1^2} = \\sqrt{2}$. Значит:</p>
<p style="text-align:center">$\\sin 45^\\circ = \\cos 45^\\circ = \\dfrac{1}{\\sqrt{2}} = \\dfrac{\\sqrt{2}}{2},\\quad \\tan 45^\\circ = \\cot 45^\\circ = 1.$</p>
<p><b>2) Треугольник 30°-60°-90°</b> — половина равностороннего. У равностороннего треугольника со стороной $2$ все углы $60^\\circ$. Высота делит его на два прямоугольных треугольника с катетами $1$ (половина основания) и $\\sqrt{2^2 - 1^2} = \\sqrt{3}$, гипотенузой $2$. Углы: $30^\\circ$ (при вершине) и $60^\\circ$ (у основания). Получаем:</p>
<p style="text-align:center">$\\sin 30^\\circ = \\dfrac{1}{2},\\; \\cos 30^\\circ = \\dfrac{\\sqrt{3}}{2},\\; \\tan 30^\\circ = \\dfrac{1}{\\sqrt{3}}.$</p>
<p style="text-align:center">$\\sin 60^\\circ = \\dfrac{\\sqrt{3}}{2},\\; \\cos 60^\\circ = \\dfrac{1}{2},\\; \\tan 60^\\circ = \\sqrt{3}.$</p>
<div style="overflow-x:auto;margin:10px 0">
<table style="width:100%;border-collapse:collapse;font-size:.95rem;background:var(--card);border:1.5px solid var(--border);border-radius:9px;overflow:hidden">
<thead><tr style="background:var(--pri-soft)">
<th style="padding:8px;border-bottom:1px solid var(--border);text-align:left">Угол</th>
<th style="padding:8px;border-bottom:1px solid var(--border)">$\\sin$</th>
<th style="padding:8px;border-bottom:1px solid var(--border)">$\\cos$</th>
<th style="padding:8px;border-bottom:1px solid var(--border)">$\\tan$</th>
<th style="padding:8px;border-bottom:1px solid var(--border)">$\\cot$</th>
</tr></thead>
<tbody>
<tr><td style="padding:8px;border-bottom:1px solid var(--border)"><b>30°</b></td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{1}{2}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{\\sqrt{3}}{2}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{1}{\\sqrt{3}}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\sqrt{3}$</td></tr>
<tr><td style="padding:8px;border-bottom:1px solid var(--border)"><b>45°</b></td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{\\sqrt{2}}{2}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{\\sqrt{2}}{2}$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$1$</td><td style="padding:8px;border-bottom:1px solid var(--border);text-align:center">$1$</td></tr>
<tr><td style="padding:8px"><b>60°</b></td><td style="padding:8px;text-align:center">$\\tfrac{\\sqrt{3}}{2}$</td><td style="padding:8px;text-align:center">$\\tfrac{1}{2}$</td><td style="padding:8px;text-align:center">$\\sqrt{3}$</td><td style="padding:8px;text-align:center">$\\tfrac{1}{\\sqrt{3}}$</td></tr>
</tbody>
</table>
</div>`);
/* IV1 — Три эталонных треугольника */
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">Слева — треугольник $30^\\circ\\text{-}60^\\circ\\text{-}90^\\circ$ (катеты $1$ и $\\sqrt{3}$, гипотенуза $2$). Справа — равнобедренный $45^\\circ\\text{-}45^\\circ\\text{-}90^\\circ$ (катеты $1$, гипотенуза $\\sqrt{2}$).</div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p3-iv1-svg" viewBox="0 0 600 220" style="width:100%;min-width:420px;height:auto;display:block"></svg>
</div>
<div style="overflow-x:auto;margin-top:10px">
<table style="width:100%;border-collapse:collapse;font-size:.92rem;background:var(--card);border:1.5px solid var(--border);border-radius:9px;overflow:hidden">
<thead><tr style="background:var(--pri-soft)">
<th style="padding:6px;border-bottom:1px solid var(--border)">Угол</th>
<th style="padding:6px;border-bottom:1px solid var(--border)">$\\sin$</th>
<th style="padding:6px;border-bottom:1px solid var(--border)">$\\cos$</th>
<th style="padding:6px;border-bottom:1px solid var(--border)">$\\tan$</th>
<th style="padding:6px;border-bottom:1px solid var(--border)">$\\cot$</th>
</tr></thead>
<tbody>
<tr><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center"><b>30°</b></td><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{1}{2}$</td><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{\\sqrt{3}}{2}$</td><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{1}{\\sqrt{3}}$</td><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center">$\\sqrt{3}$</td></tr>
<tr><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center"><b>45°</b></td><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{\\sqrt{2}}{2}$</td><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center">$\\tfrac{\\sqrt{2}}{2}$</td><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center">$1$</td><td style="padding:6px;border-bottom:1px solid var(--border);text-align:center">$1$</td></tr>
<tr><td style="padding:6px;text-align:center"><b>60°</b></td><td style="padding:6px;text-align:center">$\\tfrac{\\sqrt{3}}{2}$</td><td style="padding:6px;text-align:center">$\\tfrac{1}{2}$</td><td style="padding:6px;text-align:center">$\\sqrt{3}$</td><td style="padding:6px;text-align:center">$\\tfrac{1}{\\sqrt{3}}$</td></tr>
</tbody>
</table>
</div>
</div>`;
/* IV2 — Калькулятор тождеств */
html += `<div class="wg" id="p3-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор тождеств</div></div>
<div class="wg-help">Введи $\\sin \\alpha$ (значение от $0{,}01$ до $0{,}99$). Получишь $\\cos \\alpha$, $\\tan \\alpha$, $\\cot \\alpha$ и сам угол $\\alpha$ в градусах.</div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
<span style="font-family:'JetBrains Mono',monospace">$\\sin \\alpha$ =</span>
<input type="range" id="p3-iv2-s" min="0.01" max="0.99" step="0.01" value="0.6" style="width:200px;accent-color:var(--pri)">
<input type="number" id="p3-iv2-sn" class="tinp" style="width:90px;text-align:center" value="0.6" min="0.01" max="0.99" step="0.01">
<button class="btn primary" id="p3-iv2-go">Вычислить</button>
</div>
<div id="p3-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;text-align:center;font-size:1rem;line-height:1.85;min-height:50px"></div>
<div class="feedback" id="p3-iv2-fb"></div>
</div>`;
/* IV3 — Quickfire «Какое значение?» */
html += `<div class="wg" id="p3-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Какое значение?</div></div>
<div class="wg-help">Дано тригонометрическое выражение — выбери его точное значение из 4 вариантов.</div>
<div class="score-display"><span>Задача <b id="p3-iv3-i">1</b> / 8</span><span>Очки: <b id="p3-iv3-s">0</b> / 8</span></div>
<div id="p3-iv3-q" style="padding:14px;background:var(--pri-soft);border-radius:10px;font-size:1.2rem;text-align:center;margin-bottom:10px"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:8px">
<button class="btn primary" data-ans="half" id="p3-iv3-half">$\\tfrac{1}{2}$</button>
<button class="btn primary" data-ans="s2" id="p3-iv3-s2">$\\tfrac{\\sqrt{2}}{2}$</button>
<button class="btn primary" data-ans="s3" id="p3-iv3-s3">$\\tfrac{\\sqrt{3}}{2}$</button>
<button class="btn primary" data-ans="one" id="p3-iv3-one">$1$</button>
</div>
<div class="feedback" id="p3-iv3-fb"></div>
</div>`;
/* IV4 — Применение тождеств */
html += `<div class="wg" id="p3-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Применение тождеств</div></div>
<div class="wg-help">Реши задачу и введи число (целое или десятичную дробь до 3 знаков).</div>
<div class="score-display"><span>Задача <b id="p3-iv4-i">1</b> / 6</span><span>Очки: <b id="p3-iv4-s">0</b> / 6</span></div>
<div id="p3-iv4-q" style="padding:14px;background:var(--pri-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">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p3-iv4-ans" class="tinp" style="width:110px;text-align:center" step="0.001">
<button class="btn primary" id="p3-iv4-go">Проверить</button>
<button class="btn" id="p3-iv4-start">Заново</button>
</div>
<div class="feedback" id="p3-iv4-fb"></div>
</div>`;
html += secNav('p2', 'p4');
html += readButton('p3');
box.innerHTML = html;
renderMath(box);
/* IV1 — рисуем три эталонных треугольника */
(function(){
const svg = document.getElementById('p3-iv1-svg');
if(!svg) return;
let s = '';
// Треугольник 30-60-90: катеты 1 и sqrt(3), гипотенуза 2. Используем масштаб 60 px = 1 ед.
// Левый: BC = sqrt(3) (горизонт., прилежащий 30°), AB = 1 (вертикальный, противолежащий 30°)
// Вершины: A слева внизу, B слева вверху (прямой угол), C справа внизу.
// Угол 30° при C, угол 60° при A.
(function(){
const u = 60;
const Ax = 40, Ay = 180;
const A = {x: Ax, y: Ay};
const B = {x: Ax, y: Ay - u}; // катет AB = 1
const C = {x: Ax + Math.sqrt(3)*u, y: Ay}; // катет BC = sqrt(3)
const uBA = unitVec(B, A);
const uBC = unitVec(B, C);
const uCA = unitVec(C, A);
const uCB = unitVec(C, B);
const uAB = unitVec(A, B);
const uAC = unitVec(A, C);
s += '<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="rgba(217,119,6,.08)" stroke="#b45309" stroke-width="2" stroke-linejoin="round"/>';
s += '<polyline points="'+rightAngleMark(B, uBA, uBC, 10)+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>';
s += '<path d="'+angleArcAuto(C, uCA, uCB, 26)+'" fill="none" stroke="#dc2626" stroke-width="2"/>';
s += '<path d="'+angleArcAuto(A, uAB, uAC, 22)+'" fill="none" stroke="#0ea5e9" stroke-width="2"/>';
// подписи углов
s += '<text x="'+(C.x-30)+'" y="'+(C.y-8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#dc2626">30°</text>';
s += '<text x="'+(A.x+18)+'" y="'+(A.y-8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0ea5e9">60°</text>';
// вершины и подписи
['A','B','C'].forEach((nm,i)=>{ const P=[A,B,C][i]; s+='<circle cx="'+P.x+'" cy="'+P.y+'" r="3.5" fill="#0f172a"/>'; });
s += '<text x="'+(A.x-10)+'" y="'+(A.y+15)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700">A</text>';
s += '<text x="'+(B.x-10)+'" y="'+(B.y-4)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700">B</text>';
s += '<text x="'+(C.x+10)+'" y="'+(C.y+15)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="14" font-weight="700">C</text>';
// длины сторон
s += '<text x="'+(B.x-22)+'" y="'+((A.y+B.y)/2+4)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">1</text>';
s += '<text x="'+((B.x+C.x)/2)+'" y="'+(A.y+22)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">√3</text>';
const midAC={x:(A.x+C.x)/2, y:(A.y+C.y)/2};
const nAC={x:-(C.y-A.y), y:(C.x-A.x)};
const nL=Math.sqrt(nAC.x*nAC.x+nAC.y*nAC.y)||1;
const labP={x: midAC.x + 16*nAC.x/nL, y: midAC.y + 16*nAC.y/nL};
s += '<text x="'+labP.x+'" y="'+labP.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">2</text>';
// заголовок
s += '<text x="'+((A.x+C.x)/2)+'" y="22" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="13" font-weight="800" fill="#92400e">30\xb0-60\xb0-90\xb0</text>';
})();
// Треугольник 45-45-90: оба катета 1, гипотенуза sqrt(2). Масштаб тот же.
(function(){
const u = 90; // покрупнее
const Ax = 360, Ay = 180;
const A = {x: Ax, y: Ay};
const B = {x: Ax, y: Ay - u};
const C = {x: Ax + u, y: Ay};
const uBA = unitVec(B, A);
const uBC = unitVec(B, C);
const uCA = unitVec(C, A);
const uCB = unitVec(C, B);
const uAB = unitVec(A, B);
const uAC = unitVec(A, C);
s += '<polygon points="'+A.x+','+A.y+' '+B.x+','+B.y+' '+C.x+','+C.y+'" fill="rgba(217,119,6,.08)" stroke="#b45309" stroke-width="2" stroke-linejoin="round"/>';
s += '<polyline points="'+rightAngleMark(B, uBA, uBC, 10)+'" fill="none" stroke="#0f172a" stroke-width="1.6"/>';
s += '<path d="'+angleArcAuto(C, uCA, uCB, 28)+'" fill="none" stroke="#dc2626" stroke-width="2"/>';
s += '<path d="'+angleArcAuto(A, uAB, uAC, 26)+'" fill="none" stroke="#0ea5e9" stroke-width="2"/>';
s += '<text x="'+(C.x-30)+'" y="'+(C.y-8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#dc2626">45°</text>';
s += '<text x="'+(A.x+22)+'" y="'+(A.y-8)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0ea5e9">45°</text>';
['A','B','C'].forEach((nm,i)=>{ const P=[A,B,C][i]; s+='<circle cx="'+P.x+'" cy="'+P.y+'" r="3.5" fill="#0f172a"/>'; });
s += '<text x="'+(A.x-10)+'" y="'+(A.y+15)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700">A</text>';
s += '<text x="'+(B.x-10)+'" y="'+(B.y-4)+'" text-anchor="end" font-family="Inter,sans-serif" font-size="14" font-weight="700">B</text>';
s += '<text x="'+(C.x+10)+'" y="'+(C.y+15)+'" text-anchor="start" font-family="Inter,sans-serif" font-size="14" font-weight="700">C</text>';
s += '<text x="'+(B.x-22)+'" y="'+((A.y+B.y)/2+4)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">1</text>';
s += '<text x="'+((B.x+C.x)/2)+'" y="'+(A.y+22)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">1</text>';
const midAC={x:(A.x+C.x)/2, y:(A.y+C.y)/2};
const nAC={x:-(C.y-A.y), y:(C.x-A.x)};
const nL=Math.sqrt(nAC.x*nAC.x+nAC.y*nAC.y)||1;
const labP={x: midAC.x + 16*nAC.x/nL, y: midAC.y + 16*nAC.y/nL};
s += '<text x="'+labP.x+'" y="'+labP.y+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#b45309">√2</text>';
s += '<text x="'+((A.x+C.x)/2)+'" y="22" text-anchor="middle" font-family="Unbounded,sans-serif" font-size="13" font-weight="800" fill="#92400e">45\xb0-45\xb0-90\xb0</text>';
})();
svg.innerHTML = s;
addXp(10,'p3-iv1'); bumpProgress('p3', 15);
})();
/* IV2 — Калькулятор тождеств */
(function(){
const sl = document.getElementById('p3-iv2-s');
const inp = document.getElementById('p3-iv2-sn');
const go = document.getElementById('p3-iv2-go');
const out = document.getElementById('p3-iv2-out');
const fb = document.getElementById('p3-iv2-fb');
let solved = 0;
function sync(from){
if(from==='range') inp.value = sl.value;
else { const v = Math.max(0.01, Math.min(0.99, parseFloat(inp.value)||0.5)); sl.value = v; inp.value = v; }
}
function calc(){
const sn = parseFloat(sl.value);
if(isNaN(sn) || sn<=0 || sn>=1){ feedback(fb, false, '&#10007; $\\sin \\alpha$ должен быть в (0; 1).'); return; }
const cs = Math.sqrt(1 - sn*sn);
const tn = sn / cs;
const ct = cs / sn;
const aDeg = Math.asin(sn) * 180 / Math.PI;
out.innerHTML = '$\\sin \\alpha = '+sn.toFixed(3)+'$ &nbsp;·&nbsp; <b>$\\cos \\alpha = \\sqrt{1 - \\sin^2 \\alpha} \\approx '+cs.toFixed(3)+'$</b><br>'
+ '$\\tan \\alpha \\approx '+tn.toFixed(3)+'$ &nbsp;·&nbsp; $\\cot \\alpha \\approx '+ct.toFixed(3)+'$<br>'
+ '<span style="color:var(--pri2);font-weight:700">$\\alpha \\approx '+aDeg.toFixed(2)+'^\\circ$</span>';
renderMath(out);
feedback(fb, true, '&#10003; Все значения вычислены через основное тождество.');
solved++;
if(solved === 1){ addXp(10,'p3-iv2'); bumpProgress('p3', 15); }
}
sl.addEventListener('input', ()=>{ sync('range'); calc(); });
inp.addEventListener('input', ()=>{ sync('input'); });
go.addEventListener('click', calc);
calc();
})();
/* IV3 — Какое значение? */
(function(){
const Q = [
{ expr:'$\\sin 30^\\circ$', ans:'half', why:'эталон: $\\sin 30^\\circ = 1/2$' },
{ expr:'$\\cos 60^\\circ$', ans:'half', why:'эталон: $\\cos 60^\\circ = 1/2$' },
{ expr:'$\\sin 45^\\circ$', ans:'s2', why:'эталон: $\\sin 45^\\circ = \\sqrt{2}/2$' },
{ expr:'$\\cos 45^\\circ$', ans:'s2', why:'эталон: $\\cos 45^\\circ = \\sqrt{2}/2$' },
{ expr:'$\\sin 60^\\circ$', ans:'s3', why:'эталон: $\\sin 60^\\circ = \\sqrt{3}/2$' },
{ expr:'$\\cos 30^\\circ$', ans:'s3', why:'эталон: $\\cos 30^\\circ = \\sqrt{3}/2$' },
{ expr:'$\\sin^2 30^\\circ + \\cos^2 30^\\circ$', ans:'one', why:'основное тождество: $\\sin^2 + \\cos^2 = 1$' },
{ expr:'$\\tan 45^\\circ$', ans:'one', why:'эталон: $\\tan 45^\\circ = 1$' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p3-iv3-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p3-iv3'); bumpProgress('p3', 25); }
else if(score >= Q.length - 2){ addXp(8,'p3-iv3'); bumpProgress('p3', 15); }
return;
}
document.getElementById('p3-iv3-i').textContent = (i+1);
document.getElementById('p3-iv3-s').textContent = score;
document.getElementById('p3-iv3-q').innerHTML = Q[i].expr;
renderMath(document.getElementById('p3-iv3-q'));
document.getElementById('p3-iv3-fb').style.display = 'none';
}
function answer(a){
if(i >= Q.length) return;
const fb = document.getElementById('p3-iv3-fb');
if(a === Q[i].ans){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].why+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Нет. '+Q[i].why+'. Дальше ▶');
document.getElementById('p3-iv3-s').textContent = score;
i++;
setTimeout(show, 1100);
}
['half','s2','s3','one'].forEach(k=>{
const b = document.getElementById('p3-iv3-'+k); if(b) b.addEventListener('click', ()=>answer(k));
});
show();
})();
/* IV4 — Применение тождеств */
(function(){
const Q = [
{ q:'Если $\\sin \\alpha = 0{,}6$ и $\\alpha$ — острый, найди $\\cos \\alpha$.', ans:0.8, tol:0.02, hint:'$\\cos \\alpha = \\sqrt{1 - 0{,}36} = \\sqrt{0{,}64} = 0{,}8$' },
{ q:'$\\sin^2 70^\\circ + \\cos^2 70^\\circ = ?$', ans:1, tol:0.01, hint:'основное тождество' },
{ q:'Если $\\cos \\alpha = 0{,}5$ и $\\alpha$ — острый, найди $\\sin \\alpha$.', ans:0.866, tol:0.01, hint:'$\\sin \\alpha = \\sqrt{1 - 0{,}25} = \\sqrt{0{,}75} = \\sqrt{3}/2 \\approx 0{,}866$' },
{ q:'$\\tan 60^\\circ \\cdot \\cot 60^\\circ = ?$', ans:1, tol:0.01, hint:'$\\tan \\alpha \\cdot \\cot \\alpha = 1$' },
{ q:'Если $\\tan \\alpha = 1$, то $\\alpha = ?$ (в градусах).', ans:45, tol:0.5, hint:'$\\tan 45^\\circ = 1$' },
{ q:'$\\sin 30^\\circ + \\cos 60^\\circ = ?$', ans:1, tol:0.01, hint:'$\\tfrac{1}{2} + \\tfrac{1}{2} = 1$' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p3-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p3-iv4'); bumpProgress('p3', 25); }
else if(score >= 4){ addXp(8,'p3-iv4'); bumpProgress('p3', 15); }
return;
}
document.getElementById('p3-iv4-i').textContent = (i+1);
document.getElementById('p3-iv4-s').textContent = score;
document.getElementById('p3-iv4-q').innerHTML = Q[i].q;
document.getElementById('p3-iv4-ans').value = '';
renderMath(document.getElementById('p3-iv4-q'));
document.getElementById('p3-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p3-iv4-fb');
const ans = parseFloat(document.getElementById('p3-iv4-ans').value);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: '+Q[i].ans+' ('+Q[i].hint+'). Дальше ▶');
document.getElementById('p3-iv4-s').textContent = score;
i++;
setTimeout(show, 1400);
}
document.getElementById('p3-iv4-go').addEventListener('click', go);
document.getElementById('p3-iv4-ans').addEventListener('keydown', e=>{ if(e.key==='Enter') go(); });
document.getElementById('p3-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
show();
})();
wireReadBtn('p3');
}
/* ===== §4 sin, cos, tg, ctg тупого угла ===== */
function buildP4(){
const box = document.getElementById('p4-body');
let html = '';
html += makeCard('theory', 'Единичная окружность', '4.1', `
<p>Введём координатную плоскость. <b>Единичная окружность</b> — окружность радиуса $1$ с центром в начале координат $O$.</p>
<p>Для острого угла $\\alpha$ отложим его от положительного направления оси $Ox$ <b>против часовой стрелки</b>. Точку пересечения подвижного луча с единичной окружностью обозначим $P$. Тогда:</p>
<ul style="padding-left:22px;line-height:1.95">
<li>$\\cos \\alpha$ — это $x$-координата точки $P$</li>
<li>$\\sin \\alpha$ — это $y$-координата точки $P$</li>
</ul>
<p>Это <b>обобщение</b> старых определений: для острых углов оно совпадает с привычным «отношение катета к гипотенузе» (можно построить прямоугольный треугольник с гипотенузой $OP = 1$). Но теперь определение работает для <b>любого</b> угла от $0^\\circ$ до $180^\\circ$ — даже когда привычного прямоугольного треугольника нет.</p>
<details class="spoiler"><summary>Почему именно так?</summary><div class="spoiler-body">
Если $OP = 1$, то $\\cos \\alpha = \\dfrac{\\text{прилежащий катет}}{\\text{гипотенуза}} = \\dfrac{x_P}{1} = x_P$. Аналогично для синуса. Единичная окружность позволяет «продолжить» функции на углы $> 90^\\circ$.
</div></details>`);
html += makeCard('rule', 'Тупой угол: знаки и формулы приведения', '4.2', `
<p>Для тупого угла $\\alpha$ (где $90^\\circ < \\alpha < 180^\\circ$) точка $P$ оказывается во <b>второй четверти</b>: выше оси $Ox$ и левее оси $Oy$. Значит:</p>
<ul style="padding-left:22px;line-height:1.95">
<li>$\\sin \\alpha = y_P > 0$ — синус <b>положителен</b></li>
<li>$\\cos \\alpha = x_P < 0$ — косинус <b>отрицателен</b></li>
<li>$\\tan \\alpha = \\dfrac{\\sin \\alpha}{\\cos \\alpha} < 0$ — тангенс <b>отрицателен</b></li>
<li>$\\cot \\alpha < 0$ — котангенс <b>отрицателен</b></li>
</ul>
<p><b>Формулы приведения</b> (главные для этого параграфа):</p>
<p style="text-align:center;font-size:1.05rem">$\\boxed{\\,\\sin(180^\\circ - \\alpha) = \\sin \\alpha,\\qquad \\cos(180^\\circ - \\alpha) = -\\cos \\alpha\\,}$</p>
<p>Здесь $\\alpha$ — острый угол, а $180^\\circ - \\alpha$ — соответствующий ему тупой. Идея проста: точки $P(\\alpha)$ и $P(180^\\circ - \\alpha)$ симметричны относительно оси $Oy$ — у них одинаковая $y$-координата (синус), но противоположные $x$-координаты (косинус).</p>`);
html += makeCard('example', 'Эталонные тупые углы', '4.3', `
<p>Считаем через формулы приведения, сводя всё к острым углам $30^\\circ$, $45^\\circ$, $60^\\circ$:</p>
<ul style="padding-left:22px;line-height:1.95">
<li>$\\sin 120^\\circ = \\sin(180^\\circ - 60^\\circ) = \\sin 60^\\circ = \\dfrac{\\sqrt{3}}{2}$</li>
<li>$\\cos 120^\\circ = -\\cos 60^\\circ = -\\dfrac{1}{2}$</li>
<li>$\\sin 135^\\circ = \\sin 45^\\circ = \\dfrac{\\sqrt{2}}{2}$</li>
<li>$\\cos 135^\\circ = -\\cos 45^\\circ = -\\dfrac{\\sqrt{2}}{2}$</li>
<li>$\\sin 150^\\circ = \\sin 30^\\circ = \\dfrac{1}{2}$</li>
<li>$\\cos 150^\\circ = -\\cos 30^\\circ = -\\dfrac{\\sqrt{3}}{2}$</li>
</ul>
<p><b>Граничные случаи:</b></p>
<ul style="padding-left:22px;line-height:1.95">
<li>$\\sin 90^\\circ = 1$, $\\cos 90^\\circ = 0$ (точка $P = (0, 1)$)</li>
<li>$\\sin 180^\\circ = 0$, $\\cos 180^\\circ = -1$ (точка $P = (-1, 0)$)</li>
<li>$\\sin 0^\\circ = 0$, $\\cos 0^\\circ = 1$ (точка $P = (1, 0)$)</li>
</ul>`);
/* IV1 — Единичная окружность */
html += `<div class="wg" id="p4-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Единичная окружность</div></div>
<div class="wg-help">Двигай ползунок угла $\\alpha$ от $0^\\circ$ до $180^\\circ$. Смотри, как точка $P$ движется по окружности и как меняются $\\sin \\alpha$ (по $y$) и $\\cos \\alpha$ (по $x$). При $\\alpha > 90^\\circ$ — тупой угол, $\\cos \\alpha < 0$.</div>
<div class="sliders">
<label>Угол $\\alpha$, °<b id="p4-iv1-aval">60</b><input type="range" id="p4-iv1-a" min="0" max="180" step="1" value="60"></label>
</div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p4-iv1-svg" viewBox="0 0 400 400" style="width:100%;max-width:420px;min-width:300px;height:auto;display:block;margin:0 auto"></svg>
</div>
<div id="p4-iv1-out" style="margin-top:10px;padding:12px 14px;background:var(--pri-soft);border-radius:9px;font-size:.98rem;text-align:center;line-height:1.95"></div>
<div id="p4-iv1-tag" style="margin-top:6px;text-align:center;font-weight:700"></div>
</div>`;
/* IV2 — Формула приведения */
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">Выбери тупой угол $\\alpha$ — увидишь, как формула приведения сводит $\\sin \\alpha$ и $\\cos \\alpha$ к острому углу $180^\\circ - \\alpha$.</div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
<span style="font-family:'JetBrains Mono',monospace">$\\alpha$ =</span>
<select id="p4-iv2-a" class="tinp" style="width:110px;text-align:center">
<option value="100">100°</option>
<option value="120" selected>120°</option>
<option value="135">135°</option>
<option value="150">150°</option>
<option value="170">170°</option>
</select>
<button class="btn primary" id="p4-iv2-go">Применить формулу</button>
</div>
<div id="p4-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;font-size:1rem;line-height:1.85;min-height:50px"></div>
<div class="feedback" id="p4-iv2-fb"></div>
</div>`;
/* IV3 — Знак значения */
html += `<div class="wg" id="p4-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Знак значения</div></div>
<div class="wg-help">Каким будет знак данного тригонометрического значения: положительным, отрицательным или равным нулю?</div>
<div class="score-display"><span>Задача <b id="p4-iv3-i">1</b> / 8</span><span>Очки: <b id="p4-iv3-s">0</b> / 8</span></div>
<div id="p4-iv3-q" style="padding:14px;background:var(--pri-soft);border-radius:10px;font-size:1.2rem;text-align:center;margin-bottom:10px"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px">
<button class="btn primary" data-ans="pos" id="p4-iv3-pos">положит. (+)</button>
<button class="btn primary" data-ans="neg" id="p4-iv3-neg">отрицат. ()</button>
<button class="btn primary" data-ans="zero" id="p4-iv3-zero">равно $0$</button>
</div>
<div class="feedback" id="p4-iv3-fb"></div>
</div>`;
/* IV4 — Тренажёр тупого угла */
html += `<div class="wg" id="p4-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр тупого угла</div></div>
<div class="wg-help">Реши задачу и введи число (целое или десятичная дробь до 3 знаков; не забудь знак для отрицательных значений).</div>
<div class="score-display"><span>Задача <b id="p4-iv4-i">1</b> / 6</span><span>Очки: <b id="p4-iv4-s">0</b> / 6</span></div>
<div id="p4-iv4-q" style="padding:14px;background:var(--pri-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">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p4-iv4-ans" class="tinp" style="width:120px;text-align:center" step="0.001">
<button class="btn primary" id="p4-iv4-go">Проверить</button>
<button class="btn" id="p4-iv4-start">Заново</button>
</div>
<div class="feedback" id="p4-iv4-fb"></div>
</div>`;
html += secNav('p3', 'p5');
html += readButton('p4');
box.innerHTML = html;
renderMath(box);
/* IV1 — Единичная окружность */
(function(){
const sl = document.getElementById('p4-iv1-a');
const lab = document.getElementById('p4-iv1-aval');
const svg = document.getElementById('p4-iv1-svg');
const out = document.getElementById('p4-iv1-out');
const tag = document.getElementById('p4-iv1-tag');
const seen = new Set();
const cx = 200, cy = 200, R = 150;
function draw(){
const aDeg = +sl.value;
lab.textContent = aDeg;
const aRad = deg2rad(aDeg);
const Px = cx + R*Math.cos(aRad);
const Py = cy - R*Math.sin(aRad); // y инвертирован
let s = '';
// Сетка
s += '<rect x="0" y="0" width="400" height="400" fill="none"/>';
// Координатные оси
s += '<line x1="20" y1="'+cy+'" x2="380" y2="'+cy+'" stroke="#94a3b8" stroke-width="1"/>';
s += '<line x1="'+cx+'" y1="20" x2="'+cx+'" y2="380" stroke="#94a3b8" stroke-width="1"/>';
// Стрелки на осях
s += '<polyline points="370,'+(cy-5)+' 380,'+cy+' 370,'+(cy+5)+'" fill="none" stroke="#94a3b8" stroke-width="1"/>';
s += '<polyline points="'+(cx-5)+',30 '+cx+',20 '+(cx+5)+',30" fill="none" stroke="#94a3b8" stroke-width="1"/>';
s += '<text x="384" y="'+(cy+14)+'" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#64748b">x</text>';
s += '<text x="'+(cx+8)+'" y="22" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#64748b">y</text>';
// Единичная окружность
s += '<circle cx="'+cx+'" cy="'+cy+'" r="'+R+'" fill="none" stroke="#b45309" stroke-width="2"/>';
// Отметки 1, -1
s += '<line x1="'+(cx+R)+'" y1="'+(cy-3)+'" x2="'+(cx+R)+'" y2="'+(cy+3)+'" stroke="#64748b" stroke-width="1.5"/>';
s += '<text x="'+(cx+R)+'" y="'+(cy+16)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#64748b">1</text>';
s += '<line x1="'+(cx-R)+'" y1="'+(cy-3)+'" x2="'+(cx-R)+'" y2="'+(cy+3)+'" stroke="#64748b" stroke-width="1.5"/>';
s += '<text x="'+(cx-R)+'" y="'+(cy+16)+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#64748b">1</text>';
s += '<line x1="'+(cx-3)+'" y1="'+(cy-R)+'" x2="'+(cx+3)+'" y2="'+(cy-R)+'" stroke="#64748b" stroke-width="1.5"/>';
s += '<text x="'+(cx-10)+'" y="'+(cy-R+4)+'" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" fill="#64748b">1</text>';
// Проекции — пунктиром
s += '<line x1="'+Px+'" y1="'+Py+'" x2="'+Px+'" y2="'+cy+'" stroke="#10b981" stroke-width="2" stroke-dasharray="4 3"/>';
s += '<line x1="'+Px+'" y1="'+Py+'" x2="'+cx+'" y2="'+Py+'" stroke="#0ea5e9" stroke-width="2" stroke-dasharray="4 3"/>';
// Радиус OP
s += '<line x1="'+cx+'" y1="'+cy+'" x2="'+Px+'" y2="'+Py+'" stroke="#2563eb" stroke-width="2.5"/>';
// Дуга угла alpha
if(aDeg > 0){
const uOx = {x:1, y:0};
const uOP = {x: Math.cos(aRad), y: -Math.sin(aRad)};
s += '<path d="'+angleArcAuto({x:cx,y:cy}, uOx, uOP, 36)+'" fill="none" stroke="#dc2626" stroke-width="2.2"/>';
// Подпись α
const aHalf = aRad/2;
const tx = cx + 54*Math.cos(aHalf);
const ty = cy - 54*Math.sin(aHalf);
s += '<text x="'+tx+'" y="'+ty+'" text-anchor="middle" dominant-baseline="middle" font-family="Inter,sans-serif" font-size="14" font-weight="700" fill="#dc2626">α</text>';
}
// Точка P
s += '<circle cx="'+Px+'" cy="'+Py+'" r="5.5" fill="#dc2626" stroke="#fff" stroke-width="2"/>';
s += '<text x="'+(Px + (Math.cos(aRad)>=0?10:-10))+'" y="'+(Py - 10)+'" text-anchor="'+(Math.cos(aRad)>=0?'start':'end')+'" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#dc2626">P</text>';
// Подписи проекций
s += '<text x="'+Px+'" y="'+(cy + (Math.sin(aRad)>=0?22:-12))+'" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#0ea5e9">cos α</text>';
s += '<text x="'+(cx + (Math.cos(aRad)>=0?-14:14))+'" y="'+Py+'" text-anchor="'+(Math.cos(aRad)>=0?'end':'start')+'" dominant-baseline="middle" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#10b981">sin α</text>';
// Подпись радиуса
s += '<text x="'+(cx + R*0.55*Math.cos(aRad) + (Math.sin(aRad)>=0?-12:12))+'" y="'+(cy - R*0.55*Math.sin(aRad) - 6)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#2563eb">r=1</text>';
svg.innerHTML = s;
const sn = Math.sin(aRad), cs = Math.cos(aRad);
const tn = (Math.abs(cs) < 1e-9) ? null : sn/cs;
out.innerHTML = '$\\sin '+aDeg+'^\\circ \\approx '+sn.toFixed(3)+'$ &nbsp;·&nbsp; $\\cos '+aDeg+'^\\circ \\approx '+cs.toFixed(3)+'$<br>'
+ (tn===null ? '$\\tan '+aDeg+'^\\circ$ — не определён' : '$\\tan '+aDeg+'^\\circ \\approx '+tn.toFixed(3)+'$');
renderMath(out);
if(aDeg > 90 && aDeg < 180){
tag.innerHTML = '<span style="color:var(--pri2);background:var(--pri-soft);padding:5px 12px;border-radius:8px">⚠ Тупой угол! $\\cos \\alpha < 0$.</span>';
renderMath(tag);
} else if(aDeg === 90){
tag.innerHTML = '<span style="color:#065f46;background:var(--ok-bg);padding:5px 12px;border-radius:8px">Прямой угол.</span>';
} else if(aDeg === 180 || aDeg === 0){
tag.innerHTML = '<span style="color:#7f1d1d;background:var(--fail-bg);padding:5px 12px;border-radius:8px">Развёрнутый/нулевой угол.</span>';
} else {
tag.innerHTML = '<span style="color:#065f46;background:var(--ok-bg);padding:5px 12px;border-radius:8px">Острый угол. Обе функции положительны.</span>';
}
seen.add(aDeg);
if(seen.size >= 6 && !seen.has('done')){ addXp(10,'p4-iv1'); bumpProgress('p4', 15); seen.add('done'); }
}
sl.addEventListener('input', draw);
draw();
})();
/* IV2 — Формула приведения */
(function(){
const sel = document.getElementById('p4-iv2-a');
const go = document.getElementById('p4-iv2-go');
const out = document.getElementById('p4-iv2-out');
const fb = document.getElementById('p4-iv2-fb');
let solved = 0;
function apply(){
const aDeg = parseInt(sel.value, 10);
const beta = 180 - aDeg;
const r = deg2rad(aDeg);
const sn = Math.sin(r), cs = Math.cos(r);
out.innerHTML = '<b>Формула приведения для $\\alpha = '+aDeg+'^\\circ$:</b><br>'
+ '$\\sin '+aDeg+'^\\circ = \\sin(180^\\circ - '+aDeg+'^\\circ) = \\sin '+beta+'^\\circ \\approx '+sn.toFixed(3)+'$<br>'
+ '$\\cos '+aDeg+'^\\circ = -\\cos(180^\\circ - '+aDeg+'^\\circ) = -\\cos '+beta+'^\\circ \\approx '+cs.toFixed(3)+'$<br>'
+ '<span style="color:var(--muted);font-size:.9rem">Заметь: синус сохранил знак, косинус — поменял.</span>';
renderMath(out);
feedback(fb, true, '&#10003; Сведено к острому углу '+beta+'°.');
solved++;
if(solved === 1){ addXp(10,'p4-iv2'); bumpProgress('p4', 15); }
}
go.addEventListener('click', apply);
apply();
})();
/* IV3 — Знак значения */
(function(){
const Q = [
{ expr:'$\\sin 120^\\circ$', ans:'pos', why:'тупой угол: $\\sin > 0$' },
{ expr:'$\\cos 120^\\circ$', ans:'neg', why:'тупой угол: $\\cos < 0$' },
{ expr:'$\\sin 90^\\circ$', ans:'pos', why:'$\\sin 90^\\circ = 1 > 0$' },
{ expr:'$\\cos 90^\\circ$', ans:'zero', why:'$\\cos 90^\\circ = 0$' },
{ expr:'$\\sin 180^\\circ$', ans:'zero', why:'$\\sin 180^\\circ = 0$' },
{ expr:'$\\cos 180^\\circ$', ans:'neg', why:'$\\cos 180^\\circ = -1 < 0$' },
{ expr:'$\\sin 60^\\circ$', ans:'pos', why:'острый угол: $\\sin > 0$' },
{ expr:'$\\cos 150^\\circ$', ans:'neg', why:'тупой угол: $\\cos < 0$' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p4-iv3-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p4-iv3'); bumpProgress('p4', 25); }
else if(score >= Q.length - 2){ addXp(8,'p4-iv3'); bumpProgress('p4', 15); }
return;
}
document.getElementById('p4-iv3-i').textContent = (i+1);
document.getElementById('p4-iv3-s').textContent = score;
document.getElementById('p4-iv3-q').innerHTML = Q[i].expr;
renderMath(document.getElementById('p4-iv3-q'));
document.getElementById('p4-iv3-fb').style.display = 'none';
}
function answer(a){
if(i >= Q.length) return;
const fb = document.getElementById('p4-iv3-fb');
if(a === Q[i].ans){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].why+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Нет. '+Q[i].why+'. Дальше ▶');
document.getElementById('p4-iv3-s').textContent = score;
i++;
setTimeout(show, 1100);
}
['pos','neg','zero'].forEach(k=>{
const b = document.getElementById('p4-iv3-'+k); if(b) b.addEventListener('click', ()=>answer(k));
});
show();
})();
/* IV4 — Тренажёр тупого угла */
(function(){
const Q = [
{ q:'$\\sin 120^\\circ = ?$', ans:0.866, tol:0.01, hint:'$\\sin 120^\\circ = \\sin 60^\\circ = \\sqrt{3}/2 \\approx 0{,}866$' },
{ q:'$\\cos 120^\\circ = ?$', ans:-0.5, tol:0.02, hint:'$\\cos 120^\\circ = -\\cos 60^\\circ = -1/2$' },
{ q:'$\\sin 150^\\circ = ?$', ans:0.5, tol:0.02, hint:'$\\sin 150^\\circ = \\sin 30^\\circ = 1/2$' },
{ q:'$\\cos 150^\\circ = ?$', ans:-0.866, tol:0.01, hint:'$\\cos 150^\\circ = -\\cos 30^\\circ = -\\sqrt{3}/2$' },
{ q:'$\\cos 90^\\circ = ?$', ans:0, tol:0.01, hint:'$\\cos 90^\\circ = 0$' },
{ q:'$\\sin 180^\\circ + \\cos 180^\\circ = ?$', ans:-1, tol:0.02, hint:'$0 + (-1) = -1$' },
];
let i = 0, score = 0;
function show(){
if(i >= Q.length){
document.getElementById('p4-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
if(score === Q.length){ addXp(15,'p4-iv4'); bumpProgress('p4', 25); }
else if(score >= 4){ addXp(8,'p4-iv4'); bumpProgress('p4', 15); }
return;
}
document.getElementById('p4-iv4-i').textContent = (i+1);
document.getElementById('p4-iv4-s').textContent = score;
document.getElementById('p4-iv4-q').innerHTML = Q[i].q;
document.getElementById('p4-iv4-ans').value = '';
renderMath(document.getElementById('p4-iv4-q'));
document.getElementById('p4-iv4-fb').style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p4-iv4-fb');
const ans = parseFloat(document.getElementById('p4-iv4-ans').value);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) <= Q[i].tol){ score++; feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Ответ: '+Q[i].ans+' ('+Q[i].hint+'). Дальше ▶');
document.getElementById('p4-iv4-s').textContent = score;
i++;
setTimeout(show, 1400);
}
document.getElementById('p4-iv4-go').addEventListener('click', go);
document.getElementById('p4-iv4-ans').addEventListener('keydown', e=>{ if(e.key==='Enter') go(); });
document.getElementById('p4-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
show();
})();
wireReadBtn('p4');
}
function buildP5(){ _stubBuilder('p5', '§5', 'Формулы площади', 'p4', 'p6'); }
function buildP6(){ _stubBuilder('p6', '§6', 'Среднее геометрическое', 'p5', 'final1'); }
function buildFinal1(){
const body = document.getElementById('final1-body');
let html = '';
html += makeCard('theory', 'Финал главы 1', '★', `
<p>Итоговый раздел главы <b>«Соотношения в прямоугольном треугольнике»</b> будет добавлен в следующих обновлениях.</p>
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 7.</p>`);
html += readButton('final1');
html += secNav('p6', null);
body.innerHTML = html;
wireReadBtn('final1');
if(window.renderMathInElement) renderMath(body);
}
/* ===== Search ===== */
const SEARCH_INDEX = (function(){
const arr=[];
PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));
return arr;
})();
function initSearch(){
const modal=document.getElementById('search-modal'),inp=document.getElementById('search-input'),out=document.getElementById('search-results'),btn=document.getElementById('search-btn');
if(!modal||!inp||!out) return;
let cur=0,rows=[];
function score(q,it){ const t=(it.title+' '+it.desc).toLowerCase(); if(t.includes(q)) return 100+(it.title.toLowerCase().startsWith(q)?50:0); let s=0; q.split(/\s+/).forEach(w=>{if(w&&t.includes(w))s+=10;}); return s; }
function rank(q){ q=q.trim().toLowerCase(); if(!q) return SEARCH_INDEX.slice(0,12); return SEARCH_INDEX.map(it=>({it,s:score(q,it)})).filter(x=>x.s>0).sort((a,b)=>b.s-a.s).slice(0,20).map(x=>x.it); }
function render(){ cur=0; if(!rows.length){out.innerHTML='<div class="search-empty">Ничего не найдено</div>';return;} out.innerHTML=rows.map((r,i)=>'<button class="search-row'+(i===0?' active':'')+'" data-i="'+i+'"><div class="sr-kind">'+r.kind+'</div><div class="sr-title">'+r.title+'</div>'+(r.desc?'<div class="sr-desc">'+(r.desc.length>90?r.desc.slice(0,90)+'…':r.desc)+'</div>':'')+'</button>').join(''); out.querySelectorAll('.search-row').forEach(b=>b.addEventListener('click',()=>{cur=+b.dataset.i;pick();})); }
function pick(){ const r=rows[cur]; if(!r) return; close(); goTo(r.sec); }
function move(d){ const items=out.querySelectorAll('.search-row'); if(!items.length) return; items[cur]&&items[cur].classList.remove('active'); cur=(cur+d+items.length)%items.length; items[cur].classList.add('active'); items[cur].scrollIntoView({block:'nearest'}); }
function open(){ modal.classList.add('show'); inp.value=''; rows=rank(''); render(); setTimeout(()=>inp.focus(),50); }
function close(){ modal.classList.remove('show'); }
btn&&btn.addEventListener('click',open);
modal.addEventListener('click',e=>{if(e.target===modal)close();});
inp.addEventListener('input',()=>{rows=rank(inp.value);render();});
inp.addEventListener('keydown',e=>{ if(e.key==='ArrowDown'){e.preventDefault();move(1);}else if(e.key==='ArrowUp'){e.preventDefault();move(-1);}else if(e.key==='Enter'){e.preventDefault();pick();}else if(e.key==='Escape'){e.preventDefault();close();} });
document.addEventListener('keydown',e=>{ if((e.ctrlKey||e.metaKey)&&(e.key==='k'||e.key==='K')){ e.preventDefault(); if(modal.classList.contains('show')) close(); else open(); } });
}
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(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo('p1');
setTimeout(()=>achievement('start'), 600);
if(window.LS&&window.LS.xp){
window.LS.xp.load().then(function(s){ if(s&&s.xp>STATE.xp){ STATE.xp=s.xp; STATE.level=calcLevel(STATE.xp); saveProgress(); refreshProgressUI(); if(STATE.current) buildSidebar(STATE.current); } });
}
}
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>