diff --git a/wp-content/themes/dekart/assets/css/podgotovka-k-shkole.css b/wp-content/themes/dekart/assets/css/podgotovka-k-shkole.css index 630d408..bc36f1a 100644 --- a/wp-content/themes/dekart/assets/css/podgotovka-k-shkole.css +++ b/wp-content/themes/dekart/assets/css/podgotovka-k-shkole.css @@ -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; + } +} diff --git a/wp-content/themes/dekart/assets/js/pksh-sticky-cta.js b/wp-content/themes/dekart/assets/js/pksh-sticky-cta.js new file mode 100644 index 0000000..c009dd9 --- /dev/null +++ b/wp-content/themes/dekart/assets/js/pksh-sticky-cta.js @@ -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); +})(); diff --git a/wp-content/themes/dekart/assets/js/pksh-timer.js b/wp-content/themes/dekart/assets/js/pksh-timer.js new file mode 100644 index 0000000..637bb05 --- /dev/null +++ b/wp-content/themes/dekart/assets/js/pksh-timer.js @@ -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(); + }); +})(); diff --git a/wp-content/themes/dekart/assets/js/yandex-map.js b/wp-content/themes/dekart/assets/js/yandex-map.js new file mode 100644 index 0000000..8d2002a --- /dev/null +++ b/wp-content/themes/dekart/assets/js/yandex-map.js @@ -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 }); +})(); diff --git a/wp-content/themes/dekart/functions.php b/wp-content/themes/dekart/functions.php index 389d19f..fd354b2 100644 --- a/wp-content/themes/dekart/functions.php +++ b/wp-content/themes/dekart/functions.php @@ -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( '🔥 Осталось 2 места из 8 в группе 5–6 лет. Стартуем 1 июня' ) ->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( '

Секция: #gallery. Фотографии занятий, 3 в ряд. Если фото не добавлены — секция не выводится.

' ), + 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. * diff --git a/wp-content/themes/dekart/inc/class-pksh-data.php b/wp-content/themes/dekart/inc/class-pksh-data.php index 3c61653..116d874 100644 --- a/wp-content/themes/dekart/inc/class-pksh-data.php +++ b/wp-content/themes/dekart/inc/class-pksh-data.php @@ -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; } ?> -
+
@@ -391,10 +418,6 @@ class Pksh_Data {
- - 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 ) ) { diff --git a/wp-content/themes/dekart/page-podgotovka-k-shkole.php b/wp-content/themes/dekart/page-podgotovka-k-shkole.php index 32555e4..76db256 100644 --- a/wp-content/themes/dekart/page-podgotovka-k-shkole.php +++ b/wp-content/themes/dekart/page-podgotovka-k-shkole.php @@ -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' ); diff --git a/wp-content/themes/dekart/template-parts/map.php b/wp-content/themes/dekart/template-parts/map.php index 501fbb0..5172cb8 100644 --- a/wp-content/themes/dekart/template-parts/map.php +++ b/wp-content/themes/dekart/template-parts/map.php @@ -6,69 +6,9 @@
-
+
- - - \ No newline at end of file + \ No newline at end of file diff --git a/wp-content/themes/dekart/template-parts/pksh/contacts.php b/wp-content/themes/dekart/template-parts/pksh/contacts.php index e12e2bb..f33e46f 100644 --- a/wp-content/themes/dekart/template-parts/pksh/contacts.php +++ b/wp-content/themes/dekart/template-parts/pksh/contacts.php @@ -37,12 +37,27 @@ $opt_name = $data->get_name(); - +
Адрес:
- + - +
+ + 📍 + 5 минут пешком от ТЦ «Глобус» + + + 🅿️ + Есть бесплатная парковка у входа + + + 🚸 + Рядом со школами №9 и №12, детским садом №15 + +
+ +
Наши соцсети:
get_name();
-
+
- diff --git a/wp-content/themes/dekart/template-parts/pksh/gallery.php b/wp-content/themes/dekart/template-parts/pksh/gallery.php new file mode 100644 index 0000000..d808109 --- /dev/null +++ b/wp-content/themes/dekart/template-parts/pksh/gallery.php @@ -0,0 +1,56 @@ +get_gallery(); + +if ( empty( $gallery ) || ! is_array( $gallery ) ) { + return; +} +?> + +get_subtitle_footnote_link(); ?>
@@ -18,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {

Ваш ребёнок начнёт читать через 3 недели

-

Или мы вернём деньги и продолжим обучение бесплатно, пока не дойдём до результата. Мы — частная школа с гослицензией, а не кружок. Отвечаем за результат.

+

Или мы вернём деньги и продолжим обучение бесплатно, пока не дойдём до результата.** Мы — частная школа с гослицензией, а не кружок. Отвечаем за результат.