Global-2026-07-12

This commit is contained in:
Dekart Deploy Bot 2026-07-12 13:06:48 +00:00
parent d5dceedef6
commit 1fa6f06ccf
14 changed files with 494 additions and 152 deletions

View File

@ -1239,6 +1239,25 @@ background: rgba(124, 58, 237, 0.2);
padding-right: 32px;
}
/* ── Hero footnote (*) ── */
.page-pksh .ds-hero__footnote,
.page-pksh .ds-hero__footnote-link {
font-size: 1.1em;
color: var(--accent, #7C3AED);
text-decoration: none;
cursor: default;
vertical-align: super;
line-height: 0;
}
.page-pksh .ds-hero__footnote-link {
cursor: pointer;
transition: color 0.2s;
}
.page-pksh .ds-hero__footnote-link:hover {
color: var(--accent-hover, #6D28D9);
text-decoration: underline;
}
/* ============================================================
25. BANNER GUARANTEE MICRO
============================================================ */
@ -2122,6 +2141,39 @@ background: rgba(124, 58, 237, 0.2);
color: var(--slate-400, #94A3B8);
font-weight: 400;
}
/* ── Logistics chips (как добраться) ── */
.page-pksh .pksh-contact__logistics {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 16px 0 8px;
padding: 16px;
background: #F8F4F0;
border-radius: 16px;
border: 1px solid #EDE9E3;
}
.page-pksh .pksh-contact__logistics-item {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.875rem;
line-height: 1.45;
color: #475569;
width: 100%;
}
.page-pksh .pksh-contact__logistics-icon {
flex-shrink: 0;
font-size: 1.125rem;
line-height: 1;
}
.page-pksh .pksh-contact__logistics-text {
flex: 1;
}
.page-pksh .pksh-contact__map {
flex: 1;
border-radius: 24px;
@ -2932,3 +2984,62 @@ background: rgba(124, 58, 237, 0.2);
font-size: 0.75rem;
}
}
/* ============================================================
32. PHOTO GALLERY
3-column grid, cards with rounded corners + shadow.
Если caption пустой img заполняет всю карточку.
============================================================ */
.pksh-gallery__grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin: 28px 0 0;
}
.pksh-gallery__item {
margin: 0;
border-radius: 16px;
overflow: hidden;
background: #fff;
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.07);
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.pksh-gallery__item:hover {
transform: translateY(-4px);
box-shadow: 0 10px 32px rgba(0, 0, 0, 0.12);
}
.pksh-gallery__img {
display: block;
width: 100%;
height: auto;
aspect-ratio: 4 / 3;
object-fit: cover;
}
.pksh-gallery__caption {
display: block;
padding: 12px 16px 14px;
font-size: 0.875rem;
line-height: 1.5;
color: #334155;
text-align: center;
}
/* ── Tablet: 2 columns ── */
@media (max-width: 860px) {
.pksh-gallery__grid {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
}
/* ── Mobile: 1 column ── */
@media (max-width: 540px) {
.pksh-gallery__grid {
grid-template-columns: 1fr;
gap: 14px;
}
}

View File

@ -0,0 +1,21 @@
/**
* Sticky CTA shows desktop bar when hero scrolls out of view
*
* IntersectionObserver on .ds-hero; toggles .visible on .sticky-cta-desktop.
*
* @package Dekart
*/
(function () {
var sticky = document.querySelector('.sticky-cta-desktop');
var hero = document.querySelector('.ds-hero');
if (!sticky || !hero) return;
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
sticky.classList.toggle('visible', !entry.isIntersecting);
});
}, { threshold: 0 });
observer.observe(hero);
})();

View File

