Test commit from debug

This commit is contained in:
Dekart Deploy Bot 2026-06-28 12:13:20 +00:00
parent c7bd6cef74
commit 80f259cbee
23 changed files with 9198 additions and 273 deletions

10
.gitignore vendored
View File

@ -118,3 +118,13 @@ yandex_*.html
# !/wp-content/plugins/мой-кастомный-плагин/**
.ssh_key_for_web
.ssh_key_for_web.pub
opencode.json
AGENTS.md
.opencode/
wp-content/backups-dup-lite/
wp-content/upgrade-temp-backup/
# === Agent/Test files — dev-мусор (не в git) ===
data/
llms.txt
test.php

View File

@ -1,29 +1,24 @@
# Глобальные инструкции проекта
# Site1 — WordPress project
## Язык общения
- ВСЕГДА отвечай на русском языке.
- Код, комментарии, коммиты, техническая документация — на английском (стандарт).
- Объяснения, архитектурные решения, анализ, рекомендации — на русском.
- Если пользователь пишет на другом языке — отвечай на том же языке.
## Primary agent
- @site1 — dedicated WordPress agent (auto-activated, see `.opencode/AGENTS.md`)
## Оптимизация DeepSeek v4 Flash
- Модель: `opencode/deepseek-v4-flash-free`
- DeepSeek v4 Flash оптимизирован для быстрых инференсов с большим контекстом.
- Для сложных задач используй декомпозицию: разбивай на подзадачи, выполняй последовательно.
- Для работы с файлами: предпочитай пакетные операции (множественные чтения/записи) вместо последовательных вызовов.
- Для поиска по коду: используй glob/grep с точными паттернами, избегай избыточного сканирования.
- При работе с большими проектами — используй подзадачи (Task/Explore) для параллельного исследования.
- tool_output лимиты увеличены для работы с большими результатами (max_lines: 300, max_bytes: 10240).
- compaction включён, tail_turns: 20 — удержание контекста в длинных сессиях.
- MCP таймаут: 60s (для работы с медленными внешними сервисами).
## Quick rules
- PHP: PSR-12, type declarations, Composer
- JS: TypeScript strict, ESM, async/await
- Commits: conventional commits (`feat:`, `fix:`, `chore:`, etc.)
## Профиль разработчика
- WP-Architect — WordPress full-stack architect.
- Специализация: теминг, SEO, производительность, маркетинг.
- Доступно >80 скиллов: SEO/AEO, CRO, UI/UX, CI/CD, аналитика, безопасность.
## Security
- NEVER modify wp-config.php without explicit confirmation
- NEVER delete files in uploads/ or wp-content/ without confirmation
- ALWAYS ask before destructive actions (rm, overwrite, DB write, git push)
## Принципы работы
- Используй скиллы при каждой возможности (даже 1% совпадения).
- Caveman-режим активируется пользователем (/caveman).
- Коммиты и PR — на нормальном английском.
- Сообщай о проблемах безопасности сразу, без caveman.
## Commands
- PHP test: `vendor/bin/phpunit`
- PHP lint: `vendor/bin/phpstan analyse`
- JS build: `npm run build`
- JS test: `npm test`
- JS lint: `npm run lint`
## All details
See `.opencode/AGENTS.md` for full architecture, skills, agents list, and directory structure.

View File

@ -1,33 +0,0 @@
{
"$schema": "https://opencode.ai/config.json",
"model": "opencode/deepseek-v4-flash-free",
"instructions": ["AGENTS.md"],
"default_agent": "wp-architect",
"plugin": [
"file:///home/letoline/.config/opencode/plugins/caveman/plugin.js",
"file:///home/letoline/.config/opencode/plugins/claude-mem.js",
"@zenobius/opencode-skillful"
],
"command": {
"wp-audit": { "template": "short" },
"wp-seo": { "template": "short" },
"wp-content": { "template": "short" },
"wp-speed": { "template": "short" },
"design-persona": { "template": "short" },
"design-research": { "template": "short" },
"critique": { "template": "short" },
"gsd": { "template": "short" },
"caveman": { "template": "short" },
"caveman-commit": { "template": "short" },
"caveman-review": { "template": "short" },
"design-screen": { "template": "short" },
"design-system": { "template": "short" },
"ux-audit": { "template": "short" },
"prototype": { "template": "short" },
"school": { "template": "short" }
}
}

0
wp-content/mu-plugins/acf-loader.php Executable file → Normal file
View File

View File

