feat: update server

This commit is contained in:
Oleg Proskurin 2026-04-21 18:00:54 +07:00
parent d8f18c0d9d
commit 99bc387244
2 changed files with 345 additions and 1 deletions

View File

@ -1,3 +1,6 @@
const fs = require('fs');
const path = require('path');
module.exports = { module.exports = {
server: { server: {
baseDir: "output", baseDir: "output",
@ -13,5 +16,30 @@ module.exports = {
port: 3300, port: 3300,
open: false, open: false,
notify: false, notify: false,
ui: false ui: false,
middleware: [
{
route: "/api/save-editor",
handle: function (req, res, next) {
if (req.method !== 'POST') return next();
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
try {
const data = JSON.parse(body);
const name = path.basename(data.file || 'unknown', '.html');
const dir = path.join(__dirname, 'output', 'editor-saves');
fs.mkdirSync(dir, { recursive: true });
const savePath = path.join(dir, name + '.json');
fs.writeFileSync(savePath, JSON.stringify(data, null, 2));
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true, path: savePath }));
} catch (e) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: e.message }));
}
});
}
}
]
}; };

316
docs/daniel-checklist.html Normal file
View File

@ -0,0 +1,316 @@
<!DOCTYPE html>
<html lang="ru" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Чеклист: Данила — Математика и логика</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300..700&display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root,[data-theme="light"]{
--bg:#f7f6f2;--surf:#f9f8f5;--surf2:#fff;--off:#f0ede8;
--div:#dcd9d5;--brd:#d4d1ca;
--txt:#28251d;--muted:#7a7974;--faint:#bab9b4;
--pri:#01696f;--pri-hl:#cedcd8;
--suc:#437a22;--suc-hl:#d4dfcc;
--org:#da7101;--org-hl:#e7d7c4;
--pur:#7a39bb;--pur-hl:#dacfde;
--blu:#006494;--blu-hl:#c6d8e4;
--gld:#c98a00;--gld-hl:#e9e0c6;
--err:#a12c7b;--err-hl:#e0ced7;
--sh-sm:0 1px 3px rgba(40,37,29,.07);--sh-md:0 4px 14px rgba(40,37,29,.09);
--r-md:.5rem;--r-lg:.75rem;--r-xl:1rem;--r-full:9999px;
--t:180ms cubic-bezier(.16,1,.3,1);
}
[data-theme="dark"]{
--bg:#171614;--surf:#1c1b19;--surf2:#242320;--off:#222120;
--div:#262523;--brd:#3a3937;
--txt:#cdccca;--muted:#797876;--faint:#5a5957;
--pri:#4f98a3;--pri-hl:#2b3b3c;
--suc:#6daa45;--suc-hl:#2e3d2a;
--org:#fdab43;--org-hl:#3d3020;
--pur:#a86fdf;--pur-hl:#352a42;
--blu:#5591c7;--blu-hl:#243040;
--gld:#e8af34;--gld-hl:#36300f;
--err:#d163a7;--err-hl:#3d2535;
--sh-sm:0 1px 3px rgba(0,0,0,.25);--sh-md:0 4px 14px rgba(0,0,0,.35);
}
html{-webkit-font-smoothing:antialiased;scroll-behavior:smooth}
body{min-height:100dvh;font-family:'Inter',sans-serif;font-size:16px;color:var(--txt);background:var(--bg);transition:background var(--t),color var(--t)}
button{cursor:pointer;background:none;border:none;font:inherit;color:inherit}
:focus-visible{outline:2px solid var(--pri);outline-offset:3px;border-radius:4px}
.hdr{position:sticky;top:0;z-index:50;background:var(--surf);border-bottom:1px solid var(--div);box-shadow:var(--sh-sm);padding:.7rem 1.5rem;display:flex;align-items:center;gap:.75rem}
.logo-ic{width:32px;height:32px;border-radius:7px;background:var(--pri);display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.1rem;flex-shrink:0;font-weight:bold}
.logo-t{font-weight:700;font-size:.9rem;line-height:1.2}
.logo-s{font-size:.7rem;color:var(--muted);margin-top:1px}
.hdr-r{margin-left:auto;display:flex;align-items:center;gap:.4rem}
.btn-rst{padding:.28rem .8rem;border-radius:var(--r-full);font-size:.7rem;font-weight:600;background:var(--off);color:var(--muted);border:1px solid var(--brd);transition:all var(--t)}
.btn-rst:hover{background:var(--div);color:var(--txt)}
.btn-ic{width:32px;height:32px;border-radius:var(--r-md);display:flex;align-items:center;justify-content:center;color:var(--muted);transition:all var(--t)}
.btn-ic:hover{background:var(--off);color:var(--txt)}
.wrap{max-width:800px;margin:0 auto;padding:1.75rem 1.5rem 5rem}
.hero{background:var(--surf);border:1px solid var(--brd);border-radius:var(--r-xl);padding:1.4rem 1.75rem;margin-bottom:1.75rem;box-shadow:var(--sh-sm);display:grid;grid-template-columns:1fr auto;gap:1.25rem;align-items:center}
.h-lbl{font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);margin-bottom:.35rem}
.h-n{font-size:2rem;font-weight:800;color:var(--pri);line-height:1;font-variant-numeric:tabular-nums}
.h-of{font-size:.8rem;color:var(--muted);margin-top:.2rem}
.bw{margin-top:.9rem;height:7px;background:var(--off);border-radius:var(--r-full);overflow:hidden}
.bf{height:100%;background:var(--pri);border-radius:var(--r-full);transition:width .5s cubic-bezier(.16,1,.3,1)}
.dnt{position:relative;width:76px;height:76px;flex-shrink:0}
.dnt svg{width:76px;height:76px}
.dp{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:.9rem;color:var(--pri)}
.pills{display:flex;gap:.35rem;flex-wrap:wrap;margin-bottom:1.1rem}
.pill{padding:.22rem .75rem;border-radius:var(--r-full);font-size:.7rem;font-weight:600;border:1.5px solid var(--brd);background:var(--surf);color:var(--muted);cursor:pointer;transition:all var(--t)}
.pill:hover,.pill.on{background:var(--pri);border-color:var(--pri);color:#fff}
.card{background:var(--surf);border:1px solid var(--brd);border-radius:var(--r-xl);margin-bottom:.75rem;overflow:hidden;box-shadow:var(--sh-sm);transition:box-shadow var(--t)}
.card:hover{box-shadow:var(--sh-md)}
.ch{display:flex;align-items:center;gap:.65rem;padding:.8rem 1.1rem;cursor:pointer;user-select:none;border-bottom:1px solid transparent;transition:border-color var(--t)}
.card.open .ch{border-bottom-color:var(--div)}
.sico{width:32px;height:32px;border-radius:var(--r-md);display:flex;align-items:center;justify-content:center;font-size:.95rem;flex-shrink:0}
.stw{flex:1;min-width:0}
.st{font-weight:700;font-size:.9rem;line-height:1.2}
.sst{font-size:.68rem;color:var(--muted);margin-top:2px}
.sm{display:flex;align-items:center;gap:.55rem;flex-shrink:0}
.sc{font-size:.68rem;font-weight:700;color:var(--muted);font-variant-numeric:tabular-nums;min-width:2.8rem;text-align:right}
.mb{width:48px;height:4px;background:var(--off);border-radius:var(--r-full);overflow:hidden}
.mf{height:100%;border-radius:var(--r-full);transition:width .4s cubic-bezier(.16,1,.3,1)}
.chev{color:var(--faint);transition:transform var(--t)}
.card.open .chev{transform:rotate(180deg)}
.cb{display:none;padding:.4rem 1.1rem .9rem}
.card.open .cb{display:block}
.itm{display:flex;align-items:flex-start;gap:.6rem;padding:.5rem .3rem;border-radius:var(--r-md);cursor:pointer;transition:background var(--t)}
.itm:hover{background:var(--off)}
.itm.ok{opacity:.55}
.itm.ok .it{text-decoration:line-through;color:var(--muted)}
.ck{width:21px;height:21px;border-radius:5px;border:2px solid var(--brd);background:var(--surf2);flex-shrink:0;margin-top:2px;display:flex;align-items:center;justify-content:center;transition:all var(--t)}
.itm.ok .ck{background:var(--pri);border-color:var(--pri)}
.cki{display:none;color:#fff}
.itm.ok .cki{display:block}
.it{font-size:.875rem;line-height:1.55;color:var(--txt)}
.done-b{display:none;background:linear-gradient(120deg,var(--suc-hl),var(--pri-hl));border:1.5px solid var(--suc);border-radius:var(--r-xl);padding:1.4rem 1.75rem;text-align:center;margin-bottom:1.4rem;animation:bIn .6s cubic-bezier(.16,1,.3,1) both}
@keyframes bIn{from{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:none}}
.done-b.show{display:block}
.dem{font-size:2rem;margin-bottom:.35rem}
.dt{font-size:1.1rem;font-weight:800;color:var(--suc);margin-bottom:.25rem}
.ds{font-size:.82rem;color:var(--muted)}
@media(max-width:520px){
.wrap{padding:1rem 1rem 4rem}
.hero{grid-template-columns:1fr}
.dnt{display:none}
.hdr{padding:.6rem 1rem}
}
</style>
</head>
<body>
<header class="hdr">
<div class="logo-ic"></div>
<div>
<div class="logo-t">Данила — подготовка к школе</div>
<div class="logo-s">Математика и логика · 7 лет · сентябрь 2026</div>
</div>
<div class="hdr-r">
<button class="btn-rst" onclick="resetAll()">Сбросить</button>
<button class="btn-ic" id="themeBtn" onclick="toggleTheme()" aria-label="Переключить тему">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
</button>
</div>
</header>
<main class="wrap">
<div class="done-b" id="doneBanner">
<div class="dem">🎉</div>
<div class="dt">Все пункты пройдены!</div>
<div class="ds">Данила готов к первому классу. Отличная работа!</div>
</div>
<div class="hero">
<div>
<div class="h-lbl">Общий прогресс</div>
<div class="h-n" id="pN">0</div>
<div class="h-of" id="pO">из 36 пунктов выполнено</div>
<div class="bw"><div class="bf" id="pB" style="width:0%"></div></div>
</div>
<div class="dnt" aria-hidden="true">
<svg viewBox="0 0 76 76">
<circle cx="38" cy="38" r="30" fill="none" stroke="var(--off)" stroke-width="8"/>
<circle id="dc" cx="38" cy="38" r="30" fill="none" stroke="var(--pri)" stroke-width="8"
stroke-linecap="round" stroke-dasharray="188.5" stroke-dashoffset="188.5"
transform="rotate(-90 38 38)" style="transition:stroke-dashoffset .5s cubic-bezier(.16,1,.3,1)"/>
</svg>
<div class="dp" id="dp">0%</div>
</div>
</div>
<nav class="pills">
<button class="pill on" onclick="flt('all',this)">Все</button>
<button class="pill" onclick="flt('s1',this)">🔢 Числа</button>
<button class="pill" onclick="flt('s2',this)"> Арифметика</button>
<button class="pill" onclick="flt('s3',this)">📏 Величины</button>
<button class="pill" onclick="flt('s4',this)">🔷 Геометрия</button>
<button class="pill" onclick="flt('s5',this)">🧩 Логика</button>
<button class="pill" onclick="flt('s6',this)">🧠 Внимание</button>
</nav>
<!-- S1: ЧИСЛА — 6 пунктов из списка -->
<div class="card open" id="s1" data-s="s1">
<div class="ch" onclick="tog('s1')">
<div class="sico" style="background:var(--pri-hl);color:var(--pri)">🔢</div>
<div class="stw"><div class="st">Числа и счёт</div><div class="sst">6 пунктов</div></div>
<div class="sm"><span class="sc" id="ct-s1">0/6</span><div class="mb"><div class="mf" id="mf-s1" style="background:var(--pri);width:0%"></div></div><svg class="chev" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6 9l6 6 6-6"/></svg></div>
</div>
<div class="cb">
<div class="itm" data-id="1_1" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Понимает разницу между количественным (сколько?) и порядковым (который по счёту?) счётом</div></div>
<div class="itm" data-id="1_2" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Называет предыдущее и следующее числа к любому числу в пределах 10</div></div>
<div class="itm" data-id="1_3" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Восстанавливает числовой ряд с пропущенными числами (1, __, 3, __, 5)</div></div>
<div class="itm" data-id="1_4" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Знает чётные и нечётные числа в пределах 10</div></div>
<div class="itm" data-id="1_5" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Пишет цифры от 0 до 9</div></div>
<div class="itm" data-id="1_6" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Сравнивает числа, использует знаки &gt;, &lt;, = (6 &gt; 4)</div></div>
</div>
</div>
<!-- S2: АРИФМЕТИКА — 3 пункта из списка -->
<div class="card" id="s2" data-s="s2">
<div class="ch" onclick="tog('s2')">
<div class="sico" style="background:var(--org-hl);color:var(--org)"></div>
<div class="stw"><div class="st">Арифметика (текстовые задачи)</div><div class="sst">3 пункта</div></div>
<div class="sm"><span class="sc" id="ct-s2">0/3</span><div class="mb"><div class="mf" id="mf-s2" style="background:var(--org);width:0%"></div></div><svg class="chev" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6 9l6 6 6-6"/></svg></div>
</div>
<div class="cb">
<div class="itm" data-id="2_1" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Самостоятельно составляет задачу по картинке или условию</div></div>
<div class="itm" data-id="2_2" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Уравнивает неравное число предметов: «В одной группе 3 кружка, в другой 5. Сколько надо добавить, чтобы стало поровну?»</div></div>
<div class="itm" data-id="2_3" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Понимает понятия «больше», «меньше», «столько же», «поровну»</div></div>
</div>
</div>
<!-- S3: ВЕЛИЧИНЫ — 2 пункта из списка -->
<div class="card" id="s3" data-s="s3">
<div class="ch" onclick="tog('s3')">
<div class="sico" style="background:var(--blu-hl);color:var(--blu)">📏</div>
<div class="stw"><div class="st">Величины и измерения</div><div class="sst">2 пункта</div></div>
<div class="sm"><span class="sc" id="ct-s3">0/2</span><div class="mb"><div class="mf" id="mf-s3" style="background:var(--blu);width:0%"></div></div><svg class="chev" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6 9l6 6 6-6"/></svg></div>
</div>
<div class="cb">
<div class="itm" data-id="3_1" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Делит предмет на 2 и 4 равные части (половина, четверть)</div></div>
<div class="itm" data-id="3_2" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Измеряет условной меркой (сколько ладошек длиной стол?)</div></div>
</div>
</div>
<!-- S4: ГЕОМЕТРИЯ — 8 пунктов из списка -->
<div class="card" id="s4" data-s="s4">
<div class="ch" onclick="tog('s4')">
<div class="sico" style="background:var(--gld-hl);color:var(--gld)">🔷</div>
<div class="stw"><div class="st">Геометрия и пространство</div><div class="sst">8 пунктов</div></div>
<div class="sm"><span class="sc" id="ct-s4">0/8</span><div class="mb"><div class="mf" id="mf-s4" style="background:var(--gld);width:0%"></div></div><svg class="chev" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6 9l6 6 6-6"/></svg></div>
</div>
<div class="cb">
<div class="itm" data-id="4_1" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Называет и показывает: круг, квадрат, прямоугольник, треугольник, овал</div></div>
<div class="itm" data-id="4_2" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Называет и показывает объёмные тела: шар, куб, цилиндр, конус</div></div>
<div class="itm" data-id="4_3" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Находит предметы похожей формы в окружении (мяч — шар, окно — прямоугольник)</div></div>
<div class="itm" data-id="4_4" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Ориентируется на листе бумаги: вверху / внизу, слева / справа, в центре, по углам</div></div>
<div class="itm" data-id="4_5" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Составляет фигуры из частей (квадрат из 2 треугольников, прямоугольник из 2 квадратов)</div></div>
<div class="itm" data-id="4_6" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Рисует фигуры по клеточкам</div></div>
<div class="itm" data-id="4_7" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Знает «план» или «схему» как обозначение реального пространства</div></div>
<div class="itm" data-id="4_8" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Знает времена года и их последовательность</div></div>
</div>
</div>
<!-- S5: ЛОГИКА — 9 пунктов из списка -->
<div class="card" id="s5" data-s="s5">
<div class="ch" onclick="tog('s5')">
<div class="sico" style="background:var(--pur-hl);color:var(--pur)">🧩</div>
<div class="stw"><div class="st">Логика и мышление</div><div class="sst">9 пунктов</div></div>
<div class="sm"><span class="sc" id="ct-s5">0/9</span><div class="mb"><div class="mf" id="mf-s5" style="background:var(--pur);width:0%"></div></div><svg class="chev" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6 9l6 6 6-6"/></svg></div>
</div>
<div class="cb">
<div class="itm" data-id="5_1" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Объединяет предметы в группы по одному признаку (цвет, форма, размер)</div></div>
<div class="itm" data-id="5_2" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Объединяет по двум признакам одновременно (маленькие красные фигуры)</div></div>
<div class="itm" data-id="5_3" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Выделяет лишний предмет и объясняет почему («лишний — помидор, потому что это овощ, остальные фрукты»)</div></div>
<div class="itm" data-id="5_4" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Называет обобщающее слово для группы (стол, стул, шкаф → мебель)</div></div>
<div class="itm" data-id="5_5" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Решает простые аналогии: «Птица — гнездо, человек — ?»</div></div>
<div class="itm" data-id="5_6" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Продолжает простой паттерн (○ △ ○ △ ○ …)</div></div>
<div class="itm" data-id="5_7" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Продолжает числовой ряд с закономерностью (2, 4, 6, …)</div></div>
<div class="itm" data-id="5_8" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Выстраивает последовательность событий по картинкам (45 картинок) и составляет рассказ</div></div>
<div class="itm" data-id="5_9" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Отвечает на вопросы «почему?» и «что будет, если…?»</div></div>
</div>
</div>
<!-- S6: ВНИМАНИЕ — 6 пунктов из списка -->
<div class="card" id="s6" data-s="s6">
<div class="ch" onclick="tog('s6')">
<div class="sico" style="background:var(--err-hl);color:var(--err)">🧠</div>
<div class="stw"><div class="st">Внимание, память, регуляция</div><div class="sst">6 пунктов</div></div>
<div class="sm"><span class="sc" id="ct-s6">0/6</span><div class="mb"><div class="mf" id="mf-s6" style="background:var(--err);width:0%"></div></div><svg class="chev" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6 9l6 6 6-6"/></svg></div>
</div>
<div class="cb">
<div class="itm" data-id="6_1" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Выполняет задание, не отвлекаясь, около 1520 минут</div></div>
<div class="itm" data-id="6_2" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Удерживает в поле зрения не менее 810 предметов</div></div>
<div class="itm" data-id="6_3" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Копирует узор точно по образцу</div></div>
<div class="itm" data-id="6_4" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Повторяет ряд цифр из 57 элементов (3, 1, 7, 4, 2…)</div></div>
<div class="itm" data-id="6_5" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Запоминает и выполняет инструкцию из 3 шагов</div></div>
<div class="itm" data-id="6_6" onclick="ti(this)"><div class="ck"><svg class="cki" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></div><div class="it">Доводит задание до конца, не бросая на полпути</div></div>
</div>
</div>
<!-- ЗАМЕТКА из списка: физические объекты -->
<div style="background:var(--off);border:1px solid var(--brd);border-radius:var(--r-lg);padding:.9rem 1.1rem;margin-bottom:.75rem;font-size:.8rem;color:var(--muted);line-height:1.6">
📌 <b style="color:var(--txt)">Счётные палочки и физические объекты</b> — работа с бусами, палочками, монетами формирует понимание числа глубже, чем тетрадные упражнения (Монтессори, российские методисты)
</div>
</main>
<script>
const S={};
const SECS=['s1','s2','s3','s4','s5','s6'];
function ga(){return document.querySelectorAll('.itm[data-id]');}
function cd(){let n=0;ga().forEach(e=>{if(S[e.dataset.id])n++;});return n;}
function cs(s){const el=document.querySelectorAll('#'+s+' .itm[data-id]');let d=0;el.forEach(e=>{if(S[e.dataset.id])d++;});return{d,t:el.length};}
function upd(){
const tot=ga().length,done=cd(),pct=tot?Math.round(done/tot*100):0;
document.getElementById('pN').textContent=done;
document.getElementById('pO').textContent='из '+tot+' пунктов выполнено';
document.getElementById('pB').style.width=pct+'%';
const circ=188.5;
document.getElementById('dc').style.strokeDashoffset=circ-(circ*pct/100);
document.getElementById('dp').textContent=pct+'%';
SECS.forEach(s=>{
const{d,t}=cs(s);
const ct=document.getElementById('ct-'+s);
const mf=document.getElementById('mf-'+s);
if(ct)ct.textContent=d+'/'+t;
if(mf)mf.style.width=(t?Math.round(d/t*100):0)+'%';
});
const b=document.getElementById('doneBanner');
if(done===tot&&tot>0)b.classList.add('show');else b.classList.remove('show');
}
function ti(el){S[el.dataset.id]=!S[el.dataset.id];el.classList.toggle('ok',S[el.dataset.id]);upd();}
function tog(id){document.getElementById(id).classList.toggle('open');}
function flt(t,btn){
document.querySelectorAll('.pill').forEach(p=>p.classList.remove('on'));
btn.classList.add('on');
document.querySelectorAll('.card').forEach(c=>{c.style.display=(t==='all'||c.dataset.s===t)?'':'none';});
}
function resetAll(){
if(!confirm('Сбросить весь прогресс?'))return;
Object.keys(S).forEach(k=>delete S[k]);
document.querySelectorAll('.itm.ok').forEach(e=>e.classList.remove('ok'));
upd();
}
let dark=matchMedia('(prefers-color-scheme:dark)').matches;
function setThemeIcon(){
document.getElementById('themeBtn').innerHTML=dark
?'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>'
:'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';
}
function toggleTheme(){dark=!dark;document.documentElement.setAttribute('data-theme',dark?'dark':'light');setThemeIcon();}
document.documentElement.setAttribute('data-theme',dark?'dark':'light');
setThemeIcon();
upd();
</script>
</body>
</html>