feat: счётчики с consent, политика, фиксы дизайна
Дизайн-фиксы: - Перенесены картинки из старого WP в public/wp-content/uploads/ (header_bg.gif + 3 размера NY-baner.png 2014/12). migrate-wp.mjs обновлён: корректно обрабатывает <a><img></a>, переписывает абсолютные URL anotherreflections.ru/wp-content/... в относительные - Description в frontmatter теперь чистый plain text — без markdown-разметки - Слаги 11/95/152 → kto-my, s-23-fevralya-2011, s-nastupayushhim-novym-2012-godom - Hero-метрика: «N лет онлайн» → «с 2006 года» (последний пост 2015, активной жизни «20 лет» нет) - Drop cap (буквица) — только для постов длиннее 240 символов (короткие посты раньше выглядели обрезанными) Аналитика и конфиденциальность: - Яндекс.Метрика (counter 13938862, webvisor) и Google gtag (GT-PH39R3X) перенесены со старого WP - Cookie consent banner — самописный, без зависимостей: счётчики грузятся только после явного «Принять». Выбор хранится в localStorage + cookie ar-consent на 12 месяцев. Уведомление в духе 152-ФЗ - /privacy/ — полная политика конфиденциальности: что собираем (через Метрику и GA), для чего, как cookies устроены, права пользователя, контакт для запросов на удаление - В футере добавлены ссылки «Политика конфиденциальности» и «Контакты» - sitemap.txt + llms.txt включают /privacy/
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
- [Миры — 8 игровых вселенных](https://anotherreflections.ru/miry/)
|
- [Миры — 8 игровых вселенных](https://anotherreflections.ru/miry/)
|
||||||
- [Наши друзья](https://anotherreflections.ru/nashi-druzya/)
|
- [Наши друзья](https://anotherreflections.ru/nashi-druzya/)
|
||||||
- [Контакты](https://anotherreflections.ru/kontakty/)
|
- [Контакты](https://anotherreflections.ru/kontakty/)
|
||||||
|
- [Политика конфиденциальности](https://anotherreflections.ru/privacy/)
|
||||||
|
|
||||||
## Игровые форумы
|
## Игровые форумы
|
||||||
|
|
||||||
|
|||||||
BIN
public/wp-content/uploads/2014/12/NY-baner-150x60.png
Normal file
BIN
public/wp-content/uploads/2014/12/NY-baner-150x60.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
BIN
public/wp-content/uploads/2014/12/NY-baner-300x51.png
Normal file
BIN
public/wp-content/uploads/2014/12/NY-baner-300x51.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
public/wp-content/uploads/2014/12/NY-baner.png
Normal file
BIN
public/wp-content/uploads/2014/12/NY-baner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
public/wp-content/uploads/header_bg.gif
Normal file
BIN
public/wp-content/uploads/header_bg.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
@@ -28,15 +28,22 @@ const decodeEntities = (s) => s
|
|||||||
.replace(/>/g, '>')
|
.replace(/>/g, '>')
|
||||||
.replace(/&/g, '&');
|
.replace(/&/g, '&');
|
||||||
|
|
||||||
|
/** Заменить старые WP-uploads URL на относительные (мы скачали в public/wp-content/uploads/). */
|
||||||
|
const rewriteWpUploads = (url) =>
|
||||||
|
url.replace(/^https?:\/\/anotherreflections\.ru(\/wp-content\/uploads\/.+)$/i, '$1');
|
||||||
|
|
||||||
const htmlToMd = (html) => {
|
const htmlToMd = (html) => {
|
||||||
if (!html) return '';
|
if (!html) return '';
|
||||||
let s = html;
|
let s = html;
|
||||||
// images
|
// Сначала "вложенные" <a href="..."><img src="..."></a> — превращаем в один markdown-image со ссылкой
|
||||||
|
s = s.replace(/<a\s+[^>]*?href=["']([^"']+)["'][^>]*>\s*<img[^>]*?src=["']([^"']+)["'][^>]*?(?:alt=["']([^"']*)["'])?[^>]*?\/?>\s*<\/a>/gi,
|
||||||
|
(_, href, src, alt) => `[})](${rewriteWpUploads(href)})`);
|
||||||
|
// одиночные images
|
||||||
s = s.replace(/<img[^>]*?src=["']([^"']+)["'][^>]*?(?:alt=["']([^"']*)["'])?[^>]*?\/?>/gi,
|
s = s.replace(/<img[^>]*?src=["']([^"']+)["'][^>]*?(?:alt=["']([^"']*)["'])?[^>]*?\/?>/gi,
|
||||||
(_, src, alt) => ``);
|
(_, src, alt) => `})`);
|
||||||
// links
|
// links
|
||||||
s = s.replace(/<a\s+[^>]*?href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi,
|
s = s.replace(/<a\s+[^>]*?href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi,
|
||||||
(_, href, text) => `[${text.replace(/<[^>]+>/g,'').trim()}](${href})`);
|
(_, href, text) => `[${text.replace(/<[^>]+>/g,'').trim()}](${rewriteWpUploads(href)})`);
|
||||||
// bold
|
// bold
|
||||||
s = s.replace(/<(strong|b)\b[^>]*>([\s\S]*?)<\/\1>/gi, '**$2**');
|
s = s.replace(/<(strong|b)\b[^>]*>([\s\S]*?)<\/\1>/gi, '**$2**');
|
||||||
// italic
|
// italic
|
||||||
@@ -106,7 +113,12 @@ for (const p of data.posts) {
|
|||||||
categories: p.categories.map(c => c.name),
|
categories: p.categories.map(c => c.name),
|
||||||
categorySlugs: p.categories.map(c => c.slug),
|
categorySlugs: p.categories.map(c => c.slug),
|
||||||
tags: p.tags.map(t => t.name),
|
tags: p.tags.map(t => t.name),
|
||||||
description: (htmlToMd(p.excerpt) || htmlToMd(p.content_html)).slice(0, 200).replace(/\s+/g,' ').trim(),
|
description: (htmlToMd(p.excerpt) || htmlToMd(p.content_html))
|
||||||
|
.replace(/!?\[([^\]]*)\]\([^)]+\)/g, '$1') // strip markdown images/links для excerpt
|
||||||
|
.replace(/[*_>#]+/g, '') // strip md formatting символов
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim()
|
||||||
|
.slice(0, 200),
|
||||||
};
|
};
|
||||||
const body = htmlToMd(p.content_html);
|
const body = htmlToMd(p.content_html);
|
||||||
const out = `${fmtFrontmatter(fm)}\n\n${body}\n`;
|
const out = `${fmtFrontmatter(fm)}\n\n${body}\n`;
|
||||||
|
|||||||
37
src/components/Analytics.astro
Normal file
37
src/components/Analytics.astro
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
import { ANALYTICS } from '../consts';
|
||||||
|
const { yandexMetrika, googleGtag } = ANALYTICS;
|
||||||
|
---
|
||||||
|
<!-- Аналитика грузится только после согласия пользователя (cookie consent). -->
|
||||||
|
<script
|
||||||
|
is:inline
|
||||||
|
type="text/plain"
|
||||||
|
data-cookieconsent="statistics"
|
||||||
|
set:html={`
|
||||||
|
(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();
|
||||||
|
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
||||||
|
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");
|
||||||
|
|
||||||
|
ym(${yandexMetrika}, "init", { defer:true, clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:true });
|
||||||
|
`} />
|
||||||
|
<noscript><div><img src={`https://mc.yandex.ru/watch/${yandexMetrika}`} style="position:absolute; left:-9999px;" alt="" /></div></noscript>
|
||||||
|
|
||||||
|
<script
|
||||||
|
is:inline
|
||||||
|
type="text/plain"
|
||||||
|
data-cookieconsent="statistics"
|
||||||
|
async
|
||||||
|
src={`https://www.googletagmanager.com/gtag/js?id=${googleGtag}`}
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
is:inline
|
||||||
|
type="text/plain"
|
||||||
|
data-cookieconsent="statistics"
|
||||||
|
set:html={`
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', '${googleGtag}');
|
||||||
|
`} />
|
||||||
73
src/components/CookieConsent.astro
Normal file
73
src/components/CookieConsent.astro
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
// Минимальный self-hosted cookie consent под 152-ФЗ:
|
||||||
|
// — баннер показывается до явного выбора (cookie `ar-consent`)
|
||||||
|
// — «Принять» → активирует все <script type="text/plain" data-cookieconsent="statistics">
|
||||||
|
// — «Только необходимые» → ничего не активирует, баннер исчезает
|
||||||
|
// — выбор хранится в localStorage и cookie на 12 месяцев
|
||||||
|
---
|
||||||
|
<div id="cookie-consent" class="cookie-consent" hidden>
|
||||||
|
<div class="cookie-text">
|
||||||
|
<p>
|
||||||
|
Мы используем cookies и сервисы статистики (Яндекс.Метрика, Google Analytics),
|
||||||
|
чтобы понимать, как вы пользуетесь сайтом, и делать его лучше.
|
||||||
|
Подробнее — в <a href="/privacy/">политике конфиденциальности</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="cookie-actions">
|
||||||
|
<button type="button" class="cookie-btn cookie-btn-secondary" data-cc-deny>Только необходимые</button>
|
||||||
|
<button type="button" class="cookie-btn cookie-btn-primary" data-cc-accept>Принять</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script is:inline>
|
||||||
|
(function () {
|
||||||
|
const KEY = 'ar-consent';
|
||||||
|
const banner = document.getElementById('cookie-consent');
|
||||||
|
if (!banner) return;
|
||||||
|
|
||||||
|
const activate = () => {
|
||||||
|
document.querySelectorAll('script[type="text/plain"][data-cookieconsent="statistics"]').forEach((tpl) => {
|
||||||
|
const s = document.createElement('script');
|
||||||
|
for (const a of tpl.attributes) {
|
||||||
|
if (a.name === 'type' || a.name === 'data-cookieconsent') continue;
|
||||||
|
s.setAttribute(a.name, a.value);
|
||||||
|
}
|
||||||
|
s.text = tpl.textContent || '';
|
||||||
|
tpl.parentNode.insertBefore(s, tpl);
|
||||||
|
tpl.parentNode.removeChild(tpl);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setConsent = (value) => {
|
||||||
|
try { localStorage.setItem(KEY, value); } catch {}
|
||||||
|
const exp = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toUTCString();
|
||||||
|
document.cookie = `${KEY}=${value}; expires=${exp}; path=/; SameSite=Lax`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const current = (() => {
|
||||||
|
try {
|
||||||
|
const ls = localStorage.getItem(KEY);
|
||||||
|
if (ls) return ls;
|
||||||
|
} catch {}
|
||||||
|
const m = document.cookie.match(/(?:^|; )ar-consent=([^;]+)/);
|
||||||
|
return m ? decodeURIComponent(m[1]) : null;
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (current === 'accept') {
|
||||||
|
activate();
|
||||||
|
} else if (current === 'deny') {
|
||||||
|
// ничего не делаем — статистика не активируется
|
||||||
|
} else {
|
||||||
|
banner.hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
banner.querySelector('[data-cc-accept]')?.addEventListener('click', () => {
|
||||||
|
setConsent('accept');
|
||||||
|
banner.hidden = true;
|
||||||
|
activate();
|
||||||
|
});
|
||||||
|
banner.querySelector('[data-cc-deny]')?.addEventListener('click', () => {
|
||||||
|
setConsent('deny');
|
||||||
|
banner.hidden = true;
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -5,6 +5,11 @@ export const SOCIAL = {
|
|||||||
|
|
||||||
export const CONTACT_EMAIL = 'info@anotherreflections.ru';
|
export const CONTACT_EMAIL = 'info@anotherreflections.ru';
|
||||||
|
|
||||||
|
export const ANALYTICS = {
|
||||||
|
yandexMetrika: '13938862', // counter ID, webvisor включён
|
||||||
|
googleGtag: 'GT-PH39R3X', // GTM/GA4 measurement
|
||||||
|
};
|
||||||
|
|
||||||
/** Русская плюрализация: [1, 2-4, 5+]. Пример: plural(8, ['мир', 'мира', 'миров']) → 'миров' */
|
/** Русская плюрализация: [1, 2-4, 5+]. Пример: plural(8, ['мир', 'мира', 'миров']) → 'миров' */
|
||||||
export function plural(n: number, forms: [string, string, string]): string {
|
export function plural(n: number, forms: [string, string, string]): string {
|
||||||
const a = Math.abs(n) % 100;
|
const a = Math.abs(n) % 100;
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ categories:
|
|||||||
- "Общие новости"
|
- "Общие новости"
|
||||||
categorySlugs:
|
categorySlugs:
|
||||||
- "obshhie-novosti"
|
- "obshhie-novosti"
|
||||||
description: "[](http://anotherreflections.ru/wp-content/uploads/2014/12/NY-baner.png) Подходит к своему завершению 2014 год."
|
description: "[](/wp-content/uploads/2014/12/NY-baner.png) Подходит к своему завершению 2014 год. В жизни каждого из нас произошло много событий - ярких,"
|
||||||
---
|
---
|
||||||
|
|
||||||
[](http://anotherreflections.ru/wp-content/uploads/2014/12/NY-baner.png)
|
[](/wp-content/uploads/2014/12/NY-baner.png)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: "Кто мы?"
|
title: "Кто мы?"
|
||||||
pubDate: "2013-08-25T03:15:01+03:00"
|
pubDate: "2013-08-25T03:15:01+03:00"
|
||||||
updatedDate: "2013-10-06T02:05:52+03:00"
|
updatedDate: "2013-10-06T02:05:52+03:00"
|
||||||
slug: "11"
|
slug: "kto-my"
|
||||||
legacyId: "365"
|
legacyId: "365"
|
||||||
author: "admin"
|
author: "admin"
|
||||||
categories:
|
categories:
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
title: "С 23 февраля!"
|
title: "С 23 февраля!"
|
||||||
pubDate: "2011-02-23T00:48:39+03:00"
|
pubDate: "2011-02-23T00:48:39+03:00"
|
||||||
updatedDate: "2013-03-29T14:34:37+03:00"
|
updatedDate: "2013-03-29T14:34:37+03:00"
|
||||||
slug: "95"
|
slug: "s-23-fevralya-2011"
|
||||||
legacyId: "95"
|
legacyId: "95"
|
||||||
author: "admin"
|
author: "admin"
|
||||||
categories:
|
categories:
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
title: "С наступающим Новым 2012 годом!"
|
title: "С наступающим Новым 2012 годом!"
|
||||||
pubDate: "2012-01-01T00:27:00+03:00"
|
pubDate: "2012-01-01T00:27:00+03:00"
|
||||||
updatedDate: "2013-03-29T14:35:17+03:00"
|
updatedDate: "2013-03-29T14:35:17+03:00"
|
||||||
slug: "152"
|
slug: "s-nastupayushhim-novym-2012-godom"
|
||||||
legacyId: "152"
|
legacyId: "152"
|
||||||
author: "admin"
|
author: "admin"
|
||||||
categories:
|
categories:
|
||||||
@@ -3,6 +3,8 @@ import '../styles/global.css';
|
|||||||
import { SITE_TITLE, SITE_DESCRIPTION, SITE_URL, SITE_LANG, MAIN_NAV } from '../consts';
|
import { SITE_TITLE, SITE_DESCRIPTION, SITE_URL, SITE_LANG, MAIN_NAV } from '../consts';
|
||||||
import BrandMark from '../components/BrandMark.astro';
|
import BrandMark from '../components/BrandMark.astro';
|
||||||
import SocialLinks from '../components/SocialLinks.astro';
|
import SocialLinks from '../components/SocialLinks.astro';
|
||||||
|
import Analytics from '../components/Analytics.astro';
|
||||||
|
import CookieConsent from '../components/CookieConsent.astro';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -39,6 +41,8 @@ const year = new Date().getFullYear();
|
|||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Cormorant+Garamond:wght@500;600;700&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Cormorant+Garamond:wght@500;600;700&display=swap" rel="stylesheet" />
|
||||||
|
|
||||||
|
<Analytics />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="site-header">
|
<header class="site-header">
|
||||||
@@ -67,7 +71,14 @@ const year = new Date().getFullYear();
|
|||||||
<footer class="site-footer">
|
<footer class="site-footer">
|
||||||
<span class="footer-ornament"></span>
|
<span class="footer-ornament"></span>
|
||||||
<p>© 2006–{year} · {SITE_TITLE}</p>
|
<p>© 2006–{year} · {SITE_TITLE}</p>
|
||||||
<p><a href="/feed.xml">RSS</a> · <a href="/sitemap-index.xml">Sitemap</a></p>
|
<p>
|
||||||
|
<a href="/feed.xml">RSS</a> ·
|
||||||
|
<a href="/sitemap-index.xml">Sitemap</a> ·
|
||||||
|
<a href="/privacy/">Политика конфиденциальности</a> ·
|
||||||
|
<a href="/kontakty/">Контакты</a>
|
||||||
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<CookieConsent />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ export async function getStaticPaths() {
|
|||||||
|
|
||||||
const { entry, kind } = Astro.props;
|
const { entry, kind } = Astro.props;
|
||||||
const { Content } = await render(entry);
|
const { Content } = await render(entry);
|
||||||
|
// Буквица — только для постов с телом длиннее короткого порога.
|
||||||
|
const bodyLen = entry.body?.length ?? 0;
|
||||||
|
const useDropCap = kind === 'post' && bodyLen > 240;
|
||||||
|
|
||||||
const fmtDate = (d: Date) =>
|
const fmtDate = (d: Date) =>
|
||||||
d.toLocaleDateString('ru-RU', { year: 'numeric', month: 'long', day: 'numeric' });
|
d.toLocaleDateString('ru-RU', { year: 'numeric', month: 'long', day: 'numeric' });
|
||||||
@@ -41,7 +44,7 @@ const fmtDate = (d: Date) =>
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
<div class="post-body">
|
<div class={`post-body ${useDropCap ? 'has-dropcap' : ''}`}>
|
||||||
<Content />
|
<Content />
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const fmtDate = (d: Date) =>
|
|||||||
<div class="hero-meta">
|
<div class="hero-meta">
|
||||||
<span><strong>{totalWorlds}</strong> {plural(totalWorlds, ['мир', 'мира', 'миров'])}</span>
|
<span><strong>{totalWorlds}</strong> {plural(totalWorlds, ['мир', 'мира', 'миров'])}</span>
|
||||||
<span><strong>{totalPosts}</strong> {plural(totalPosts, ['публикация', 'публикации', 'публикаций'])}</span>
|
<span><strong>{totalPosts}</strong> {plural(totalPosts, ['публикация', 'публикации', 'публикаций'])}</span>
|
||||||
<span><strong>{new Date().getFullYear() - SITE_FOUNDED}</strong> {plural(new Date().getFullYear() - SITE_FOUNDED, ['год', 'года', 'лет'])} онлайн</span>
|
<span><strong>с {SITE_FOUNDED}</strong> года</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
96
src/pages/privacy.astro
Normal file
96
src/pages/privacy.astro
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
|
import { CONTACT_EMAIL, SITE_URL } from '../consts';
|
||||||
|
const lastUpdated = '20 мая 2026 г.';
|
||||||
|
---
|
||||||
|
<BaseLayout
|
||||||
|
title="Политика конфиденциальности"
|
||||||
|
description="Какие данные собирает сайт anotherreflections.ru, для чего и как с ними обращаются."
|
||||||
|
>
|
||||||
|
<article class="post">
|
||||||
|
<header>
|
||||||
|
<h1>Политика конфиденциальности</h1>
|
||||||
|
<div class="post-meta">
|
||||||
|
<time>Обновлено: {lastUpdated}</time>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="post-body">
|
||||||
|
<p>
|
||||||
|
Настоящая Политика описывает, какие данные собирает сайт <a href={SITE_URL}>anotherreflections.ru</a>
|
||||||
|
(далее — «Сайт»), для чего они используются и как с ними обращаются. Политика составлена
|
||||||
|
в соответствии с Федеральным законом от 27.07.2006 № 152-ФЗ «О персональных данных».
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>1. Какие данные мы собираем</h2>
|
||||||
|
<p>Сайт не требует регистрации и не запрашивает у посетителя ни имени, ни телефона, ни почты.</p>
|
||||||
|
<p>
|
||||||
|
При посещении сайта автоматически собираются обезличенные технические данные через
|
||||||
|
сервисы статистики:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Яндекс.Метрика</strong> — IP-адрес (обезличенный), тип устройства и браузера,
|
||||||
|
источник перехода, поведение на страницах, в том числе запись действий (Вебвизор).</li>
|
||||||
|
<li><strong>Google Analytics</strong> — аналогичный обезличенный набор технических данных
|
||||||
|
о посещении.</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Эти данные собираются <em>только после</em> того, как вы согласились на использование
|
||||||
|
статистики через всплывающее уведомление о cookies. Если вы выбрали «Только необходимые»,
|
||||||
|
статистика не загружается.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>2. Cookies</h2>
|
||||||
|
<p>Сайт использует следующие cookies:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Технические</strong> — для запоминания вашего выбора по уведомлению о cookies
|
||||||
|
(cookie <code>ar-consent</code>, срок 12 месяцев). Без согласия не устанавливаются никакие
|
||||||
|
другие.</li>
|
||||||
|
<li><strong>Аналитические</strong> (после согласия) — устанавливаются скриптами
|
||||||
|
Яндекс.Метрики и Google Analytics для подсчёта уникальных посетителей и анализа
|
||||||
|
поведения. Подробнее — на страницах
|
||||||
|
<a href="https://yandex.ru/legal/metrica_termsofuse/" target="_blank" rel="noopener">Условия использования Яндекс.Метрики</a>
|
||||||
|
и <a href="https://policies.google.com/privacy" target="_blank" rel="noopener">Политика конфиденциальности Google</a>.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>3. Зачем мы это делаем</h2>
|
||||||
|
<p>Статистика помогает нам понимать:</p>
|
||||||
|
<ul>
|
||||||
|
<li>сколько людей и откуда приходит на сайт;</li>
|
||||||
|
<li>какие разделы и публикации читают чаще;</li>
|
||||||
|
<li>какие страницы работают плохо (медленно загружаются, неудобны на мобильном).</li>
|
||||||
|
</ul>
|
||||||
|
<p>На основе этого мы улучшаем сайт. Данные используются в обезличенном виде и не позволяют идентифицировать конкретного человека.</p>
|
||||||
|
|
||||||
|
<h2>4. Передача данных третьим лицам</h2>
|
||||||
|
<p>
|
||||||
|
Обезличенные технические данные передаются операторам сервисов статистики
|
||||||
|
(Яндекс, Google) — это необходимо для работы самих сервисов. Иным третьим лицам ничего не передаётся.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>5. Ваши права</h2>
|
||||||
|
<p>Вы можете в любой момент:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Отозвать согласие на статистику — очистите cookies сайта или удалите <code>ar-consent</code>
|
||||||
|
в DevTools. При следующем визите снова появится уведомление, выберите «Только необходимые».</li>
|
||||||
|
<li>Запросить удаление ваших данных из систем статистики — обратитесь напрямую к операторам
|
||||||
|
(<a href="https://yandex.ru/support/metrica/" target="_blank" rel="noopener">Яндекс</a>,
|
||||||
|
<a href="https://support.google.com/analytics/" target="_blank" rel="noopener">Google</a>).</li>
|
||||||
|
<li>Связаться с администрацией сайта по адресу <a href={`mailto:${CONTACT_EMAIL}`}>{CONTACT_EMAIL}</a>
|
||||||
|
по любым вопросам, связанным с данными.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>6. Изменения</h2>
|
||||||
|
<p>
|
||||||
|
Мы можем обновлять эту Политику. Актуальная версия всегда доступна по адресу
|
||||||
|
<a href="/privacy/">/privacy/</a>. Дата последнего обновления указана в начале документа.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>7. Контакты</h2>
|
||||||
|
<p>
|
||||||
|
По вопросам обработки персональных данных:
|
||||||
|
<a href={`mailto:${CONTACT_EMAIL}`}>{CONTACT_EMAIL}</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</BaseLayout>
|
||||||
@@ -10,6 +10,7 @@ export async function GET(_context: APIContext) {
|
|||||||
'/',
|
'/',
|
||||||
'/miry/',
|
'/miry/',
|
||||||
'/kontakty/',
|
'/kontakty/',
|
||||||
|
'/privacy/',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Категории — собираем из постов
|
// Категории — собираем из постов
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ pre {
|
|||||||
.post-body {
|
.post-body {
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
}
|
}
|
||||||
.post-body p:first-of-type::first-letter {
|
.post-body.has-dropcap p:first-of-type::first-letter {
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
font-size: 3.2em;
|
font-size: 3.2em;
|
||||||
float: left;
|
float: left;
|
||||||
@@ -563,6 +563,64 @@ pre {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================== COOKIE CONSENT ============================== */
|
||||||
|
.cookie-consent {
|
||||||
|
position: fixed;
|
||||||
|
left: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
bottom: 1rem;
|
||||||
|
max-width: 720px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1.1rem 1.4rem;
|
||||||
|
background: rgba(17, 20, 29, 0.96);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid var(--border-strong);
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
z-index: 60;
|
||||||
|
font-size: .9rem;
|
||||||
|
}
|
||||||
|
.cookie-text { flex: 1 1 320px; }
|
||||||
|
.cookie-text p { margin: 0; color: var(--fg-muted); line-height: 1.5; }
|
||||||
|
.cookie-text a { color: var(--link); }
|
||||||
|
.cookie-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: .55rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.cookie-btn {
|
||||||
|
padding: .55em 1.1em;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--bg-elev);
|
||||||
|
color: var(--fg);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: .88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color .2s, background .2s, color .2s;
|
||||||
|
}
|
||||||
|
.cookie-btn:hover { border-color: var(--accent); color: var(--accent); }
|
||||||
|
.cookie-btn-primary {
|
||||||
|
background: var(--accent-deep);
|
||||||
|
border-color: var(--accent-deep);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.cookie-btn-primary:hover {
|
||||||
|
background: var(--accent);
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.cookie-consent { padding: .9rem 1rem; left: .5rem; right: .5rem; bottom: .5rem; }
|
||||||
|
.cookie-actions { width: 100%; justify-content: flex-end; }
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================== SOCIAL RAIL ============================== */
|
/* ============================== SOCIAL RAIL ============================== */
|
||||||
.social-rail {
|
.social-rail {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
Reference in New Issue
Block a user