@ -0,0 +1,56 @@
/*
front-page.css стили для шаблона front-page.php
Извлечены из inline style="..." для соответствия
стандартам и оптимизации скорости загрузки
*/
/* ---- Блок "Наши услуги" (блоки-карточки) ---- */
.advantages__block-item {
border-width: 2px;
border-style: solid;
}
.advantages__block-item--school { border-color: #A05A95; }
.advantages__block-item--preschool { border-color: #F682A5; }
.advantages__block-item--afterschool { border-color: #FEDF6F; }
.advantages__block-item--exam { border-color: #F9A474; }
.advantages__block-item--camp { border-color: #C9DA8F; }
.advantages__block-item--clubs { border-color: #B8CEDC; }
/* ---- Информационный блок ---- */
.info__block--compact {
padding-bottom: 0;
margin-top: 40px;
}
.info__block-content_text--full {
max-width: 100%;
}
/* ---- CTA-блок (бесплатная экскурсия) ---- */
.cta-block {
background: linear-gradient(90deg, #fdf6ff 0%, #f5e8f5 100%);
padding: 60px 0;
border-top: 2px solid #A05A95;
border-bottom: 2px solid #A05A95;
margin: 40px 0;
text-align: center;
}
.cta-wrapper {
max-width: 900px;
margin: 0 auto;
}
.cta-wrapper .page_h2 {
margin-bottom: 20px;
line-height: 1.3;
}
.cta-wrapper p {
font-size: 1.2rem;
color: #444;
margin-bottom: 30px;
}
.cta-wrapper .btn-page {
margin: 0 auto;
display: block;
padding: 18px 45px;
font-size: 1.3rem;
box-shadow: 0 4px 15px rgba(160, 90, 149, .3);
}

View File

@ -4490,11 +4490,12 @@ footer a:hover,
.btn-close-resume-form,
.btn-close-request-form {
position: absolute;
top: -10px;
right: -10px;
top: 12px;
right: 12px;
background: #fff0;
border: none;
z-index: 10;
z-index: 99999;
cursor: pointer;
}
.btn-close-resume-form img,
@ -7001,7 +7002,8 @@ footer a:hover,
}
.container {
max-width: 101.25vw;
max-width: 100%;
padding: 0 20px;
}
.footer-wrapper {
@ -9623,3 +9625,653 @@ footer a:hover,
}
/* end mobile= */
/*
🎯 REDESIGN 2026 Bento Grid + Mobile-First CRO
*/
/* ── 1. Новая цветовая палитра ── */
:root {
--accent: #7C3AED;
--accent-hover: #6D28D9;
--accent-light: #F5F3FF;
--accent-50: #F5F3FF;
--accent-100: #EDE9FE;
--accent-200: #DDD6FE;
--accent-500: #8B5CF6;
--accent-700: #5B21B6;
--green-cta: #059669;
--green-hover: #047857;
--green-light: #D1FAE5;
--bg-primary: #FFFFFF;
--bg-soft: #F8FAFC;
--bg-card: #FFFFFF;
--bg-bento-alt: #F1F5F9;
--text-primary: #0F172A;
--text-secondary: #475569;
--text-tertiary: #94A3B8;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
--shadow-md: 0 4px 12px rgba(0,0,0,0.08);
--shadow-lg: 0 10px 30px rgba(0,0,0,0.1);
--shadow-xl: 0 20px 50px rgba(0,0,0,0.15);
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--radius-pill: 28px;
}
/* ── 2. Bento Grid System ── */
.bento {
display: grid;
gap: 16px;
padding: 16px;
max-width: 1280px;
margin: 0 auto;
}
@media (min-width: 640px) {
.bento {
grid-template-columns: repeat(6, 1fr);
gap: 16px;
}
}
@media (min-width: 1024px) {
.bento {
grid-template-columns: repeat(12, 1fr);
gap: 20px;
padding: 24px;
}
}
.bento__hero { grid-column: 1 / -1; grid-row: auto; }
.bento__stat { grid-column: 1 / -1; }
.bento__half { grid-column: 1 / -1; }
.bento__third { grid-column: 1 / -1; }
.bento__full { grid-column: 1 / -1; }
.bento__wide { grid-column: 1 / -1; }
@media (min-width: 640px) {
.bento__hero { grid-column: span 4; grid-row: span 2; }
.bento__stat { grid-column: span 2; }
.bento__half { grid-column: span 3; }
.bento__third { grid-column: span 2; }
.bento__wide { grid-column: span 4; }
}
@media (min-width: 1024px) {
.bento__hero { grid-column: span 6; grid-row: span 2; }
.bento__stat { grid-column: span 3; }
.bento__half { grid-column: span 6; }
.bento__third { grid-column: span 4; }
.bento__wide { grid-column: span 8; }
}
.bento-card {
background: var(--bg-card);
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-sm);
transition: box-shadow .2s, transform .2s;
}
.bento-card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.bento-card--highlight {
background: var(--accent);
color: white;
}
.bento-card--soft {
background: var(--bg-soft);
}
.bento-card--green {
background: var(--green-light);
}
/* ── 3. Social Counters ── */
.social-counters {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 16px;
justify-content: center;
}
@media (min-width: 640px) {
.social-counters {
justify-content: flex-start;
}
}
.social-counter {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
background: var(--bg-soft);
border-radius: var(--radius-sm);
font-size: 14px;
color: var(--text-secondary);
flex: 1;
min-width: 140px;
}
.social-counter__icon {
font-size: 20px;
flex-shrink: 0;
}
.social-counter__number {
font-weight: 700;
color: var(--accent);
font-size: 18px;
white-space: nowrap;
}
.social-counter__label {
color: var(--text-tertiary);
font-size: 12px;
line-height: 1.2;
}
/* ── 4. Sticky CTA Mobile ── */
.sticky-cta-mobile {
display: none;
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
padding: 8px 16px max(12px, env(safe-area-inset-bottom));
background: linear-gradient(to top, rgba(255,255,255,0.98) 60%, transparent);
pointer-events: none;
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
@media (max-width: 639px) {
.sticky-cta-mobile {
display: block;
}
body {
padding-bottom: 80px;
}
}
.sticky-cta-btn {
pointer-events: auto;
width: 100%;
height: 56px;
border: none;
border-radius: var(--radius-pill);
background: linear-gradient(135deg, var(--accent), var(--accent-500));
color: white;
font-size: 18px;
font-weight: 700;
font-family: 'Roboto', sans-serif;
cursor: pointer;
box-shadow: var(--shadow-lg);
transition: transform .15s, box-shadow .15s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.sticky-cta-btn:active {
transform: scale(0.97);
box-shadow: var(--shadow-md);
}
/* ── 5. Guarantee Block ── */
.guarantee-block {
background: linear-gradient(135deg, var(--accent-light), #FEF3C7);
border-radius: var(--radius-xl);
padding: 32px 24px;
text-align: center;
border: 2px solid var(--accent-200);
position: relative;
overflow: hidden;
}
.guarantee-block::before {
content: '🛡️';
position: absolute;
top: -10px;
right: -10px;
font-size: 80px;
opacity: 0.15;
transform: rotate(15deg);
}
.guarantee-block__text {
font-size: 16px;
line-height: 1.6;
color: var(--text-secondary);
max-width: 560px;
margin: 0 auto;
}
.guarantee-block__badge {
display: inline-block;
margin-top: 16px;
padding: 6px 20px;
background: var(--accent);
color: white;
border-radius: var(--radius-pill);
font-size: 14px;
font-weight: 600;
}
@media (min-width: 640px) {
.guarantee-block {
padding: 40px 32px;
}
}
/* ── 6. Hero Risk Reversal Badge ── */
.hero-risk-reversal {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 14px;
background: var(--green-light);
color: #065F46;
border-radius: var(--radius-pill);
font-size: 13px;
font-weight: 600;
margin-top: 8px;
}
/* ── 7. Transformation Case ── */
.transformation-case {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
padding: 24px;
background: var(--bg-soft);
border-radius: var(--radius-xl);
position: relative;
}
@media (min-width: 640px) {
.transformation-case {
grid-template-columns: 1fr auto 1fr;
align-items: center;
}
}
.transformation-step {
text-align: center;
padding: 16px;
border-radius: var(--radius-md);
}
.transformation-step--before {
background: #FEF2F2;
border-left: 4px solid #EF4444;
}
.transformation-step--process {
background: var(--accent-light);
}
.transformation-step--after {
background: var(--green-light);
border-right: 4px solid var(--green-cta);
}
.transformation-step__label {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-tertiary);
font-weight: 600;
margin-bottom: 8px;
}
.transformation-step__title {
font-size: 18px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 4px;
}
.transformation-step__desc {
font-size: 14px;
color: var(--text-secondary);
line-height: 1.4;
}
.transformation-arrow {
display: none;
font-size: 28px;
color: var(--accent);
text-align: center;
}
@media (min-width: 640px) {
.transformation-arrow {
display: block;
}
}
/* ── 8. Outcome Card ── */
.outcome-card {
display: flex;
flex-direction: column;
gap: 24px;
padding: 32px 24px;
background: linear-gradient(135deg, #EEF2FF, #F5F3FF);
border-radius: var(--radius-xl);
}
@media (min-width: 640px) {
.outcome-card {
flex-direction: row;
align-items: center;
padding: 40px 32px;
}
}
.outcome-card__metrics {
display: flex;
flex-wrap: wrap;
gap: 20px;
flex: 1;
}
.outcome-metric {
flex: 1;
min-width: 120px;
}
.outcome-metric__value {
font-family: 'Oswald', sans-serif;
font-size: 36px;
font-weight: 700;
color: var(--accent);
line-height: 1;
}
.outcome-metric__label {
font-size: 14px;
color: var(--text-secondary);
margin-top: 4px;
}
.outcome-card__cta {
flex-shrink: 0;
}
/* ── 9. Final CTA Section ── */
.cta-final {
background: linear-gradient(135deg, var(--accent), #5B21B6);
color: white;
border-radius: var(--radius-xl);
padding: 48px 24px;
text-align: center;
}
.cta-final__title {
font-family: 'Oswald', sans-serif;
font-size: 28px;
font-weight: 600;
margin-bottom: 12px;
line-height: 1.2;
}
.cta-final__subtitle {
font-size: 16px;
opacity: 0.9;
margin-bottom: 24px;
line-height: 1.5;
}
.cta-final .btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 16px 40px;
background: white;
color: var(--accent);
border: none;
border-radius: var(--radius-pill);
font-size: 18px;
font-weight: 700;
font-family: 'Roboto', sans-serif;
cursor: pointer;
box-shadow: var(--shadow-lg);
transition: transform .15s;
}
.cta-final .btn:active {
transform: scale(0.97);
}
@media (min-width: 640px) {
.cta-final {
padding: 64px 48px;
}
.cta-final__title {
font-size: 36px;
}
}
/* ── 10. Button System — Unified ── */
.btn-primary {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 14px 32px;
background: linear-gradient(135deg, var(--accent), var(--accent-500));
color: white;
border: none;
border-radius: var(--radius-pill);
font-size: 16px;
font-weight: 600;
font-family: 'Roboto', sans-serif;
cursor: pointer;
transition: transform .15s, box-shadow .15s;
box-shadow: var(--shadow-sm);
text-decoration: none;
min-height: 48px;
}
.btn-primary:hover,
.btn-primary:focus-visible {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn-primary:active {
transform: translateY(0);
}
.btn-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 28px;
background: transparent;
color: var(--accent);
border: 2px solid var(--accent-200);
border-radius: var(--radius-pill);
font-size: 15px;
font-weight: 600;
font-family: 'Roboto', sans-serif;
cursor: pointer;
transition: background .15s, border-color .15s;
text-decoration: none;
min-height: 44px;
}
.btn-secondary:hover,
.btn-secondary:focus-visible {
background: var(--accent-light);
border-color: var(--accent);
}
/* ── 11. Mobile: hide old "Оставить заявку" text ── */
.btn-banner .btn-free-lesson-header {
/* Keeping existing class but text updated */
}
/* ── 12. Banner enhancement — Risk Reversal Tag ── */
.banner__content .risk-reversal-tag {
display: inline-block;
margin-top: 8px;
padding: 4px 16px;
background: rgba(255,255,255,0.15);
border-radius: var(--radius-pill);
font-size: 14px;
color: #FFD43E;
font-weight: 500;
backdrop-filter: blur(4px);
}
/* ── 13. Section spacers for Bento sections ── */
.pksh-section {
padding: 40px 16px;
}
@media (min-width: 640px) {
.pksh-section {
padding: 60px 24px;
}
}
@media (min-width: 1024px) {
.pksh-section {
padding: 80px 32px;
}
}
.pksh-section--alt {
background: var(--bg-soft);
}
/* ── 14. Mobile CTA — full width tap target ── */
@media (max-width: 639px) {
.btn-write.btn-primary.mobile,
.btn-page.btn-write {
width: calc(100% - 32px);
margin: 8px auto;
height: 52px;
font-size: 17px !important;
border-radius: var(--radius-pill);
}
}
/* ── 15. Price/Bento Cards refinement ── */
.pksh-price-card {
border-radius: var(--radius-xl);
padding: 28px 20px;
background: var(--bg-card);
box-shadow: var(--shadow-sm);
border: 1px solid #E2E8F0;
transition: box-shadow .2s, border-color .2s;
}
.pksh-price-card:hover {
box-shadow: var(--shadow-md);
border-color: var(--accent-200);
}
.pksh-price-card--featured {
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent), var(--shadow-md);
}
.pksh-price-card__title {
font-family: 'Oswald', sans-serif;
font-size: 22px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
}
.pksh-price-card__price {
font-size: 32px;
font-weight: 700;
color: var(--accent);
margin-bottom: 16px;
}
.pksh-price-card__features {
list-style: none;
padding: 0;
margin: 0 0 20px;
display: flex;
flex-direction: column;
gap: 10px;
}
.pksh-price-card__features li {
font-size: 14px;
color: var(--text-secondary);
display: flex;
align-items: center;
gap: 8px;
}
/* ── 16. Animations ── */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.bento-card {
animation: fadeInUp 0.4s ease-out both;
}
.bento-card:nth-child(1) { animation-delay: 0.05s; }
.bento-card:nth-child(2) { animation-delay: 0.1s; }
.bento-card:nth-child(3) { animation-delay: 0.15s; }
.bento-card:nth-child(4) { animation-delay: 0.2s; }
.bento-card:nth-child(5) { animation-delay: 0.25s; }
.bento-card--static {
animation: none;
}
/* ── Reduced Motion ── */
@media (prefers-reduced-motion: reduce) {
.bento-card {
animation: none;
}
.sticky-cta-btn {
transition: none;
}
.btn-primary,
.btn-secondary {
transition: none;
}
}
/* ── 17. Modal Form Enhancement ── */
.modal-request-form {
z-index: 9999;
}
/* ── 18. Section Spacing Fix for main ── */
main {
gap: 0;
}
main > .pksh-section:first-child {
padding-top: 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,11 @@
--transition-fast: 0.2s ease;
--transition-normal: 0.3s ease;
--color-bg: #fff;
--color-bg-warm: #F8F4EF;
--color-bg-alt: #EEF3EE;
--bg-hero: var(--color-bg-warm);
--bg-white: var(--color-bg);
--bg-mint: var(--color-bg-alt);
--accent: #7C3AED;
--accent-hover: #6D28D9;
--accent-light: #F5F3FF;
@ -771,8 +776,8 @@
gap: 20px;
justify-content: space-between;
}
.reviews-faq .box__accordion_label::after {
background-image: none !important;
.reviews-faq .accordion .box__accordion .box__accordion_label::after {
background-image: none;
}
.reviews-faq .box__accordion_label span {
font-size: clamp(18px, 2.2vw, 22px);
@ -1198,10 +1203,9 @@
/* Сетка отзывов: мобильный скролл */
@media (max-width: 768px) {
/* ─── Hero: фото скрыто, текст на всю ширину с отступами ─── */
.reviews-hero {
padding-left: 20px;
padding-right: 20px;
/* ─── Container: единый отступ для всех секций ─── */
.page-reviews .container {
padding: 0 20px;
}
.reviews-hero__image {
display: none;
@ -1210,7 +1214,7 @@
max-width: none;
}
.reviews-hero__metrics {
flex-wrap: nowrap;
flex-wrap: wrap;
gap: 10px;
}
.reviews-intro__grid {
@ -1261,10 +1265,10 @@
display: block;
}
/* ─── Бейдж в колонке имени: убираем inline-переопределения с мобилки ─── */
/* ─── Бейдж в колонке имени ─── */
.reviews-cards .lw-card .lw-badge--verified {
margin-top: 6px !important;
margin-left: 0 !important;
margin-top: 6px;
margin-left: 0;
}
/* ─── Звёзды: абсолютно в правый верхний угол карточки ─── */
@ -1877,6 +1881,23 @@
}
}
/* ─── SECTION-BG (full-width background utilities) ─── */
.section-bg {
position: relative;
}
.section-bg::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: calc(-50vw + 50%);
width: 100vw;
z-index: -1;
}
.section-bg[data-bg="hero"]::before { background: var(--bg-hero); }
.section-bg[data-bg="white"]::before { background: var(--bg-white); }
.section-bg[data-bg="mint"]::before { background: var(--color-bg-alt); }
/* ─── PREFERS-REDUCED-MOTION ─── */
@media (prefers-reduced-motion: reduce) {
.reveal,
@ -1903,4 +1924,44 @@
}
}
/* ─── САМЫЕ МАЛЕНЬКИЕ ЭКРАНЫ (≤375px) ─── */
@media (max-width: 375px) {
.reviews-hero__h1 {
font-size: 22px;
}
.reviews-hero__metrics {
gap: 6px;
}
.reviews-hero__metric {
padding: 10px 6px;
}
.reviews-hero__metric-number {
font-size: 20px;
}
.reviews-hero__metric-label {
font-size: 10px;
}
.reviews-cards .lw-grid--3 {
grid-template-columns: 1fr;
}
.results__card {
flex: 0 0 50%;
max-width: 50%;
padding: 16px 8px;
}
.results__grid {
gap: 0;
}
.cross-links__grid {
grid-template-columns: 1fr;
}
.final-cta__title {
font-size: 22px;
}
.reviews-hero__cta-btn {
padding: 14px 20px;
font-size: 15px;
}
}

View File

@ -52,12 +52,20 @@ document.addEventListener("click", function (event) {
}
if (target.classList.contains("btn-write")) {
modalRequestForm.style="";
document.querySelector('html').style = "overflow: hidden;";
return;
}
if (target.classList.contains("btn-close-request-form-img")) {
if (target.classList.contains("btn-close-request-form") || target.classList.contains("btn-close-request-form-img")) {
modalRequestForm.style="display: none;";
document.querySelector('html').style = "";
return;
}
}
// Клик по фону (затемнению) — закрыть модалку
if (target === modalRequestForm) {
modalRequestForm.style="display: none;";
document.querySelector('html').style = "";
return;
}
if(target.classList.contains("hover-effect")) {
target.classList.remove("hover-effect");
@ -230,4 +238,93 @@ setTimeout(() => {
document.querySelectorAll('.header-menu').forEach(item => {
item.classList.add('hover-effect');
})
})
/* ── Scroll Progress Bar ── */
(function() {
var progressBar = document.createElement('div');
progressBar.className = 'pksh-scroll-progress';
document.body.prepend(progressBar);
function updateProgress() {
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
var docHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
var progress = (scrollTop / docHeight) * 100;
progressBar.style.width = progress + '%';
}
window.addEventListener('scroll', updateProgress, { passive: true });
window.addEventListener('resize', updateProgress, { passive: true });
updateProgress();
})();
/* ── Desktop Sticky CTA — show after hero ── */
(function() {
var sticky = document.querySelector('.sticky-cta-desktop');
var hero = document.querySelector('.banner');
if (!sticky || !hero) return;
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
// Show sticky CTA when hero is NOT visible (scrolled past)
sticky.classList.toggle('visible', !entry.isIntersecting);
});
}, { threshold: 0 });
observer.observe(hero);
})();
/* ── Mobile banner padding: compensate for fixed header ── */
(function() {
var banner = document.querySelector('.page-pksh .banner');
var header = document.querySelector('.header');
if (banner && header && window.innerWidth <= 860) {
var headerH = header.offsetHeight || 76;
banner.style.paddingTop = headerH + 'px';
banner.style.marginTop = '-' + headerH + 'px';
}
})();
/* ── .open-bitrix-form → opens modal form ── */
document.querySelectorAll('.open-bitrix-form').forEach(btn => {
btn.addEventListener('click', function(e) {
const modal = document.querySelector('.modal-request-form');
if (modal) {
modal.style = "";
}
});
});
// Закрытие модалки по Escape
document.addEventListener("keydown", function(e) {
if (e.key === "Escape") {
const modal = document.querySelector('.modal-request-form');
if (modal && modal.style.display !== "none") {
modal.style.display = "none";
document.querySelector('html').style = "";
}
}
});
/* ── FAQ Accordion (mystyle-accordion) ── */
document.querySelectorAll('.mystyle-accordion-trigger').forEach(trigger => {
trigger.addEventListener('click', function(e) {
const item = this.closest('.mystyle-accordion-item');
if (!item) return;
const isOpen = item.classList.contains('active');
// Закрываем все другие аккордеоны в том же контейнере
const parent = item.parentElement;
if (parent) {
parent.querySelectorAll('.mystyle-accordion-item.active').forEach(other => {
if (other !== item) {
other.classList.remove('active');
}
});
}
if (isOpen) {
item.classList.remove('active');
} else {
item.classList.add('active');
}
});
});

View File

@ -0,0 +1,45 @@
/**
* front-page.js обработчики для шаблона front-page.php
* Извлечены из inline <script> в отдельный файл
*/
document.addEventListener('DOMContentLoaded', function () {
const tabs = document.querySelectorAll('.tab-clicked');
if (!tabs.length) return;
tabs.forEach(function (tab) {
tab.addEventListener('click', function (event) {
const tabBtn = event.currentTarget;
const tabsBlock = tabBtn.closest('[class*="tabs"]') || tabBtn.parentNode.parentNode.parentNode;
const tabNum = tabBtn.parentNode.getAttribute('data-tab-btn');
const content = tabsBlock.querySelector('.tabs-content');
if (!content || !tabNum) return;
// Анимация переключения табов
content.style.opacity = '0';
content.style.filter = 'blur(5px)';
content.style.left = '-50%';
tabsBlock.querySelectorAll('.tab').forEach(function (t) {
t.classList.remove('active');
});
const activeBtn = tabsBlock.querySelector('[data-tab-btn="' + tabNum + '"]');
if (activeBtn) activeBtn.classList.add('active');
setTimeout(function () {
content.style.opacity = '';
content.style.filter = '';
content.style.left = '';
tabsBlock.querySelectorAll('.page-tab').forEach(function (t, i) {
if (i !== Number(tabNum) - 1) {
t.classList.remove('active');
} else {
t.classList.add('active');
}
});
}, 300);
});
});
});

View File

@ -39,7 +39,7 @@ document.addEventListener('DOMContentLoaded', function () {
// ───────────────────────────────────────────────
document.querySelectorAll('.review-card__btn-more').forEach(function (btn) {
btn.addEventListener('click', function () {
const textBlock = this.parentNode.querySelector('.lw-card__text');
const textBlock = this.closest('.lw-card').querySelector('.lw-card__text');
if (textBlock) {
textBlock.classList.toggle('review-card__text--expanded');
this.textContent = textBlock.classList.contains('review-card__text--expanded')

View File

@ -0,0 +1,171 @@
<?php
/**
* Deploy Runner HTTP-triggered migration executor
* Usage: https://site.com/wp-content/themes/dekart/deploy-runner.php?migrate=003
*
* Self-destructs after execution (renames to .done).
* Requires wp-config.php for DB credentials.
*/
// Prevent WordPress from intercepting
define('WP_USE_THEMES', false);
$allowed_migrations = ['003', '004'];
$migrate = isset($_GET['migrate']) ? $_GET['migrate'] : '';
if (!in_array($migrate, $allowed_migrations)) {
header('Content-Type: text/plain');
die("Usage: ?migrate=003 (allowed: " . implode(', ', $allowed_migrations) . ")\n");
}
// Load WordPress config for DB credentials
$wp_config_path = dirname(__FILE__) . '/../../../wp-config.php';
if (!file_exists($wp_config_path)) {
// Try parent directory
$wp_config_path = dirname(__FILE__) . '/../../wp-config.php';
}
if (!file_exists($wp_config_path)) {
$wp_config_path = dirname(__FILE__) . '/../wp-config.php';
}
require_once $wp_config_path;
header('Content-Type: text/plain; charset=utf-8');
echo "=== Deploy Runner: migration $migrate ===\n\n";
// ─── Shared helpers ──────────────────────────────────────────────────
function dp_get_wpdb() {
$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if (!$conn) {
die("DB connection failed: " . mysqli_connect_error() . "\n");
}
echo "Connected to DB: " . DB_NAME . "\n";
return $conn;
}
function dp_get_theme_dir() {
// Our deploy-runner lives in the theme root
$theme_dir = dirname(__FILE__);
echo "Theme dir: {$theme_dir}\n";
return $theme_dir;
}
if ($migrate === '003') {
// Fix banner_text typo: rарантированная → гарантированная
$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if (!$conn) {
die("DB connection failed: " . mysqli_connect_error() . "\n");
}
echo "Connected to DB: " . DB_NAME . "\n\n";
$post_id = 2;
$meta_keys = ['_banner_text', 'banner_text'];
$fixed = 0;
foreach ($meta_keys as $meta_key) {
$res = mysqli_query($conn,
"SELECT meta_id, meta_value FROM wp_postmeta WHERE post_id = $post_id AND meta_key = '$meta_key'");
while ($row = mysqli_fetch_assoc($res)) {
$old_val = $row['meta_value'];
$new_val = str_replace('rарантированная', 'гарантированная', $old_val);
$new_val = str_replace('rантированная', 'гантированная', $new_val);
if ($new_val !== $old_val) {
$meta_id = (int)$row['meta_id'];
$escaped = mysqli_real_escape_string($conn, $new_val);
mysqli_query($conn,
"UPDATE wp_postmeta SET meta_value = '$escaped' WHERE meta_id = $meta_id");
echo "✅ FIXED meta_id=$meta_key key=$meta_key\n";
echo " OLD: $old_val\n";
echo " NEW: $new_val\n\n";
$fixed++;
}
}
}
if ($fixed === 0) {
echo "✅ Nothing to fix — typo already corrected\n";
}
echo "\nTotal meta values updated: $fixed\n";
mysqli_close($conn);
}
if ($migrate === '004') {
// ─── 004a: Remove post 740 (custom-css-js orphan) ───
$conn = dp_get_wpdb();
$post_740 = mysqli_query($conn, "SELECT ID FROM wp_posts WHERE ID = 740 AND post_type = 'custom-css-js'");
if (mysqli_num_rows($post_740) > 0) {
// Hard-delete post + meta
mysqli_query($conn, "DELETE FROM wp_posts WHERE ID = 740");
mysqli_query($conn, "DELETE FROM wp_postmeta WHERE post_id = 740");
echo "✅ DELETED post ID 740 (custom-css-js)\n";
// Remove physical file if exists
$theme_dir = dp_get_theme_dir();
$upload_dir = dirname(dirname($theme_dir)) . '/uploads';
$files_to_check = [
$upload_dir . '/custom-css-js/740.css',
$upload_dir . '/custom-css-js/740.min.css',
];
foreach ($files_to_check as $file) {
if (file_exists($file)) {
unlink($file);
echo "✅ DELETED file: {$file}\n";
}
}
} else {
echo " Post ID 740 not found — already removed\n";
}
// ─── 004b: Clean wp_custom_css option ───
// We need wp-load.php for get_option/update_option
$wp_load = dirname(__FILE__) . '/../../../wp-load.php';
if (!file_exists($wp_load)) {
$wp_load = dirname(__FILE__) . '/../../wp-load.php';
}
if (file_exists($wp_load)) {
require_once $wp_load;
echo "✅ Loaded WordPress\n";
$option_name = 'wp_custom_css';
$current = get_option($option_name, '');
if (!empty($current)) {
$before = strlen($current);
$cleaned = preg_replace('/\.(pksh|ms-|ms-text-)[^}]+}\s*/', '', $current);
$cleaned = preg_replace('/\.(pksh|ms-|ms-text-)[^{]*\{\s*\}\s*/', '', $cleaned);
$cleaned = trim($cleaned);
$after = strlen($cleaned);
$diff = $before - $after;
if ($diff > 0) {
update_option($option_name, $cleaned);
echo "✅ wp_custom_css: {$before}{$after} chars (removed {$diff})\n";
} else {
echo " wp_custom_css unchanged ({$before} chars)\n";
}
} else {
echo " wp_custom_css option is empty\n";
}
if (function_exists('opcache_reset')) {
opcache_reset();
echo "✅ OPcache reset\n";
}
} else {
echo "⚠️ wp-load.php not found — skipping wp_custom_css cleanup\n";
}
mysqli_close($conn);
}
echo "\n=== Migration complete ===\n";
// Self-destruct: rename this file
$self = __FILE__;
$done = $self . '.done';
rename($self, $done);
echo "Self-destruct: renamed to " . basename($done) . "\n";

View File

@ -19,10 +19,15 @@
'theme_location' => 'top',
) );
?>
<nav class="footer_links" aria-label="Дополнительные ссылки">
<ul class="footer_links-col">
<li><a href="/otzyvy/">Отзывы</a></li>
</ul>
</nav>
</div>
<div class="footer__col">
<button class="btn-write btn-lilac desktop"><?php esc_html_e('Записаться', 'dekart'); ?></button>
<button class="btn-write btn-lilac mobile"><?php esc_html_e('Бесплатное занятие', 'dekart'); ?></button>
<button class="btn-write btn-primary desktop"><?php esc_html_e('Бесплатное пробное', 'dekart'); ?></button>
<button class="btn-write btn-primary mobile"><?php esc_html_e('Бесплатно', 'dekart'); ?></button>
<div class="footer__link-icon">
<img width="19" height="32"
src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/icon-location.svg"
@ -69,7 +74,20 @@
alt="Закрыть"
loading="lazy">
</button>
<?php echo carbon_get_theme_option('site_bitrix_script'); ?>
<?php
// Приоритет: page-specific meta > theme option
$b24_script = '';
if ( is_singular() ) {
$page_script = carbon_get_post_meta( get_the_ID(), 'pksh_bitrix_script' );
if ( ! empty( $page_script ) ) {
$b24_script = $page_script;
}
}
if ( empty( $b24_script ) ) {
$b24_script = carbon_get_theme_option('site_bitrix_script');
}
echo $b24_script;
?>
</div>
</div>
@ -90,5 +108,24 @@ document.addEventListener("DOMContentLoaded", function() {
}
});
</script>
<!-- Sticky CTA Mobile -->
<div class="sticky-cta-mobile" aria-hidden="true">
<button class="sticky-cta-btn btn-write" aria-label="Записаться на бесплатное пробное">
Записаться на бесплатное
</button>
</div>
<!-- Sticky CTA Desktop (появляется при скролле) -->
<div class="sticky-cta-desktop" aria-hidden="true">
<div class="sticky-cta-desktop__inner">
<div class="sticky-cta-desktop__info">
<span class="sticky-cta-desktop__badge">🎯 Подготовка к школе</span>
<span class="sticky-cta-desktop__text">ул. Комсомольская, 11 первое занятие <strong>бесплатно</strong></span>
</div>
<button class="sticky-cta-desktop__btn btn-write">Записаться на бесплатное пробное</button>
</div>
</div>
</body>
</html>

View File

@ -5,6 +5,11 @@ Template Name: шаблон главной страницы
get_header();
$post_id = get_the_ID();
// ─── ДАННЫЕ ФИЛИАЛА ───
$branch_email = get_option( 'branch_email' ) ?: carbon_get_theme_option( 'site_mail' ) ?: '';
$branch_phone = get_option( 'branch_phone' ) ?: carbon_get_theme_option( 'site_phone' ) ?: '';
$branch_name = get_option( 'branch_name' ) ?: carbon_get_theme_option( 'site_name' ) ?: 'Декарт';
?>
<main class="main-page">
@ -61,14 +66,14 @@ $post_id = get_the_ID();
<h2 class="page_h2">Наши услуги</h2>
<div class="advantages__block-content">
<div class="advantages__block-row">
<div class="advantages__block-item" style="border:2px solid #A05A95">
<div class="advantages__block-item advantages__block-item--school">
<div class="advantages__block-item_title">Начальная школа</div>
<p>Обучение ведётся по программе «Перспектива» и методике Занкова в соответствии с ФГОС, включая изучение английского языка с 1 класса.</p>
<div class="advantages__block-item-img">
<a href="/nachalnaya-shkola/">Узнать подробнее &gt;</a>
</div>
</div>
<div class="advantages__block-item" style="border:2px solid #F682A5">
<div class="advantages__block-item advantages__block-item--preschool">
<?php // ИСПРАВЛЕНО: была опечатка a/vantages__block-item_title ?>
<div class="advantages__block-item_title">Подготовка к школе</div>
<p>Программа для будущих первоклассников: развитие базовых навыков, логики и речи через игровые задания.</p>
@ -76,7 +81,7 @@ $post_id = get_the_ID();
<a href="/podgotovka-k-shkole/">Узнать подробнее &gt;</a>
</div>
</div>
<div class="advantages__block-item" style="border:2px solid #FEDF6F">
<div class="advantages__block-item advantages__block-item--afterschool">
<div class="advantages__block-item_title">Группы продлённого дня</div>
<p>Помощь с домашними заданиями, творческие и развивающие занятия.</p>
<div class="advantages__block-item-img">
@ -85,21 +90,21 @@ $post_id = get_the_ID();
</div>
</div>
<div class="advantages__block-row">
<div class="advantages__block-item" style="border:2px solid #F9A474">
<div class="advantages__block-item advantages__block-item--exam">
<div class="advantages__block-item_title">Подготовка к экзаменам ОГЭ, ЕГЭ и ВПР</div>
<p>Интенсивная подготовка к экзаменам ВПР, ОГЭ и ЕГЭ по всем предметам школьной программы.</p>
<div class="advantages__block-item-img">
<a href="https://schelkovo.dekart-school.ru/">Узнать подробнее &gt;</a>
<a href="<?php echo esc_url( home_url( '/' ) ); ?>">Узнать подробнее &gt;</a>
</div>
</div>
<div class="advantages__block-item" style="border:2px solid #C9DA8F">
<div class="advantages__block-item advantages__block-item--camp">
<div class="advantages__block-item_title">Летний городской лагерь</div>
<p>Образование, творчество и наука в тематических сменах.</p>
<div class="advantages__block-item-img">
<a href="/gorodskoj-lager/">Узнать подробнее &gt;</a>
</div>
</div>
<div class="advantages__block-item" style="border:2px solid #B8CEDC">
<div class="advantages__block-item advantages__block-item--clubs">
<div class="advantages__block-item_title">Кружки и секции</div>
<p>Творческие студии, спортивные секции и развивающие занятия для детей всех возрастов.</p>
<div class="advantages__block-item-img"></div>
@ -120,11 +125,11 @@ $post_id = get_the_ID();
$block_text_1 = '<p>В нашей частной школе в Щелково мы знаем каждого ученика по имени. Небольшие классы до 12 человек, педагоги с опытом от 10 лет и углублённый английский с 1 класса. Работаем по программе «Перспектива» и методике Занкова. Принимаем детей из Щелково, Фрязино и Ивантеевки. Лицензия Министерства образования Московской области.</p>';
}
?>
<section class="info__block" style="padding-bottom:0;margin-top:40px">
<section class="info__block info__block--compact">
<div class="container">
<div class="info__block-wrapper">
<div class="info__block-content">
<div class="info__block-content_text" style="max-width:100%">
<div class="info__block-content_text info__block-content_text--full">
<?php echo wp_kses_post( $block_text_1 ); ?>
</div>
</div>
@ -132,12 +137,22 @@ $post_id = get_the_ID();
</div>
</section>
<section class="cta-block" style="background:linear-gradient(90deg,#fdf6ff 0%,#f5e8f5 100%);padding:60px 0;border-top:2px solid #A05A95;border-bottom:2px solid #A05A95;margin:40px 0;text-align:center">
<section class="cta-block">
<div class="container">
<div class="cta-wrapper" style="max-width:900px;margin:0 auto">
<h2 class="page_h2" style="margin-bottom:20px;line-height:1.3">Запишитесь на бесплатную экскурсию по школе и познакомьтесь с нашей программой!</h2>
<p style="font-size:1.2rem;color:#444;margin-bottom:30px">Получите персональную консультацию и узнайте стоимость обучения прямо сейчас.</p>
<button class="btn-write btn-lilac btn-page" style="margin:0 auto;display:block;padding:18px 45px;font-size:1.3rem;box-shadow:0 4px 15px rgba(160,90,149,.3)">Записаться на экскурсию</button>
<div class="cta-wrapper">
<h2 class="page_h2">Запишитесь на бесплатную экскурсию по школе и познакомьтесь с нашей программой!</h2>
<p>Получите персональную консультацию и узнайте стоимость обучения прямо сейчас.</p>
<button class="btn-write btn-lilac btn-page">Записаться на экскурсию</button>
</div>
</div>
</section>
<!-- ССЫЛКА НА СТРАНИЦУ ОТЗЫВОВ ДЛЯ УСИЛЕНИЯ ПЕРЕЛИНКОВКИ -->
<section class="reviews-link-block">
<div class="container">
<div class="reviews-link-wrapper">
<p>Узнайте, что говорят о нас родители и ученики </p>
<a href="/otzyvy/" class="btn-outline btn-page">Читать все отзывы</a>
</div>
</div>
</section>
@ -207,8 +222,8 @@ $post_id = get_the_ID();
"sameAs": ["https://vk.com/shkola_dekart"],
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+79303334151",
"email": "shelkovo@dekart-school.ru",
"telephone": "<?php echo esc_attr( $branch_phone ?: '+79303334151' ); ?>",
"email": "<?php echo esc_attr( $branch_email ?: 'shelkovo@dekart-school.ru' ); ?>",
"contactType": "customer service",
"areaServed": "RU",
"availableLanguage": "Russian"
@ -296,27 +311,7 @@ $post_id = get_the_ID();
}
</script>
<?php
?>
<script>
document.addEventListener('click', function(event) {
if (!event.target.classList.contains('tab-clicked')) return;
var tabsBlock = event.target.parentNode.parentNode.parentNode;
var tabNum = event.target.parentNode.getAttribute('data-tab-btn');
var content = tabsBlock.querySelector('.tabs-content');
if (!content) return;
content.style.cssText = 'opacity:0;filter:blur(5px);left:-50%';
tabsBlock.querySelectorAll('.tab').forEach(function(t){ t.classList.remove('active'); });
tabsBlock.querySelector('[data-tab-btn="' + tabNum + '"]').classList.add('active');
setTimeout(function() {
content.style.cssText = '';
tabsBlock.querySelectorAll('.page-tab').forEach(function(t, i) {
if (i !== Number(tabNum) - 1) t.classList.remove('active');
else t.classList.add('active');
});
}, 300);
});
</script>
<?php get_template_part('template-parts/map'); ?>

View File

@ -189,6 +189,9 @@ function add_carbon() {
Field::make( 'text', 'site_map_link', 'Ссылка на карту:' ),
Field::make( 'text', 'site_location', 'Координаты адреса (для яндекc карт):' ),
Field::make( 'text', 'site_frame_reviews', 'Ссылка на страницу с отзывами:' ),
Field::make( 'text', 'site_lk_url', 'Ссылка на личный кабинет (ЛК):' )
->set_default_value( 'https://lk.dekart-school.ru/' )
->set_help_text( 'URL личного кабинета для учеников/родителей. По умолчанию: https://lk.dekart-school.ru/' ),
Field::make( 'textarea', 'site_bitrix_script', 'script (формы) БИТРИКС24:' ),
Field::make( 'textarea', 'site_bitrix_vacancies', 'script (для формы связаться с нами) БИТРИКС24:' ),
Field::make( 'checkbox', 'check_banner', 'Отображать плашку со скидкой на формах "Запись на бесплатное пробное занятие"' )
@ -247,6 +250,9 @@ function add_carbon() {
Field::make( 'text', 'schema_legal_name', 'Юридическое название (legalName)' )
->set_width( 50 )
->set_help_text( 'Полное юр. название организации. Например: "ИП Иванов Иван Иванович"' ),
Field::make( 'textarea', 'schema_description', 'Описание организации (description)' )
->set_width( 100 )
->set_help_text( 'Короткое описание для Schema.org Organization → School. Появляется в сниппете поисковиков. Рекомендуется 12 предложения: тип школы, классы, местоположение. Пример: "Частная школа полного дня в Щёлково. Обучение с 1 по 11 класс, подготовка к ЕГЭ и ОГЭ, индивидуальный подход." Если пусто — собирается автоматически из названия и города.' ),
Field::make( 'text', 'schema_postal_code', 'Почтовый индекс (postalCode)' )
->set_width( 50 )
->set_help_text( 'Индекс для address.postalCode в микроразметке.' ),
@ -292,6 +298,9 @@ function add_carbon() {
) ),
Field::make( 'media_gallery', 'schema_photos', 'Фотографии филиала (image[])' )
->set_help_text( 'Выберите фотографии интерьера/экстерьера для image[].' ),
Field::make( 'textarea', 'schema_area_served', 'Города присутствия (areaServed)' )
->set_width( 100 )
->set_help_text( 'Список городов через запятую, где работает школа. Первый город должен совпадать с городом филиала. Пример: Щёлково, Фрязино, Монино, Ивантеевка, Королёв. Влияет на локальный поиск Google и Яндекса — школа будет показываться по этим городам. Если пусто — используется только город филиала.' ),
));
// (пусто — старые поля banner_img_desktop, banner_img_mobile,
@ -1023,8 +1032,12 @@ function add_carbon() {
->set_html( '<p style="color: #64748b; font-size: 13px; margin: 0 0 12px;">Секция: <strong>.reviews-intro</strong> — SEO-текст в начале страницы. Управляется из админки, чтобы можно было править текст без редактирования шаблона.</p>' ),
Field::make( 'text', 'reviews_intro_price_text', 'Стоимость обучения (текст)' )
->set_default_value( 'от 40 000 ₽ в месяц' )
->set_width( 100 )
->set_width( 50 )
->set_help_text( 'Текст стоимости в блоке .reviews-intro__info. Пример: «от 40 000 ₽ в месяц» или «от 35 000 ₽/мес». Если поле пусто — строка не выводится.' ),
Field::make( 'text', 'reviews_intro_hours', 'Режим работы (текст)' )
->set_default_value( 'ежедневно с 8:00 до 19:00, кроме воскресенья' )
->set_width( 50 )
->set_help_text( 'Текст режима работы в блоке .reviews-intro__info. Отображается на странице отзывов под SEO-текстом. Пример: «ежедневно с 8:00 до 19:00, кроме воскресенья» или «Пн–Пт: 08:0019:00, Сб: 09:0014:00». Если пусто — строка не выводится.' ),
Field::make( 'textarea', 'reviews_intro_lead', 'Лид-абзац (первый, крупный)' )
->set_default_value( 'Частная школа Декарт в Щёлково — это образовательное пространство, где каждый ребёнок получает внимание, заботу и качественные знания. Мы работаем в Щёлковском городском округе с 2010 года и за это время помогли раскрыть потенциал сотням учеников. Отзывы родителей о школе Декарт — это реальные истории семей, которые доверили нам образование своих детей. Мы публикуем их, чтобы вы могли принять взвешенное решение.' )
->set_help_text( 'Первый, самый заметный абзац. Отображается крупнее остальных, отделён линией. 150180 символов — оптимально. Должен вовлекать и отвечать на главный вопрос посетителя.' ),
@ -1252,9 +1265,12 @@ function exclude_custom_taxonomy_posts($query) {
function add_og_image() {
$term = get_queried_object();
if ($term->term_id) {
$snippet = carbon_get_term_meta( $term->term_id, 'snippet' );
if ($snippet != '') echo '<meta property="og:image" content="' . wp_get_attachment_url($snippet) . '" >' . "\n";
if ( ! is_a( $term, 'WP_Term' ) || empty( $term->term_id ) ) {
return;
}
$snippet = carbon_get_term_meta( $term->term_id, 'snippet' );
if ( $snippet ) {
echo '<meta property="og:image" content="' . esc_url( wp_get_attachment_url( $snippet ) ) . '" >' . "\n";
}
}
add_action( 'wp_head', 'add_og_image' );
@ -1317,12 +1333,8 @@ function enqueue_template_specific_styles() {
);
}
if ( is_page_template('page-reviews.php') ) {
wp_enqueue_style(
'phosphor-icons',
'https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/style.css',
array(),
'2.1.1'
);
// Phosphor Icons загружаются через preload+onload в wp_head (page-reviews.php:46)
// Не используем wp_enqueue_style — избегаем render-blocking
wp_enqueue_style(
'reviews-page-style',
get_template_directory_uri() . '/assets/css/reviews-page.css',
@ -1665,7 +1677,6 @@ function dekart_branch_register_settings() {
register_setting( 'dekart_branch_group', 'branch_social' );
register_setting( 'dekart_branch_group', 'branch_messengers' );
register_setting( 'dekart_branch_group', 'branch_button_script', array( 'sanitize_callback' => 'dekart_sanitize_script_field' ) );
register_setting( 'dekart_branch_group', 'branch_hours', array( 'sanitize_callback' => 'sanitize_text_field' ) );
register_setting( 'dekart_branch_group', 'branch_myreviews_widget', array( 'sanitize_callback' => 'sanitize_text_field' ) );
register_setting( 'dekart_branch_group', 'branch_ext_yandex', array( 'sanitize_callback' => 'esc_url_raw' ) );
register_setting( 'dekart_branch_group', 'branch_ext_google', array( 'sanitize_callback' => 'esc_url_raw' ) );
@ -1673,6 +1684,12 @@ function dekart_branch_register_settings() {
register_setting( 'dekart_branch_group', 'branch_ext_otzovik', array( 'sanitize_callback' => 'esc_url_raw' ) );
register_setting( 'dekart_branch_group', 'branch_ext_other_name', array( 'sanitize_callback' => 'sanitize_text_field' ) );
register_setting( 'dekart_branch_group', 'branch_ext_other_url', array( 'sanitize_callback' => 'esc_url_raw' ) );
register_setting( 'dekart_branch_group', 'branch_og_site_name', array( 'sanitize_callback' => 'sanitize_text_field' ) );
register_setting( 'dekart_branch_group', 'branch_ext_review_count', array( 'sanitize_callback' => 'absint' ) );
register_setting( 'dekart_branch_group', 'branch_avg_rating', array( 'sanitize_callback' => 'sanitize_text_field' ) );
register_setting( 'dekart_branch_group', 'branch_remaining_seats', array( 'sanitize_callback' => 'absint' ) );
register_setting( 'dekart_branch_group', 'branch_schema_description', array( 'sanitize_callback' => 'sanitize_textarea_field' ) );
register_setting( 'dekart_branch_group', 'branch_area_served', array( 'sanitize_callback' => 'sanitize_text_field' ) );
// Поля branch_map_url, branch_founded, branch_photos — удалены.
// Они дублировали CF таб «SEO и микроразметка» (schema_*).
// Чтение: inc/schema.php → dekart_schema_option() → CF → Settings API (backward compat).
@ -1844,26 +1861,40 @@ function dekart_branch_settings_page() {
<label>Полное название организации</label>
<input type="text" name="branch_name" value="<?php echo esc_attr( get_option( 'branch_name', 'Декарт' ) ); ?>">
</div>
<div class="branch-field">
<label>OG: Site Name (для мета-тега og:site_name)</label>
<input type="text" name="branch_og_site_name" value="<?php echo esc_attr( get_option( 'branch_og_site_name', 'Частная школа Декарт в Щёлково' ) ); ?>" style="width:100%;max-width:500px">
<p class="desc">Значение тега <code>og:site_name</code> отображается в сниппетах Яндекса и Google. Рекомендуется: «Частная школа Декарт в Щёлково». Если оставить пустым будет использован заголовок сайта из настроек WordPress.</p>
</div>
<div class="branch-field">
<label>Ценовой диапазон</label>
<input type="text" name="branch_price_range" value="<?php echo esc_attr( get_option( 'branch_price_range', '$$' ) ); ?>">
<p class="desc">Для микроразметки. Например: "$", "$$", "$$$".</p>
</div>
<div class="branch-field">
<label>Средний рейтинг школы (для метрик)</label>
<input type="text" name="branch_avg_rating" value="<?php echo esc_attr( get_option( 'branch_avg_rating', '4.9' ) ); ?>" style="width:100%;max-width:120px">
<p class="desc">Отображается в блоке метрик на странице отзывов. Пример: 4.9</p>
</div>
<div class="branch-field">
<label>Оставшиеся места (для триггера срочности)</label>
<input type="number" name="branch_remaining_seats" value="<?php echo esc_attr( get_option( 'branch_remaining_seats', '12' ) ); ?>" style="width:100%;max-width:120px" min="0">
<p class="desc">Отображается в финальном CTA на странице отзывов: «осталось N мест».</p>
</div>
<div class="branch-field">
<label>Логотип организации (ID вложения)</label>
<input type="number" name="branch_logo" value="<?php echo esc_attr( get_option( 'branch_logo', '' ) ); ?>" min="0" style="max-width:120px">
<p class="desc">ID изображения из Медиафайлы. Для JSON-LD image.</p>
</div>
</div>
</div>
<div class="branch-section collapsed">
<h2> РЕЖИМ РАБОТЫ</h2>
<div class="branch-section-body">
<div class="branch-field">
<label>Режим работы школы</label>
<input type="text" name="branch_hours" value="<?php echo esc_attr( get_option( 'branch_hours', 'ежедневно с 8:00 до 19:00, кроме воскресенья' ) ); ?>" style="width:100%;max-width:500px">
<p class="desc">Текст с режимом работы. Появляется на странице отзывов после вступительного текста.</p>
<label>Описание организации (для Schema.org)</label>
<textarea name="branch_schema_description" rows="3" style="width:100%;max-width:500px"><?php echo esc_textarea( get_option( 'branch_schema_description', '' ) ); ?></textarea>
<p class="desc">Появляется в сниппете Google и Яндекса как описание организации. Рекомендуется 12 предложения: тип школы, какие классы, особенности. Пример: «Частная школа полного дня в Щёлково. Обучение с 1 по 11 класс, подготовка к ЕГЭ и ОГЭ, индивидуальный подход.» Если пусто собирается автоматически из названия и города филиала.</p>
</div>
<div class="branch-field">
<label>Города присутствия (для Schema.org areaServed)</label>
<input type="text" name="branch_area_served" value="<?php echo esc_attr( get_option( 'branch_area_served', 'Щёлково, Фрязино, Монино' ) ); ?>" style="width:100%;max-width:500px">
<p class="desc">Города через запятую, где работает школа. Первым укажите город филиала. Влияет на локальный п