@ -0,0 +1,50 @@
/**
* Honest countdown timer Подготовка к школе
*
* Reads deadline timestamp (seconds) from .honest-timer[data-deadline].
* Finds [data-timer] children within that timer container only.
* Updates (d, h, m, s) via requestAnimationFrame. Stops at 00:00:00:00.
*
* Поддерживает несколько таймеров на одной странице (cta-test + final-cta).
* Каждый экземпляр получает собственную замыкание.
*
* @package Dekart
*/
(function () {
var timers = document.querySelectorAll('.honest-timer');
if (!timers.length) return;
function pad(n) {
return n < 10 ? '0' + n : '' + n;
}
[].forEach.call(timers, function (timerEl) {
var deadline = parseInt(timerEl.getAttribute('data-deadline'), 10);
if (!deadline || deadline <= 0) return;
var deadlineMs = deadline * 1000;
var els = timerEl.querySelectorAll('[data-timer]');
if (!els.length) return;
var map = {};
[].forEach.call(els, function (el) {
map[el.getAttribute('data-timer')] = el;
});
function tick() {
var diff = deadlineMs - Date.now();
if (diff <= 0) {
for (var key in map) map[key].textContent = '00';
return;
}
map.d.textContent = pad(Math.floor(diff / 86400000));
map.h.textContent = pad(Math.floor((diff % 86400000) / 3600000));
map.m.textContent = pad(Math.floor((diff % 3600000) / 60000));
map.s.textContent = pad(Math.floor((diff % 60000) / 1000));
requestAnimationFrame(tick);
}
tick();
});
})();

View File

