Files
Maxim Dolgolyov 5b103ab606 refactor(lab): превью симуляций вынесены в общий lab-previews.js (единый источник)
~45 SVG-превью (P_*) и хелперы _grid/_axes/_svg вынесены из lab-glue.js в
общий /js/lab-previews.js: window.LabPreviews (карта id→SVG, 40 симуляций) +
window.__LabP (по имени, lab-glue берёт алиасы оттуда). SIMS не тронут.
lab.html подключает lab-previews.js перед lab-glue.js. Теперь дашборд берёт
настоящие превью симуляций из того же источника → «Лаборатория дня» крутит
весь каталог, а не 6 захардкоженных. Дублирование 6 превью устранено.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:34:29 +03:00

775 lines
60 KiB
JavaScript
Raw Permalink 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.
'use strict';
/*
* lab-previews.js — ЕДИНЫЙ источник SVG-превью симуляций лаборатории.
* Вынесено из lab-glue.js, чтобы превью были доступны и на /lab (карточки),
* и на дашборде («Лаборатория дня»), без загрузки тяжёлого lab-glue.js.
*
* window.LabPreviews — карта id симуляции -> SVG-строка (для дашборда/реестра).
* window.__LabP — те же превью по имени P_* (lab-glue.js берёт их отсюда).
*
* Грузить ПЕРЕД lab-glue.js (на /lab) и на дашборде.
*/
(function () {
function _grid(fg='rgba(255,255,255,0.06)') {
return `<g stroke="${fg}" stroke-width="1">
<line x1="45" y1="0" x2="45" y2="140"/><line x1="90" y1="0" x2="90" y2="140"/>
<line x1="135" y1="0" x2="135" y2="140"/><line x1="180" y1="0" x2="180" y2="140"/>
<line x1="225" y1="0" x2="225" y2="140"/>
<line x1="0" y1="35" x2="270" y2="35"/><line x1="0" y1="70" x2="270" y2="70"/>
<line x1="0" y1="105" x2="270" y2="105"/>
</g>`;
}
function _axes() {
return `<line x1="0" y1="70" x2="262" y2="70" stroke="rgba(255,255,255,0.32)" stroke-width="1.5"/>
<line x1="135" y1="140" x2="135" y2="6" stroke="rgba(255,255,255,0.32)" stroke-width="1.5"/>
<polygon points="265,70 258,67 258,73" fill="rgba(255,255,255,0.32)"/>
<polygon points="135,4 132,11 138,11" fill="rgba(255,255,255,0.32)"/>`;
}
function _svg(body) {
return `<svg class="sim-preview" viewBox="0 0 270 140" xmlns="http://www.w3.org/2000/svg">
<rect width="270" height="140" fill="#0D0D1A"/>${body}</svg>`;
}
/* 1 — Graph */
const P_GRAPH = _svg(`${_grid()}${_axes()}
<path d="M 15,132 Q 135,20 255,132" stroke="#9B5DE5" stroke-width="2.5" fill="none"/>
<path d="M 0,70 C 34,30 56,30 90,70 C 124,110 146,110 180,70 C 214,30 236,30 270,70"
stroke="#06D6E0" stroke-width="2" fill="none" opacity="0.75"/>`);
/* 2 — Transform: three shifted/scaled sines */
const P_TRANSFORM = _svg(`${_grid()}${_axes()}
<path d="M 0,70 C 34,30 56,30 90,70 C 124,110 146,110 180,70 C 214,30 236,30 270,70"
stroke="#9B5DE5" stroke-width="2" fill="none" opacity="0.9"/>
<path d="M 0,53 C 22,24 42,24 67,53 C 92,82 112,82 135,53 C 158,24 178,24 202,53 C 227,82 248,82 270,53"
stroke="#06D6E0" stroke-width="2" fill="none" opacity="0.65"/>
<path d="M 0,85 C 45,36 80,36 135,85 C 190,134 225,134 270,85"
stroke="#F15BB5" stroke-width="2" fill="none" opacity="0.55"/>`);
/* 3 — Triangle geometry */
const P_TRIANGLE = _svg(`${_grid('rgba(255,255,255,0.04)')}
<polygon points="60,115 210,115 135,25" fill="rgba(155,93,229,0.1)" stroke="#9B5DE5" stroke-width="2"/>
<line x1="60" y1="115" x2="173" y2="70" stroke="rgba(6,214,224,0.5)" stroke-width="1.3" stroke-dasharray="4,3"/>
<line x1="210" y1="115" x2="98" y2="70" stroke="rgba(6,214,224,0.5)" stroke-width="1.3" stroke-dasharray="4,3"/>
<line x1="135" y1="25" x2="135" y2="115" stroke="rgba(6,214,224,0.5)" stroke-width="1.3" stroke-dasharray="4,3"/>
<circle cx="135" cy="78" r="3" fill="#06D6E0"/>
<rect x="131" y="111" width="8" height="8" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="1.2"/>`);
/* 4 — Inscribed/circumscribed circles */
const P_CIRCLES = _svg(`${_grid('rgba(255,255,255,0.04)')}
<polygon points="80,118 190,118 135,22" fill="rgba(155,93,229,0.08)" stroke="#9B5DE5" stroke-width="1.8"/>
<circle cx="135" cy="85" r="33" fill="none" stroke="#06D6E0" stroke-width="1.8" stroke-dasharray="5,3" opacity="0.7"/>
<circle cx="135" cy="55" r="52" fill="none" stroke="#F15BB5" stroke-width="1.5" stroke-dasharray="5,3" opacity="0.5"/>
<circle cx="135" cy="85" r="3" fill="#06D6E0" opacity="0.8"/>
<circle cx="135" cy="55" r="3" fill="#F15BB5" opacity="0.8"/>`);
/* 5 — Quadratic roots: parabola crossing x-axis */
const P_QUADRATIC = _svg(`${_grid()}${_axes()}
<path d="M 55,125 Q 135,8 215,125" stroke="#9B5DE5" stroke-width="2.5" fill="none"/>
<circle cx="85" cy="70" r="5" fill="#F15BB5" stroke="#fff" stroke-width="1.5"/>
<circle cx="185" cy="70" r="5" fill="#F15BB5" stroke="#fff" stroke-width="1.5"/>
<line x1="85" y1="68" x2="85" y2="125" stroke="rgba(241,91,181,0.35)" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="185" y1="68" x2="185" y2="125" stroke="rgba(241,91,181,0.35)" stroke-width="1" stroke-dasharray="3,3"/>
<text x="135" y="136" font-size="9" fill="rgba(255,255,255,0.4)" text-anchor="middle" font-family="Manrope,sans-serif">D = b²− 4ac</text>`);
/* 6 — 3D geometry: isometric cube */
const P_3D = _svg(`${_grid('rgba(255,255,255,0.04)')}
<polygon points="135,20 210,58 210,115 135,77" fill="rgba(155,93,229,0.15)" stroke="#9B5DE5" stroke-width="1.8"/>
<polygon points="135,20 60,58 60,115 135,77" fill="rgba(155,93,229,0.08)" stroke="#9B5DE5" stroke-width="1.8"/>
<polygon points="60,58 135,20 210,58 135,96" fill="rgba(155,93,229,0.22)" stroke="#9B5DE5" stroke-width="1.8"/>
<line x1="135" y1="77" x2="135" y2="96" stroke="#9B5DE5" stroke-width="1.8"/>
<text x="135" y="132" font-size="9" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">V = a³</text>`);
/* 7 — Probability: histogram bars */
const P_PROB = _svg(`${_grid()}
<line x1="30" y1="15" x2="30" y2="118" stroke="rgba(255,255,255,0.35)" stroke-width="1.5"/>
<line x1="28" y1="118" x2="255" y2="118" stroke="rgba(255,255,255,0.35)" stroke-width="1.5"/>
<rect x="38" y="90" width="24" height="28" fill="rgba(155,93,229,0.6)" rx="2"/>
<rect x="68" y="72" width="24" height="46" fill="rgba(155,93,229,0.7)" rx="2"/>
<rect x="98" y="44" width="24" height="74" fill="rgba(155,93,229,0.85)" rx="2"/>
<rect x="128" y="32" width="24" height="86" fill="#9B5DE5" rx="2"/>
<rect x="158" y="50" width="24" height="68" fill="rgba(155,93,229,0.8)" rx="2"/>
<rect x="188" y="76" width="24" height="42" fill="rgba(155,93,229,0.65)" rx="2"/>
<rect x="218" y="96" width="24" height="22" fill="rgba(155,93,229,0.5)" rx="2"/>`);
/* 8 — Normal distribution: bell curve */
const P_NORMAL = _svg(`${_grid()}
<line x1="10" y1="118" x2="260" y2="118" stroke="rgba(255,255,255,0.32)" stroke-width="1.5"/>
<path d="M 10,116 C 50,115 80,110 100,90 C 115,72 125,35 135,22 C 145,35 155,72 170,90 C 190,110 220,115 260,116"
stroke="#9B5DE5" stroke-width="2.5" fill="none"/>
<path d="M 100,90 C 115,72 125,35 135,22 C 145,35 155,72 170,90 L 170,118 L 100,118 Z"
fill="rgba(155,93,229,0.15)"/>
<line x1="135" y1="22" x2="135" y2="118" stroke="rgba(255,255,255,0.2)" stroke-width="1" stroke-dasharray="3,3"/>
<text x="135" y="132" font-size="9" fill="rgba(255,255,255,0.4)" text-anchor="middle" font-family="Manrope,sans-serif">μ = 0, σ = 1</text>`);
/* 8b — Trig circle */
const P_TRIGCIRCLE = _svg(`${_grid('rgba(255,255,255,0.04)')}
<line x1="30" y1="70" x2="240" y2="70" stroke="rgba(255,255,255,0.25)" stroke-width="1.2"/>
<line x1="135" y1="8" x2="135" y2="132" stroke="rgba(255,255,255,0.25)" stroke-width="1.2"/>
<circle cx="135" cy="70" r="52" fill="none" stroke="rgba(255,255,255,0.18)" stroke-width="1.8"/>
<line x1="135" y1="70" x2="172" y2="33" stroke="rgba(255,255,255,0.45)" stroke-width="1.3"/>
<line x1="135" y1="70" x2="172" y2="70" stroke="#06D6E0" stroke-width="2.5"/>
<line x1="172" y1="70" x2="172" y2="33" stroke="#EF476F" stroke-width="2.5"/>
<circle cx="172" cy="33" r="5" fill="#9B5DE5"/>
<path d="M 148,70 A 13,13 0 0,0 144,60" stroke="rgba(155,93,229,0.6)" stroke-width="1.5" fill="none"/>
<text x="135" y="136" font-size="9" fill="rgba(255,255,255,0.4)" text-anchor="middle" font-family="Manrope,sans-serif">sin · cos · tg · ctg</text>`);
/* 9 — Projectile motion */
const P_PROJECTILE = _svg(`${_grid('rgba(255,255,255,0.04)')}
<line x1="15" y1="118" x2="255" y2="118" stroke="rgba(255,255,255,0.32)" stroke-width="1.5"/>
<path d="M 20,118 Q 135,18 250,118" stroke="#06D6E0" stroke-width="2.5" fill="none"/>
<circle cx="20" cy="118" r="5" fill="#06D6E0"/>
<line x1="20" y1="118" x2="52" y2="80" stroke="rgba(6,214,224,0.8)" stroke-width="1.5"
marker-end="url(#arr)"/>
<defs><marker id="arr" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto">
<path d="M0,0 L6,3 L0,6 Z" fill="#06D6E0"/>
</marker></defs>
<line x1="135" y1="18" x2="135" y2="118" stroke="rgba(255,255,255,0.15)" stroke-width="1" stroke-dasharray="3,3"/>
<text x="135" y="132" font-size="9" fill="rgba(255,255,255,0.4)" text-anchor="middle" font-family="Manrope,sans-serif">x = v₀cos(α)·t</text>`);
/* 10 — Pendulum */
const P_PENDULUM = _svg(`${_grid('rgba(255,255,255,0.04)')}
<line x1="135" y1="15" x2="165" y2="95" stroke="rgba(255,255,255,0.5)" stroke-width="2"/>
<circle cx="165" cy="100" r="12" fill="rgba(6,214,224,0.25)" stroke="#06D6E0" stroke-width="2"/>
<line x1="135" y1="15" x2="95" y2="95" stroke="rgba(255,255,255,0.2)" stroke-width="1.5" stroke-dasharray="4,3"/>
<circle cx="95" cy="100" r="12" fill="none" stroke="rgba(6,214,224,0.3)" stroke-width="1.5" stroke-dasharray="3,3"/>
<path d="M 110,60 A 55,55 0 0 1 160,60" fill="none" stroke="rgba(6,214,224,0.4)" stroke-width="1.2" stroke-dasharray="3,3"/>
<circle cx="135" cy="15" r="4" fill="rgba(255,255,255,0.5)"/>
<text x="135" y="132" font-size="9" fill="rgba(255,255,255,0.4)" text-anchor="middle" font-family="Manrope,sans-serif">T = 2π√(l/g)</text>`);
/* 11 — Collision */
const P_COLLISION = _svg(`${_grid('rgba(255,255,255,0.04)')}
<line x1="15" y1="70" x2="255" y2="70" stroke="rgba(255,255,255,0.15)" stroke-width="1"/>
<circle cx="70" cy="70" r="28" fill="rgba(6,214,224,0.15)" stroke="#06D6E0" stroke-width="2"/>
<text x="70" y="75" font-size="11" fill="#06D6E0" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">m₁</text>
<line x1="100" y1="70" x2="120" y2="70" stroke="#06D6E0" stroke-width="2" marker-end="url(#a2)"/>
<circle cx="195" cy="70" r="20" fill="rgba(241,91,181,0.15)" stroke="#F15BB5" stroke-width="2"/>
<text x="195" y="75" font-size="11" fill="#F15BB5" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">m₂</text>
<line x1="175" y1="70" x2="155" y2="70" stroke="#F15BB5" stroke-width="2" marker-end="url(#a3)"/>
<defs>
<marker id="a2" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" fill="#06D6E0"/></marker>
<marker id="a3" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" fill="#F15BB5"/></marker>
</defs>`);
/* 13 — Electric circuit */
const P_CIRCUIT = _svg(`${_grid('rgba(255,255,255,0.04)')}
<rect x="30" y="25" width="210" height="90" fill="none" stroke="rgba(255,255,255,0.25)" stroke-width="1.5" rx="4"/>
<line x1="30" y1="70" x2="70" y2="70" stroke="#06D6E0" stroke-width="2"/>
<rect x="70" y="58" width="36" height="24" fill="rgba(6,214,224,0.15)" stroke="#06D6E0" stroke-width="1.8" rx="3"/>
<text x="88" y="74" font-size="10" fill="#06D6E0" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">R₁</text>
<line x1="106" y1="70" x2="130" y2="70" stroke="#06D6E0" stroke-width="2"/>
<rect x="130" y="58" width="36" height="24" fill="rgba(6,214,224,0.15)" stroke="#06D6E0" stroke-width="1.8" rx="3"/>
<text x="148" y="74" font-size="10" fill="#06D6E0" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">R₂</text>
<line x1="166" y1="70" x2="190" y2="70" stroke="#06D6E0" stroke-width="2"/>
<rect x="190" y="56" width="18" height="28" fill="rgba(241,91,181,0.15)" stroke="#F15BB5" stroke-width="1.8" rx="3"/>
<line x1="208" y1="70" x2="240" y2="70" stroke="#06D6E0" stroke-width="2"/>
<text x="135" y="132" font-size="9" fill="rgba(255,255,255,0.4)" text-anchor="middle" font-family="Manrope,sans-serif">I = U/R</text>`);
/* 14 — Magnetic field */
const P_MAGNETIC = _svg(`
<rect width="270" height="140" fill="#05050F"/>
${_grid('rgba(155,93,229,0.06)')}
<defs>
<radialGradient id="mg1" cx="38%" cy="50%"><stop offset="0%" stop-color="#06D6E0" stop-opacity=".55"/><stop offset="100%" stop-color="#06D6E0" stop-opacity="0"/></radialGradient>
<radialGradient id="mg2" cx="62%" cy="50%"><stop offset="0%" stop-color="#F15BB5" stop-opacity=".55"/><stop offset="100%" stop-color="#F15BB5" stop-opacity="0"/></radialGradient>
</defs>
<rect width="270" height="140" fill="url(#mg1)" opacity=".7"/>
<rect width="270" height="140" fill="url(#mg2)" opacity=".7"/>
<ellipse cx="95" cy="70" rx="45" ry="45" fill="none" stroke="#06D6E0" stroke-width="1.4" stroke-dasharray="5,3" opacity=".6"/>
<ellipse cx="95" cy="70" rx="70" ry="60" fill="none" stroke="#06D6E0" stroke-width="1" stroke-dasharray="4,4" opacity=".3"/>
<ellipse cx="175" cy="70" rx="45" ry="45" fill="none" stroke="#F15BB5" stroke-width="1.4" stroke-dasharray="5,3" opacity=".6"/>
<ellipse cx="175" cy="70" rx="70" ry="60" fill="none" stroke="#F15BB5" stroke-width="1" stroke-dasharray="4,4" opacity=".3"/>
<path d="M95,30 C160,30 110,70 175,70" stroke="rgba(255,255,255,0.25)" stroke-width="1.2" fill="none"/>
<path d="M95,110 C160,110 110,70 175,70" stroke="rgba(255,255,255,0.25)" stroke-width="1.2" fill="none"/>
<circle cx="95" cy="70" r="11" fill="rgba(5,5,20,0.9)" stroke="#06D6E0" stroke-width="2.2"/>
<circle cx="95" cy="70" r="4" fill="#06D6E0"/>
<circle cx="175" cy="70" r="11" fill="rgba(5,5,20,0.9)" stroke="#F15BB5" stroke-width="2.2"/>
<line x1="170" y1="65" x2="180" y2="75" stroke="#F15BB5" stroke-width="2"/>
<line x1="180" y1="65" x2="170" y2="75" stroke="#F15BB5" stroke-width="2"/>
<text x="135" y="132" font-size="9" fill="rgba(255,255,255,0.4)" text-anchor="middle" font-family="Manrope,sans-serif">B = μ₀I / 2πr</text>`);
/* 14 — Electric field lines */
const P_FIELD = _svg(`${_grid('rgba(255,255,255,0.04)')}
<circle cx="135" cy="70" r="10" fill="rgba(155,93,229,0.3)" stroke="#9B5DE5" stroke-width="2"/>
<text x="135" y="74" font-size="10" fill="#9B5DE5" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="800">+</text>
<g stroke="#9B5DE5" stroke-width="1.3" fill="none" opacity="0.6">
<path d="M135,60 L135,20"/><path d="M135,80 L135,120"/>
<path d="M125,63 L95,38"/><path d="M145,63 L175,38"/>
<path d="M125,77 L95,102"/><path d="M145,77 L175,102"/>
<path d="M122,70 L80,70"/><path d="M148,70 L190,70"/>
<path d="M125,64 L102,42"/><path d="M145,64 L168,42"/>
<path d="M125,76 L102,98"/><path d="M145,76 L168,98"/>
</g>
<circle cx="135" cy="20" r="2" fill="#9B5DE5" opacity="0.5"/>
<circle cx="135" cy="120" r="2" fill="#9B5DE5" opacity="0.5"/>
<circle cx="80" cy="70" r="2" fill="#9B5DE5" opacity="0.5"/>
<circle cx="190" cy="70" r="2" fill="#9B5DE5" opacity="0.5"/>`);
/* 15 — Thin lens */
const P_LENS = _svg(`${_grid('rgba(255,255,255,0.04)')}
<line x1="10" y1="70" x2="260" y2="70" stroke="rgba(255,255,255,0.25)" stroke-width="1"/>
<path d="M 135,20 Q 155,70 135,120 Q 115,70 135,20" fill="rgba(6,214,224,0.12)" stroke="#06D6E0" stroke-width="2"/>
<line x1="30" y1="45" x2="135" y2="45" stroke="#9B5DE5" stroke-width="1.8"/>
<line x1="135" y1="45" x2="230" y2="90" stroke="#9B5DE5" stroke-width="1.8"/>
<line x1="30" y1="70" x2="230" y2="70" stroke="#06D6E0" stroke-width="1.5" stroke-dasharray="3,3" opacity="0.5"/>
<line x1="30" y1="95" x2="135" y2="95" stroke="#F15BB5" stroke-width="1.8"/>
<line x1="135" y1="95" x2="230" y2="55" stroke="#F15BB5" stroke-width="1.8"/>
<circle cx="30" cy="70" r="5" fill="#9B5DE5" opacity="0.7"/>
<line x1="30" y1="40" x2="30" y2="100" stroke="rgba(255,255,255,0.4)" stroke-width="1.5"/>`);
/* 16 — Refraction */
const P_REFRACTION = _svg(`
<rect width="270" height="70" fill="#0D0D1A"/>
<rect y="70" width="270" height="70" fill="rgba(6,214,224,0.07)"/>
<line x1="0" y1="70" x2="270" y2="70" stroke="rgba(6,214,224,0.35)" stroke-width="1.5" stroke-dasharray="6,4"/>
<line x1="135" y1="10" x2="135" y2="130" stroke="rgba(255,255,255,0.15)" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="60" y1="15" x2="135" y2="70" stroke="#9B5DE5" stroke-width="2.5"/>
<polygon points="135,70 127,50 143,50" fill="#9B5DE5" opacity="0.7"/>
<line x1="135" y1="70" x2="195" y2="125" stroke="#06D6E0" stroke-width="2.5"/>
<polygon points="195,125 183,112 196,107" fill="#06D6E0" opacity="0.7"/>
<path d="M 135,50 A 22,22 0 0 0 118,70" fill="none" stroke="rgba(155,93,229,0.5)" stroke-width="1.2"/>
<path d="M 135,90 A 28,28 0 0 1 157,70" fill="none" stroke="rgba(6,214,224,0.5)" stroke-width="1.2"/>
<text x="118" y="63" font-size="9" fill="rgba(155,93,229,0.8)" font-family="Manrope,sans-serif">α</text>
<text x="152" y="87" font-size="9" fill="rgba(6,214,224,0.8)" font-family="Manrope,sans-serif">β</text>
<text x="135" y="136" font-size="9" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">n₁sinα = n₂sinβ</text>`);
/* 17 — Mirrors */
const P_MIRROR = _svg(`${_grid('rgba(255,255,255,0.04)')}
<line x1="10" y1="70" x2="260" y2="70" stroke="rgba(255,255,255,0.25)" stroke-width="1"/>
<path d="M 200,15 Q 184,70 200,125" fill="none" stroke="#06D6E0" stroke-width="2.5"/>
<line x1="200" y1="20" x2="210" y2="30" stroke="rgba(6,214,224,0.25)" stroke-width="1.5"/>
<line x1="200" y1="45" x2="210" y2="55" stroke="rgba(6,214,224,0.25)" stroke-width="1.5"/>
<line x1="200" y1="70" x2="210" y2="80" stroke="rgba(6,214,224,0.25)" stroke-width="1.5"/>
<line x1="200" y1="95" x2="210" y2="105" stroke="rgba(6,214,224,0.25)" stroke-width="1.5"/>
<line x1="200" y1="118" x2="210" y2="128" stroke="rgba(6,214,224,0.25)" stroke-width="1.5"/>
<circle cx="130" cy="70" r="4" fill="#06D6E0" opacity="0.8"/>
<text x="130" y="84" text-anchor="middle" font-size="9" fill="#06D6E0" font-family="Manrope,sans-serif">F</text>
<line x1="50" y1="70" x2="50" y2="30" stroke="#9B5DE5" stroke-width="2"/>
<polygon points="50,30 44,42 56,42" fill="#9B5DE5"/>
<line x1="50" y1="30" x2="200" y2="30" stroke="#06D6E0" stroke-width="1.5"/>
<line x1="200" y1="30" x2="70" y2="105" stroke="#06D6E0" stroke-width="1.5"/>
<line x1="50" y1="30" x2="200" y2="70" stroke="#7BF5A4" stroke-width="1.5"/>
<line x1="200" y1="70" x2="70" y2="105" stroke="#7BF5A4" stroke-width="1.5"/>
<line x1="70" y1="70" x2="70" y2="105" stroke="#EF476F" stroke-width="2"/>
<polygon points="70,105 64,93 76,93" fill="#EF476F"/>`);
/* 18 — Isoprocesses */
const P_ISOPROCESS = _svg(`${_grid('rgba(255,255,255,0.04)')}
<line x1="30" y1="10" x2="30" y2="125" stroke="rgba(255,255,255,0.3)" stroke-width="1.5"/>
<line x1="30" y1="125" x2="265" y2="125" stroke="rgba(255,255,255,0.3)" stroke-width="1.5"/>
<path d="M 50,20 Q 140,60 240,110" fill="none" stroke="#EF476F" stroke-width="2" opacity="0.5" stroke-dasharray="4,3"/>
<path d="M 50,20 Q 130,80 230,118" fill="none" stroke="#FFD166" stroke-width="2" opacity="0.5" stroke-dasharray="4,3"/>
<line x1="50" y1="20" x2="50" y2="118" stroke="#06D6E0" stroke-width="2" opacity="0.5" stroke-dasharray="4,3"/>
<line x1="50" y1="20" x2="230" y2="20" stroke="#7BF5A4" stroke-width="2" opacity="0.5" stroke-dasharray="4,3"/>
<path d="M 50,20 Q 120,55 220,108" fill="none" stroke="#EF476F" stroke-width="2.5"/>
<circle cx="50" cy="20" r="5" fill="#9B5DE5"/>
<circle cx="220" cy="108" r="5" fill="#EF476F"/>
<text x="240" y="113" font-size="9" fill="#EF476F" font-family="Manrope,sans-serif">2</text>
<text x="40" y="16" font-size="9" fill="#9B5DE5" font-family="Manrope,sans-serif">1</text>
<text x="255" y="128" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">V</text>
<text x="18" y="12" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">P</text>`);
/* ── Chemistry / Molecular Physics previews ── */
const P_GAS = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
<rect x="6" y="6" width="258" height="128" rx="4" fill="none" stroke="rgba(155,93,229,0.4)" stroke-width="2"/>
${[
[40,30,'#4CC9F0'],[70,80,'#7BF5A4'],[110,25,'#EF476F'],[150,60,'#FFD166'],[190,30,'#4CC9F0'],
[220,90,'#EF476F'],[55,110,'#7BF5A4'],[95,65,'#4CC9F0'],[130,110,'#EF476F'],[170,40,'#FFD166'],
[210,115,'#4CC9F0'],[240,55,'#7BF5A4'],[30,70,'#FFD166'],[80,120,'#EF476F'],[165,95,'#4CC9F0']
].map(([x,y,c])=>`<circle cx="${x}" cy="${y}" r="5" fill="${c}" opacity="0.85"/>`).join('')}
<rect x="6" y="105" width="258" height="29" rx="3" fill="rgba(0,0,0,0.55)"/>
<rect x="18" y="112" width="40" height="12" rx="2" fill="rgba(155,93,229,0.25)"/>
<rect x="18" y="112" width="14" height="12" rx="2" fill="rgba(155,93,229,0.6)"/>
<rect x="70" y="112" width="40" height="12" rx="2" fill="rgba(155,93,229,0.25)"/>
<rect x="70" y="112" width="22" height="12" rx="2" fill="#7BF5A4" opacity="0.7"/>
<rect x="122" y="112" width="40" height="12" rx="2" fill="rgba(155,93,229,0.25)"/>
<rect x="122" y="112" width="30" height="12" rx="2" fill="#EF476F" opacity="0.7"/>
<text x="202" y="121" font-size="8" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">PV=nRT</text>`);
/* ── Законы Ньютона ── */
const P_NEWTON = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
<line x1="0" y1="105" x2="270" y2="105" stroke="rgba(255,255,255,0.22)" stroke-width="2"/>
<rect x="80" y="75" width="50" height="30" rx="5" fill="rgba(6,214,224,0.18)" stroke="#06D6E0" stroke-width="2"/>
<line x1="130" y1="90" x2="175" y2="90" stroke="#EF476F" stroke-width="2.5" marker-end="url(#na)"/>
<defs><marker id="na" markerWidth="7" markerHeight="7" refX="5" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 Z" fill="#EF476F"/></marker></defs>
<text x="153" y="84" font-size="9" fill="#EF476F" font-family="Manrope,sans-serif" font-weight="700">F</text>
<line x1="105" y1="75" x2="105" y2="55" stroke="rgba(255,255,255,0.3)" stroke-width="1.2" stroke-dasharray="3,2"/>
<line x1="105" y1="55" x2="175" y2="55" stroke="rgba(255,255,255,0.18)" stroke-width="1" stroke-dasharray="3,3"/>
<circle cx="65" cy="90" r="12" fill="rgba(155,93,229,0.2)" stroke="#9B5DE5" stroke-width="1.8"/>
<text x="65" y="94" font-size="9" fill="#9B5DE5" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">m₂</text>
<line x1="195" y1="90" x2="220" y2="90" stroke="#9B5DE5" stroke-width="2" stroke-dasharray="4,3" marker-end="url(#nb)"/>
<defs><marker id="nb" markerWidth="7" markerHeight="7" refX="5" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 Z" fill="#9B5DE5"/></marker></defs>
<text x="135" y="130" font-size="8" fill="rgba(255,255,255,0.4)" text-anchor="middle" font-family="Manrope,sans-serif">a = F/m · III законы Ньютона</text>`);
/* ── Песочница сил ── */
const P_SANDBOX = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
${_grid('rgba(255,255,255,0.03)')}
<line x1="0" y1="115" x2="270" y2="115" stroke="rgba(155,93,229,0.35)" stroke-width="2"/>
<rect x="55" y="82" width="44" height="33" rx="6" fill="rgba(239,71,111,0.22)" stroke="#EF476F" stroke-width="1.8"/>
<text x="77" y="103" font-size="8" fill="#fff" text-anchor="middle" font-family="monospace" font-weight="700">5кг</text>
<circle cx="180" cy="88" r="18" fill="rgba(76,201,240,0.18)" stroke="#4CC9F0" stroke-width="1.8"/>
<text x="180" y="92" font-size="8" fill="#fff" text-anchor="middle" font-family="monospace" font-weight="700">8кг</text>
<line x1="99" y1="95" x2="140" y2="95" stroke="#FFD166" stroke-width="2.2" marker-end="url(#sa)"/>
<line x1="198" y1="88" x2="238" y2="68" stroke="#7BF5A4" stroke-width="2.2" marker-end="url(#sb)"/>
<defs>
<marker id="sa" markerWidth="7" markerHeight="7" refX="5" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 Z" fill="#FFD166"/></marker>
<marker id="sb" markerWidth="7" markerHeight="7" refX="5" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 Z" fill="#7BF5A4"/></marker>
</defs>
<text x="120" y="87" font-size="8" fill="#FFD166" font-family="monospace">F₁</text>
<text x="225" y="63" font-size="8" fill="#7BF5A4" font-family="monospace">F₂</text>
<text x="135" y="133" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">Песочница сил · F = ma</text>`);
const P_HYDRO = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
${_grid('rgba(255,255,255,0.03)')}
<!-- left tall beaker with water + submerged body -->
<path d="M 38,30 L 38,118 Q 38,124 44,124 L 96,124 Q 102,124 102,118 L 102,30" fill="none" stroke="rgba(255,255,255,0.45)" stroke-width="1.6"/>
<rect x="40" y="56" width="60" height="66" rx="2" fill="rgba(41,121,255,0.32)"/>
<path d="M 40,56 Q 55,52 70,56 T 100,56 L 100,60 L 40,60 Z" fill="rgba(76,201,240,0.55)"/>
<text x="70" y="48" font-size="7.5" fill="rgba(76,201,240,0.9)" text-anchor="middle" font-family="monospace">P = ρgh</text>
<!-- submerged cube + buoyancy arrow up -->
<rect x="55" y="80" width="28" height="22" rx="3" fill="rgba(255,209,102,0.55)" stroke="#FFD166" stroke-width="1.4"/>
<line x1="69" y1="80" x2="69" y2="62" stroke="#7BF5A4" stroke-width="2.2" marker-end="url(#hb)"/>
<text x="79" y="72" font-size="7.5" fill="#7BF5A4" font-family="monospace">F_A</text>
<!-- U-tube manometer right -->
<path d="M 150,40 L 150,108 Q 150,116 158,116 L 198,116 Q 206,116 206,108 L 206,40" fill="none" stroke="rgba(255,255,255,0.45)" stroke-width="1.6"/>
<path d="M 152,80 L 168,80 L 168,114 L 188,114 L 188,68 L 204,68 L 204,80" fill="none" stroke="none"/>
<rect x="152" y="80" width="16" height="34" fill="rgba(76,201,240,0.55)"/>
<rect x="188" y="68" width="16" height="46" fill="rgba(76,201,240,0.55)"/>
<line x1="168" y1="80" x2="188" y2="68" stroke="rgba(76,201,240,0.55)" stroke-width="6" stroke-linecap="round"/>
<line x1="172" y1="80" x2="184" y2="80" stroke="rgba(255,209,102,0.7)" stroke-width="1" stroke-dasharray="2 2"/>
<line x1="192" y1="68" x2="204" y2="68" stroke="rgba(255,209,102,0.7)" stroke-width="1" stroke-dasharray="2 2"/>
<text x="178" y="36" font-size="7.5" fill="rgba(76,201,240,0.9)" text-anchor="middle" font-family="monospace">Δh</text>
<line x1="178" y1="38" x2="178" y2="66" stroke="rgba(255,209,102,0.85)" stroke-width="1.2" marker-end="url(#hb)"/>
<!-- communicating vessels (small inline icon) -->
<path d="M 224,80 L 224,116 L 258,116 L 258,52" fill="none" stroke="rgba(255,255,255,0.45)" stroke-width="1.4"/>
<rect x="226" y="100" width="6" height="14" fill="rgba(76,201,240,0.55)"/>
<rect x="250" y="100" width="6" height="14" fill="rgba(76,201,240,0.55)"/>
<line x1="232" y1="114" x2="250" y2="114" stroke="rgba(76,201,240,0.55)" stroke-width="3" stroke-linecap="round"/>
<defs>
<marker id="hb" markerWidth="7" markerHeight="7" refX="5" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 Z" fill="#7BF5A4"/></marker>
</defs>
<text x="135" y="135" font-size="7.5" fill="rgba(255,255,255,0.4)" text-anchor="middle" font-family="Manrope,sans-serif">Архимед · Паскаль · капиллярность</text>`);
/* ── coming soon chem previews (simple) ── */
const P_KINETICS = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
${_grid()}
<path d="M 20,120 C 60,90 100,50 140,35 S 220,28 260,26" fill="none" stroke="#34d399" stroke-width="2" stroke-linecap="round"/>
<path d="M 20,30 C 60,55 100,100 140,112 S 220,118 260,120" fill="none" stroke="#EF476F" stroke-width="2" stroke-linecap="round"/>
<circle cx="140" cy="35" r="4" fill="#34d399"/>
<circle cx="140" cy="112" r="4" fill="#EF476F"/>
<text x="20" y="18" font-size="9" fill="rgba(52,211,153,0.8)" font-family="Manrope,sans-serif">[C] продукт</text>
<text x="180" y="130" font-size="9" fill="rgba(239,71,111,0.8)" font-family="Manrope,sans-serif">[A] реагент</text>`);
const P_EQUILIBRIUM = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
<text x="135" y="30" font-size="11" fill="rgba(255,255,255,0.7)" text-anchor="middle" font-family="Manrope,sans-serif">A + B ⇌ C + D</text>
<rect x="30" y="50" width="60" height="70" rx="4" fill="rgba(155,93,229,0.15)" stroke="rgba(155,93,229,0.4)" stroke-width="1.5"/>
<rect x="100" y="75" width="70" height="45" rx="4" fill="rgba(6,214,224,0.12)" stroke="rgba(6,214,224,0.35)" stroke-width="1.5"/>
<rect x="180" y="55" width="60" height="65" rx="4" fill="rgba(241,91,181,0.12)" stroke="rgba(241,91,181,0.35)" stroke-width="1.5"/>
<text x="60" y="90" font-size="9" fill="#9B5DE5" text-anchor="middle" font-family="Manrope,sans-serif">A,B</text>
<text x="135" y="101" font-size="8" fill="#06D6E0" text-anchor="middle" font-family="Manrope,sans-serif">K</text>
<text x="210" y="91" font-size="9" fill="#F15BB5" text-anchor="middle" font-family="Manrope,sans-serif">C,D</text>`);
const P_ELECTROLYSIS = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
<rect x="20" y="30" width="230" height="90" rx="6" fill="rgba(6,214,224,0.07)" stroke="rgba(6,214,224,0.2)" stroke-width="1.5"/>
<rect x="50" y="20" width="12" height="80" rx="3" fill="#9B5DE5" opacity="0.8"/>
<rect x="208" y="20" width="12" height="80" rx="3" fill="#EF476F" opacity="0.8"/>
${[55,58,61,64,67,70].map(x=>`<circle cx="${x}" cy="${110-Math.random()*20|0}" r="2.5" fill="rgba(155,93,229,0.6)"/>`).join('')}
${[210,214,218,222,226].map(x=>`<circle cx="${x}" cy="${100-Math.random()*15|0}" r="2.5" fill="rgba(239,71,111,0.6)"/>`).join('')}
<text x="56" y="15" font-size="8" fill="#9B5DE5" text-anchor="middle" font-family="Manrope,sans-serif"></text>
<text x="214" y="15" font-size="8" fill="#EF476F" text-anchor="middle" font-family="Manrope,sans-serif">+</text>`);
const P_BOHR = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
<circle cx="135" cy="70" r="8" fill="#FFD166" opacity="0.9"/>
<ellipse cx="135" cy="70" rx="30" ry="10" fill="none" stroke="rgba(155,93,229,0.4)" stroke-width="1.5"/>
<ellipse cx="135" cy="70" rx="55" ry="18" fill="none" stroke="rgba(6,214,224,0.3)" stroke-width="1.5"/>
<ellipse cx="135" cy="70" rx="80" ry="27" fill="none" stroke="rgba(241,91,181,0.25)" stroke-width="1.5"/>
<circle cx="165" cy="70" r="4" fill="#9B5DE5"/>
<circle cx="90" cy="70" r="4" fill="#06D6E0"/>
<circle cx="215" cy="70" r="4" fill="#F15BB5"/>
<line x1="165" y1="60" x2="190" y2="35" stroke="rgba(255,214,0,0.6)" stroke-width="1.5" stroke-dasharray="3,2"/>
<circle cx="190" cy="35" r="3" fill="#FFD166" opacity="0.8"/>`);
const P_ORBITALS = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
<ellipse cx="135" cy="70" rx="60" ry="25" fill="rgba(155,93,229,0.15)" stroke="rgba(155,93,229,0.5)" stroke-width="1.5"/>
<ellipse cx="135" cy="70" rx="25" ry="60" fill="rgba(6,214,224,0.1)" stroke="rgba(6,214,224,0.4)" stroke-width="1.5"/>
<circle cx="135" cy="70" r="6" fill="#FFD166" opacity="0.9"/>
<circle cx="95" cy="70" r="5" fill="#9B5DE5" opacity="0.8"/>
<circle cx="175" cy="70" r="5" fill="#9B5DE5" opacity="0.8"/>`);
const P_PH = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
${_grid()}
<path d="M 20,110 L 60,108 L 100,105 L 120,90 L 130,30 L 140,75 L 180,40 L 220,35 L 260,32"
fill="none" stroke="#34d399" stroke-width="2" stroke-linecap="round"/>
<line x1="20" y1="70" x2="260" y2="70" stroke="rgba(255,255,255,0.15)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="20" y="18" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">pH</text>
<text x="240" y="130" font-size="9" fill="rgba(255,255,255,0.4)" font-family="Manrope,sans-serif">V</text>`);
const P_CHEMSANDBOX = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
${_grid()}
<rect x="85" y="20" width="100" height="70" rx="8" fill="none" stroke="rgba(75,205,155,0.4)" stroke-width="1.5"/>
<rect x="88" y="55" width="94" height="32" rx="4" fill="rgba(75,205,155,0.15)"/>
<circle cx="110" cy="71" r="4" fill="rgba(255,200,60,0.5)"/>
<circle cx="130" cy="68" r="3" fill="rgba(255,255,255,0.3)"/>
<circle cx="150" cy="73" r="3.5" fill="rgba(90,200,235,0.4)"/>
<text x="135" y="105" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif" text-anchor="middle">A + B <svg class="ic" viewBox="0 0 24 24"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg> C + D</text>
<rect x="40" y="115" width="28" height="18" rx="4" fill="rgba(255,255,255,0.05)" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/>
<rect x="75" y="115" width="28" height="18" rx="4" fill="rgba(255,255,255,0.05)" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/>
<rect x="110" y="115" width="28" height="18" rx="4" fill="rgba(255,255,255,0.05)" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/>
<rect x="145" y="115" width="28" height="18" rx="4" fill="rgba(255,255,255,0.05)" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/>
<rect x="180" y="115" width="28" height="18" rx="4" fill="rgba(255,255,255,0.05)" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/>
<circle cx="54" cy="121" r="3" fill="#78D278"/><circle cx="89" cy="121" r="3" fill="#7BF5A4"/>
<circle cx="124" cy="121" r="3" fill="#4CC9F0"/><circle cx="159" cy="121" r="3" fill="#9BB8CC"/>
<circle cx="194" cy="121" r="3" fill="#FFD166"/>`);
const P_STOICHIOMETRY = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
${_grid()}
<rect x="18" y="28" width="52" height="68" rx="5" fill="none" stroke="rgba(155,184,204,0.5)" stroke-width="1.5"/>
<rect x="20" y="58" width="48" height="36" rx="3" fill="rgba(155,184,204,0.12)"/>
<circle cx="32" cy="74" r="4" fill="rgba(155,184,204,0.7)"/>
<circle cx="45" cy="70" r="4" fill="rgba(155,184,204,0.7)"/>
<circle cx="58" cy="76" r="4" fill="rgba(155,184,204,0.7)"/>
<text x="44" y="46" font-size="9" fill="rgba(155,184,204,0.9)" font-family="Manrope,sans-serif" text-anchor="middle">Zn</text>
<rect x="78" y="28" width="60" height="68" rx="5" fill="none" stroke="rgba(120,210,120,0.5)" stroke-width="1.5"/>
<rect x="80" y="58" width="56" height="36" rx="3" fill="rgba(120,210,120,0.12)"/>
<circle cx="92" cy="72" r="3.5" fill="rgba(120,210,120,0.7)"/>
<circle cx="103" cy="76" r="3.5" fill="rgba(120,210,120,0.7)"/>
<circle cx="114" cy="70" r="3.5" fill="rgba(120,210,120,0.7)"/>
<circle cx="125" cy="74" r="3.5" fill="rgba(120,210,120,0.7)"/>
<text x="108" y="46" font-size="9" fill="rgba(120,210,120,0.9)" font-family="Manrope,sans-serif" text-anchor="middle">2HCl</text>
<text x="150" y="68" font-size="14" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif" text-anchor="middle">&#8594;</text>
<rect x="162" y="28" width="52" height="68" rx="5" fill="none" stroke="rgba(76,201,240,0.5)" stroke-width="1.5"/>
<rect x="164" y="58" width="48" height="36" rx="3" fill="rgba(76,201,240,0.12)"/>
<circle cx="176" cy="74" r="4" fill="rgba(76,201,240,0.7)"/>
<circle cx="189" cy="70" r="4" fill="rgba(76,201,240,0.7)"/>
<circle cx="202" cy="76" r="4" fill="rgba(76,201,240,0.7)"/>
<text x="184" y="46" font-size="9" fill="rgba(76,201,240,0.9)" font-family="Manrope,sans-serif" text-anchor="middle">ZnCl&#8322;</text>
<rect x="222" y="28" width="36" height="68" rx="5" fill="none" stroke="rgba(255,209,102,0.5)" stroke-width="1.5"/>
<circle cx="235" cy="68" r="3" fill="rgba(255,209,102,0.7)"/>
<circle cx="248" cy="74" r="3" fill="rgba(255,209,102,0.7)"/>
<text x="240" y="46" font-size="9" fill="rgba(255,209,102,0.9)" font-family="Manrope,sans-serif" text-anchor="middle">H&#8322;</text>
<text x="135" y="118" font-size="8" fill="rgba(239,71,111,0.7)" font-family="Manrope,sans-serif" text-anchor="middle">&#9679; лимит</text>`);
/* Periodic Table — 6×4 coloured cell grid */
const P_PERIODIC = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
${(function(){
const cols=18,rows=4,pad=6,w=(270-pad*2)/cols,h=(140-pad*2)/rows;
const colors=['#EF476F','#FF6B35','#FFD166','#7BF5A4','#C77DFF','#A8DADC',
'#7B8EF7','#06D6E0','#9B5DE5','#F15BB5','#EF476F','#FF6B35',
'#06D6E0','#7B8EF7','#FFD166','#C77DFF','#A8DADC','#7BF5A4'];
let s='';
for(let r=0;r<rows;r++) for(let c=0;c<cols;c++){
const x=pad+c*w, y=pad+r*h;
const skip=(r===0&&c>=2&&c<=15)||(r===1&&c>=2&&c<=11);
if(!skip) s+=`<rect x="${x.toFixed(1)}" y="${y.toFixed(1)}" width="${(w-1.5).toFixed(1)}" height="${(h-1.5).toFixed(1)}" rx="2" fill="${colors[c]}" opacity="0.7"/>`;
}
return s;
})()}
<text x="135" y="134" font-size="8" fill="rgba(255,255,255,0.35)" font-family="Manrope,sans-serif" text-anchor="middle">118 элементов</text>`);
const P_CRYSTAL = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
${[
[80,40],[135,40],[190,40],
[55,75],[110,75],[165,75],[220,75],
[80,110],[135,110],[190,110]
].map(([x,y],i)=>`<circle cx="${x}" cy="${y}" r="${i%2===0?7:5}" fill="${i%2===0?'#9B5DE5':'#06D6E0'}" opacity="0.8"/>`).join('')}
<line x1="80" y1="40" x2="135" y2="40" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="135" y1="40" x2="190" y2="40" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="80" y1="40" x2="55" y2="75" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="135" y1="40" x2="110" y2="75" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="190" y1="40" x2="165" y2="75" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="190" y1="40" x2="220" y2="75" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="55" y1="75" x2="80" y2="110" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="110" y1="75" x2="135" y2="110" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="165" y1="75" x2="190" y2="110" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="80" y1="110" x2="135" y2="110" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>
<line x1="135" y1="110" x2="190" y2="110" stroke="rgba(255,255,255,0.2)" stroke-width="1.5"/>`);
const P_CELLDIVISION = _svg(`
<rect width="270" height="140" fill="#0e0e18"/>
<ellipse cx="135" cy="70" rx="78" ry="54" fill="rgba(34,211,153,0.07)" stroke="rgba(34,211,153,0.5)" stroke-width="2"/>
<ellipse cx="135" cy="70" rx="44" ry="28" fill="rgba(155,93,229,0.08)" stroke="rgba(155,93,229,0.3)" stroke-width="1" stroke-dasharray="4,3"/>
<line x1="55" y1="70" x2="215" y2="70" stroke="rgba(255,214,0,0.35)" stroke-width="1.2" stroke-dasharray="3,2"/>
<rect x="98" y="57" width="9" height="15" rx="2" fill="#EF476F" opacity="0.9"/>
<rect x="112" y="57" width="9" height="15" rx="2" fill="#FF6B35" opacity="0.9"/>
<rect x="126" y="57" width="9" height="15" rx="2" fill="#9B5DE5" opacity="0.9"/>
<rect x="140" y="57" width="9" height="15" rx="2" fill="#FFD166" opacity="0.9"/>
<rect x="154" y="57" width="9" height="15" rx="2" fill="#EF476F" opacity="0.9"/>
<rect x="98" y="75" width="9" height="15" rx="2" fill="#EF476F" opacity="0.9"/>
<rect x="112" y="75" width="9" height="15" rx="2" fill="#FF6B35" opacity="0.9"/>
<rect x="126" y="75" width="9" height="15" rx="2" fill="#9B5DE5" opacity="0.9"/>
<rect x="140" y="75" width="9" height="15" rx="2" fill="#FFD166" opacity="0.9"/>
<rect x="154" y="75" width="9" height="15" rx="2" fill="#EF476F" opacity="0.9"/>
<line x1="135" y1="16" x2="114" y2="57" stroke="rgba(255,214,0,0.4)" stroke-width="1"/>
<line x1="135" y1="16" x2="135" y2="57" stroke="rgba(255,214,0,0.4)" stroke-width="1"/>
<line x1="135" y1="124" x2="114" y2="90" stroke="rgba(255,214,0,0.4)" stroke-width="1"/>
<line x1="135" y1="124" x2="135" y2="90" stroke="rgba(255,214,0,0.4)" stroke-width="1"/>
<circle cx="135" cy="15" r="5" fill="rgba(255,214,0,0.7)"/>
<circle cx="135" cy="125" r="5" fill="rgba(255,214,0,0.7)"/>
<text x="135" y="137" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">Метафаза · митоз</text>`);
const P_PHOTOSYNTHESIS = _svg(`
<rect width="270" height="140" fill="#0a0e14"/>
<ellipse cx="135" cy="72" rx="100" ry="48" fill="rgba(34,211,153,0.07)" stroke="rgba(34,211,153,0.45)" stroke-width="2"/>
<rect x="52" y="60" width="166" height="22" rx="7" fill="rgba(34,211,153,0.18)" stroke="rgba(34,211,153,0.5)" stroke-width="1.5"/>
<line x1="70" y1="12" x2="79" y2="59" stroke="#FFD166" stroke-width="1.8" stroke-dasharray="3,2" opacity="0.8"/>
<line x1="100" y1="8" x2="107" y2="59" stroke="#FFD166" stroke-width="1.8" stroke-dasharray="3,2" opacity="0.8"/>
<line x1="135" y1="6" x2="135" y2="59" stroke="#FFD166" stroke-width="1.8" stroke-dasharray="3,2" opacity="0.8"/>
<line x1="170" y1="8" x2="163" y2="59" stroke="#FFD166" stroke-width="1.8" stroke-dasharray="3,2" opacity="0.8"/>
<circle cx="70" cy="11" r="5" fill="#FFD166" opacity="0.9"/>
<circle cx="100" cy="7" r="5" fill="#FFD166" opacity="0.9"/>
<circle cx="135" cy="5" r="5" fill="#FFD166" opacity="0.9"/>
<circle cx="170" cy="7" r="5" fill="#FFD166" opacity="0.9"/>
<text x="52" y="98" font-size="8" fill="rgba(6,214,224,0.8)" font-family="Manrope,sans-serif">H₂O</text>
<text x="90" y="106" font-size="8" fill="rgba(255,255,255,0.45)" font-family="Manrope,sans-serif">CO₂</text>
<text x="168" y="52" font-size="8" fill="#9B5DE5" font-family="Manrope,sans-serif">ATP</text>
<text x="185" y="98" font-size="8" fill="#22d399" font-family="Manrope,sans-serif">G3P</text>
<text x="135" y="135" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">Световые реакции · цикл Кальвина</text>`);
const P_ANGRYBIRDS = _svg(`
<rect width="270" height="140" fill="#0f1923"/>
<rect x="0" y="108" width="270" height="32" fill="#3d6b47"/>
<line x1="0" y1="108" x2="270" y2="108" stroke="rgba(255,255,255,0.1)" stroke-width="1"/>
<rect x="175" y="68" width="22" height="40" fill="#b5651d" stroke="#7a3f0a" stroke-width="1.5"/>
<rect x="158" y="56" width="56" height="14" fill="#b5651d" stroke="#7a3f0a" stroke-width="1.5"/>
<rect x="168" y="40" width="18" height="18" fill="#a8d8ea" stroke="#5badd4" stroke-width="1.5"/>
<rect x="202" y="78" width="16" height="30" fill="#7a7a7a" stroke="#444" stroke-width="1.5"/>
<circle cx="232" cy="100" r="10" fill="#22c55e" stroke="#166534" stroke-width="1.5"/>
<circle cx="215" cy="99" r="10" fill="#22c55e" stroke="#166534" stroke-width="1.5"/>
<path d="M 32,102 Q 90,38 148,98" stroke="#ef476f" stroke-width="2.5" fill="none" stroke-dasharray="4,3"/>
<circle cx="32" cy="102" r="9" fill="#e63946"/>
<circle cx="36" cy="98" r="2.5" fill="#fff"/>
<circle cx="37.5" cy="97.5" r="1.1" fill="#111"/>
<line x1="28" y1="94" x2="38" y2="98" stroke="#333" stroke-width="1.5" stroke-linecap="round"/>
<circle cx="21" cy="106" r="6.5" fill="#888" opacity="0.7"/>
<circle cx="10" cy="106" r="5.5" fill="#ffd166" opacity="0.5"/>
<line x1="18" y1="93" x2="22" y2="80" stroke="rgba(255,255,255,0.18)" stroke-width="5" stroke-linecap="round"/>
<line x1="22" y1="80" x2="26" y2="93" stroke="rgba(255,255,255,0.18)" stroke-width="5" stroke-linecap="round"/>
<text x="135" y="130" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">Физика полёта · импульс · разрушение</text>`);
const P_WAVES = _svg(`${_grid()}
<line x1="0" y1="70" x2="270" y2="70" stroke="rgba(255,255,255,0.13)" stroke-width="1" stroke-dasharray="4,3"/>
<path d="M 0,70 C 17,26 33,26 67,70 C 101,114 117,114 135,70 C 153,26 169,26 202,70 C 236,114 252,114 270,70"
stroke="#9B5DE5" stroke-width="2" fill="none" opacity="0.7"/>
<path d="M 0,70 C 22,18 44,18 90,70 C 136,122 158,122 180,70 C 202,18 224,18 270,70"
stroke="#06D6E0" stroke-width="1.5" fill="none" opacity="0.5"/>
<path d="M 0,70 C 12,10 28,8 50,40 C 72,72 88,118 112,85 C 136,52 155,18 180,50 C 205,82 240,108 270,70"
stroke="#F15BB5" stroke-width="2.5" fill="none" opacity="0.9"/>
<text x="135" y="132" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">v = \u03bbf \u00b7 y = A sin(\u03c9t \u2212 kx) \u00b7 \u0441\u0442\u043e\u044f\u0447\u0438\u0435 \u0432\u043e\u043b\u043d\u044b</text>`);
/* Radioactive decay preview */
const P_RADIOACTIVE = _svg(`${_grid()}
<circle cx="55" cy="45" r="5" fill="#9B5DE5" opacity="0.9"/>
<circle cx="90" cy="65" r="5" fill="#9B5DE5" opacity="0.9"/>
<circle cx="38" cy="80" r="5" fill="#9B5DE5" opacity="0.7"/>
<circle cx="75" cy="95" r="5" fill="#EF476F" opacity="0.9"/>
<circle cx="110" cy="50" r="5" fill="#EF476F" opacity="0.85"/>
<circle cx="130" cy="85" r="5" fill="#4CAF50" opacity="0.85"/>
<circle cx="155" cy="55" r="5" fill="#9B5DE5" opacity="0.8"/>
<circle cx="170" cy="90" r="5" fill="#4CAF50" opacity="0.75"/>
<circle cx="200" cy="45" r="5" fill="#4CAF50" opacity="0.9"/>
<circle cx="215" cy="80" r="5" fill="#4CAF50" opacity="0.85"/>
<circle cx="240" cy="60" r="5" fill="#9B5DE5" opacity="0.7"/>
<path d="M 20,115 Q 67,42 135,52 Q 200,62 270,110"
fill="none" stroke="#9B5DE5" stroke-width="2" opacity="0.55" stroke-dasharray="5,3"/>
<path d="M 20,115 Q 100,110 175,100 Q 230,92 270,85"
fill="none" stroke="#4CAF50" stroke-width="1.5" opacity="0.5"/>
<text x="135" y="132" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">N(t) = N₀·e⁻λt · T½ · цепочки распада</text>`);
/* Heat Engines preview */
const P_HEATENGINE = _svg(`${_grid('rgba(255,255,255,0.04)')}
<line x1="30" y1="10" x2="30" y2="125" stroke="rgba(255,255,255,0.3)" stroke-width="1.5"/>
<line x1="30" y1="125" x2="265" y2="125" stroke="rgba(255,255,255,0.3)" stroke-width="1.5"/>
<path d="M 55,18 Q 100,30 140,75 Q 160,100 190,115" fill="none" stroke="#EF476F" stroke-width="2.2" opacity="0.85"/>
<path d="M 190,115 Q 205,118 215,110 Q 230,90 225,60" fill="none" stroke="#FFD166" stroke-width="2" opacity="0.8"/>
<path d="M 225,60 Q 200,40 160,32 Q 110,22 55,18" fill="none" stroke="#06D6E0" stroke-width="2.2" opacity="0.85"/>
<path d="M 55,18 Q 48,16 44,22 Q 38,38 45,60 Q 50,80 55,18" fill="rgba(155,93,229,0.12)" stroke="#9B5DE5" stroke-width="1" opacity="0.5"/>
<circle cx="55" cy="18" r="4" fill="#EF476F"/>
<circle cx="190" cy="115" r="4" fill="#FFD166"/>
<circle cx="225" cy="60" r="4" fill="#06D6E0"/>
<text x="44" y="14" font-size="9" fill="#EF476F" font-family="Manrope,sans-serif">A</text>
<text x="195" y="126" font-size="9" fill="#FFD166" font-family="Manrope,sans-serif">B</text>
<text x="230" y="58" font-size="9" fill="#06D6E0" font-family="Manrope,sans-serif">C</text>
<text x="255" y="128" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">V</text>
<text x="18" y="12" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">P</text>
<text x="135" y="115" font-size="8" fill="rgba(155,93,229,0.7)" font-family="Manrope,sans-serif" text-anchor="middle">η = 1 Tc/Th</text>`);
/* Geometry (planimetry) preview */
const P_GEOMETRY = _svg(`${_grid('rgba(255,255,255,0.04)')}
<circle cx="135" cy="70" r="50" fill="rgba(155,93,229,0.07)" stroke="#9B5DE5" stroke-width="1.5"/>
<polygon points="85,99 185,99 135,20" fill="rgba(6,214,224,0.08)" stroke="#06D6E0" stroke-width="1.8"/>
<line x1="85" y1="99" x2="162" y2="57" stroke="rgba(241,91,181,0.45)" stroke-width="1.2" stroke-dasharray="4,3"/>
<line x1="185" y1="99" x2="109" y2="57" stroke="rgba(241,91,181,0.45)" stroke-width="1.2" stroke-dasharray="4,3"/>
<line x1="135" y1="20" x2="135" y2="99" stroke="rgba(241,91,181,0.45)" stroke-width="1.2" stroke-dasharray="4,3"/>
<circle cx="135" cy="64" r="4" fill="#06D6E0" opacity="0.9"/>
<circle cx="85" cy="99" r="4" fill="#9B5DE5"/>
<circle cx="185" cy="99" r="4" fill="#9B5DE5"/>
<circle cx="135" cy="20" r="4" fill="#9B5DE5"/>
<text x="78" y="111" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">A</text>
<text x="188" y="111" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">B</text>
<text x="131" y="16" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">C</text>`);
/* Race sim preview — two objects on a track, x(t) lines */
const P_RACE = _svg(`${_grid('rgba(255,255,255,0.05)')}
<line x1="20" y1="75" x2="250" y2="75" stroke="rgba(255,255,255,0.18)" stroke-width="8" stroke-linecap="round"/>
<line x1="20" y1="75" x2="250" y2="75" stroke="rgba(30,32,50,0.9)" stroke-width="6" stroke-linecap="round"/>
<circle cx="75" cy="75" r="9" fill="rgba(6,214,224,0.2)" stroke="#06D6E0" stroke-width="2"/>
<circle cx="185" cy="75" r="9" fill="rgba(239,71,111,0.2)" stroke="#EF476F" stroke-width="2"/>
<line x1="30" y1="110" x2="130" y2="30" stroke="#06D6E0" stroke-width="2" opacity="0.85"/>
<line x1="30" y1="30" x2="200" y2="110" stroke="#EF476F" stroke-width="2" opacity="0.85"/>
<circle cx="107" cy="60" r="5" fill="#FF6B6B" stroke="#fff" stroke-width="1.5"/>
<text x="115" y="57" font-size="9" fill="#FF6B6B" font-family="Manrope,sans-serif" font-weight="700">встреча</text>
<text x="135" y="130" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">x = x₀ + v₀t + at²/2</text>`);
/* Logic Circuits preview */
const P_LOGIC = _svg(`${_grid('rgba(255,255,255,0.04)')}
<rect x="20" y="38" width="60" height="30" fill="rgba(155,93,229,0.12)" stroke="#9B5DE5" stroke-width="1.5" rx="4"/>
<text x="50" y="57" font-size="9" fill="#9B5DE5" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">AND</text>
<rect x="130" y="20" width="60" height="30" fill="rgba(6,214,224,0.12)" stroke="#06D6E0" stroke-width="1.5" rx="4"/>
<text x="160" y="39" font-size="9" fill="#06D6E0" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">XOR</text>
<rect x="130" y="60" width="60" height="30" fill="rgba(241,91,181,0.12)" stroke="#F15BB5" stroke-width="1.5" rx="4"/>
<text x="160" y="79" font-size="9" fill="#F15BB5" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">AND</text>
<circle cx="18" cy="45" r="4" fill="#4ADE80"/>
<circle cx="18" cy="58" r="4" fill="rgba(255,255,255,0.35)"/>
<line x1="22" y1="45" x2="80" y2="40" stroke="#4ADE80" stroke-width="1.5"/>
<line x1="22" y1="58" x2="80" y2="55" stroke="rgba(255,255,255,0.3)" stroke-width="1.5"/>
<line x1="80" y1="53" x2="112" y2="35" stroke="#4ADE80" stroke-width="1.5"/>
<line x1="80" y1="53" x2="112" y2="75" stroke="#4ADE80" stroke-width="1.5"/>
<line x1="190" y1="35" x2="230" y2="35" stroke="#4ADE80" stroke-width="1.5"/>
<line x1="190" y1="75" x2="230" y2="75" stroke="rgba(255,255,255,0.3)" stroke-width="1.5"/>
<circle cx="234" cy="35" r="5" fill="#4ADE80" opacity="0.9"/>
<circle cx="234" cy="75" r="5" fill="rgba(255,255,255,0.25)"/>
<text x="240" y="39" font-size="8" fill="#4ADE80" font-family="Manrope,sans-serif" font-weight="700">S</text>
<text x="240" y="79" font-size="8" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif" font-weight="700">C</text>
<text x="135" y="130" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">S = A⊕B · C = A∧B · Таблица истинности`);
/* Qualitative Analysis preview */
const P_QUALANALYSIS = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
<rect x="30" y="25" width="30" height="80" rx="6" fill="none" stroke="rgba(200,210,255,0.55)" stroke-width="1.5"/>
<rect x="32" y="42" width="26" height="60" rx="4" fill="rgba(255,255,255,0.12)"/>
<rect x="32" y="77" width="26" height="25" rx="3" fill="rgba(100,180,255,0.4)"/>
<rect x="100" y="25" width="30" height="80" rx="6" fill="none" stroke="rgba(200,210,255,0.55)" stroke-width="1.5"/>
<rect x="102" y="42" width="26" height="60" rx="4" fill="rgba(255,200,80,0.1)"/>
<rect x="102" y="62" width="26" height="40" rx="3" fill="rgba(200,20,20,0.55)"/>
<rect x="170" y="25" width="30" height="80" rx="6" fill="none" stroke="rgba(200,210,255,0.55)" stroke-width="1.5"/>
<rect x="172" y="42" width="26" height="60" rx="4" fill="rgba(80,200,80,0.1)"/>
<rect x="172" y="70" width="26" height="32" rx="3" fill="rgba(255,255,150,0.35)"/>
<text x="45" y="118" font-size="7" fill="rgba(100,180,255,0.9)" text-anchor="middle" font-family="Manrope,sans-serif">Cl</text>
<text x="115" y="118" font-size="7" fill="rgba(200,80,80,0.9)" text-anchor="middle" font-family="Manrope,sans-serif">Fe(III)</text>
<text x="185" y="118" font-size="7" fill="rgba(200,200,80,0.9)" text-anchor="middle" font-family="Manrope,sans-serif">SO4</text>
<text x="135" y="18" font-size="8" fill="rgba(155,93,229,0.7)" text-anchor="middle" font-family="Manrope,sans-serif">AgNO3</text>
<line x1="45" y1="22" x2="45" y2="27" stroke="rgba(200,200,200,0.5)" stroke-width="1"/>
<line x1="115" y1="22" x2="115" y2="27" stroke="rgba(200,200,200,0.5)" stroke-width="1"/>
<line x1="185" y1="22" x2="185" y2="27" stroke="rgba(200,200,200,0.5)" stroke-width="1"/>
<text x="135" y="136" font-size="8" fill="rgba(255,255,255,0.3)" text-anchor="middle" font-family="Manrope,sans-serif">AgCl / Fe(SCN) / BaSO4`);
/* Organic Chemistry preview — benzene ring + OH group */
const P_ORGANIC = _svg(`
<rect width="270" height="140" fill="#0D0D1A"/>
${_grid('rgba(255,255,255,0.03)')}
<!-- benzene ring -->
<polygon points="115,44 145,44 160,70 145,96 115,96 100,70" fill="rgba(155,93,229,0.08)" stroke="#9B5DE5" stroke-width="1.8"/>
<!-- alternating double bonds (inner circle shorthand) -->
<circle cx="130" cy="70" r="18" fill="none" stroke="#9B5DE5" stroke-width="1.2" stroke-dasharray="5,4" opacity="0.55"/>
<!-- C labels -->
<text x="108" y="46" font-size="9" fill="#9B5DE5" font-family="Manrope,sans-serif" font-weight="700">C</text>
<text x="145" y="46" font-size="9" fill="#9B5DE5" font-family="Manrope,sans-serif" font-weight="700">C</text>
<text x="164" y="73" font-size="9" fill="#9B5DE5" font-family="Manrope,sans-serif" font-weight="700">C</text>
<text x="145" y="99" font-size="9" fill="#9B5DE5" font-family="Manrope,sans-serif" font-weight="700">C</text>
<text x="108" y="99" font-size="9" font-family="Manrope,sans-serif" font-weight="700" fill="#9B5DE5">C</text>
<text x="94" y="73" font-size="9" fill="#9B5DE5" font-family="Manrope,sans-serif" font-weight="700">C</text>
<!-- -OH substituent -->
<line x1="160" y1="70" x2="195" y2="55" stroke="rgba(255,255,255,0.4)" stroke-width="1.5"/>
<circle cx="200" cy="52" r="9" fill="rgba(239,71,111,0.25)" stroke="#EF476F" stroke-width="1.5"/>
<text x="200" y="56" font-size="9" fill="#EF476F" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">O</text>
<line x1="209" y1="48" x2="218" y2="40" stroke="rgba(255,255,255,0.3)" stroke-width="1.2"/>
<circle cx="222" cy="37" r="6" fill="rgba(224,224,224,0.2)" stroke="#E0E0E0" stroke-width="1.2"/>
<text x="222" y="41" font-size="8" fill="#E0E0E0" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">H</text>
<!-- chain fragment on right -->
<line x1="100" y1="70" x2="65" y2="70" stroke="rgba(255,255,255,0.35)" stroke-width="1.5"/>
<circle cx="58" cy="70" r="9" fill="rgba(155,93,229,0.2)" stroke="#9B5DE5" stroke-width="1.5"/>
<text x="58" y="74" font-size="9" fill="#9B5DE5" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">C</text>
<text x="135" y="134" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">Конструктор · Ряды · Качественные реакции</text>`);
/* Solutions preview */
const P_SOLUTIONS = _svg(`
<rect x="88" y="20" width="58" height="88" rx="3" fill="none" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
<rect x="89" y="52" width="56" height="55" rx="2" fill="rgba(76,201,240,0.55)"/>
<path d="M89,52 Q108,47 117,52 Q127,57 145,52" fill="none" stroke="rgba(255,255,255,0.3)" stroke-width="1.5"/>
<line x1="90" y1="66" x2="96" y2="66" stroke="rgba(255,255,255,0.25)" stroke-width="1"/>
<line x1="90" y1="81" x2="96" y2="81" stroke="rgba(255,255,255,0.25)" stroke-width="1"/>
<line x1="90" y1="96" x2="96" y2="96" stroke="rgba(255,255,255,0.25)" stroke-width="1"/>
<text x="117" y="80" font-size="15" fill="rgba(255,255,255,0.9)" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="800">20%</text>
<path d="M172,32 Q175,24 179,32 Q183,41 179,45 Q175,49 172,45 Q168,41 172,32 Z" fill="#4CC9F0" opacity="0.85"/>
<line x1="193" y1="20" x2="250" y2="90" stroke="rgba(255,255,255,0.08)" stroke-width="1" stroke-dasharray="3,3"/>
<text x="221" y="42" font-size="8" fill="rgba(76,201,240,0.9)" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">ω%</text>
<text x="221" y="56" font-size="8" fill="rgba(155,93,229,0.9)" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">C&#x2d;M</text>
<text x="221" y="70" font-size="8" fill="rgba(241,91,181,0.9)" text-anchor="middle" font-family="Manrope,sans-serif" font-weight="700">ν моль</text>
<text x="135" y="123" font-size="8" fill="rgba(76,201,240,0.75)" text-anchor="middle" font-family="Manrope,sans-serif">ω = m&#8320;/m&#8203; · 100%</text>
<text x="135" y="135" font-size="7" fill="rgba(255,255,255,0.3)" text-anchor="middle" font-family="Manrope,sans-serif">Калькулятор · Разбавление · Смешивание · S(T)</text>`);
// Экспорт по имени P_* (для lab-glue.js)
window.__LabP = { P_GRAPH, P_TRANSFORM, P_TRIANGLE, P_CIRCLES, P_QUADRATIC, P_3D, P_PROB, P_NORMAL, P_TRIGCIRCLE, P_PROJECTILE, P_PENDULUM, P_COLLISION, P_CIRCUIT, P_MAGNETIC, P_FIELD, P_LENS, P_REFRACTION, P_MIRROR, P_ISOPROCESS, P_GAS, P_NEWTON, P_SANDBOX, P_HYDRO, P_KINETICS, P_EQUILIBRIUM, P_ELECTROLYSIS, P_BOHR, P_ORBITALS, P_PH, P_CHEMSANDBOX, P_STOICHIOMETRY, P_PERIODIC, P_CRYSTAL, P_CELLDIVISION, P_PHOTOSYNTHESIS, P_ANGRYBIRDS, P_WAVES, P_RADIOACTIVE, P_HEATENGINE, P_GEOMETRY, P_RACE, P_LOGIC, P_QUALANALYSIS, P_ORGANIC, P_SOLUTIONS };
// Экспорт по id симуляции (для дашборда и реестра)
window.LabPreviews = {
"graph": P_GRAPH,
"graphtransform": P_TRANSFORM,
"geometry": P_GEOMETRY,
"triangle": P_TRIANGLE,
"quadratic": P_QUADRATIC,
"stereo": P_3D,
"probability": P_PROB,
"trigcircle": P_TRIGCIRCLE,
"normaldist": P_NORMAL,
"projectile": P_PROJECTILE,
"pendulum": P_PENDULUM,
"collision": P_COLLISION,
"emfield": P_MAGNETIC,
"circuit": P_CIRCUIT,
"hydrostatics": P_HYDRO,
"dynamics": P_SANDBOX,
"opticsbench": P_LENS,
"isoprocess": P_ISOPROCESS,
"waves": P_WAVES,
"radioactive": P_RADIOACTIVE,
"race": P_RACE,
"heatengine": P_HEATENGINE,
"logic": P_LOGIC,
"molphys": P_GAS,
"chemistry": P_KINETICS,
"equilibrium": P_EQUILIBRIUM,
"electrolysis": P_ELECTROLYSIS,
"bohratom": P_BOHR,
"orbitals": P_ORBITALS,
"titration": P_PH,
"chemsandbox": P_CHEMSANDBOX,
"stoichiometry": P_STOICHIOMETRY,
"crystal": P_CRYSTAL,
"qualanalysis": P_QUALANALYSIS,
"periodic": P_PERIODIC,
"organic": P_ORGANIC,
"solutions": P_SOLUTIONS,
"celldivision": P_CELLDIVISION,
"photosynthesis": P_PHOTOSYNTHESIS,
"angrybirds": P_ANGRYBIRDS
};
})();