Global-2026-07-12
This commit is contained in:
parent
d5dceedef6
commit
1fa6f06ccf
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
21
wp-content/themes/dekart/assets/js/pksh-sticky-cta.js
Normal file
21
wp-content/themes/dekart/assets/js/pksh-sticky-cta.js
Normal 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);
|
||||
})();
|
||||
50
wp-content/themes/dekart/assets/js/pksh-timer.js
Normal file
50
wp-content/themes/dekart/assets/js/pksh-timer.js
Normal 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();
|
||||
});
|
||||
})();
|
||||
79
wp-content/themes/dekart/assets/js/yandex-map.js
Normal file
79
wp-content/themes/dekart/assets/js/yandex-map.js
Normal 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 });
|
||||
})();
|
||||
@ -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> в группе 5–6 лет. Стартуем <strong>1 июня</strong>' )
|
||||
->set_help_text( 'Можно использовать HTML-теги: <strong>, <br>. Текст о срочности/дефиците мест под кнопкой.' ),
|
||||
@ -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( 'Добавьте фотографии с занятий. Оптимальная ширина: 600–1200 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.
|
||||
*
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 ) ) {
|
||||
|
||||
@ -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' );
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
||||
56
wp-content/themes/dekart/template-parts/pksh/gallery.php
Normal file
56
wp-content/themes/dekart/template-parts/pksh/gallery.php
Normal 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
|
||||
@ -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>
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user