@ -0,0 +1,79 @@
/**
* Yandex Maps lazy loader shared across all pages
*
* Activates only when #ya-map[data-coords] is present.
* Loads Yandex Maps API on first scroll > 100px (passive) or after 3s fallback.
* Creates map with a placemark at the given coordinates.
*
* Required data attributes on #ya-map:
* data-coords comma-separated lat,lng (e.g. "55.7558,37.6173")
* data-label placemark text (e.g. "Детский центр «Декарт»")
*
* @package Dekart
*/
(function () {
var mapEl = document.getElementById('ya-map');
if (!mapEl) return;
var coordsAttr = mapEl.getAttribute('data-coords');
var label = mapEl.getAttribute('data-label') || '';
if (!coordsAttr) return;
var parts = coordsAttr.split(',');
if (parts.length < 2) return;
var mapCoords = [parseFloat(parts[0]), parseFloat(parts[1])];
if (isNaN(mapCoords[0]) || isNaN(mapCoords[1])) return;
var mapLoaded = false;
var apiLoading = false;
function initMap() {
if (typeof ymaps === 'undefined') return;
ymaps.ready(function () {
var map = new ymaps.Map('ya-map', {
center: mapCoords,
zoom: 16
}, {
searchControlProvider: 'yandex#search'
});
var placemark = new ymaps.Placemark(
mapCoords,
{ iconContent: label },
{ 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.onerror = function () {
apiLoading = false;
};
script.onload = initMap;
document.head.appendChild(script);
}
// Scroll trigger — first >100px
function onScroll() {
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
if (scrollTop > 100) {
loadYandexMaps();
window.removeEventListener('scroll', onScroll);
}
}
// Fallback — 3 seconds after page load
window.addEventListener('load', function () {
setTimeout(function () {
if (!mapLoaded && !apiLoading) loadYandexMaps();
}, 3000);
});
window.addEventListener('scroll', onScroll, { passive: true });
})();

View File

@ -551,11 +551,14 @@ function add_carbon() {
->set_width( 50 )
->set_default_value( 'Ваш ребёнок готов к первому классу? Научим читать, считать и писать за 4 месяца — без слёз и стресса' )
->set_help_text( 'Главный заголовок на первом экране.' ),
Field::make( 'textarea', 'banner_subtitle', 'Подзаголовок' )
->set_width( 50 )
->set_default_value( 'Первое занятие — бесплатно. Результат через 3 недели — или вернём деньги' )
->set_help_text( 'Текст под заголовком. Опционально.' ),
Field::make( 'text', 'banner_scarcity', 'Текст дефицита (срочность)' )
Field::make( 'textarea', 'banner_subtitle', 'Подзаголовок' )
->set_width( 50 )
->set_default_value( 'Первое занятие — бесплатно. Результат через 3 недели — или вернём деньги' )
->set_help_text( 'Текст под заголовком. Опционально.' ),
Field::make( 'text', 'banner_subtitle_footnote_link', 'Ссылка для звёздочки (*) в подзаголовке' )
->set_width( 50 )
->set_help_text( 'URL для иконки * после текста подзаголовка. Если не указано — * неактивна.' ),
Field::make( 'text', 'banner_scarcity', 'Текст дефицита (срочность)' )
->set_width( 50 )
->set_default_value( '🔥 Осталось <strong>2 места из 8</strong> в группе 56 лет. Стартуем <strong>1 июня</strong>' )
->set_help_text( 'Можно использовать HTML-теги: &lt;strong&gt;, &lt;br&gt;. Текст о срочности/дефиците мест под кнопкой.' ),
@ -782,6 +785,24 @@ function add_carbon() {
->set_attribute( 'placeholder', 'Чтение, Счёт до 100, Письмо печатными буквами' )
->set_help_text( 'Например: "Чтение", "Счёт до 100", "Письмо печатными буквами"' ),
)),
))
// ====================================================
// Вкладка: Фотогалерея
// ====================================================
->add_tab('📸 Фотогалерея', array(
Field::make( 'html', 'pksh_gallery_tab_desc', '' )
->set_html( '<p style="color: #64748b; font-size: 13px; margin: 0 0 12px;">Секция: <strong>#gallery</strong>. Фотографии занятий, 3 в ряд. Если фото не добавлены — секция не выводится.</p>' ),
Field::make( 'complex', 'pksh_gallery', 'Фотографии' )
->set_layout( 'tabbed-horizontal' )
->set_help_text( 'Добавьте фотографии с занятий. Оптимальная ширина: 6001200 px.' )
->add_fields( array(
Field::make( 'image', 'gallery_image', 'Фото' )
->set_width( 60 )
->set_help_text( 'Рекомендуемый размер: 800×600 px, соотношение 4:3 или 3:2.' ),
Field::make( 'text', 'gallery_caption', 'Подпись (опционально)' )
->set_width( 40 )
->set_help_text( 'Короткая подпись под фото. Если не указана — только фото.' ),
)),
));
// ====================================================
@ -1439,6 +1460,16 @@ function dekart_parse_b24_script( string $script ): array {
}
function enqueue_template_specific_styles() {
// Yandex Maps — загружается на всех страницах.
// JS сам проверяет наличие #ya-map[data-coords] перед активацией.
wp_enqueue_script(
'yandex-map',
get_template_directory_uri() . '/assets/js/yandex-map.js',
array(),
filemtime( get_template_directory() . '/assets/js/yandex-map.js' ),
true
);
if ( is_page_template('page-camp.php') ) {
wp_enqueue_style(
'template-specific-style',
@ -1486,6 +1517,20 @@ function enqueue_template_specific_styles() {
array(),
'2.1.' . filemtime(get_template_directory() . '/assets/css/podgotovka-k-shkole.css')
);
wp_enqueue_script(
'pksh-timer',
get_template_directory_uri() . '/assets/js/pksh-timer.js',
array(),
filemtime( get_template_directory() . '/assets/js/pksh-timer.js' ),
true
);
wp_enqueue_script(
'pksh-sticky-cta',
get_template_directory_uri() . '/assets/js/pksh-sticky-cta.js',
array(),
filemtime( get_template_directory() . '/assets/js/pksh-sticky-cta.js' ),
true
);
}
if ( is_page_template('front-page.php') || is_front_page() ) {
wp_enqueue_style(
@ -1505,6 +1550,11 @@ function enqueue_template_specific_styles() {
}
add_action('wp_enqueue_scripts', 'enqueue_template_specific_styles');
// Удаляем wp-block-library (Gutenberg inline стили) — тема кастомная, блоки не используются
add_action('wp_enqueue_scripts', static function () {
wp_dequeue_style('wp-block-library');
}, 100);
/**
* Helper: determine if current page needs Swiper slider assets.
*

View File

@ -31,6 +31,7 @@ class Pksh_Data {
private ?array $teachers = null;
private ?array $reviews = null;
private ?array $faq = null;
private ?array $gallery = null;
private function __construct() {
$this->post_id = get_the_ID();
@ -202,6 +203,16 @@ class Pksh_Data {
return $this->page['subtitle'];
}
/**
* Ссылка для * в подзаголовке hero
*/
public function get_subtitle_footnote_link(): string {
if ( ! array_key_exists( 'subtitle_footnote_link', $this->page ) ) {
$this->page['subtitle_footnote_link'] = carbon_get_post_meta( $this->post_id, 'banner_subtitle_footnote_link' ) ?: '';
}
return $this->page['subtitle_footnote_link'];
}
// ============================================================
// SOCIAL COUNTERS (complex)
// ============================================================
@ -339,6 +350,22 @@ class Pksh_Data {
return $this->faq;
}
// ============================================================
// GALLERY (complex)
// ============================================================
/**
* Массив фотографий (complex field pksh_gallery)
* @return array[]
*/
public function get_gallery(): array {
if ( null === $this->gallery ) {
$raw = carbon_get_post_meta( $this->post_id, 'pksh_gallery' );
$this->gallery = is_array( $raw ) ? $raw : [];
}
return $this->gallery;
}
// ============================================================
// HONEST TIMER (shared helper)
// ============================================================
@ -367,7 +394,7 @@ class Pksh_Data {
return;
}
?>
<div class="honest-timer">
<div class="honest-timer" data-deadline="<?php echo (int) $ts; ?>">
<span class="honest-timer__label"><?php echo esc_html( $label ); ?></span>
<div class="honest-timer__digits">
<span class="honest-timer__unit">
@ -391,10 +418,6 @@ class Pksh_Data {
</span>
</div>
</div>
<script>window.__HONEST_DEADLINE=<?php echo (int) $ts; ?>;</script>
<script>
!function(){var e=window.__HONEST_DEADLINE;if(!e)return;e*=1e3;var t=document.querySelectorAll('[data-timer]');if(!t.length)return;var n={};t.forEach(function(r){n[r.getAttribute('data-timer')]=r});var o=function(r){return r<10?'0'+r:''+r};!function r(){var t=e-Date.now();if(t<=0){for(var u in n)n[u].textContent='00';return}n.d.textContent=o(Math.floor(t/864e5)),n.h.textContent=o(Math.floor(t%864e5/36e5)),n.m.textContent=o(Math.floor(t%36e5/6e4)),n.s.textContent=o(Math.floor(t%6e4/1e3)),requestAnimationFrame(r)}()}();
</script>
<?php
}
}

View File

@ -25,12 +25,32 @@ add_filter( 'wpseo_json_ld_output', '__return_false' );
*/
add_filter( 'wpseo_opengraph_type', 'dekart_schema_og_type', 10, 1 );
function dekart_schema_og_type( string $type ): string {
if ( is_page( 2 ) ) {
if ( is_page( 2 ) || is_page_template( 'page-camp.php' ) ) {
return 'website';
}
return $type;
}
/**
* Убираем article:* OG-теги для страницы Подготовки к школе (ID=2),
* чтобы не было конфликта og:type=website с article:modified_time.
* Аналог page-camp.php:15-27.
*/
add_filter( 'wpseo_frontend_presenter_classes', function( array $presenters ): array {
if ( ! is_page( 2 ) ) {
return $presenters;
}
$presenters = array_filter( $presenters, function( string $p ): bool {
return strpos( $p, 'Article_Modified_Time' ) === false
&& strpos( $p, 'Article_Published_Time' ) === false
&& strpos( $p, 'Article_Author' ) === false
&& strpos( $p, 'Article_Publisher' ) === false
&& strpos( $p, 'Label1' ) === false
&& strpos( $p, 'Data1' ) === false;
} );
return $presenters;
} );
/**
* Главная функция: сборка @graph и вывод в wp_head.
* Срабатывает только на странице ID=2 (Подготовка к школе).
@ -766,6 +786,43 @@ function dekart_schema_course(): ?array {
),
);
// review — 1-2 отзыва прямо в Course (Google требует для звёзд в rich snippet)
$reviews_raw = carbon_get_post_meta( 2, 'pksh_reviews_list' );
if ( ! empty( $reviews_raw ) && is_array( $reviews_raw ) ) {
$review_items = array();
foreach ( $reviews_raw as $rv ) {
$author = isset( $rv['review_author'] ) ? trim( $rv['review_author'] ) : '';
$body = isset( $rv['review_text'] ) ? trim( $rv['review_text'] ) : '';
$rating = isset( $rv['review_rating'] ) ? intval( $rv['review_rating'] ) : 0;
if ( empty( $author ) || empty( $body ) || $rating < 1 || $rating > 5 ) {
continue;
}
$item = array(
'@type' => 'Review',
'author' => array( '@type' => 'Person', 'name' => wp_strip_all_tags( $author ) ),
'reviewBody' => wp_strip_all_tags( $body ),
'reviewRating' => array(
'@type' => 'Rating',
'ratingValue' => (string) $rating,
'bestRating' => '5',
'worstRating' => '1',
),
'itemReviewed' => array( '@id' => $page_url . '#course' ),
);
$date = isset( $rv['review_date'] ) ? trim( $rv['review_date'] ) : '';
if ( $date ) {
$item['datePublished'] = sanitize_text_field( $date );
}
$review_items[] = $item;
if ( count( $review_items ) >= 2 ) {
break;
}
}
if ( ! empty( $review_items ) ) {
$course['review'] = $review_items;
}
}
// educationalLevel — уровень образования (условно)
if ( ! empty( $level ) && is_string( $level ) ) {
$course['educationalLevel'] = wp_strip_all_tags( trim( $level ) );
@ -790,10 +847,7 @@ function dekart_schema_course(): ?array {
foreach ( $skills as $skill ) {
$name = isset( $skill['skill'] ) ? trim( $skill['skill'] ) : '';
if ( $name ) {
$teaches[] = array(
'@type' => 'Thing',
'name' => wp_strip_all_tags( $name ),
);
$teaches[] = wp_strip_all_tags( $name );
}
}
if ( ! empty( $teaches ) ) {

View File

@ -40,6 +40,7 @@ get_header();
get_template_part( 'template-parts/block-price' );
get_template_part( 'template-parts/pksh/teachers' );
get_template_part( 'template-parts/pksh/gallery' );
get_template_part( 'template-parts/pksh/guarantee-standalone' );
get_template_part( 'template-parts/pksh/faq' );
get_template_part( 'template-parts/pksh/reviews' );

View File

@ -6,69 +6,9 @@
<?php echo esc_html( carbon_get_theme_option( 'site_address' ) ); ?>
<span><?php echo esc_html( carbon_get_theme_option( 'site_name' ) ); ?></span>
</div>
<div id="ya-map" style="width: 100%; height: 420px;"></div>
<div id="ya-map" style="width: 100%; height: 420px;"
data-coords="<?php echo esc_attr( carbon_get_theme_option( 'site_location' ) ); ?>"
data-label="<?php echo esc_attr( carbon_get_theme_option( 'site_name' ) ); ?>"></div>
</div>
</div>
</div>
<script>
(function() {
// Координаты и флаги — берём из PHP один раз
var mapCoords = [<?php echo esc_js( carbon_get_theme_option( 'site_location' ) ); ?>];
var mapLabel = '<?php echo esc_js( carbon_get_theme_option( 'site_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;
// Динамически создаём тег <script> — API грузится только сейчас,
// а не при загрузке страницы. Экономит ~200kb в критическом пути.
var script = document.createElement('script');
script.src = 'https://api-maps.yandex.ru/2.1/?lang=ru_RU';
script.onerror = function() { console.warn("Yandex Maps API failed to load"); apiLoading = false; };
script.onload = initMap;
document.head.appendChild(script);
}
// Запускаем загрузку при первом скролле > 100px
function onScroll() {
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
if (scrollTop > 100) {
loadYandexMaps();
window.removeEventListener('scroll', onScroll);
}
}
// Запасной вариант: если карта видна без скролла (большой экран)
// — грузим через 3 секунды после загрузки страницы
window.addEventListener('load', function() {
setTimeout(function() {
if (!mapLoaded && !apiLoading) {
loadYandexMaps();
}
}, 3000);
});
window.addEventListener('scroll', onScroll, { passive: true });
})();
</script>
</div>

View File

@ -37,12 +37,27 @@ $opt_name = $data->get_name();
<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 ) ) : ?>
<?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 endif; ?>
<?php if ( ! empty( $opt_social ) && is_array( $opt_social ) ) : ?>
<div class="pksh-contact__logistics">
<span class="pksh-contact__logistics-item">
<span class="pksh-contact__logistics-icon">📍</span>
<span class="pksh-contact__logistics-text">5 минут пешком от ТЦ «Глобус»</span>
</span>
<span class="pksh-contact__logistics-item">
<span class="pksh-contact__logistics-icon">🅿️</span>
<span class="pksh-contact__logistics-text">Есть бесплатная парковка у входа</span>
</span>
<span class="pksh-contact__logistics-item">
<span class="pksh-contact__logistics-icon">🚸</span>
<span class="pksh-contact__logistics-text">Рядом со школами №9 и №12, детским садом №15</span>
</span>
</div>
<?php if ( ! empty( $opt_social ) && is_array( $opt_social ) ) : ?>
<div class="pksh-contact__subtitle">Наши соцсети:</div>
<div class="pksh-contact__social">
<?php foreach ( $opt_social as $item ) :
@ -58,58 +73,11 @@ $opt_name = $data->get_name();
<?php endif; ?>
</div>
<div class="pksh-contact__map">
<div id="ya-map" style="width: 100%; height: 100%; min-height: 400px;"></div>
<div id="ya-map" style="width: 100%; height: 100%; min-height: 400px;"
data-coords="<?php echo esc_attr( $opt_coords ?: '55.7558,37.6173' ); ?>"
data-label="<?php echo esc_attr( $opt_name ?: 'Детский центр «Декарт»' ); ?>"></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.onerror = function() { <?php if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) : ?>console.warn("Yandex Maps API failed to load");<?php endif; ?> apiLoading = false; };
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>

View File

@ -0,0 +1,56 @@
<?php
/**
* Фотогалерея (сетка 3 колонки)
* .bento .pksh-section #gallery
*
* @package Dekart
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$data = Pksh_Data::instance();
$gallery = $data->get_gallery();
if ( empty( $gallery ) || ! is_array( $gallery ) ) {
return;
}
?>
<section class="bento pksh-section" id="gallery" aria-label="Фотогалерея">
<div class="bento__full section-header--centered">
<span class="bento-label">Фотогалерея</span>
<h2 class="bento-h2">Как проходят занятия</h2>
<p class="bento-p">Фотографии с наших уроков посмотрите, как увлекательно и продуктивно проходят занятия в центре «Декарт».</p>
</div>
<div class="bento__full">
<div class="pksh-gallery__grid">
<?php foreach ( $gallery as $item ) :
$img_id = $item['gallery_image'] ?? 0;
$caption = $item['gallery_caption'] ?? '';
if ( empty( $img_id ) ) {
continue;
}
$img_full = wp_get_attachment_image_url( $img_id, 'full' );
$img_thumb = wp_get_attachment_image_url( $img_id, 'medium_large' );
$img_alt = $caption ? wp_strip_all_tags( $caption ) : esc_attr__( 'Фото занятия', 'dekart' );
if ( empty( $img_full ) || empty( $img_thumb ) ) {
continue;
}
?>
<figure class="pksh-gallery__item">
<img class="pksh-gallery__img" src="<?php echo esc_url( $img_thumb ); ?>"
alt="<?php echo esc_attr( $img_alt ); ?>"
loading="lazy" decoding="async">
<?php if ( $caption ) : ?>
<figcaption class="pksh-gallery__caption"><?php echo esc_html( $caption ); ?></figcaption>
<?php endif; ?>
</figure>
<?php endforeach; ?>
</div>
</div>
</section>
<?php

View File

@ -9,6 +9,9 @@
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$data = Pksh_Data::instance();
$footnote_url = $data->get_subtitle_footnote_link();
?>
<section class="bento pksh-section" aria-label="Гарантия">
<div class="bento__full">
@ -18,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {
</div>
<div class="guarantee-hero__content">
<h2 class="bento-h2">Ваш ребёнок начнёт читать через 3 недели</h2>
<p class="guarantee-hero__text">Или <strong>мы вернём деньги</strong> и продолжим обучение бесплатно, пока не дойдём до результата. Мы частная школа <strong>с гослицензией</strong>, а не кружок. Отвечаем за результат.</p>
<p class="guarantee-hero__text">Или <strong>мы вернём деньги</strong> и продолжим обучение бесплатно, пока не дойдём до результата.<?php if ( $footnote_url ) : ?><sup><a href="<?php echo esc_url( $footnote_url ); ?>" class="ds-hero__footnote-link" target="_blank" rel="noopener noreferrer">*</a></sup><?php else : ?><sup class="ds-hero__footnote">*</sup><?php endif; ?> Мы — частная школа <strong>с гослицензией</strong>, а не кружок. Отвечаем за результат.</p>
</div>
<nav class="pksh-anchor-nav" aria-label="Навигация по странице">
<a href="#how-it-works" class="pksh-anchor-link">Как проходят занятия</a>

View File

@ -39,9 +39,11 @@ $hero_metrics = $data->get_social_counters();
<h1 class="ds-hero__h1"><?php echo esc_html( $h1_hero ?: 'Подготовка к школе в Щёлково' ); ?></h1>
<?php if ( '' !== $subtitle_hero ) : ?>
<p class="ds-hero__subtitle"><?php echo esc_html( $subtitle_hero ); ?></p>
<?php endif; ?>
<?php if ( '' !== $subtitle_hero ) :
$footnote_url = $data->get_subtitle_footnote_link();
?>
<p class="ds-hero__subtitle"><?php echo esc_html( $subtitle_hero ); ?><?php if ( $footnote_url ) : ?><sup><a href="<?php echo esc_url( $footnote_url ); ?>" class="ds-hero__footnote-link" target="_blank" rel="noopener noreferrer">*</a></sup><?php else : ?><sup class="ds-hero__footnote">*</sup><?php endif; ?></p>
<?php endif; ?>
<?php if ( ! empty( $hero_metrics ) && is_array( $hero_metrics ) ) : ?>
<div class="ds-hero__metrics">

View File

@ -26,19 +26,3 @@ if ( ! defined( 'ABSPATH' ) ) {
<button class="sticky-cta-desktop__btn btn-write">Записаться</button>
</div>
</div>
<script>
(function() {
var sticky = document.querySelector('.sticky-cta-desktop');
var hero = document.querySelector('.ds-hero');
if (!sticky || !hero) return;
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
sticky.classList.toggle('visible', !entry.isIntersecting);
});
}, { threshold: 0 });
observer.observe(hero);
})();
</script>