feat: скаффолд Astro 5 SSG (главная + /privacy + consent gate)
Some checks failed
deploy / deploy (push) Failing after 14s
Some checks failed
deploy / deploy (push) Failing after 14s
- Главная: hero, адрес Люблинская 100 (Аквапарк ФЭНТАЗИ), 4 кликабельных tel:, карта Яндекса - /privacy: политика 152-ФЗ + ConsentRevoke (отозвать/сбросить) - Аналитика перенесена 1:1 с WP: Яндекс.Метрика 47169531 (Webvisor) + GA4 GT-WRF7ZZ8 - Скрипты в type=text/plain, активируются после согласия (pit-consent в localStorage+cookie) - robots.txt с явным Allow для GPTBot/ClaudeBot/PerplexityBot/Google-Extended/CCBot - llms.txt + ai.txt (spawning.ai стандарт) - IndexNow ключ 901a779d62ca4702ad810c863b45e1f7 - JSON-LD AutoPartsStore с адресом и 4 телефонами - nginx:1.29-alpine runtime, контейнер на :4147 - Gitea Actions deploy.yml + Trivy scan + IndexNow ping
This commit is contained in:
26
src/components/Analytics.astro
Normal file
26
src/components/Analytics.astro
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
import { ANALYTICS } from '../consts';
|
||||
const ym = ANALYTICS.yandexMetrika;
|
||||
const ga = ANALYTICS.googleGtag;
|
||||
---
|
||||
|
||||
{ym && (
|
||||
<script type="text/plain" data-cookieconsent="statistics" is:inline define:vars={{ ym }}>
|
||||
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||
m[i].l=1*new Date();
|
||||
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
|
||||
(window,document,"script","https://mc.yandex.ru/metrika/tag.js","ym");
|
||||
window.ym(ym, "init", { clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:true });
|
||||
</script>
|
||||
)}
|
||||
{ga && (
|
||||
<script type="text/plain" data-cookieconsent="statistics" is:inline src={`https://www.googletagmanager.com/gtag/js?id=${ga}`}></script>
|
||||
)}
|
||||
{ga && (
|
||||
<script type="text/plain" data-cookieconsent="statistics" is:inline define:vars={{ ga }}>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){ dataLayer.push(arguments); }
|
||||
gtag('js', new Date());
|
||||
gtag('config', ga);
|
||||
</script>
|
||||
)}
|
||||
77
src/components/ConsentRevoke.astro
Normal file
77
src/components/ConsentRevoke.astro
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
// Виджет на /privacy/: показывает текущее состояние согласия и две кнопки —
|
||||
// «Отозвать согласие» (deny) и «Сбросить выбор» (показать баннер заново).
|
||||
---
|
||||
|
||||
<div class="consent-revoke" id="consent-revoke">
|
||||
<p class="cr-status" id="cr-status">Проверка статуса согласия…</p>
|
||||
<div class="cr-buttons">
|
||||
<button type="button" id="cr-deny" class="cr-btn cr-btn-secondary">Отозвать согласие</button>
|
||||
<button type="button" id="cr-reset" class="cr-btn cr-btn-secondary">Сбросить выбор</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.consent-revoke {
|
||||
border: 1px solid var(--rule);
|
||||
background: var(--paper-soft);
|
||||
padding: 1rem 1.25rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
.cr-status { margin: 0 0 0.75rem; font-size: 0.95rem; }
|
||||
.cr-buttons { display: flex; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.cr-btn {
|
||||
font-family: inherit;
|
||||
font-size: 0.88rem;
|
||||
padding: 0.45rem 0.9rem;
|
||||
border: 1px solid var(--ink);
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
cursor: pointer;
|
||||
}
|
||||
.cr-btn:hover { background: var(--ink); color: var(--paper); }
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
(function () {
|
||||
const KEY = 'pit-consent';
|
||||
const status = document.getElementById('cr-status');
|
||||
const denyBtn = document.getElementById('cr-deny');
|
||||
const resetBtn = document.getElementById('cr-reset');
|
||||
if (!status) return;
|
||||
|
||||
function readConsent() {
|
||||
try { return localStorage.getItem(KEY); } catch { return null; }
|
||||
}
|
||||
function setCookie(value) {
|
||||
const exp = new Date(Date.now() + 365 * 24 * 3600 * 1000).toUTCString();
|
||||
document.cookie = `${KEY}=${value}; expires=${exp}; path=/; SameSite=Lax`;
|
||||
}
|
||||
function clearCookie() {
|
||||
document.cookie = `${KEY}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
|
||||
}
|
||||
function clearStored() {
|
||||
try { localStorage.removeItem(KEY); } catch {}
|
||||
clearCookie();
|
||||
}
|
||||
function render() {
|
||||
const v = readConsent();
|
||||
if (v === 'accept') status.textContent = 'Согласие на сбор аналитики дано. Скрипты Яндекс.Метрики и Google Analytics активны.';
|
||||
else if (v === 'deny') status.textContent = 'Согласие отозвано. Аналитические скрипты не загружаются.';
|
||||
else status.textContent = 'Выбор не сделан. При следующем визите появится баннер согласия.';
|
||||
}
|
||||
render();
|
||||
|
||||
denyBtn?.addEventListener('click', () => {
|
||||
try { localStorage.setItem(KEY, 'deny'); } catch {}
|
||||
setCookie('deny');
|
||||
render();
|
||||
location.reload();
|
||||
});
|
||||
resetBtn?.addEventListener('click', () => {
|
||||
clearStored();
|
||||
render();
|
||||
location.reload();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
101
src/components/CookieConsent.astro
Normal file
101
src/components/CookieConsent.astro
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
// 152-ФЗ cookie consent baner. Хранит выбор в localStorage + cookie pit-consent.
|
||||
// Активирует скрипты Analytics при согласии (см. Analytics.astro).
|
||||
---
|
||||
|
||||
<div id="cookie-consent" hidden role="dialog" aria-labelledby="cc-title" aria-describedby="cc-desc">
|
||||
<div class="cc-inner">
|
||||
<div class="cc-text">
|
||||
<strong id="cc-title">Мы используем cookies</strong>
|
||||
<p id="cc-desc">
|
||||
Сайт использует cookies и системы аналитики (Яндекс.Метрика, Google Analytics) для
|
||||
анонимной статистики посещений. Подробнее — в <a href="/privacy/">политике конфиденциальности</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="cc-buttons">
|
||||
<button type="button" id="cc-deny" class="cc-btn cc-btn-secondary">Отклонить</button>
|
||||
<button type="button" id="cc-accept" class="cc-btn cc-btn-primary">Принять</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#cookie-consent {
|
||||
position: fixed;
|
||||
left: 1rem;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
z-index: 1000;
|
||||
background: var(--paper);
|
||||
border: 1px solid var(--rule-strong);
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
||||
padding: 1rem 1.25rem;
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
.cc-inner { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
@media (min-width: 640px) {
|
||||
.cc-inner { flex-direction: row; align-items: center; }
|
||||
}
|
||||
.cc-text strong { font-family: var(--font-serif); font-size: 1.05rem; }
|
||||
.cc-text p { margin: 0.3rem 0 0; font-size: 0.88rem; color: var(--ink-soft); line-height: 1.5; }
|
||||
.cc-buttons { display: flex; gap: 0.5rem; flex-shrink: 0; }
|
||||
.cc-btn {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--rule-strong);
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
cursor: pointer;
|
||||
}
|
||||
.cc-btn-primary {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.cc-btn-primary:hover { background: var(--accent-soft); }
|
||||
.cc-btn-secondary:hover { color: var(--accent); border-color: var(--accent); }
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
(function () {
|
||||
const KEY = 'pit-consent';
|
||||
const el = document.getElementById('cookie-consent');
|
||||
if (!el) return;
|
||||
|
||||
function setCookie(value) {
|
||||
const exp = new Date(Date.now() + 365 * 24 * 3600 * 1000).toUTCString();
|
||||
document.cookie = `${KEY}=${value}; expires=${exp}; path=/; SameSite=Lax`;
|
||||
}
|
||||
|
||||
function activateAnalytics() {
|
||||
document.querySelectorAll('script[type="text/plain"][data-cookieconsent="statistics"]').forEach((s) => {
|
||||
const n = document.createElement('script');
|
||||
if (s.src) n.src = s.src;
|
||||
else n.textContent = s.textContent || '';
|
||||
if (s.async) n.async = true;
|
||||
document.head.appendChild(n);
|
||||
});
|
||||
}
|
||||
|
||||
function decide(value) {
|
||||
try { localStorage.setItem(KEY, value); } catch {}
|
||||
setCookie(value);
|
||||
el.hidden = true;
|
||||
if (value === 'accept') activateAnalytics();
|
||||
}
|
||||
|
||||
const saved = (() => {
|
||||
try { return localStorage.getItem(KEY); } catch { return null; }
|
||||
})();
|
||||
if (saved === 'accept') { activateAnalytics(); return; }
|
||||
if (saved === 'deny') return;
|
||||
el.hidden = false;
|
||||
|
||||
document.getElementById('cc-accept')?.addEventListener('click', () => decide('accept'));
|
||||
document.getElementById('cc-deny')?.addEventListener('click', () => decide('deny'));
|
||||
})();
|
||||
</script>
|
||||
14
src/components/Footer.astro
Normal file
14
src/components/Footer.astro
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
import { SITE_TITLE } from '../consts';
|
||||
const year = new Date().getFullYear();
|
||||
---
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-inner">
|
||||
<p class="copy">© {year} {SITE_TITLE}. Все права защищены.</p>
|
||||
<nav class="footer-nav">
|
||||
<a href="/">Главная</a>
|
||||
<a href="/privacy/">Конфиденциальность</a>
|
||||
</nav>
|
||||
</div>
|
||||
</footer>
|
||||
45
src/consts.ts
Normal file
45
src/consts.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/** Идентичность сайта. */
|
||||
export const SITE_TITLE = 'Автозапчасти «ПитСтоп»';
|
||||
export const SITE_TAGLINE = 'В наличии и под заказ';
|
||||
export const SITE_DESCRIPTION =
|
||||
'Магазин автозапчастей «ПитСтоп». Запчасти в наличии и под заказ. Москва, Люблинская 100, Аквапарк «ФЭНТАЗИ».';
|
||||
export const SITE_URL = 'https://pitstopavto.su';
|
||||
export const SITE_LANG = 'ru-RU';
|
||||
|
||||
/** Контакты магазина. */
|
||||
export const ADDRESS = {
|
||||
postal: '109382',
|
||||
region: 'Москва',
|
||||
street: 'ул. Люблинская, д. 100',
|
||||
building: 'в здании Аквапарка «ФЭНТАЗИ»',
|
||||
full: '109382, г. Москва, ул. Люблинская, д. 100, в здании Аквапарка «ФЭНТАЗИ»',
|
||||
};
|
||||
|
||||
/** Кликабельные телефоны (display + tel: href). */
|
||||
export const PHONES: Array<{ display: string; href: string }> = [
|
||||
{ display: '8 (495) 369-58-44', href: 'tel:+74953695844' },
|
||||
{ display: '8 (495) 592-62-31', href: 'tel:+74955926231' },
|
||||
{ display: '8 (903) 544-24-19', href: 'tel:+79035442419' },
|
||||
{ display: '8 (903) 759-50-29', href: 'tel:+79037595029' },
|
||||
];
|
||||
|
||||
/** Геокоординаты для embed Яндекс.Карт (Люблинская 100). */
|
||||
export const GEO = {
|
||||
// Точка: Москва, Люблинская 100 (Аквапарк ФЭНТАЗИ)
|
||||
lat: 55.658856,
|
||||
lon: 37.747512,
|
||||
zoom: 16,
|
||||
};
|
||||
|
||||
/** Аналитика: реальные ID из текущего WP-сайта (перенесены 1:1). */
|
||||
export const ANALYTICS = {
|
||||
yandexMetrika: '47169531', // с Webvisor
|
||||
googleGtag: 'GT-WRF7ZZ8',
|
||||
};
|
||||
|
||||
/** ИП/ООО для политики конфиденциальности — заполнить когда заказчик пришлёт. */
|
||||
export const OPERATOR = {
|
||||
name: '',
|
||||
inn: '',
|
||||
email: '',
|
||||
};
|
||||
65
src/layouts/Base.astro
Normal file
65
src/layouts/Base.astro
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
import '@fontsource/ibm-plex-sans/400.css';
|
||||
import '@fontsource/ibm-plex-sans/500.css';
|
||||
import '@fontsource/ibm-plex-sans/700.css';
|
||||
import '../styles/global.css';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import CookieConsent from '../components/CookieConsent.astro';
|
||||
import Analytics from '../components/Analytics.astro';
|
||||
import { SITE_TITLE, SITE_DESCRIPTION, SITE_URL, SITE_LANG, ADDRESS, PHONES } from '../consts';
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const { title, description = SITE_DESCRIPTION } = Astro.props;
|
||||
const fullTitle = title ? `${title} — ${SITE_TITLE}` : `${SITE_TITLE} — в наличии и под заказ`;
|
||||
const url = new URL(Astro.url.pathname, SITE_URL).toString();
|
||||
|
||||
const jsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'AutoPartsStore',
|
||||
'@id': `${SITE_URL}/#store`,
|
||||
name: SITE_TITLE,
|
||||
url: `${SITE_URL}/`,
|
||||
description: SITE_DESCRIPTION,
|
||||
telephone: PHONES.map((p) => p.display),
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
streetAddress: `${ADDRESS.street}, ${ADDRESS.building}`,
|
||||
addressLocality: ADDRESS.region,
|
||||
postalCode: ADDRESS.postal,
|
||||
addressCountry: 'RU',
|
||||
},
|
||||
};
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang={SITE_LANG}>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{fullTitle}</title>
|
||||
<meta name="description" content={description} />
|
||||
<link rel="canonical" href={url} />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content={fullTitle} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:url" content={url} />
|
||||
<meta property="og:locale" content="ru_RU" />
|
||||
<meta property="og:site_name" content={SITE_TITLE} />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
|
||||
<script type="application/ld+json" is:inline set:html={JSON.stringify(jsonLd)} />
|
||||
|
||||
<Analytics />
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
<Footer />
|
||||
<CookieConsent />
|
||||
</body>
|
||||
</html>
|
||||
63
src/pages/index.astro
Normal file
63
src/pages/index.astro
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
import { SITE_TITLE, SITE_TAGLINE, ADDRESS, PHONES, GEO } from '../consts';
|
||||
|
||||
const mapSrc = `https://yandex.ru/map-widget/v1/?ll=${GEO.lon}%2C${GEO.lat}&z=${GEO.zoom}&pt=${GEO.lon}%2C${GEO.lat}%2Cpm2rdm`;
|
||||
---
|
||||
|
||||
<Base>
|
||||
<section class="hero">
|
||||
<div class="container hero-inner">
|
||||
<p class="hero-eyebrow">Магазин автозапчастей</p>
|
||||
<h1>
|
||||
Авто<span class="accent">запчасти</span><br />
|
||||
«ПитСтоп»
|
||||
</h1>
|
||||
<p class="hero-tagline">{SITE_TAGLINE}. Москва, Люблинская 100.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<h2>Мы переехали</h2>
|
||||
<p class="address-block">
|
||||
<span class="address-postal">{ADDRESS.postal}</span><br />
|
||||
<strong>г. {ADDRESS.region}, {ADDRESS.street}</strong><br />
|
||||
{ADDRESS.building}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="alt">
|
||||
<div class="container">
|
||||
<h2>Телефоны</h2>
|
||||
<ul class="phones">
|
||||
{PHONES.map((p) => (
|
||||
<li>
|
||||
<a href={p.href} aria-label={`Позвонить ${p.display}`}>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.9.36 1.78.69 2.6a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.48-1.48a2 2 0 0 1 2.11-.45c.82.33 1.7.56 2.6.69A2 2 0 0 1 22 16.92z"/>
|
||||
</svg>
|
||||
{p.display}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<h2>Как нас найти</h2>
|
||||
<div class="map-wrap">
|
||||
<iframe
|
||||
src={mapSrc}
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer-when-downgrade"
|
||||
title="Карта проезда — Автозапчасти ПитСтоп"
|
||||
allow="geolocation"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Base>
|
||||
86
src/pages/privacy.astro
Normal file
86
src/pages/privacy.astro
Normal file
@@ -0,0 +1,86 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
import ConsentRevoke from '../components/ConsentRevoke.astro';
|
||||
import { SITE_TITLE, SITE_URL } from '../consts';
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
---
|
||||
|
||||
<Base title="Политика конфиденциальности">
|
||||
<article class="prose">
|
||||
<h1>Политика конфиденциальности</h1>
|
||||
<p class="updated">Редакция от {today}</p>
|
||||
|
||||
<p>
|
||||
Настоящая политика определяет порядок обработки персональных данных и сведений о
|
||||
пользователях сайта <a href={SITE_URL}>{SITE_URL}</a> (далее — Сайт), принадлежащего
|
||||
магазину {SITE_TITLE}, в соответствии с Федеральным законом № 152-ФЗ
|
||||
«О персональных данных».
|
||||
</p>
|
||||
|
||||
<h2>1. Какие данные собираются</h2>
|
||||
<p>Сайт не содержит форм обратной связи, регистрации и заказов. Сами по себе персональные данные
|
||||
посетителей не запрашиваются и не сохраняются на сервере Сайта.</p>
|
||||
<p>
|
||||
При посещении Сайта (при условии вашего согласия) подключаются системы сбора анонимной
|
||||
статистики:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Яндекс.Метрика</strong> (счётчик № <code>47169531</code>) — собирает обезличенные
|
||||
данные о посещениях, в том числе IP-адрес, тип браузера и устройства, источник перехода,
|
||||
просматриваемые страницы, действия на странице (включая запись сессий — Вебвизор).
|
||||
</li>
|
||||
<li>
|
||||
<strong>Google Analytics 4</strong> (идентификатор <code>GT-WRF7ZZ8</code>) — собирает
|
||||
обезличенные данные о посещениях, сессиях и устройствах.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Данные обрабатываются операторами систем аналитики (ООО «ЯНДЕКС» и Google LLC) в соответствии
|
||||
с их собственными политиками конфиденциальности. Сайт получает только агрегированные отчёты.
|
||||
</p>
|
||||
|
||||
<h2>2. Cookies</h2>
|
||||
<p>
|
||||
Сайт использует следующие cookies:
|
||||
</p>
|
||||
<ul>
|
||||
<li><code>pit-consent</code> — служебная cookie, хранит ваш выбор о согласии на аналитику
|
||||
(срок 12 месяцев);</li>
|
||||
<li>cookies Яндекс.Метрики (<code>_ym_*</code>) и Google Analytics (<code>_ga</code>,
|
||||
<code>_gid</code>) — устанавливаются только после получения вашего согласия.</li>
|
||||
</ul>
|
||||
|
||||
<h2>3. Цели обработки</h2>
|
||||
<ul>
|
||||
<li>анализ статистики посещений и качества Сайта;</li>
|
||||
<li>улучшение удобства использования Сайта;</li>
|
||||
<li>оценка эффективности рекламных каналов (при использовании).</li>
|
||||
</ul>
|
||||
|
||||
<h2>4. Согласие и его отзыв</h2>
|
||||
<p>
|
||||
При первом посещении вам показывается баннер с возможностью принять или отклонить
|
||||
использование систем аналитики. До получения согласия скрипты Яндекс.Метрики и Google
|
||||
Analytics на странице не запускаются.
|
||||
</p>
|
||||
<p>
|
||||
Вы можете в любой момент отозвать согласие или сбросить выбор:
|
||||
</p>
|
||||
|
||||
<ConsentRevoke />
|
||||
|
||||
<h2>5. Контакты</h2>
|
||||
<p>
|
||||
Контактные данные магазина указаны на <a href="/">главной странице</a>. По вопросам
|
||||
обработки данных вы можете связаться по любому из указанных там телефонов.
|
||||
</p>
|
||||
|
||||
<h2>6. Изменения</h2>
|
||||
<p>
|
||||
Действующая редакция политики всегда доступна по адресу
|
||||
<a href={`${SITE_URL}/privacy/`}>{SITE_URL}/privacy/</a>. Изменения вступают в силу с момента
|
||||
публикации на этой странице.
|
||||
</p>
|
||||
</article>
|
||||
</Base>
|
||||
175
src/styles/global.css
Normal file
175
src/styles/global.css
Normal file
@@ -0,0 +1,175 @@
|
||||
:root {
|
||||
--ink: #0d0d0d;
|
||||
--ink-soft: #4a4a4a;
|
||||
--paper: #fafaf7;
|
||||
--paper-soft: #f1efe8;
|
||||
--rule: #d8d4c8;
|
||||
--rule-strong: #b3ad9b;
|
||||
--accent: #f5b400; /* янтарный — отсылка к авто-тематике */
|
||||
--accent-soft: #d99a00;
|
||||
--font-sans: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
|
||||
--reading-max: 64rem;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
color: var(--ink);
|
||||
background: var(--paper);
|
||||
line-height: 1.55;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
img, svg, iframe { max-width: 100%; display: block; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a:hover { color: var(--accent-soft); }
|
||||
|
||||
.container {
|
||||
max-width: var(--reading-max);
|
||||
margin: 0 auto;
|
||||
padding: 0 1.25rem;
|
||||
}
|
||||
|
||||
/* ── Hero ───────────────────────────────────────────────────────── */
|
||||
.hero {
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
padding: 4rem 0 3rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.hero::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
repeating-linear-gradient(135deg, transparent 0 16px, rgba(245,180,0,0.04) 16px 18px);
|
||||
pointer-events: none;
|
||||
}
|
||||
.hero-inner { position: relative; }
|
||||
.hero-eyebrow {
|
||||
font-size: 0.78rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
.hero h1 {
|
||||
font-size: clamp(2.4rem, 6vw, 4.2rem);
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.05;
|
||||
margin: 0;
|
||||
}
|
||||
.hero h1 .accent { color: var(--accent); }
|
||||
.hero-tagline {
|
||||
font-size: clamp(1.05rem, 1.6vw, 1.25rem);
|
||||
color: rgba(250,250,247,0.72);
|
||||
margin: 1rem 0 0;
|
||||
max-width: 38rem;
|
||||
}
|
||||
|
||||
/* ── Sections ───────────────────────────────────────────────────── */
|
||||
section { padding: 3.5rem 0; }
|
||||
section.alt { background: var(--paper-soft); }
|
||||
section h2 {
|
||||
font-size: clamp(1.5rem, 3vw, 2rem);
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 0 0 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
section h2::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 0.5rem;
|
||||
height: 1.4rem;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
/* ── Адрес ──────────────────────────────────────────────────────── */
|
||||
.address-block {
|
||||
font-size: clamp(1.1rem, 1.6vw, 1.35rem);
|
||||
line-height: 1.6;
|
||||
max-width: 40rem;
|
||||
}
|
||||
.address-block strong { font-weight: 700; }
|
||||
.address-postal { color: var(--ink-soft); font-size: 0.92rem; letter-spacing: 0.05em; }
|
||||
|
||||
/* ── Телефоны ───────────────────────────────────────────────────── */
|
||||
.phones {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
|
||||
gap: 0.75rem;
|
||||
margin: 1.5rem 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.phones a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
background: var(--paper);
|
||||
border: 1px solid var(--rule-strong);
|
||||
padding: 0.85rem 1.1rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
transition: all 120ms ease;
|
||||
}
|
||||
.phones a:hover {
|
||||
background: var(--ink);
|
||||
color: var(--accent);
|
||||
border-color: var(--ink);
|
||||
}
|
||||
.phones a svg { flex-shrink: 0; }
|
||||
|
||||
/* ── Карта ──────────────────────────────────────────────────────── */
|
||||
.map-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
border: 1px solid var(--rule-strong);
|
||||
overflow: hidden;
|
||||
background: var(--paper-soft);
|
||||
}
|
||||
.map-wrap iframe { width: 100%; height: 100%; border: 0; }
|
||||
@media (max-width: 640px) { .map-wrap { aspect-ratio: 4 / 3; } }
|
||||
|
||||
/* ── Footer ─────────────────────────────────────────────────────── */
|
||||
.site-footer {
|
||||
background: var(--ink);
|
||||
color: rgba(250,250,247,0.7);
|
||||
padding: 2rem 0;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
.footer-inner {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.footer-inner .copy { margin: 0; font-size: 0.9rem; }
|
||||
.footer-nav { display: flex; gap: 1.25rem; font-size: 0.9rem; }
|
||||
.footer-nav a:hover { color: var(--accent); }
|
||||
|
||||
/* ── Reading pages (privacy) ────────────────────────────────────── */
|
||||
.prose {
|
||||
max-width: 44rem;
|
||||
margin: 3rem auto;
|
||||
padding: 0 1.25rem;
|
||||
}
|
||||
.prose h1 {
|
||||
font-size: clamp(1.8rem, 3.5vw, 2.4rem);
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
.prose h2 { font-size: 1.3rem; margin: 2rem 0 0.5rem; }
|
||||
.prose p, .prose li { font-size: 1rem; color: var(--ink); }
|
||||
.prose ul, .prose ol { padding-left: 1.5rem; }
|
||||
.prose .updated { color: var(--ink-soft); font-size: 0.85rem; margin: 0 0 2rem; }
|
||||
.prose a { color: var(--accent-soft); text-decoration: underline; }
|
||||
Reference in New Issue
Block a user