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/мой-кастомный-плагин/** # !/wp-content/plugins/мой-кастомный-плагин/**
.ssh_key_for_web .ssh_key_for_web
.ssh_key_for_web.pub .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 ## Quick rules
- Модель: `opencode/deepseek-v4-flash-free` - PHP: PSR-12, type declarations, Composer
- DeepSeek v4 Flash оптимизирован для быстрых инференсов с большим контекстом. - JS: TypeScript strict, ESM, async/await
- Для сложных задач используй декомпозицию: разбивай на подзадачи, выполняй последовательно. - Commits: conventional commits (`feat:`, `fix:`, `chore:`, etc.)
- Для работы с файлами: предпочитай пакетные операции (множественные чтения/записи) вместо последовательных вызовов.
- Для поиска по коду: используй glob/grep с точными паттернами, избегай избыточного сканирования.
- При работе с большими проектами — используй подзадачи (Task/Explore) для параллельного исследования.
- tool_output лимиты увеличены для работы с большими результатами (max_lines: 300, max_bytes: 10240).
- compaction включён, tail_turns: 20 — удержание контекста в длинных сессиях.
- MCP таймаут: 60s (для работы с медленными внешними сервисами).
## Профиль разработчика ## Security
- WP-Architect — WordPress full-stack architect. - NEVER modify wp-config.php without explicit confirmation
- Специализация: теминг, SEO, производительность, маркетинг. - NEVER delete files in uploads/ or wp-content/ without confirmation
- Доступно >80 скиллов: SEO/AEO, CRO, UI/UX, CI/CD, аналитика, безопасность. - ALWAYS ask before destructive actions (rm, overwrite, DB write, git push)
## Принципы работы ## Commands
- Используй скиллы при каждой возможности (даже 1% совпадения). - PHP test: `vendor/bin/phpunit`
- Caveman-режим активируется пользователем (/caveman). - PHP lint: `vendor/bin/phpstan analyse`
- Коммиты и PR — на нормальном английском. - JS build: `npm run build`
- Сообщай о проблемах безопасности сразу, без caveman. - 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-resume-form,
.btn-close-request-form { .btn-close-request-form {
position: absolute; position: absolute;
top: -10px; top: 12px;
right: -10px; right: 12px;
background: #fff0; background: #fff0;
border: none; border: none;
z-index: 10; z-index: 99999;
cursor: pointer;
} }
.btn-close-resume-form img, .btn-close-resume-form img,
@ -7001,7 +7002,8 @@ footer a:hover,
} }
.container { .container {
max-width: 101.25vw; max-width: 100%;
padding: 0 20px;
} }
.footer-wrapper { .footer-wrapper {
@ -9623,3 +9625,653 @@ footer a:hover,
} }
/* end mobile= */ /* 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-fast: 0.2s ease;
--transition-normal: 0.3s ease; --transition-normal: 0.3s ease;
--color-bg: #fff; --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: #7C3AED;
--accent-hover: #6D28D9; --accent-hover: #6D28D9;
--accent-light: #F5F3FF; --accent-light: #F5F3FF;
@ -771,8 +776,8 @@
gap: 20px; gap: 20px;
justify-content: space-between; justify-content: space-between;
} }
.reviews-faq .box__accordion_label::after { .reviews-faq .accordion .box__accordion .box__accordion_label::after {
background-image: none !important; background-image: none;
} }
.reviews-faq .box__accordion_label span { .reviews-faq .box__accordion_label span {
font-size: clamp(18px, 2.2vw, 22px); font-size: clamp(18px, 2.2vw, 22px);
@ -1198,10 +1203,9 @@
/* Сетка отзывов: мобильный скролл */ /* Сетка отзывов: мобильный скролл */
@media (max-width: 768px) { @media (max-width: 768px) {
/* ─── Hero: фото скрыто, текст на всю ширину с отступами ─── */ /* ─── Container: единый отступ для всех секций ─── */
.reviews-hero { .page-reviews .container {
padding-left: 20px; padding: 0 20px;
padding-right: 20px;
} }
.reviews-hero__image { .reviews-hero__image {
display: none; display: none;
@ -1210,7 +1214,7 @@
max-width: none; max-width: none;
} }
.reviews-hero__metrics { .reviews-hero__metrics {
flex-wrap: nowrap; flex-wrap: wrap;
gap: 10px; gap: 10px;
} }
.reviews-intro__grid { .reviews-intro__grid {
@ -1261,10 +1265,10 @@
display: block; display: block;
} }
/* ─── Бейдж в колонке имени: убираем inline-переопределения с мобилки ─── */ /* ─── Бейдж в колонке имени ─── */
.reviews-cards .lw-card .lw-badge--verified { .reviews-cards .lw-card .lw-badge--verified {
margin-top: 6px !important; margin-top: 6px;
margin-left: 0 !important; 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 ─── */ /* ─── PREFERS-REDUCED-MOTION ─── */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.reveal, .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,10 +52,18 @@ document.addEventListener("click", function (event) {
} }
if (target.classList.contains("btn-write")) { if (target.classList.contains("btn-write")) {
modalRequestForm.style=""; modalRequestForm.style="";
document.querySelector('html').style = "overflow: hidden;";
return; 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;"; modalRequestForm.style="display: none;";
document.querySelector('html').style = "";
return;
}
// Клик по фону (затемнению) — закрыть модалку
if (target === modalRequestForm) {
modalRequestForm.style="display: none;";
document.querySelector('html').style = "";
return; return;
} }
@ -231,3 +239,92 @@ setTimeout(() => {
document.querySelectorAll('.header-menu').forEach(item => { document.querySelectorAll('.header-menu').forEach(item => {
item.classList.add('hover-effect'); 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) { document.querySelectorAll('.review-card__btn-more').forEach(function (btn) {
btn.addEventListener('click', function () { btn.addEventListener('click', function () {
const textBlock = this.parentNode.querySelector('.lw-card__text'); const textBlock = this.closest('.lw-card').querySelector('.lw-card__text');
if (textBlock) { if (textBlock) {
textBlock.classList.toggle('review-card__text--expanded'); textBlock.classList.toggle('review-card__text--expanded');
this.textContent = textBlock.classList.contains('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', 'theme_location' => 'top',
) ); ) );
?> ?>
<nav class="footer_links" aria-label="Дополнительные ссылки">
<ul class="footer_links-col">
<li><a href="/otzyvy/">Отзывы</a></li>
</ul>
</nav>
</div> </div>
<div class="footer__col"> <div class="footer__col">
<button class="btn-write btn-lilac desktop"><?php esc_html_e('Записаться', 'dekart'); ?></button> <button class="btn-write btn-primary 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 mobile"><?php esc_html_e('Бесплатно', 'dekart'); ?></button>
<div class="footer__link-icon"> <div class="footer__link-icon">
<img width="19" height="32" <img width="19" height="32"
src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/icon-location.svg" src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/icon-location.svg"
@ -69,7 +74,20 @@
alt="Закрыть" alt="Закрыть"
loading="lazy"> loading="lazy">
</button> </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>
</div> </div>
@ -90,5 +108,24 @@ document.addEventListener("DOMContentLoaded", function() {
} }
}); });
</script> </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> </body>
</html> </html>

View File

@ -5,6 +5,11 @@ Template Name: шаблон главной страницы
get_header(); get_header();
$post_id = get_the_ID(); $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"> <main class="main-page">
@ -61,14 +66,14 @@ $post_id = get_the_ID();
<h2 class="page_h2">Наши услуги</h2> <h2 class="page_h2">Наши услуги</h2>
<div class="advantages__block-content"> <div class="advantages__block-content">
<div class="advantages__block-row"> <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> <div class="advantages__block-item_title">Начальная школа</div>
<p>Обучение ведётся по программе «Перспектива» и методике Занкова в соответствии с ФГОС, включая изучение английского языка с 1 класса.</p> <p>Обучение ведётся по программе «Перспектива» и методике Занкова в соответствии с ФГОС, включая изучение английского языка с 1 класса.</p>
<div class="advantages__block-item-img"> <div class="advantages__block-item-img">
<a href="/nachalnaya-shkola/">Узнать подробнее &gt;</a> <a href="/nachalnaya-shkola/">Узнать подробнее &gt;</a>
</div> </div>
</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 ?> <?php // ИСПРАВЛЕНО: была опечатка a/vantages__block-item_title ?>
<div class="advantages__block-item_title">Подготовка к школе</div> <div class="advantages__block-item_title">Подготовка к школе</div>
<p>Программа для будущих первоклассников: развитие базовых навыков, логики и речи через игровые задания.</p> <p>Программа для будущих первоклассников: развитие базовых навыков, логики и речи через игровые задания.</p>
@ -76,7 +81,7 @@ $post_id = get_the_ID();
<a href="/podgotovka-k-shkole/">Узнать подробнее &gt;</a> <a href="/podgotovka-k-shkole/">Узнать подробнее &gt;</a>
</div> </div>
</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> <div class="advantages__block-item_title">Группы продлённого дня</div>
<p>Помощь с домашними заданиями, творческие и развивающие занятия.</p> <p>Помощь с домашними заданиями, творческие и развивающие занятия.</p>
<div class="advantages__block-item-img"> <div class="advantages__block-item-img">
@ -85,21 +90,21 @@ $post_id = get_the_ID();
</div> </div>
</div> </div>
<div class="advantages__block-row"> <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> <div class="advantages__block-item_title">Подготовка к экзаменам ОГЭ, ЕГЭ и ВПР</div>
<p>Интенсивная подготовка к экзаменам ВПР, ОГЭ и ЕГЭ по всем предметам школьной программы.</p> <p>Интенсивная подготовка к экзаменам ВПР, ОГЭ и ЕГЭ по всем предметам школьной программы.</p>
<div class="advantages__block-item-img"> <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> </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> <div class="advantages__block-item_title">Летний городской лагерь</div>
<p>Образование, творчество и наука в тематических сменах.</p> <p>Образование, творчество и наука в тематических сменах.</p>
<div class="advantages__block-item-img"> <div class="advantages__block-item-img">
<a href="/gorodskoj-lager/">Узнать подробнее &gt;</a> <a href="/gorodskoj-lager/">Узнать подробнее &gt;</a>
</div> </div>
</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> <div class="advantages__block-item_title">Кружки и секции</div>
<p>Творческие студии, спортивные секции и развивающие занятия для детей всех возрастов.</p> <p>Творческие студии, спортивные секции и развивающие занятия для детей всех возрастов.</p>
<div class="advantages__block-item-img"></div> <div class="advantages__block-item-img"></div>
@ -120,11 +125,11 @@ $post_id = get_the_ID();
$block_text_1 = '<p>В нашей частной школе в Щелково мы знаем каждого ученика по имени. Небольшие классы до 12 человек, педагоги с опытом от 10 лет и углублённый английский с 1 класса. Работаем по программе «Перспектива» и методике Занкова. Принимаем детей из Щелково, Фрязино и Ивантеевки. Лицензия Министерства образования Московской области.</p>'; $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="container">
<div class="info__block-wrapper"> <div class="info__block-wrapper">
<div class="info__block-content"> <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 ); ?> <?php echo wp_kses_post( $block_text_1 ); ?>
</div> </div>
</div> </div>
@ -132,12 +137,22 @@ $post_id = get_the_ID();
</div> </div>
</section> </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="container">
<div class="cta-wrapper" style="max-width:900px;margin:0 auto"> <div class="cta-wrapper">
<h2 class="page_h2" style="margin-bottom:20px;line-height:1.3">Запишитесь на бесплатную экскурсию по школе и познакомьтесь с нашей программой!</h2> <h2 class="page_h2">Запишитесь на бесплатную экскурсию по школе и познакомьтесь с нашей программой!</h2>
<p style="font-size:1.2rem;color:#444;margin-bottom:30px">Получите персональную консультацию и узнайте стоимость обучения прямо сейчас.</p> <p>Получите персональную консультацию и узнайте стоимость обучения прямо сейчас.</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> <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>
</div> </div>
</section> </section>
@ -207,8 +222,8 @@ $post_id = get_the_ID();
"sameAs": ["https://vk.com/shkola_dekart"], "sameAs": ["https://vk.com/shkola_dekart"],
"contactPoint": { "contactPoint": {
"@type": "ContactPoint", "@type": "ContactPoint",
"telephone": "+79303334151", "telephone": "<?php echo esc_attr( $branch_phone ?: '+79303334151' ); ?>",
"email": "shelkovo@dekart-school.ru", "email": "<?php echo esc_attr( $branch_email ?: 'shelkovo@dekart-school.ru' ); ?>",
"contactType": "customer service", "contactType": "customer service",
"areaServed": "RU", "areaServed": "RU",
"availableLanguage": "Russian" "availableLanguage": "Russian"
@ -296,27 +311,7 @@ $post_id = get_the_ID();
} }
</script> </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'); ?> <?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_map_link', 'Ссылка на карту:' ),
Field::make( 'text', 'site_location', 'Координаты адреса (для яндекc карт):' ), Field::make( 'text', 'site_location', 'Координаты адреса (для яндекc карт):' ),
Field::make( 'text', 'site_frame_reviews', 'Ссылка на страницу с отзывами:' ), 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_script', 'script (формы) БИТРИКС24:' ),
Field::make( 'textarea', 'site_bitrix_vacancies', 'script (для формы связаться с нами) БИТРИКС24:' ), Field::make( 'textarea', 'site_bitrix_vacancies', 'script (для формы связаться с нами) БИТРИКС24:' ),
Field::make( 'checkbox', 'check_banner', 'Отображать плашку со скидкой на формах "Запись на бесплатное пробное занятие"' ) Field::make( 'checkbox', 'check_banner', 'Отображать плашку со скидкой на формах "Запись на бесплатное пробное занятие"' )
@ -247,6 +250,9 @@ function add_carbon() {
Field::make( 'text', 'schema_legal_name', 'Юридическое название (legalName)' ) Field::make( 'text', 'schema_legal_name', 'Юридическое название (legalName)' )
->set_width( 50 ) ->set_width( 50 )
->set_help_text( 'Полное юр. название организации. Например: "ИП Иванов Иван Иванович"' ), ->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)' ) Field::make( 'text', 'schema_postal_code', 'Почтовый индекс (postalCode)' )
->set_width( 50 ) ->set_width( 50 )
->set_help_text( 'Индекс для address.postalCode в микроразметке.' ), ->set_help_text( 'Индекс для address.postalCode в микроразметке.' ),
@ -292,6 +298,9 @@ function add_carbon() {
) ), ) ),
Field::make( 'media_gallery', 'schema_photos', 'Фотографии филиала (image[])' ) Field::make( 'media_gallery', 'schema_photos', 'Фотографии филиала (image[])' )
->set_help_text( 'Выберите фотографии интерьера/экстерьера для 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, // (пусто — старые поля 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>' ), ->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', 'Стоимость обучения (текст)' ) Field::make( 'text', 'reviews_intro_price_text', 'Стоимость обучения (текст)' )
->set_default_value( 'от 40 000 ₽ в месяц' ) ->set_default_value( 'от 40 000 ₽ в месяц' )
->set_width( 100 ) ->set_width( 50 )
->set_help_text( 'Текст стоимости в блоке .reviews-intro__info. Пример: «от 40 000 ₽ в месяц» или «от 35 000 ₽/мес». Если поле пусто — строка не выводится.' ), ->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', 'Лид-абзац (первый, крупный)' ) Field::make( 'textarea', 'reviews_intro_lead', 'Лид-абзац (первый, крупный)' )
->set_default_value( 'Частная школа Декарт в Щёлково — это образовательное пространство, где каждый ребёнок получает внимание, заботу и качественные знания. Мы работаем в Щёлковском городском округе с 2010 года и за это время помогли раскрыть потенциал сотням учеников. Отзывы родителей о школе Декарт — это реальные истории семей, которые доверили нам образование своих детей. Мы публикуем их, чтобы вы могли принять взвешенное решение.' ) ->set_default_value( 'Частная школа Декарт в Щёлково — это образовательное пространство, где каждый ребёнок получает внимание, заботу и качественные знания. Мы работаем в Щёлковском городском округе с 2010 года и за это время помогли раскрыть потенциал сотням учеников. Отзывы родителей о школе Декарт — это реальные истории семей, которые доверили нам образование своих детей. Мы публикуем их, чтобы вы могли принять взвешенное решение.' )
->set_help_text( 'Первый, самый заметный абзац. Отображается крупнее остальных, отделён линией. 150180 символов — оптимально. Должен вовлекать и отвечать на главный вопрос посетителя.' ), ->set_help_text( 'Первый, самый заметный абзац. Отображается крупнее остальных, отделён линией. 150180 символов — оптимально. Должен вовлекать и отвечать на главный вопрос посетителя.' ),
@ -1252,9 +1265,12 @@ function exclude_custom_taxonomy_posts($query) {
function add_og_image() { function add_og_image() {
$term = get_queried_object(); $term = get_queried_object();
if ($term->term_id) { if ( ! is_a( $term, 'WP_Term' ) || empty( $term->term_id ) ) {
return;
}
$snippet = carbon_get_term_meta( $term->term_id, 'snippet' ); $snippet = carbon_get_term_meta( $term->term_id, 'snippet' );
if ($snippet != '') echo '<meta property="og:image" content="' . wp_get_attachment_url($snippet) . '" >' . "\n"; if ( $snippet ) {
echo '<meta property="og:image" content="' . esc_url( wp_get_attachment_url( $snippet ) ) . '" >' . "\n";
} }
} }
add_action( 'wp_head', 'add_og_image' ); add_action( 'wp_head', 'add_og_image' );
@ -1317,12 +1333,8 @@ function enqueue_template_specific_styles() {
); );
} }
if ( is_page_template('page-reviews.php') ) { if ( is_page_template('page-reviews.php') ) {
wp_enqueue_style( // Phosphor Icons загружаются через preload+onload в wp_head (page-reviews.php:46)
'phosphor-icons', // Не используем wp_enqueue_style — избегаем render-blocking
'https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/style.css',
array(),
'2.1.1'
);
wp_enqueue_style( wp_enqueue_style(
'reviews-page-style', 'reviews-page-style',
get_template_directory_uri() . '/assets/css/reviews-page.css', 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_social' );
register_setting( 'dekart_branch_group', 'branch_messengers' ); 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_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_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_yandex', array( 'sanitize_callback' => 'esc_url_raw' ) );
register_setting( 'dekart_branch_group', 'branch_ext_google', 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_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_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_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 — удалены. // Поля branch_map_url, branch_founded, branch_photos — удалены.
// Они дублировали CF таб «SEO и микроразметка» (schema_*). // Они дублировали CF таб «SEO и микроразметка» (schema_*).
// Чтение: inc/schema.php → dekart_schema_option() → CF → Settings API (backward compat). // Чтение: inc/schema.php → dekart_schema_option() → CF → Settings API (backward compat).
@ -1844,26 +1861,40 @@ function dekart_branch_settings_page() {
<label>Полное название организации</label> <label>Полное название организации</label>
<input type="text" name="branch_name" value="<?php echo esc_attr( get_option( 'branch_name', 'Декарт' ) ); ?>"> <input type="text" name="branch_name" value="<?php echo esc_attr( get_option( 'branch_name', 'Декарт' ) ); ?>">
</div> </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"> <div class="branch-field">
<label>Ценовой диапазон</label> <label>Ценовой диапазон</label>
<input type="text" name="branch_price_range" value="<?php echo esc_attr( get_option( 'branch_price_range', '$$' ) ); ?>"> <input type="text" name="branch_price_range" value="<?php echo esc_attr( get_option( 'branch_price_range', '$$' ) ); ?>">
<p class="desc">Для микроразметки. Например: "$", "$$", "$$$".</p> <p class="desc">Для микроразметки. Например: "$", "$$", "$$$".</p>
</div> </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"> <div class="branch-field">
<label>Логотип организации (ID вложения)</label> <label>Логотип организации (ID вложения)</label>
<input type="number" name="branch_logo" value="<?php echo esc_attr( get_option( 'branch_logo', '' ) ); ?>" min="0" style="max-width:120px"> <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> <p class="desc">ID изображения из Медиафайлы. Для JSON-LD image.</p>
</div> </div>
</div>
</div>
<div class="branch-section collapsed">
<h2> РЕЖИМ РАБОТЫ</h2>
<div class="branch-section-body">
<div class="branch-field"> <div class="branch-field">
<label>Режим работы школы</label> <label>Описание организации (для Schema.org)</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"> <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">Текст с режимом работы. Появляется на странице отзывов после вступительного текста.</p> <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">Города через запятую, где работает школа. Первым укажите город филиала. Влияет на локальный поиск школа будет показываться жителям этих городов. Пример: <code>Щёлково, Фрязино, Монино, Ивантеевка, Королёв</code></p>
</div> </div>
</div> </div>
</div> </div>
@ -1922,6 +1953,11 @@ function dekart_branch_settings_page() {
<label>Другая платформа (ссылка)</label> <label>Другая платформа (ссылка)</label>
<input type="url" name="branch_ext_other_url" value="<?php echo esc_attr( get_option( 'branch_ext_other_url', '' ) ); ?>" style="width:100%;max-width:500px"> <input type="url" name="branch_ext_other_url" value="<?php echo esc_attr( get_option( 'branch_ext_other_url', '' ) ); ?>" style="width:100%;max-width:500px">
</div> </div>
<div class="branch-field">
<label>Количество отзывов на внешних площадках</label>
<input type="number" name="branch_ext_review_count" value="<?php echo esc_attr( get_option( 'branch_ext_review_count', '247' ) ); ?>" style="width:100%;max-width:200px" min="0">
<p class="desc">Отображается в бейдже под метриками: «4.9 из 5 на основе N отзывов на внешних площадках».</p>
</div>
</div> </div>
</div> </div>

View File

@ -104,7 +104,7 @@ if ( is_front_page() ) {
src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/phone-volume.svg" src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/phone-volume.svg"
alt="Телефон" loading="lazy"> alt="Телефон" loading="lazy">
</a> </a>
<a href="https://lk.dekart-school.ru/" target="_blank" rel="noopener noreferrer" <a href="<?php echo esc_url( carbon_get_theme_option( 'site_lk_url' ) ?: 'https://lk.dekart-school.ru/' ); ?>" target="_blank" rel="noopener noreferrer"
aria-label="Личный кабинет"> aria-label="Личный кабинет">
<img width="18" height="18" <img width="18" height="18"
src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/icon-user-w.svg" src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/icon-user-w.svg"
@ -137,8 +137,8 @@ if ( is_front_page() ) {
href="tel:<?php echo preg_replace('/[^0-9+]/', '', carbon_get_theme_option('site_phone')); ?>"> href="tel:<?php echo preg_replace('/[^0-9+]/', '', carbon_get_theme_option('site_phone')); ?>">
<span><?php echo esc_html( carbon_get_theme_option('site_phone') ); ?></span> <span><?php echo esc_html( carbon_get_theme_option('site_phone') ); ?></span>
</a> </a>
<button class="btn-free-lesson-header btn-lilac desktop btn-write"> <button class="btn-free-lesson-header btn-primary desktop btn-write">
<?php esc_html_e('Оставить заявку', 'dekart'); ?> <?php esc_html_e('Записаться на бесплатное', 'dekart'); ?>
</button> </button>
</div> </div>
@ -158,14 +158,14 @@ if ( is_front_page() ) {
'theme_location' => 'top', 'theme_location' => 'top',
) ); ) );
?> ?>
<button class="btn-free-lesson-header btn-lilac mobile btn-write"> <button class="btn-free-lesson-header btn-primary mobile btn-write">
<?php esc_html_e('Записаться на визит', 'dekart'); ?> <?php esc_html_e('Записаться на бесплатное', 'dekart'); ?>
</button> </button>
<button class="btn-close-menu btn-close-header-menu mobile"> <button class="btn-close-menu btn-close-header-menu mobile">
<?php esc_html_e('ЗАКРЫТЬ МЕНЮ', 'dekart'); ?> <?php esc_html_e('ЗАКРЫТЬ МЕНЮ', 'dekart'); ?>
</button> </button>
</div> </div>
<a class="inter-lk desktop" href="https://lk.dekart-school.ru/" target="_blank" rel="noopener noreferrer"> <a class="inter-lk desktop" href="<?php echo esc_url( carbon_get_theme_option( 'site_lk_url' ) ?: 'https://lk.dekart-school.ru/' ); ?>" target="_blank" rel="noopener noreferrer">
<img width="18" height="23" <img width="18" height="23"
src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/icon-user.svg" src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/icon-user.svg"
alt="Личный кабинет" loading="lazy"> alt="Личный кабинет" loading="lazy">

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,712 @@
<?php
/**
* Template Name: Подготовка к школе (Bento 2026)
*
* Асимметричная Bento-сетка. Нейрокопирайтинг.
* CSS: main.css (секции Bento/Social/Guarantee) + podgotovka-k-shkole.css (переопределения 2026)
*
* @package Dekart
*/
get_header();
$post_id = get_the_ID();
// ─── ДАННЫЕ ОРГАНИЗАЦИИ ───
$opt_address = get_option( 'branch_address' ) ?: carbon_get_theme_option( 'site_address' ) ?: carbon_get_post_meta( $post_id, 'contact_address' ) ?: '';
$opt_coords = get_option( 'branch_coords' ) ?: carbon_get_post_meta( $post_id, 'contact_location' ) ?: '';
$opt_phone = get_option( 'branch_phone' ) ?: carbon_get_theme_option( 'site_phone' ) ?: carbon_get_post_meta( $post_id, 'contact_tel' ) ?: '';
$opt_email = get_option( 'branch_email' ) ?: carbon_get_theme_option( 'site_mail' ) ?: carbon_get_post_meta( $post_id, 'contact_mail' ) ?: '';
$opt_name = get_option( 'branch_name' ) ?: carbon_get_theme_option( 'site_name' ) ?: 'Декарт';
$opt_social_raw = get_option( 'branch_social' ) ?: carbon_get_post_meta( $post_id, 'social_media' ) ?: [];
?>
<main class="page page-pksh">
<?php get_template_part('template-parts/block-banner'); ?>
<!-- ════════════════════════════════════════════ -->
<!-- Social Proof Confidence counters (4 cards) -->
<!-- ════════════════════════════════════════════ -->
<?php
$social_counters = carbon_get_post_meta( $post_id, 'pksh_social_counters' );
$social_icons = array( '🎓', '📅', '⭐', '👩‍🏫' );
?>
<section class="bento pksh-section" aria-label="Социальное доказательство">
<div class="bento__full">
<div class="social-counters">
<?php if ( ! empty( $social_counters ) ) : ?>
<?php foreach ( $social_counters as $i => $counter ) :
$icon = isset( $social_icons[ $i ] ) ? $social_icons[ $i ] : '✅';
$number = $counter['counter_number'] ?? '';
$label = $counter['counter_label'] ?? '';
if ( empty( $number ) ) continue;
?>
<div class="social-counter">
<div class="social-counter__top"></div>
<div class="social-counter__body">
<span class="social-counter__icon"><?php echo esc_html( $icon ); ?></span>
<div class="social-counter__number"><?php echo esc_html( $number ); ?></div>
<div class="social-counter__label"><?php echo esc_html( $label ); ?></div>
</div>
</div>
<?php endforeach; ?>
<?php else : ?>
<!-- Fallback, если поля не заполнены -->
<div class="social-counter">
<div class="social-counter__top"></div>
<div class="social-counter__body">
<span class="social-counter__icon">🎓</span>
<div class="social-counter__number">500+</div>
<div class="social-counter__label">выпускников в топ-школах</div>
</div>
</div>
<div class="social-counter">
<div class="social-counter__top"></div>
<div class="social-counter__body">
<span class="social-counter__icon">📅</span>
<div class="social-counter__number">12</div>
<div class="social-counter__label">лет готовим к школе</div>
</div>
</div>
<div class="social-counter">
<div class="social-counter__top"></div>
<div class="social-counter__body">
<span class="social-counter__icon"></span>
<div class="social-counter__number">4.8</div>
<div class="social-counter__label">средняя оценка родителей</div>
</div>
</div>
<div class="social-counter">
<div class="social-counter__top"></div>
<div class="social-counter__body">
<span class="social-counter__icon">👩‍🏫</span>
<div class="social-counter__number">20+</div>
<div class="social-counter__label">опытных педагогов</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Micro-Commitment: Checklist download -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section" aria-label="Скачать чек-лист">
<div class="bento__full">
<div class="micro-commit">
<div class="micro-commit__content">
<span class="bento-label">Бесплатно</span>
<h2 class="bento-h2">Скачайте чек-лист готовности к школе</h2>
<p class="bento-p">10 признаков, что ваш ребёнок готов к 1-му классу. Проверьте за 5 минут и узнайте, на какие навыки обратить внимание.</p>
<button class="btn-primary btn-write micro-commit__btn">Получить чек-лист бесплатно</button>
<p class="micro-commit__disclaimer">Отправляя заявку, вы соглашаетесь на обработку персональных данных</p>
</div>
<div class="micro-commit__visual">
<svg class="micro-commit__svg" viewBox="0 0 130 170" fill="none" aria-hidden="true">
<!-- Paper background -->
<rect x="8" y="4" width="114" height="162" rx="8" fill="white" stroke="#DED3C0" stroke-width="1.2"/>
<!-- Header line -->
<rect x="24" y="20" width="82" height="4" rx="2" fill="#7C3AED" opacity="0.15"/>
<!-- Checklist items -->
<rect x="24" y="38" width="12" height="12" rx="3" fill="#7C3AED" opacity="0.15"/>
<path d="M26 44l3 3 5-5" stroke="#7C3AED" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" opacity="0.4"/>
<rect x="44" y="40" width="50" height="3" rx="1.5" fill="#DED3C0" opacity="0.6"/>
<rect x="24" y="62" width="12" height="12" rx="3" fill="#7C3AED" opacity="0.15"/>
<path d="M26 68l3 3 5-5" stroke="#7C3AED" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" opacity="0.4"/>
<rect x="44" y="64" width="62" height="3" rx="1.5" fill="#DED3C0" opacity="0.6"/>
<rect x="24" y="86" width="12" height="12" rx="3" fill="#7C3AED" opacity="0.15"/>
<rect x="44" y="88" width="42" height="3" rx="1.5" fill="#DED3C0" opacity="0.6"/>
<rect x="24" y="110" width="12" height="12" rx="3" fill="#7C3AED" opacity="0.15"/>
<rect x="44" y="112" width="56" height="3" rx="1.5" fill="#DED3C0" opacity="0.6"/>
<!-- Pencil illustration -->
<g transform="translate(92, 130) rotate(-30)">
<rect x="0" y="0" width="6" height="28" rx="1" fill="#7C3AED" opacity="0.12"/>
<polygon points="0,0 6,0 3,-6" fill="#7C3AED" opacity="0.15"/>
</g>
</svg>
</div>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Guarantee Hero (Risk Reversal Showcase) -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section pksh-section--alt" aria-label="Гарантия">
<div class="bento__full">
<div class="guarantee-hero">
<div class="guarantee-hero__badges">
<div class="guarantee-hero__shield">🛡️</div>
</div>
<div class="guarantee-hero__content">
<h2 class="bento-h2">Ваш ребёнок начнёт читать через 3 недели</h2>
<p class="guarantee-hero__text">Или <strong>мы вернём деньги</strong> и продолжим обучение бесплатно, пока не дойдём до результата. Мы частная школа <strong>с гослицензией</strong>, а не кружок. Отвечаем за результат.</p>
<button class="btn-primary btn-write guarantee-hero__btn">Записаться на бесплатное пробное</button>
</div>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Anchor Navigation jump links -->
<!-- ════════════════════════════════════════════ -->
<nav class="pksh-anchor-nav" aria-label="Навигация по странице">
<a href="#how-it-works" class="pksh-anchor-link">Как проходят занятия</a>
<a href="#results" class="pksh-anchor-link">Результаты</a>
<a href="#programs" class="pksh-anchor-link">Программы</a>
<a href="#teachers" class="pksh-anchor-link">Преподаватели</a>
<a href="#faq" class="pksh-anchor-link">Вопросы</a>
</nav>
<!-- ════════════════════════════════════════════ -->
<!-- Bento Row 1: How it works + Why us -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section" id="how-it-works" aria-label="Как проходят занятия">
<div class="bento__half bento-card bento-card--soft">
<span class="bento-label">Как проходит занятие</span>
<h2 class="bento-h2">30 минут игры = 1 шаг к школе</h2>
<p class="bento-p">Никаких скучных прописей. Каждое занятие построено так, что ребёнок <strong>сам хочет</strong> вернуться.</p>
<div class="steps-3">
<div class="step">
<span class="step__num">1</span>
<div>
<strong>Вход в тему</strong>
<span>Короткая история или загадка включаем интерес</span>
</div>
</div>
<div class="step">
<span class="step__num">2</span>
<div>
<strong>Игровое задание</strong>
<span>Чтение, счёт, логика через механику игры</span>
</div>
</div>
<div class="step">
<span class="step__num">3</span>
<div>
<strong>Рефлексия</strong>
<span>Ребёнок сам рассказывает, чему научился</span>
</div>
</div>
</div>
</div>
<div class="bento__half bento-card bento-card--objections">
<span class="bento-label">Почему родители выбирают нас</span>
<h2 class="bento-h2">Три главных вопроса честные ответы</h2>
<div class="objection-list">
<div class="objection-item">
<div class="objection-item__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
</div>
<div>
<strong>Не перегрузят ли ребёнка?</strong>
<p>Занятия по 3040 минут. Каждые 7 минут смена активности. Ребёнок не устаёт.</p>
</div>
</div>
<div class="objection-item">
<div class="objection-item__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
</div>
<div>
<strong>Успеем ли до сентября?</strong>
<p>Да. Наша программа рассчитана на 46 месяцев. Первые результаты через 3 недели.</p>
</div>
</div>
<div class="objection-item">
<div class="objection-item__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
</div>
<div>
<strong>Дорого?</strong>
<p>От 600 за занятие. Первое бесплатно. А если не понравится вернём деньги.</p>
</div>
</div>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Bento Row 2: Results / Data -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section pksh-section--alt" id="results" aria-label="Результаты учеников">
<div class="bento__full bento-card bento-card--highlight-results">
<span class="bento-label bento-label--light">Результаты наших учеников</span>
<h2 class="bento-h2 bento-h2--light">К чему мы приводим детей</h2>
<div class="results-grid">
<div class="result-item">
<span class="result-item__value">95%</span>
<span class="result-item__desc">поступают в школы и лицеи Щёлкова без стресса</span>
</div>
<div class="result-item">
<span class="result-item__value">3 нед.</span>
<span class="result-item__desc"> первый осмысленный слог у нечитавших детей</span>
</div>
<div class="result-item">
<span class="result-item__value">87%</span>
<span class="result-item__desc">родителей отмечают рост самостоятельности</span>
</div>
<div class="result-item">
<span class="result-item__value">2,5×</span>
<span class="result-item__desc">быстрее адаптируются в первом классе</span>
</div>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Bento Row 3: Programs / Курсы -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section" id="programs" aria-label="Программы подготовки">
<div class="bento__full">
<span class="bento-label">Программы</span>
<h2 class="bento-h2">Выберите формат подготовки</h2>
<p class="bento-p">Все программы ведут действующие педагоги начальных классов.
Группы до 8 человек каждому ребёнку уделяем внимание.</p>
<div class="programs-grid">
<?php
$pksh_programs = carbon_get_post_meta($post_id, 'pksh_programs');
if (is_array($pksh_programs) && !empty($pksh_programs)) :
foreach ($pksh_programs as $program) :
$icon = $program['program_icon'] ?? '🎓';
$title = $program['program_title'] ?? '';
$intensity = $program['program_intensity'] ?? '';
$schedule_raw = $program['program_schedule'] ?? '';
$duration = $program['program_duration'] ?? '90 мин';
$structure_raw = $program['program_lesson_structure'] ?? '';
$includes_raw = $program['program_includes'] ?? '';
$price_sub = $program['program_price_subscription'] ?? '';
$price_single = $program['program_price_single'] ?? '';
$btn_text = $program['program_button_text'] ?? 'Записаться';
$btn_url = $program['program_button_url'] ?? '/zapis/';
$schedule_lines = array_filter(explode("\n", $schedule_raw));
$structure_lines = array_filter(explode("\n", $structure_raw));
$includes_lines = array_filter(explode("\n", $includes_raw));
?>
<div class="program-card">
<div class="program-card__header">
<span class="program-card__icon"><?php echo esc_html($icon); ?></span>
<h3 class="program-card__title"><?php echo esc_html($title); ?></h3>
</div>
<div class="program-card__body">
<?php if ($intensity) : ?>
<div class="program-card__intensity">
<span class="program-card__label">Интенсивность:</span>
<?php echo esc_html($intensity); ?>
</div>
<?php endif; ?>
<?php if (!empty($schedule_lines)) : ?>
<div class="program-card__schedule">
<span class="program-card__label">📅 Расписание:</span>
<ul>
<?php foreach ($schedule_lines as $line) : ?>
<li><?php echo esc_html(trim($line)); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php if (!empty($structure_lines) || $duration) : ?>
<div class="program-card__structure">
<span class="program-card__label"> Структура занятия (<?php echo esc_html($duration); ?>):</span>
<ul class="program-card__timeline">
<?php foreach ($structure_lines as $line) : ?>
<?php
$line = trim($line);
// Parse "15 мин 🧠 Нейрогимнастика" into time and name
$parts = explode(' ', $line, 2);
$time_part = $parts[0] ?? '';
$name_part = $parts[1] ?? $line;
?>
<li class="program-card__timeline-item">
<span class="program-card__timeline-badge"><?php echo esc_html($time_part); ?></span>
<span class="program-card__timeline-name"><?php echo esc_html($name_part); ?></span>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php if (!empty($includes_lines)) : ?>
<div class="program-card__includes">
<span class="program-card__label">Программа включает:</span>
<ul>
<?php foreach ($includes_lines as $line) : ?>
<li><?php echo esc_html(trim($line)); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php if ($price_sub || $price_single) : ?>
<div class="program-card__pricing">
<?php if ($price_sub) : ?>
<div class="program-card__price-main">
<span class="program-card__price-value"><?php echo esc_html($price_sub); ?></span>
<span class="program-card__price-period">за 4 недели</span>
</div>
<?php endif; ?>
<?php if ($price_single) : ?>
<div class="program-card__price-single">
<?php echo esc_html($price_single); ?> за одно занятие (вне абонемента)
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<a href="<?php echo esc_url($btn_url); ?>" class="program-card__btn">
<?php echo esc_html($btn_text); ?>
</a>
</div>
</div>
<?php
endforeach;
else :
// Fallback, если программы не заполнены в админке
?>
<div class="program-card">
<div class="program-card__header">
<span class="program-card__icon">📖</span>
<h3 class="program-card__title">Базовая программа</h3>
</div>
<div class="program-card__body">
<p style="color: #94a3b8; font-style: italic;">Заполните программы в админке Настройки Подготовки к школе Программы</p>
</div>
</div>
<?php endif; ?>
</div>
<div class="programs-note">
<p>Первое пробное занятие <strong>бесплатно</strong>. При покупке абонемента на месяц скидка 10%.
<a href="#final-cta" class="programs-note__link">Записаться на бесплатное пробное</a></p>
</div>
<div class="pksh-fomo">
<span class="pksh-fomo__dot"></span>
<span class="pksh-fomo__text">🔥 4 из 8 мест в группе 56 лет <strong>уже заняты</strong>. Успейте записаться!</span>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Bento Row 4: Schedule & Price -->
<!-- ════════════════════════════════════════════ -->
<?php get_template_part('template-parts/block-price'); ?>
<!-- ════════════════════════════════════════════ -->
<!-- Bento Row 4: Teachers (camp-style cards) -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section pksh-section--teachers" id="teachers" aria-label="Преподаватели">
<div class="bento__full">
<h2 class="bento-h2">Наши преподаватели</h2>
<div class="pksh-teachers__scroll-x">
<div class="pksh-teachers__wrapper">
<?php
$pksh_teachers = carbon_get_post_meta(get_the_ID(), 'pksh_teachers');
if (is_array($pksh_teachers) && !empty($pksh_teachers)) :
foreach ($pksh_teachers as $item) :
$t_name = $item['teacher_name'] ?? '';
$t_photo = $item['teacher_photo'] ?? 0;
$t_subj = $item['teacher_subject'] ?? '';
$t_about = $item['teacher_about'] ?? '';
?>
<div class="pksh-teachers__item">
<div class="pksh-teachers__card">
<div class="pksh-teachers__card-top">
<?php if ($t_photo) : ?>
<img class="pksh-teachers__photo" loading="lazy" width="130" height="130"
src="<?php echo esc_url(wp_get_attachment_url($t_photo)); ?>"
alt="<?php echo esc_attr($t_name); ?>">
<?php else : ?>
<img class="pksh-teachers__photo" loading="lazy" width="130" height="130"
src="<?php echo get_template_directory_uri(); ?>/assets/images/no-photo.png"
alt="<?php echo esc_attr($t_name); ?>">
<?php endif; ?>
</div>
<div class="pksh-teachers__card-body">
<h3 class="pksh-teachers__name"><?php echo esc_html($t_name); ?></h3>
<?php if ($t_subj) : ?>
<p class="pksh-teachers__subj"><?php echo esc_html($t_subj); ?></p>
<?php endif; ?>
<?php if ($t_about) : ?>
<p class="pksh-teachers__about"><?php echo esc_html($t_about); ?></p>
<?php endif; ?>
</div>
</div>
</div>
<?php
endforeach;
endif;
?>
</div>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Bento Row 5: Guarantee (standalone) -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section pksh-section--alt" id="guarantee" aria-label="Гарантия">
<div class="bento__full">
<div class="guarantee-block guarantee-block--centered">
<h2 class="bento-h2">🛡️ Гарантия результата</h2>
<p class="guarantee-block__text">
Если через 3 месяца ваш ребёнок не начнёт читать &mdash;
мы вернём деньги <strong>и продолжим обучение бесплатно</strong>,
пока не дойдём до результата.
</p>
<span class="guarantee-block__badge">Так работают только те, кто уверен в своём методе</span>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Bento Row 6: FAQ (как на странице лагеря) -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section pksh-section--alt" id="faq" aria-label="Вопросы и ответы">
<div class="bento__full pksh-faq">
<h2 class="bento-h2">Часто задаваемые вопросы</h2>
<?php
$faq_items = carbon_get_post_meta($post_id, 'pksh_faq_items');
if (!empty($faq_items) && is_array($faq_items)) :
$col1 = '';
$col2 = '';
$count = 0;
foreach ($faq_items as $item) :
$q = $item['faq_question'] ?? '';
$a = $item['faq_answer'] ?? '';
if (empty($q) && empty($a)) continue;
$accordion = '<div class="box__accordion">'
. '<div class="box__accordion_label" role="button" tabindex="0">'
. '<span>' . esc_html($q) . '</span>'
. '<svg role="presentation" focusable="false" width="24px" height="24px" viewBox="0 0 24 24">'
. '<g stroke="none" stroke-width="2px" fill="none" fill-rule="evenodd" stroke-linecap="square">'
. '<g transform="translate(1.000000, 1.000000)" stroke="currentColor">'
. '<path d="M0,11 L22,11"></path>'
. '<path d="M11,0 L11,22"></path>'
. '</g></g></svg></div>'
. '<div class="box__accordion_content">'
. '<div class="box__accordion_content-text">' . wp_kses_post($a) . '</div>'
. '</div></div>';
if ($count % 2 === 0) {
$col1 .= $accordion;
} else {
$col2 .= $accordion;
}
$count++;
endforeach;
if (!empty($col1) || !empty($col2)) :
?>
<div class="pksh-faq__wrapper">
<div class="pksh-faq__col"><?php echo $col1; ?></div>
<div class="pksh-faq__col"><?php echo $col2; ?></div>
</div>
<?php endif; endif; ?>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Reviews (lw-card inline, camp-style) -->
<!-- ════════════════════════════════════════════ -->
<section class="pksh-reviews" id="pksh-reviews" aria-label="Отзывы">
<h2 class="bento-h2">Что говорят родители</h2>
<?php
$review_items = carbon_get_post_meta($post_id, 'pksh_reviews_list');
$total_count = is_array($review_items) ? count($review_items) : 0;
if ($total_count > 0) :
$items = array_values($review_items);
$render_card = function($item) {
$author = $item['review_author'] ?? '';
$text = $item['review_text'] ?? '';
$raw_rating = $item['review_rating'] ?? '';
$avatar_id = !empty($item['review_avatar']) ? (int)$item['review_avatar'] : 0;
$stars = max(1, min(5, (int)$raw_rating));
$name = $author ?: 'Анонимно';
if (empty($text)) return '';
ob_start();
?>
<div class="lw-card">
<div class="lw-flex lw-flex--row lw-gap-12 lw-items-center">
<div class="lw-avatar lw-avatar--48">
<?php if ($avatar_id && wp_attachment_is_image($avatar_id)) : ?>
<?php echo wp_get_attachment_image($avatar_id, [48, 48], false, ['class' => 'lw-avatar__img', 'loading' => 'lazy', 'alt' => esc_attr($name)]); ?>
<?php else : ?>
<img class="lw-avatar__img" src="https://ui-avatars.com/api/?name=<?php echo urlencode($name); ?>&amp;background=random&amp;color=fff&amp;size=96" alt="<?php echo esc_attr($name); ?>" loading="lazy" width="48" height="48">
<?php endif; ?>
</div>
<div class="lw-flex lw-flex--col">
<div class="lw-text-dark lw-font-semibold"><?php echo esc_html($name); ?></div>
</div>
<div class="lw-badge lw-badge--verified">Проверенный отзыв</div>
</div>
<div class="lw-stars lw-mt-8">
<?php for ($i = 1; $i <= 5; $i++) : ?>
<svg class="lw-star <?php echo $i <= $stars ? 'lw-star--filled' : ''; ?>" width="16" height="16" viewBox="0 0 21 19" fill="none">
<path d="M9.58463 1.50884C9.89101 0.602477 11.1729 0.602476 11.4793 1.50884L13.1158 6.35007C13.2531 6.75637 13.6343 7.02985 14.0631 7.02985H19.2721C20.2504 7.02985 20.6467 8.28958 19.8447 8.84971L15.6995 11.7446C15.335 11.9991 15.1823 12.4636 15.3247 12.8847L16.9239 17.6158C17.233 18.5302 16.1954 19.3085 15.404 18.7559L11.1045 15.7533C10.7606 15.5131 10.3033 15.5131 9.95941 15.7533L5.65991 18.7559C4.86857 19.3085 3.83092 18.5302 4.14001 17.6158L5.73926 12.8847C5.8816 12.4636 5.72891 11.9991 5.36448 11.7446L1.21924 8.84971C0.417196 8.28958 0.813532 7.02985 1.79181 7.02985H7.00081C7.42969 7.02985 7.81081 6.75637 7.94815 6.35007L9.58463 1.50884Z" fill="currentColor"/>
</svg>
<?php endfor; ?>
</div>
<div class="lw-card__text lw-mt-8"><?php echo wp_kses_post($text); ?></div>
</div>
<?php
return ob_get_clean();
};
?>
<div class="lw-scroll-x" data-pksh-reviews>
<div class="lw-grid lw-grid--3 lw-gap-20" data-pksh-grid data-pksh-collapsed>
<?php foreach ($items as $item) echo $render_card($item); ?>
</div>
<?php if ($total_count > 4) : ?>
<div class="pksh-reviews__actions">
<button class="pksh-reviews__toggle" data-pksh-toggle type="button">Показать ещё</button>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<script>
(function(){
var grid = document.querySelector('[data-pksh-grid]');
var toggle = document.querySelector('[data-pksh-toggle]');
if (!grid || !toggle) return;
toggle.addEventListener('click', function(){
var expanded = grid.hasAttribute('data-pksh-expanded');
if (expanded) {
grid.removeAttribute('data-pksh-expanded');
grid.setAttribute('data-pksh-collapsed', '');
toggle.textContent = 'Показать ещё';
} else {
grid.removeAttribute('data-pksh-collapsed');
grid.setAttribute('data-pksh-expanded', '');
toggle.textContent = 'Скрыть';
}
});
})();
</script>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Final CTA Low Friction -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section pksh-section--alt" id="final-cta" aria-label="Запись">
<div class="bento__full">
<div class="final-cta">
<h2 class="bento-h2">Первое занятие бесплатно</h2>
<p class="final-cta__text">Ребёнок познакомится с педагогом, попробует задания. Вы увидите методику своими глазами. Никакой оплаты и договоров.</p>
<!-- Форма открывается в модалке (footer.php) скрипт из вкладки Битрикс24 в админке -->
<button class="btn-primary btn-primary--large open-bitrix-form btn-write">Записаться на бесплатное пробное</button>
<p class="final-cta__footnote">🔥 Осталось 2 места в группе 56 лет. Успейте!</p>
</div>
</div>
</section>
<!-- ════════════════════════════════════════════ -->
<!-- Contacts как на странице лагеря -->
<!-- ════════════════════════════════════════════ -->
<section class="bento pksh-section" id="pksh-contact" aria-label="Контакты">
<div class="bento__full pksh-contact">
<h2 class="bento-h2">Наш адрес</h2>
<div class="pksh-contact__wrapper">
<div class="pksh-contact__info">
<?php if (!empty($opt_phone)) : ?>
<div class="pksh-contact__subtitle">Телефон:</div>
<a class="pksh-contact__value" href="tel:+<?php echo preg_replace('/[^0-9]/', '', $opt_phone); ?>"><?php echo esc_html($opt_phone); ?></a>
<?php endif; ?>
<?php if (!empty($opt_email)) : ?>
<div class="pksh-contact__subtitle">Почта:</div>
<a class="pksh-contact__value" href="mailto:<?php echo esc_attr($opt_email); ?>"><?php echo esc_html($opt_email); ?></a>
<?php endif; ?>
<?php if (!empty($opt_address)) : ?>
<div class="pksh-contact__subtitle">Адрес:</div>
<div class="pksh-contact__value"><?php echo esc_html($opt_address); ?></div>
<?php endif; ?>
<?php if (!empty($opt_social_raw) && is_array($opt_social_raw)) : ?>
<div class="pksh-contact__subtitle">Наши соцсети:</div>
<div class="pksh-contact__social">
<?php foreach ($opt_social_raw as $item) :
$link = $item['url'] ?? '';
$name = $item['name'] ?? '';
if ($link) : ?>
<a class="pksh-contact__social-link" href="<?php echo esc_url($link); ?>" target="_blank" rel="noopener noreferrer" title="<?php echo esc_attr($name); ?>">
<?php echo esc_html($name ?: $link); ?>
</a>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div class="pksh-contact__map">
<div id="ya-map" style="width: 100%; height: 100%; min-height: 400px;"></div>
</div>
</div>
</div>
</section>
<script>
(function() {
var mapCoords = [<?php echo esc_js($opt_coords ?: '55.7558,37.6173'); ?>];
var mapLabel = '<?php echo esc_js($opt_name); ?>';
var mapLoaded = false;
var apiLoading = false;
function initMap() {
ymaps.ready(function() {
var map = new ymaps.Map('ya-map', {
center: mapCoords,
zoom: 16
}, {
searchControlProvider: 'yandex#search'
});
var placemark = new ymaps.Placemark(
mapCoords,
{ iconContent: mapLabel },
{ preset: 'islands#redStretchyIcon' }
);
map.geoObjects.add(placemark);
mapLoaded = true;
});
}
function loadYandexMaps() {
if (mapLoaded || apiLoading) return;
apiLoading = true;
var script = document.createElement('script');
script.src = 'https://api-maps.yandex.ru/2.1/?lang=ru_RU';
script.onload = initMap;
document.head.appendChild(script);
}
function onScroll() {
if ((window.pageYOffset || document.documentElement.scrollTop) > 100) {
loadYandexMaps();
window.removeEventListener('scroll', onScroll);
}
}
window.addEventListener('load', function() {
setTimeout(function() {
if (!mapLoaded && !apiLoading) loadYandexMaps();
}, 3000);
});
window.addEventListener('scroll', onScroll, { passive: true });
})();
</script>
</main>
<?php
get_footer();

View File

@ -21,9 +21,12 @@ add_filter( 'wpseo_opengraph_type', function ( $type ) {
add_filter( 'wpseo_frontend_presenter_classes', function ( $presenters ) { add_filter( 'wpseo_frontend_presenter_classes', function ( $presenters ) {
if ( is_page_template( 'page-reviews.php' ) ) { if ( is_page_template( 'page-reviews.php' ) ) {
$presenters = array_filter( $presenters, function ( $p ) { $presenters = array_filter( $presenters, function ( $p ) {
return strpos( $p, 'Article_' ) === false // Open_Graph + Twitter — отключаем весь блок OG/Twitter-тегов Yoast, т.к. выводим свои
return strpos( $p, 'Open_Graph' ) === false
&& strpos( $p, 'Article_' ) === false
&& strpos( $p, 'Label1' ) === false && strpos( $p, 'Label1' ) === false
&& strpos( $p, 'Data1' ) === false; && strpos( $p, 'Data1' ) === false
&& strpos( $p, 'Twitter' ) === false;
} ); } );
} }
return $presenters; return $presenters;
@ -39,8 +42,10 @@ add_action( 'wp_head', function () {
$post_id = get_queried_object_id(); $post_id = get_queried_object_id();
$thumb_id = get_post_thumbnail_id( $post_id ); $thumb_id = get_post_thumbnail_id( $post_id );
// Preconnect для Phosphor Icons (CDN) // Preconnect + preload для Phosphor Icons (CDN, render-blocking → отложенная загрузка)
echo '<link rel="preconnect" href="https://unpkg.com" crossorigin />' . "\n"; echo '<link rel="preconnect" href="https://unpkg.com" crossorigin />' . "\n";
echo '<link rel="preload" as="style" href="https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/style.css" onload="this.onload=null;this.rel=\'stylesheet\'">' . "\n";
echo '<noscript><link rel="stylesheet" href="https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/style.css"></noscript>' . "\n";
$og_title = wp_get_document_title(); $og_title = wp_get_document_title();
$og_description = get_post_meta( $post_id, '_yoast_wpseo_metadesc', true ) ?: get_bloginfo( 'description' ); $og_description = get_post_meta( $post_id, '_yoast_wpseo_metadesc', true ) ?: get_bloginfo( 'description' );
@ -68,8 +73,17 @@ add_action( 'wp_head', function () {
echo '<meta property="og:description" content="' . esc_attr( $og_description ) . '" />' . "\n"; echo '<meta property="og:description" content="' . esc_attr( $og_description ) . '" />' . "\n";
echo '<meta property="og:url" content="' . esc_url( $og_url ) . '" />' . "\n"; echo '<meta property="og:url" content="' . esc_url( $og_url ) . '" />' . "\n";
echo '<meta property="og:type" content="' . esc_attr( $og_type ) . '" />' . "\n"; echo '<meta property="og:type" content="' . esc_attr( $og_type ) . '" />' . "\n";
echo '<meta property="og:site_name" content="' . esc_attr( get_bloginfo( 'name' ) ) . '" />' . "\n"; $og_site_name = get_option( 'branch_og_site_name' ) ?: sprintf( 'Частная школа %s %s', get_option( 'branch_name', 'Декарт' ), get_option( 'branch_city_prep', 'в Щёлково' ) );
echo '<meta property="og:site_name" content="' . esc_attr( $og_site_name ) . '" />' . "\n";
echo '<meta property="og:locale" content="ru_RU" />' . "\n"; echo '<meta property="og:locale" content="ru_RU" />' . "\n";
// Twitter Card (из того же featured image)
echo '<meta name="twitter:card" content="summary_large_image" />' . "\n";
echo '<meta name="twitter:title" content="' . esc_attr( $og_title ) . '" />' . "\n";
echo '<meta name="twitter:description" content="' . esc_attr( $og_description ) . '" />' . "\n";
if ( $thumb_id && $img_data ) {
echo '<meta name="twitter:image" content="' . esc_url( $img_data[0] ) . '" />' . "\n";
}
}, 1 ); }, 1 );
// Критический CSS для LCP вынесен в assets/css/reviews-page.css. // Критический CSS для LCP вынесен в assets/css/reviews-page.css.
@ -96,15 +110,15 @@ $branch_messengers = get_option( 'branch_messengers', array() );
// ─── КОНТЕНТ СТРАНИЦЫ (Carbon Fields post_meta) ─── // ─── КОНТЕНТ СТРАНИЦЫ (Carbon Fields post_meta) ───
// Метрики доверия // Метрики доверия (только из Carbon Fields — никаких хардкод-значений)
$metric_1_num = carbon_get_post_meta( $post_id, 'reviews_hero_metric_1_number' ) ?: '500+'; $metric_1_num = carbon_get_post_meta( $post_id, 'reviews_hero_metric_1_number' );
$metric_1_lbl = carbon_get_post_meta( $post_id, 'reviews_hero_metric_1_label' ) ?: 'счастливых семей'; $metric_1_lbl = carbon_get_post_meta( $post_id, 'reviews_hero_metric_1_label' );
$metric_2_num = carbon_get_post_meta( $post_id, 'reviews_hero_metric_2_number' ) ?: '12+'; $metric_2_num = carbon_get_post_meta( $post_id, 'reviews_hero_metric_2_number' );
$metric_2_lbl = carbon_get_post_meta( $post_id, 'reviews_hero_metric_2_label' ) ?: 'лет в образовании'; $metric_2_lbl = carbon_get_post_meta( $post_id, 'reviews_hero_metric_2_label' );
$metric_3_num = carbon_get_post_meta( $post_id, 'reviews_hero_metric_3_number' ) ?: '4.9'; $metric_3_num = carbon_get_post_meta( $post_id, 'reviews_hero_metric_3_number' );
$metric_3_lbl = carbon_get_post_meta( $post_id, 'reviews_hero_metric_3_label' ) ?: 'рейтинг на Яндексе'; $metric_3_lbl = carbon_get_post_meta( $post_id, 'reviews_hero_metric_3_label' );
$metric_4_num = carbon_get_post_meta( $post_id, 'reviews_hero_metric_4_number' ) ?: '14'; $metric_4_num = carbon_get_post_meta( $post_id, 'reviews_hero_metric_4_number' );
$metric_4_lbl = carbon_get_post_meta( $post_id, 'reviews_hero_metric_4_label' ) ?: 'отзывов о школе'; $metric_4_lbl = carbon_get_post_meta( $post_id, 'reviews_hero_metric_4_label' );
// Результаты // Результаты
$results_title = carbon_get_post_meta( $post_id, 'reviews_results_title' ) ?: 'Декарт в цифрах'; $results_title = carbon_get_post_meta( $post_id, 'reviews_results_title' ) ?: 'Декарт в цифрах';
@ -140,15 +154,47 @@ if ( ! $jsonld_img_url ) {
$jsonld_img_url = $logo_id ? wp_get_attachment_url( $logo_id ) : ''; $jsonld_img_url = $logo_id ? wp_get_attachment_url( $logo_id ) : '';
} }
// ─── СОЦСЕТИ ДЛЯ JSON-LD ─── // ─── ИЗОБРАЖЕНИЕ ШКОЛЫ ДЛЯ JSON-LD (image) ───
// Используем featured image главной страницы (page_on_front).
// Schema.org рекомендует минимум 696px ширины.
$school_image_url = '';
$front_page_id = (int) get_option( 'page_on_front' );
if ( $front_page_id && has_post_thumbnail( $front_page_id ) ) {
$img_data = wp_get_attachment_image_src( get_post_thumbnail_id( $front_page_id ), 'full' );
if ( $img_data && $img_data[1] >= 696 ) {
$school_image_url = $img_data[0];
}
}
// ─── СОЦСЕТИ ДЛЯ JSON-LD (sameAs) ───
$jsonld_social = array(); $jsonld_social = array();
// 1. branch_social (Settings API) — формат: [['name' => 'VK', 'url' => 'https://...']]
// или CF social_media fallback: [['social_media_name' => '', 'social_media_link' => 'https://...']]
if ( is_array( $branch_social ) ) { if ( is_array( $branch_social ) ) {
foreach ( $branch_social as $s ) { foreach ( $branch_social as $s ) {
// Settings API: ['name', 'url']
if ( ! empty( $s['url'] ) ) { if ( ! empty( $s['url'] ) ) {
$jsonld_social[] = $s['url']; $jsonld_social[] = $s['url'];
} }
// CF theme option social_media: ['social_media_name', 'social_media_link']
if ( ! empty( $s['social_media_link'] ) ) {
$jsonld_social[] = $s['social_media_link'];
} }
} }
}
// 2. branch_messengers — мессенджеры (WhatsApp, Telegram) тоже в sameAs
if ( is_array( $branch_messengers ) ) {
foreach ( $branch_messengers as $m ) {
if ( ! empty( $m['url'] ) ) {
$jsonld_social[] = $m['url'];
}
}
}
// Удаляем дубликаты (если VK есть и в соцсетях, и в мессенджерах)
$jsonld_social = array_values( array_unique( $jsonld_social ) );
?> ?>
<main class="page-reviews"> <main class="page-reviews">
@ -181,33 +227,39 @@ if ( is_array( $branch_social ) ) {
<h1 class="reviews-hero__h1">Отзывы о частной школе «Декарт» <?php echo esc_html( $branch_city_prep ); ?></h1> <h1 class="reviews-hero__h1">Отзывы о частной школе «Декарт» <?php echo esc_html( $branch_city_prep ); ?></h1>
<p class="reviews-hero__subtitle">Родители доверяют нам самое главное вот что они говорят о нас</p> <p class="reviews-hero__subtitle">Родители доверяют нам самое главное вот что они говорят о нас</p>
<?php
// Собираем только те метрики, которые заполнены в админке (и число, и подпись)
$hero_metrics = array();
if ( $metric_1_num && $metric_1_lbl ) {
$hero_metrics[] = array( 'num' => $metric_1_num, 'lbl' => $metric_1_lbl );
}
if ( $metric_2_num && $metric_2_lbl ) {
$hero_metrics[] = array( 'num' => $metric_2_num, 'lbl' => $metric_2_lbl );
}
if ( $metric_3_num && $metric_3_lbl ) {
$hero_metrics[] = array( 'num' => $metric_3_num, 'lbl' => $metric_3_lbl );
}
if ( $metric_4_num && $metric_4_lbl ) {
$hero_metrics[] = array( 'num' => $metric_4_num, 'lbl' => $metric_4_lbl );
}
if ( ! empty( $hero_metrics ) ) : ?>
<div class="reviews-hero__metrics"> <div class="reviews-hero__metrics">
<?php foreach ( $hero_metrics as $m ) : ?>
<div class="reviews-hero__metric reveal"> <div class="reviews-hero__metric reveal">
<span class="reviews-hero__metric-number"><?php echo esc_html( $metric_1_num ); ?></span> <span class="reviews-hero__metric-number"><?php echo esc_html( $m['num'] ); ?></span>
<span class="reviews-hero__metric-label"><?php echo esc_html( $metric_1_lbl ); ?></span> <span class="reviews-hero__metric-label"><?php echo esc_html( $m['lbl'] ); ?></span>
</div>
<div class="reviews-hero__metric reveal">
<span class="reviews-hero__metric-number"><?php echo esc_html( $metric_2_num ); ?></span>
<span class="reviews-hero__metric-label"><?php echo esc_html( $metric_2_lbl ); ?></span>
</div>
<div class="reviews-hero__metric reveal">
<span class="reviews-hero__metric-number"><?php echo esc_html( $metric_3_num ); ?></span>
<span class="reviews-hero__metric-label"><?php echo esc_html( $metric_3_lbl ); ?></span>
</div>
<div class="reviews-hero__metric reveal">
<span class="reviews-hero__metric-number"><?php echo esc_html( $metric_4_num ); ?></span>
<span class="reviews-hero__metric-label"><?php echo esc_html( $metric_4_lbl ); ?></span>
</div> </div>
<?php endforeach; ?>
</div> </div>
<?php endif; ?>
<!-- Бейдж агрегированного рейтинга (social proof) --> <!-- Бейдж агрегированного рейтинга (social proof) -->
<?php $ext_review_count = get_option( 'branch_ext_review_count', '247' ); ?>
<div class="reviews-hero__rating-badge"> <div class="reviews-hero__rating-badge">
<span class="reviews-hero__rating-stars">★★★★★</span> <span class="reviews-hero__rating-stars">★★★★★</span>
<span class="reviews-hero__rating-text">4.9 из 5 на основе 247 отзывов на внешних площадках</span> <span class="reviews-hero__rating-text">4.9 из 5 на основе <?php echo esc_html( $ext_review_count ); ?> отзывов на внешних площадках</span>
</div> </div>
<!-- TODO: заменить «247» на реальное суммарное количество отзывов со всех площадок -->
<button type="button" class="reviews-hero__cta-btn js-reviews-cta" data-goal="hero-cta"> <button type="button" class="reviews-hero__cta-btn js-reviews-cta" data-goal="hero-cta">
Записаться на экскурсию Записаться на экскурсию
</button> </button>
@ -267,7 +319,12 @@ $intro_price_txt = carbon_get_post_meta( $post_id, 'reviews_intro_price_text' );
<?php endif; ?> <?php endif; ?>
</div> </div>
<div class="reviews-intro__info"> <div class="reviews-intro__info">
<p><strong>Режим работы школы:</strong> <?php echo esc_html( get_option( 'branch_hours', 'ежедневно с 8:00 до 19:00, кроме воскресенья' ) ); ?>.</p> <?php
$intro_hours = carbon_get_post_meta( $post_id, 'reviews_intro_hours' );
if ( ! empty( $intro_hours ) ) :
?>
<p><strong>Режим работы школы:</strong> <?php echo esc_html( $intro_hours ); ?>.</p>
<?php endif; ?>
<?php if ( ! empty( $intro_price_txt ) ) : ?> <?php if ( ! empty( $intro_price_txt ) ) : ?>
<p><strong>Стоимость обучения:</strong> <?php echo esc_html( $intro_price_txt ); ?>. <a href="/nachalnaya-shkola/">Узнать подробнее о ценах →</a></p> <p><strong>Стоимость обучения:</strong> <?php echo esc_html( $intro_price_txt ); ?>. <a href="/nachalnaya-shkola/">Узнать подробнее о ценах →</a></p>
<?php endif; ?> <?php endif; ?>
@ -320,7 +377,7 @@ $intro_price_txt = carbon_get_post_meta( $post_id, 'reviews_intro_price_text' );
<section class="section-bg reviews-grid" data-bg="white" id="reviews-list"> <section class="section-bg reviews-grid" data-bg="white" id="reviews-list">
<div class="container"> <div class="container">
<!-- H2 для SEO: ключевые слова «отзывы родителей», «частная школа», «Щёлково» --> <!-- H2 для SEO: ключевые слова «отзывы родителей», «частная школа», «Щёлково» -->
<h2 class="reviews-grid__h2">Отзывы родителей о частной школе в Щёлково</h2> <h2 class="reviews-grid__h2">Отзывы родителей о частной школе <?php echo esc_html( $branch_city_prep ); ?></h2>
<p class="reviews-grid__intro">Все отзывы проходят проверку мы связываемся с родителями и убеждаемся в достоверности каждого отзыва. Здесь собраны настоящие истории семей, чьи дети учатся в частной школе «Декарт» в Щёлково. Читайте, знакомьтесь и присоединяйтесь!</p> <p class="reviews-grid__intro">Все отзывы проходят проверку мы связываемся с родителями и убеждаемся в достоверности каждого отзыва. Здесь собраны настоящие истории семей, чьи дети учатся в частной школе «Декарт» в Щёлково. Читайте, знакомьтесь и присоединяйтесь!</p>
<?php <?php
@ -351,7 +408,7 @@ $intro_price_txt = carbon_get_post_meta( $post_id, 'reviews_intro_price_text' );
$is_long = strlen( wp_strip_all_tags( $text ) ) > 350; $is_long = strlen( wp_strip_all_tags( $text ) ) > 350;
$batch = (int) ceil( $card_idx / 12 ); $batch = (int) ceil( $card_idx / 12 );
?> ?>
<div class="lw-card reveal" data-batch="<?php echo (int) $batch; ?>"> <article class="lw-card reveal" data-batch="<?php echo (int) $batch; ?>">
<div class="lw-flex lw-flex--row lw-gap-12 lw-items-start"> <div class="lw-flex lw-flex--row lw-gap-12 lw-items-start">
<?php <?php
$review_photo_url = $item['review_photo'] ?? ''; $review_photo_url = $item['review_photo'] ?? '';
@ -372,7 +429,7 @@ $intro_price_txt = carbon_get_post_meta( $post_id, 'reviews_intro_price_text' );
Преподаватель: <a href="<?php echo esc_url( get_permalink( $teacher[0]['id'] ) ); ?>" class="review-card__teacher-link"><?php echo esc_html( get_the_title( $teacher[0]['id'] ) ); ?></a> Преподаватель: <a href="<?php echo esc_url( get_permalink( $teacher[0]['id'] ) ); ?>" class="review-card__teacher-link"><?php echo esc_html( get_the_title( $teacher[0]['id'] ) ); ?></a>
</div> </div>
<?php endif; ?> <?php endif; ?>
<div class="lw-badge lw-badge--verified" style="margin-top:6px; margin-left:0; align-self:flex-start;"> Проверенный отзыв</div> <div class="lw-badge lw-badge--verified"> Проверенный отзыв</div>
</div> </div>
</div> </div>
@ -395,7 +452,7 @@ $intro_price_txt = carbon_get_post_meta( $post_id, 'reviews_intro_price_text' );
<span class="review-card__date"><?php echo esc_html( $date ); ?></span> <span class="review-card__date"><?php echo esc_html( $date ); ?></span>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </article>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
</div> </div>
@ -562,7 +619,7 @@ $intro_price_txt = carbon_get_post_meta( $post_id, 'reviews_intro_price_text' );
</section> </section>
<?php endif; ?> <?php endif; ?>
<!-- TODO: загрузить 35 видеоотзывов родителей (YouTube или VK Video) в CF reviews_video --> <?php /* TODO: загрузить 35 видеоотзывов родителей (YouTube или VK Video) в CF reviews_video */ ?>
<!-- ════════════════════════════════════════════ <!-- ════════════════════════════════════════════
ВНЕШНИЕ ПЛОЩАДКИ (E-E-A-T) ВНЕШНИЕ ПЛОЩАДКИ (E-E-A-T)
@ -770,8 +827,8 @@ $intro_price_txt = carbon_get_post_meta( $post_id, 'reviews_intro_price_text' );
<div class="final-cta__triggers"> <div class="final-cta__triggers">
<div class="final-cta__trigger"> <div class="final-cta__trigger">
<span class="final-cta__trigger-icon">📚</span> <span class="final-cta__trigger-icon">📚</span>
<span>Набор в 1 класс на 2026/27 учебный год открыт <strong>осталось 12 мест</strong></span> <?php $remaining_seats = get_option( 'branch_remaining_seats', '12' ); ?>
<!-- TODO: уточнить реальное количество свободных мест --> <span>Набор в 1 класс на 2026/27 учебный год открыт <strong>осталось <?php echo esc_html( $remaining_seats ); ?> мест</strong></span>
</div> </div>
<div class="final-cta__trigger"> <div class="final-cta__trigger">
<span class="final-cta__trigger-icon">📅</span> <span class="final-cta__trigger-icon">📅</span>
@ -1061,40 +1118,130 @@ $org_address = array(
'streetAddress' => $branch_addr, 'streetAddress' => $branch_addr,
'addressLocality' => $branch_city, 'addressLocality' => $branch_city,
'addressRegion' => $branch_region, 'addressRegion' => $branch_region,
'postalCode' => get_option( 'branch_postal_code', '' ) ?: '', 'postalCode' => get_option( 'branch_postal_code', '' ) ?: carbon_get_theme_option( 'schema_postal_code' ) ?: '',
'addressCountry' => 'RU', 'addressCountry' => 'RU',
); );
// ─── ДАННЫЕ СХЕМЫ (CF theme options «SEO и микроразметка» + Settings API fallback) ───
$schema_description = carbon_get_theme_option( 'schema_description' ) ?: get_option( 'branch_schema_description', '' );
$schema_founded = carbon_get_theme_option( 'schema_founded' ) ?: get_option( 'branch_founded', '' );
$schema_map_url = carbon_get_theme_option( 'schema_map_url' );
$schema_area_raw = carbon_get_theme_option( 'schema_area_served' ) ?: get_option( 'branch_area_served', '' );
$hours_cf = carbon_get_theme_option( 'schema_hours' );
// ─── openingHoursSpecification (CF complex → Settings API textarea) ───
$hours_spec = array();
if ( ! empty( $hours_cf ) && is_array( $hours_cf ) ) {
foreach ( $hours_cf as $entry ) {
$day = isset( $entry['day_of_week'] ) ? trim( $entry['day_of_week'] ) : '';
if ( empty( $day ) ) {
continue;
}
$is_off = ! empty( $entry['day_off'] );
if ( $is_off ) {
$hours_spec[] = array(
'@type' => 'OpeningHoursSpecification',
'dayOfWeek' => $day,
'opens' => '00:00',
'closes' => '00:00',
);
} else {
$from = isset( $entry['opens'] ) ? trim( $entry['opens'] ) : '';
$to = isset( $entry['closes'] ) ? trim( $entry['closes'] ) : '';
if ( $from && $to ) {
$hours_spec[] = array(
'@type' => 'OpeningHoursSpecification',
'dayOfWeek' => $day,
'opens' => $from,
'closes' => $to,
);
}
}
}
} else {
// Fallback: читаем из текстового поля отзывов (только dayOfWeek|opens|closes)
$hours_raw = carbon_get_post_meta( $post_id, 'reviews_intro_hours' );
if ( ! empty( $hours_raw ) && is_string( $hours_raw ) ) {
$lines = explode( "\n", $hours_raw );
foreach ( $lines as $line ) {
$line = trim( $line );
if ( empty( $line ) ) {
continue;
}
$parts = explode( '|', $line );
$day = isset( $parts[0] ) ? trim( $parts[0] ) : '';
if ( empty( $day ) ) {
continue;
}
if ( isset( $parts[2] ) && 'closed' === strtolower( trim( $parts[2] ) ) ) {
$hours_spec[] = array(
'@type' => 'OpeningHoursSpecification',
'dayOfWeek' => $day,
'opens' => '00:00',
'closes' => '00:00',
);
continue;
}
$from = isset( $parts[1] ) ? trim( $parts[1] ) : '';
$to = isset( $parts[2] ) ? trim( $parts[2] ) : '';
if ( $day && $from && $to ) {
$hours_spec[] = array(
'@type' => 'OpeningHoursSpecification',
'dayOfWeek' => $day,
'opens' => $from,
'closes' => $to,
);
}
}
}
}
// ─── areaServed — города присутствия ───
$area_served = array();
$area_served[] = array( '@type' => 'City', 'name' => $branch_city );
if ( ! empty( $schema_area_raw ) && is_string( $schema_area_raw ) ) {
$cities = array_map( 'trim', explode( ',', $schema_area_raw ) );
foreach ( $cities as $c ) {
if ( $c && $c !== $branch_city ) {
$area_served[] = array( '@type' => 'City', 'name' => $c );
}
}
} else {
// Fallback: только город филиала
}
// ─── description — описание организации ───
if ( ! empty( $schema_description ) && is_string( $schema_description ) ) {
$org_description = wp_strip_all_tags( trim( $schema_description ) );
} else {
$org_description = 'Частная школа полного дня в ' . $branch_city . '. Обучение с 1 по 11 класс, подготовка к ЕГЭ и ОГЭ, индивидуальный подход.';
}
$organization = array( $organization = array(
'@type' => 'School', '@type' => array( 'School', 'LocalBusiness' ),
'@id' => home_url( '/' ) . '#organization', '@id' => home_url( '/' ) . '#organization',
'name' => $branch_name, 'name' => $branch_name,
'description' => 'Частная школа полного дня в ' . $branch_city . '. Обучение с 1 по 11 класс, подготовка к ЕГЭ и ОГЭ, индивидуальный подход.', 'description' => $org_description,
'url' => home_url( '/' ), 'url' => home_url( '/' ),
'address' => $org_address, 'address' => $org_address,
'priceRange' => get_option( 'branch_price_range' ) ?: 'от 40 000 ₽/мес', 'areaServed' => $area_served,
'openingHoursSpecification' => array(
array(
'@type' => 'OpeningHoursSpecification',
'dayOfWeek' => array( 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday' ),
'opens' => '08:00',
'closes' => '19:00',
),
array(
'@type' => 'OpeningHoursSpecification',
'dayOfWeek' => 'Saturday',
'opens' => '09:00',
'closes' => '14:00',
),
),
'areaServed' => array(
array( '@type' => 'City', 'name' => $branch_city ),
array( '@type' => 'City', 'name' => 'Фрязино' ),
array( '@type' => 'City', 'name' => 'Монино' ),
),
// foundingDate задаётся в branch-опциях (SEO-таб) или в CF schema_founding_date
); );
// openingHoursSpecification — из CF или Settings API (условно)
if ( ! empty( $hours_spec ) ) {
$organization['openingHoursSpecification'] = $hours_spec;
}
// hasMap — ссылка на карту (условно)
if ( ! empty( $schema_map_url ) && is_string( $schema_map_url ) ) {
$organization['hasMap'] = esc_url( trim( $schema_map_url ) );
}
// foundingDate — год основания (условно)
if ( ! empty( $schema_founded ) && is_string( $schema_founded ) ) {
$organization['foundingDate'] = trim( $schema_founded );
}
// GeoCoordinates на уровне Organization (не внутри PostalAddress) // GeoCoordinates на уровне Organization (не внутри PostalAddress)
if ( ! empty( $branch_coords ) && false !== strpos( $branch_coords, ',' ) ) { if ( ! empty( $branch_coords ) && false !== strpos( $branch_coords, ',' ) ) {
$coords_parts = explode( ',', $branch_coords ); $coords_parts = explode( ',', $branch_coords );
@ -1118,6 +1265,9 @@ if ( $branch_phone ) {
if ( $jsonld_img_url ) { if ( $jsonld_img_url ) {
$organization['logo'] = $jsonld_img_url; $organization['logo'] = $jsonld_img_url;
} }
if ( $school_image_url ) {
$organization['image'] = $school_image_url;
}
if ( ! empty( $jsonld_social ) ) { if ( ! empty( $jsonld_social ) ) {
$organization['sameAs'] = $jsonld_social; $organization['sameAs'] = $jsonld_social;
} }
@ -1135,6 +1285,13 @@ if ( $branch_phone || $branch_email ) {
$organization['contactPoint'] = array( $cp ); $organization['contactPoint'] = array( $cp );
} }
// AggregateRating — вкладываем напрямую в Organization (School), а не отдельным узлом @graph.
// Google показывает звёздочки в выдаче только когда aggregateRating — свойство Organization/LocalBusiness.
if ( $agg_rating ) {
unset( $agg_rating['itemReviewed'] );
$organization['aggregateRating'] = $agg_rating;
}
// ─── WebSite ─── // ─── WebSite ───
$ld_website = array( $ld_website = array(
'@type' => 'WebSite', '@type' => 'WebSite',
@ -1143,6 +1300,14 @@ $ld_website = array(
'name' => $branch_name, 'name' => $branch_name,
'inLanguage' => 'ru-RU', 'inLanguage' => 'ru-RU',
'publisher' => array( '@id' => home_url( '/' ) . '#organization' ), 'publisher' => array( '@id' => home_url( '/' ) . '#organization' ),
'potentialAction' => array(
'@type' => 'SearchAction',
'target' => array(
'@type' => 'EntryPoint',
'urlTemplate' => home_url( '/' ) . '?s={search_term_string}',
),
'query-input' => 'required name=search_term_string',
),
); );
// ─── WebPage ─── // ─── WebPage ───
@ -1152,7 +1317,6 @@ $ld_webpage = array(
'url' => get_permalink( $post_id ), 'url' => get_permalink( $post_id ),
'inLanguage' => 'ru-RU', 'inLanguage' => 'ru-RU',
'name' => wp_get_document_title() ?: 'Отзывы — ' . $branch_name, 'name' => wp_get_document_title() ?: 'Отзывы — ' . $branch_name,
'description' => 'Реальные отзывы родителей и учеников о частной школе Декарт в Щёлково. Истории поступлений, мнения о программах и педагогах.',
'isPartOf' => array( '@id' => home_url( '/' ) . '#website' ), 'isPartOf' => array( '@id' => home_url( '/' ) . '#website' ),
'about' => array( '@id' => home_url( '/' ) . '#organization' ), 'about' => array( '@id' => home_url( '/' ) . '#organization' ),
'speakable' => array( 'speakable' => array(
@ -1161,6 +1325,32 @@ $ld_webpage = array(
), ),
); );
// description — Yoast SEO → динамическая сборка с городом
$webpage_description = get_post_meta( $post_id, '_yoast_wpseo_metadesc', true );
if ( ! $webpage_description ) {
$webpage_description = sprintf(
'Реальные отзывы родителей и учеников о частной школе Декарт в %s. Истории поступлений, мнения о программах и педагогах.',
$branch_city
);
}
$ld_webpage['description'] = wp_strip_all_tags( $webpage_description );
// image — featured image страницы (если есть)
$page_thumb_id = get_post_thumbnail_id( $post_id );
if ( $page_thumb_id ) {
$page_img = wp_get_attachment_image_url( $page_thumb_id, 'full' );
if ( $page_img ) {
$ld_webpage['image'] = esc_url( $page_img );
}
}
// datePublished / dateModified — даты публикации страницы (сигнал свежести)
$post_obj = get_post( $post_id );
if ( $post_obj ) {
$ld_webpage['datePublished'] = get_the_date( 'c', $post_obj );
$ld_webpage['dateModified'] = get_the_modified_date( 'c', $post_obj );
}
// ─── Breadcrumb ─── // ─── Breadcrumb ───
$ld_breadcrumb = array( $ld_breadcrumb = array(
'@type' => 'BreadcrumbList', '@type' => 'BreadcrumbList',
@ -1188,11 +1378,6 @@ $ld_graph = array_merge(
$ld_faq $ld_faq
); );
// Если есть AggregateRating — добавляем к webpage
if ( $agg_rating ) {
$ld_graph[] = $agg_rating;
}
if ( ! empty( $ld_graph ) ) { if ( ! empty( $ld_graph ) ) {
$jsonld_data = array( $jsonld_data = array(
'@context' => 'https://schema.org', '@context' => 'https://schema.org',

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,45 @@
<?php <?php
/**
* Hero section cinematic split-screen redesign 2026
*
* Fields loaded from Carbon Fields tab "🖼️ Герой (баннер)".
* Город и адрес из Настроек филиала (branch_city_prep, branch_address).
*
* @package Dekart
*/
$post_id = get_the_ID(); $post_id = get_the_ID();
$page_breadcrumbs = carbon_get_post_meta( $post_id, 'page_breadcrumbs' ); $page_breadcrumbs = carbon_get_post_meta( $post_id, 'page_breadcrumbs' );
// Поля из Carbon Fields (вкладка "🖼️ Герой")
$banner_img_id = carbon_get_post_meta( $post_id, 'banner_image' );
$h1 = carbon_get_post_meta( $post_id, 'banner_h1' );
$subtitle = carbon_get_post_meta( $post_id, 'banner_subtitle' );
$scarcity = carbon_get_post_meta( $post_id, 'banner_scarcity' );
// Город и адрес из Настроек филиала
$branch_city = get_option( 'branch_city_prep', 'в Щёлково' );
$branch_address = get_option( 'branch_address', 'ул. Комсомольская, 11' );
?> ?>
<section class="banner"> <section class="hero-pksh" aria-label="Hero">
<?php if ( ! empty( $page_breadcrumbs ) ) { ?>
<nav class="container" aria-label="Хлебные крошки"> <!-- ── Background layers ── -->
<div class="hero-pksh__bg" aria-hidden="true">
<div class="hero-pksh__blob hero-pksh__blob--1"></div>
<div class="hero-pksh__blob hero-pksh__blob--2"></div>
<div class="hero-pksh__blob hero-pksh__blob--3"></div>
<div class="hero-pksh__pattern"></div>
</div>
<!-- ── Breadcrumbs ── -->
<?php if ( ! empty( $page_breadcrumbs ) ) : ?>
<nav class="hero-pksh__breadcrumbs container" aria-label="Хлебные крошки">
<ul class="breadcrumbs"> <ul class="breadcrumbs">
<li> <li>
<a href="<?php echo esc_url( home_url() ); ?>/" class="home fa-home" title="Главная страница"> <a href="<?php echo esc_url( home_url() ); ?>/" class="home fa-home" title="Главная страница">
<span style="display:none;">Главная</span> <span style="display:none;">Главная</span>
<svg width="21" height="16" viewBox="0 0 21 16" fill="none"> <svg width="21" height="16" viewBox="0 0 21 16" fill="none">
<path d="M11.6473 10.6896H12.1473V11.1896V15.4691H16.4353C16.5143 15.4691 16.5756 15.4462 16.6432 15.3788C16.7108 15.3113 16.7333 15.2505 16.7333 15.1725V9.36976L10.0513 3.8712L3.36922 9.36976V15.1725C3.36922 15.2505 3.39178 15.3113 3.45937 15.3788C3.52698 15.4462 3.5883 15.4691 3.66723 15.4691H7.95527V11.1896V10.6896H8.45527H11.6473ZM10.3711 1.74407L18.9226 8.86174L19.5643 8.09722L16.9141 5.8987L16.7333 5.74874V5.51388V0.537319H14.5413V2.86272V3.93127L13.7209 3.2466L10.6815 0.709949C10.5114 0.572829 10.3095 0.499981 10.0513 0.499981C9.79293 0.499981 9.59094 0.572914 9.42075 0.710194L0.538196 8.09712L1.17996 8.86174L9.73142 1.74407L10.0513 1.47783L10.3711 1.74407Z" fill="white" stroke="white"/> <path d="M11.6473 10.6896H12.1473V11.1896V15.4691H16.4353C16.5143 15.4691 16.5756 15.4462 16.6432 15.3788C16.7108 15.3113 16.7333 15.2505 16.7333 15.1725V9.36976L10.0513 3.8712L3.36922 9.36976V15.1725C3.36922 15.2505 3.39178 15.3113 3.45937 15.3788C3.52698 15.4462 3.5883 15.4691 3.66723 15.4691H7.95527V11.1896V10.6896H8.45527H11.6473ZM10.3711 1.74407L18.9226 8.86174L19.5643 8.09722L16.9141 5.8987L16.7333 5.74874V5.51388V0.537319H14.5413V2.86272V3.93127L13.7209 3.2466L10.6815 0.709949C10.5114 0.572829 10.3095 0.499981 10.0513 0.499981C9.79293 0.499981 9.59094 0.572914 9.42075 0.710194L0.538196 8.09712L1.17996 8.86174L9.73142 1.74407L10.0513 1.47783L10.3711 1.74407Z" fill="currentColor" stroke="currentColor"/>
</svg> </svg>
</a> </a>
</li> </li>
@ -31,37 +60,123 @@ $page_breadcrumbs = carbon_get_post_meta( $post_id, 'page_breadcrumbs' );
<?php endforeach; ?> <?php endforeach; ?>
</ul> </ul>
</nav> </nav>
<?php } ?> <?php endif; ?>
<div class="banner-wrapper"> <!-- ── Main hero layout: split screen ── -->
<?php <div class="hero-pksh__layout">
$foto = carbon_get_post_meta( $post_id, 'banner_img_desktop' );
$foto_mobile = carbon_get_post_meta( $post_id, 'banner_img_mobile' ); <!-- ====== LEFT COLUMN: Content ====== -->
if ( empty( $foto_mobile ) ) { <div class="hero-pksh__content">
$foto_mobile = $foto;
} <!-- Address pill -->
if ( $foto ) : <div class="hero-pksh__address">
?> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
<picture class="banner-fon"> <span><?php echo esc_html( $branch_address ); ?> — центр <?php echo esc_html( str_replace( array( 'в ', 'В ' ), '', $branch_city ) ); ?></span>
<source srcset="<?php echo esc_url( wp_get_attachment_url( $foto ) ); ?>" media="(min-width: 860px)"> </div>
<img src="<?php echo esc_url( wp_get_attachment_url( $foto_mobile ) ); ?>" alt="<?php echo esc_attr( get_post_meta( $foto, '_wp_attachment_image_alt', true ) ); ?>">
</picture> <!-- Main headline -->
<h1 class="hero-pksh__title">
<?php echo esc_html( $h1 ?: 'Подготовка к школе в Щёлково' ); ?>
</h1>
<!-- Subtitle -->
<?php if ( '' !== $subtitle ) : ?>
<p class="hero-pksh__subtitle"><?php echo esc_html( $subtitle ); ?></p>
<?php endif; ?> <?php endif; ?>
<div class="banner__content desktop">
<h1 class="page_title"><?php echo esc_html( carbon_get_post_meta( $post_id, 'banner_h1' ) ); ?></h1> <!-- CTA + Scarcity -->
<?php if ( carbon_get_post_meta( $post_id, 'banner_text' ) !== '' ) : ?> <div class="hero-pksh__actions">
<p class="banner_text"><?php echo esc_html( carbon_get_post_meta( $post_id, 'banner_text' ) ); ?></p> <button class="hero-pksh__cta btn-write">Записаться на бесплатное пробное</button>
<?php endif; ?> <div class="hero-pksh__scarcity">
<button class="btn-free-lesson-header btn-lilac btn-banner btn-write">Оставить заявку</button> <?php echo wp_kses_post( $scarcity ?: '🔥 Осталось <strong>2 места из 8</strong> в группе 56 лет. Стартуем <strong>1 июня</strong>' ); ?>
</div> </div>
</div> </div>
<!-- Trust bar -->
<div class="hero-pksh__trust">
<div class="hero-pksh__trust-item">
<span class="hero-pksh__trust-icon"></span>
<span class="hero-pksh__trust-label">Рейтинг <strong>4.8</strong></span>
</div>
<div class="hero-pksh__trust-item">
<span class="hero-pksh__trust-icon">🎓</span>
<span class="hero-pksh__trust-label"><strong>500+</strong> выпускников</span>
</div>
<div class="hero-pksh__trust-item">
<span class="hero-pksh__trust-icon">📜</span>
<span class="hero-pksh__trust-label">Гослицензия</span>
</div>
<div class="hero-pksh__trust-item">
<span class="hero-pksh__trust-icon">🛡️</span>
<span class="hero-pksh__trust-label">Гарантия результата</span>
</div>
</div>
</div>
<!-- ====== RIGHT COLUMN: Photo of children + floating badges ====== -->
<div class="hero-pksh__visual">
<!-- Purple glow backdrop -->
<div class="hero-pksh__visual-glow" aria-hidden="true"></div>
<!-- Hero photo the emotional anchor -->
<div class="hero-pksh__photo-wrap">
<div class="hero-pksh__photo-frame">
<?php if ( $banner_img_id ) : ?>
<?php echo wp_get_attachment_image( $banner_img_id, 'full', false, array(
'class' => 'hero-pksh__photo',
'loading' => 'eager',
'decoding' => 'async',
) ); ?>
<?php else : ?>
<img
src="<?php echo esc_url( get_template_directory_uri() . '/assets/images/girl_and_a_boy.webp' ); ?>"
alt="Девочка и мальчик — весёлые занятия подготовкой к школе"
class="hero-pksh__photo"
loading="eager"
decoding="async"
width="689"
height="689"
/>
<?php endif; ?>
</div>
<!-- Photo accent glow ring -->
<div class="hero-pksh__photo-ring" aria-hidden="true"></div>
</div>
<!-- Floating badges sticky-note style around photo -->
<div class="hero-pksh__float hero-pksh__float--1">
<span class="hero-pksh__float-icon">👩‍🏫</span>
<span>Педагоги с опытом 15+ лет</span>
</div>
<div class="hero-pksh__float hero-pksh__float--2">
<span class="hero-pksh__float-icon">🎮</span>
<span>Игровая методика</span>
</div>
<div class="hero-pksh__float hero-pksh__float--3">
<span class="hero-pksh__float-icon">📚</span>
<span>Авторская программа</span>
</div>
<div class="hero-pksh__float hero-pksh__float--4">
<span>🌟 Рейтинг <strong>4.8</strong></span>
</div>
<!-- Decorative shapes -->
<div class="hero-pksh__deco hero-pksh__deco--1" aria-hidden="true"></div>
<div class="hero-pksh__deco hero-pksh__deco--2" aria-hidden="true"></div>
<div class="hero-pksh__deco hero-pksh__deco--3" aria-hidden="true"></div>
<div class="hero-pksh__deco hero-pksh__deco--4" aria-hidden="true"></div>
<!-- Sparkle particles -->
<div class="hero-pksh__sparkle" aria-hidden="true"></div>
<div class="hero-pksh__sparkle" aria-hidden="true"></div>
<div class="hero-pksh__sparkle" aria-hidden="true"></div>
<div class="hero-pksh__sparkle" aria-hidden="true"></div>
<div class="hero-pksh__sparkle" aria-hidden="true"></div>
</div>
</div>
</section> </section>
<div class="banner__content mobile">
<p class="page_title"><?php echo esc_html( carbon_get_post_meta( $post_id, 'banner_h1' ) ); ?></p>
<?php if ( carbon_get_post_meta( $post_id, 'banner_text' ) !== '' ) : ?>
<p class="banner_text"><?php echo esc_html( carbon_get_post_meta( $post_id, 'banner_text' ) ); ?></p>
<?php endif; ?>
</div>
<button class="mobile btn-write btn-lilac btn-page">Записаться</button>

View File

@ -1,5 +1,31 @@
<?php <?php
$check_btn_discont = carbon_get_theme_option( 'check_btn_discont' ); $check_btn_discont = carbon_get_theme_option( 'check_btn_discont' );
$price_hour1 = carbon_get_theme_option( 'price_hour1' );
$price_hour2 = carbon_get_theme_option( 'price_hour2' );
$price_hour3 = carbon_get_theme_option( 'price_hour3' );
$price_hour4 = carbon_get_theme_option( 'price_hour4' );
$price_hour_discount1 = carbon_get_theme_option( 'price_hour_discount1' );
$price_hour_discount2 = carbon_get_theme_option( 'price_hour_discount2' );
$price_hour_discount3 = carbon_get_theme_option( 'price_hour_discount3' );
$price_hour_discount4 = carbon_get_theme_option( 'price_hour_discount4' );
// Cast to int — prevents PHP 8 TypeError when Carbon returns empty string
$price_hour1 = (int)($price_hour1 ?: 0);
$price_hour2 = (int)($price_hour2 ?: 0);
$price_hour3 = (int)($price_hour3 ?: 0);
$price_hour4 = (int)($price_hour4 ?: 0);
$price_hour_discount1 = (int)($price_hour_discount1 ?: 0);
$price_hour_discount2 = (int)($price_hour_discount2 ?: 0);
$price_hour_discount3 = (int)($price_hour_discount3 ?: 0);
$price_hour_discount4 = (int)($price_hour_discount4 ?: 0);
// Hide price section entirely if admin hasn't filled prices yet
$has_prices = ($price_hour1 > 0 || $price_hour2 > 0 || $price_hour3 > 0 || $price_hour4 > 0);
$has_discounts = ($price_hour_discount1 > 0 || $price_hour_discount2 > 0 || $price_hour_discount3 > 0 || $price_hour_discount4 > 0);
if ($has_prices || $has_discounts) :
?> ?>
<section class="price"> <section class="price">
<div class="container"> <div class="container">
@ -19,27 +45,6 @@ $check_btn_discont = carbon_get_theme_option( 'check_btn_discont' );
<?php } ?> <?php } ?>
</ul> </ul>
<?php $early_bird_discount = carbon_get_theme_option( 'early_bird_discount' ); <?php $early_bird_discount = carbon_get_theme_option( 'early_bird_discount' );
$price_hour1 = carbon_get_theme_option( 'price_hour1' );
$price_hour2 = carbon_get_theme_option( 'price_hour2' );
$price_hour3 = carbon_get_theme_option( 'price_hour3' );
$price_hour4 = carbon_get_theme_option( 'price_hour4' );
$price_hour_discount1 = carbon_get_theme_option( 'price_hour_discount1' );
$price_hour_discount2 = carbon_get_theme_option( 'price_hour_discount2' );
$price_hour_discount3 = carbon_get_theme_option( 'price_hour_discount3' );
$price_hour_discount4 = carbon_get_theme_option( 'price_hour_discount4' );
if( !isset($price_hour1) ) $price_hour1 = 0;
if( !isset($price_hour2) ) $price_hour2 = 0;
if( !isset($price_hour3) ) $price_hour3 = 0;
if( !isset($price_hour4) ) $price_hour4 = 0;
if( !isset($price_hour_discount1) ) $price_hour_discount1 = 0;
if( !isset($price_hour_discount2) ) $price_hour_discount2 = 0;
if( !isset($price_hour_discount3) ) $price_hour_discount3 = 0;
if( !isset($price_hour_discount4) ) $price_hour_discount4 = 0;
?> ?>
<div class="tabs-content"> <div class="tabs-content">
<div class="page-tab active" data-tab-content="1"> <div class="page-tab active" data-tab-content="1">
@ -118,3 +123,4 @@ $check_btn_discont = carbon_get_theme_option( 'check_btn_discont' );
</div> </div>
</div> </div>
</section> </section>
<?php endif; ?>

View File

@ -131,6 +131,9 @@ if ($the_query->have_posts()) :
<div class='custom-button-next reviews-owl-next'></div> <div class='custom-button-next reviews-owl-next'></div>
<div class='custom-button-prev reviews-owl-prev'></div> <div class='custom-button-prev reviews-owl-prev'></div>
</div> </div>
<div class="reviews-all-link" style="text-align:center; margin-top:24px;">
<a href="/otzyvy/" class="btn-outline">Все отзывы </a>
</div>
</div> </div>
</section> </section>
<?php <?php