1<!doctype html> 2<html class="no-js" lang="ru" id="discours-app" ng-controller="HeadCtrl as headCtrl" hreflang-tags> 3 <head> 4 <!-- Core meta: charset must be first --> 5 <meta charset="utf-8"> 6 7 <!-- Storage shim: в Chrome (Block-third-party-cookies ÑÑÑогий Ñежим), Brave, in-app 8 бÑаÑзеÑÐ°Ñ (Android WebView, Facebook, DuckDuckGo) и iframe-sandbox обÑаÑение к 9 localStorage/sessionStorage ÐºÐ¸Ð´Ð°ÐµÑ SecurityError DOMException.code=18. 10 Vendor-библиоÑеки (Angular core, Asteroid) ÑиÑаÑÑ storage без try/catch и 11 падаÑÑ Ñ unhandled error â Sentry FRONTEND-KR/MB. ÐодменÑем на in-memory shim 12 ÐРлÑбого дÑÑгого ÑкÑипÑа (вклÑÑÐ°Ñ Ð¸Ð½Ð»Ð°Ð¹Ð½ ниже и main bundle), ÑÑÐ¾Ð±Ñ Ð²Ñе 13 ÑиÑаÑели ÑабоÑали без падений. Persistence ÑеÑÑеÑÑÑ Ð² пÑÐµÐ´ÐµÐ»Ð°Ñ Ð¾Ð´Ð½Ð¾Ð¹ ÑеÑÑии, 14 ÑÑо пÑиемлемо Ð´Ð»Ñ privacy-mode/in-app ÑÑенаÑиев. --> 15
15<script> 16 (function () { 17 function makeMemoryStorage() { 18 var store = Object.create(null); 19 return { 20 getItem: function (k) { 21 return Object.prototype.hasOwnProperty.call(store, k) ? store[k] : null; 22 }, 23 setItem: function (k, v) { 24 store[k] = String(v); 25 }, 26 removeItem: function (k) { 27 delete store[k]; 28 }, 29 clear: function () { 30 store = Object.create(null); 31 }, 32 key: function (i) { 33 return Object.keys(store)[i] || null; 34 }, 35 get length() { 36 return Object.keys(store).length; 37 }, 38 }; 39 } 40 function isUsable(name) { 41 try { 42 var s = window[name]; 43 if (!s) return false; 44 var probe = '__d_probe__'; 45 s.setItem(probe, probe); 46 s.removeItem(probe); 47 return true; 48 } catch (_) { 49 return false; 50 } 51 } 52 function patch(name) { 53 if (isUsable(name)) return; 54 try { 55 Object.defineProperty(window, name, { 56 value: makeMemoryStorage(), 57 writable: false, 58 configurable: true, 59 }); 60 } catch (_) { 61 /* в ÑÐµÐ´ÐºÐ¸Ñ Ð±ÑаÑзеÑÐ°Ñ defineProperty запÑеÑÑн â Sentry ignoreErrors Ð¿Ð¾Ð¹Ð¼Ð°ÐµÑ */ 62 } 63 } 64 patch('localStorage'); 65 patch('sessionStorage'); 66 })(); 67 </script>
67 68 69 <!-- Assets/media domains: опÑеделÑем ÐÐ preconnect и body-ÑкÑипÑов (LCP-опÑимизаÑиÑ) --> 70 <!-- MUST run before any inline script that reads window.discours.ASSETS_DOMAIN --> 71 <!-- MEDIA_DOMAIN â пÑÑмÑе ÑÐ°Ð¹Ð»Ñ (аÑдио, докÑменÑÑ) Ñ R2 custom domain; 72 на зеÑÐºÐ°Ð»Ð°Ñ Ð¸ÑполÑзÑеÑÑÑ Ð½ÐµÐ·Ð°Ð±Ð»Ð¾ÐºÐ¸ÑованнÑй media.dscrs.site (Ñа же логика, 73 ÑÑо ASSETS_DOMAIN). ÐоÑÑеблÑеÑÑÑ generate-config.js â constant S3_SERVER. --> 74
74<script> 75 window.discours = window.discours || {}; 76 (function () { 77 var main = false; 78 try { 79 var h = window.location.hostname; 80 main = /(?:^|\.)discours\.io$/.test(h) || /^(?:localhost|127\.0\.0\.1)$/.test(h); 81 } catch (_) {} 82 window.discours.ASSETS_DOMAIN = main 83 ? 'https://assets.discours.io' 84 : 'https://assets.dscrs.site'; 85 window.discours.MEDIA_DOMAIN = main 86 ? 'https://media.discours.io' 87 : 'https://media.dscrs.site'; 88 })(); 89 </script>
89 90 91 <!-- Performance: Preconnect to critical origins FIRST (before any other resources) --> 92 <!-- Placed immediately after charset for maximum benefit - starts TCP/TLS handshake early --> 93 <link rel="preconnect" href="https://api.discours.io" crossorigin> 94 <!-- Assets: NO crossorigin â images загÑÑжаÑÑÑÑ ÑеÑез <img> (non-CORS). 95 crossorigin preconnect ÑоздаÑÑ CORS-Ñоединение, непÑигодное Ð´Ð»Ñ non-CORS image requests. 96 ÐодклÑÑаем ÑолÑко нÑжнÑй домен â лиÑний preconnect ÑÑаÑÐ¸Ñ TCP-Ñоединение впÑÑÑÑÑ. --> 97
97<script> 98 (function () { 99 var l = document.createElement('link'); 100 l.rel = 'preconnect'; 101 l.href = window.discours.ASSETS_DOMAIN; 102 document.head.appendChild(l); 103 })(); 104 </script>
104 105 <!-- ÐÐ¾ÐºÐ°Ð»Ñ ÐРзагÑÑзки Angular. 106 Ð ÑазмеÑке lang="ru" Ð·Ð°Ñ Ð°Ñдкожен, а hreflangTagsDirective.js вÑÑÑавлÑÐµÑ 107 ÑеалÑнÑÑ Ð»Ð¾ÐºÐ°Ð»Ñ ÑолÑко поÑле bootstrap. Ðо ÑÑого моменÑа ÑÑÑаниÑа /en 108 обÑÑвлена ÑкÑинÑидеÑам и кÑаÑлеÑам как ÑÑÑÑкаÑ. СÑавим lang ÑÑÐ°Ð·Ñ â 109 знаÑение Ñо же, ÑÑо веÑнÑÑ localeService.getLocale(), Ñак ÑÑо позже 110 диÑекÑива пÑоÑÑо подÑвеÑÐ´Ð¸Ñ ÐµÐ³Ð¾ (идемпоÑенÑно). --> 111
111<script> 112 (function () { 113 var p = window.location.pathname; 114 if (p === '/en' || p.indexOf('/en/') === 0) { 115 document.documentElement.lang = 'en'; 116 } 117 })(); 118 </script>
118 119 120 <!-- dns-prefetch не нÑжен Ð´Ð»Ñ api/assets: preconnect вÑÑе Ñже вклÑÑÐ°ÐµÑ DNS resolution. --> 121 <!-- dns-prefetch Ð´Ð»Ñ Ð²Ð¸Ð´ÐµÐ¾ÑеÑвиÑов: ÑазÑеÑаем DNS заÑанее, ÑÑÐ¾Ð±Ñ ÑÑкоÑиÑÑ Ð¿ÐµÑвÑй Play --> 122 <link rel="dns-prefetch" href="https://www.youtube.com"> 123 <link rel="dns-prefetch" href="https://player.vimeo.com"> 124 <link rel="dns-prefetch" href="https://img.youtube.com"> 125 126 <!-- Core meta continued --> 127 <meta name="viewport" content="width=device-width,initial-scale=1"> 128 <!-- ТолÑко light: ÑÑÐ¼Ð½Ð°Ñ Ñема CSS не гоÑова, "light dark" вÑзÑÐ²Ð°ÐµÑ Ð±ÐµÐ»Ñе вÑпÑÑки пÑи загÑÑзке --> 129 <meta name="color-scheme" content="light"> 130 <meta content="IE=edge" http-equiv="X-UA-Compatible"> 131 <!-- Referer наÑÑÐ¶Ñ â ÑолÑко origin, без пÑÑи и паÑамеÑÑов. ÐÐ»Ñ ÑовÑеменнÑÑ Ð±ÑаÑзеÑов 132 ÑÑо Ð¸Ñ Ð¶Ðµ поведение по ÑмолÑаниÑ, а Ð´Ð»Ñ ÑÑаÑÑÑ (Android 8-9, Windows 7 â замеÑÐ½Ð°Ñ 133 ÑаÑÑÑ ÑиÑаÑелей из-под блокиÑовок) закÑÑÐ²Ð°ÐµÑ ÑÑеÑкÑ: Ñ 20.08.2026 поÑÑал VPN 134 оÑкÑÑÐ²Ð°ÐµÑ Ð¿ÐµÑепиÑÐºÑ Ð¿Ð¾ ÑÑÑлке из пиÑÑма `/vpn?ticket=<uuid>`, а ÑÑÐ¾Ñ Ð½Ð¾Ð¼ÐµÑ Ð´Ð°ÑÑ 135 доÑÑÑп к ÑазговоÑÑ. Ðо ÑÑаÑÐ¾Ð¼Ñ Ð´ÐµÑолÑÑ `no-referrer-when-downgrade` он ÑÐµÑ Ð°Ð» Ð±Ñ 136 Ñеликом в логи лÑбого внеÑнего ÑайÑа, на коÑоÑÑй Ñеловек ÐºÐ»Ð¸ÐºÐ½ÐµÑ Ñо ÑÑÑаниÑÑ. --> 137 <meta name="referrer" content="strict-origin-when-cross-origin"> 138 <base href="/"> 139 140 <!-- SEO: Primary meta tags --> 141 <title>ÐиÑкÑÑÑ â оÑкÑÑÑÑй жÑÑнал о кÑлÑÑÑÑе, наÑке и обÑеÑÑве</title> 142 <meta name="description" content="ÐезавиÑÐ¸Ð¼Ð°Ñ Ð¼ÐµÐ´Ð¸Ð°Ð¿Ð»Ð°ÑÑоÑма о кÑлÑÑÑÑе, наÑке, иÑкÑÑÑÑве и обÑеÑÑве Ñ Ð¾ÑкÑÑÑой гоÑизонÑалÑной ÑедакÑией."> 143 <meta name="keywords" content="жÑÑнал, ÐиÑкÑÑÑ, наÑка, кÑлÑÑÑÑа, иÑкÑÑÑÑво, обÑеÑÑво, ÑÑаÑÑи, инÑеÑвÑÑ, ÑепоÑÑажи, иÑÑледованиÑ, ÑообÑеÑÑво, лиÑеÑаÑÑÑа, мÑзÑка, видео, ÑоÑогÑаÑиÑ, живопиÑÑ, discours.io"> 144 145 <!-- SEO: Google Discover optimization - allow large previews for better visibility --> 146 <!-- max-image-preview:large - enables large image thumbnails in Discover --> 147 <!-- max-snippet:-1 - allows full text snippets (no character limit) --> 148 <!-- max-video-preview:-1 - allows full video previews (for expo/video content) --> 149 <meta name="robots" content="max-image-preview:large, max-snippet:-1, max-video-preview:-1"> 150 151 <!-- SEO: Open Graph --> 152 <!-- og:site_name â один из Ñигналов, по коÑоÑÑм Google вÑбиÑÐ°ÐµÑ Â«Ð¸Ð¼Ñ ÑайÑа» 153 в вÑдаÑе (ÑÑÑока над заголовком ÑниппеÑа). Сигналов ÑеÑÑÑе: WebSite.name 154 из JSON-LD, ÑÑÐ¾Ñ Ñег, <title> главной и заголовки. РаÑÑ Ð¾Ð´ÑÑÑÑ â 155 Google показÑÐ²Ð°ÐµÑ Ð´Ð¾Ð¼ÐµÐ½, и до 18.08.2026 Ñак и бÑло: «discours.io» 156 вмеÑÑо «ÐиÑкÑÑÑ», поÑÐ¾Ð¼Ñ ÑÑо здеÑÑ ÑÑоÑло лаÑинÑкое «Discours», а 157 WebSite.name (воÑкеÑ, json-ld.js) оÑдавал киÑиллиÑеÑкое «ÐиÑкÑÑÑ». 158 ÐенÑÑÑ ÑолÑко паÑой Ñ WebSite.name; лаÑинÑÐºÐ°Ñ ÑоÑма живÑÑ Ð² 159 alternateName и подÑÑавлÑеÑÑÑ ÑÑда ÑкÑипÑом Ð´Ð»Ñ /en ниже. 160 СÑоÑож â scripts/sitename-signals.test.js. --> 161 <meta property="og:site_name" content="ÐиÑкÑÑÑ"> 162 <meta property="og:title" content="ÐиÑкÑÑÑ â оÑкÑÑÑÑй жÑÑнал о кÑлÑÑÑÑе, наÑке и обÑеÑÑве"> 163 <meta property="og:description" content="ÐезавиÑÐ¸Ð¼Ð°Ñ Ð¼ÐµÐ´Ð¸Ð°Ð¿Ð»Ð°ÑÑоÑма о кÑлÑÑÑÑе, наÑке, иÑкÑÑÑÑве и обÑеÑÑве Ñ Ð¾ÑкÑÑÑой гоÑизонÑалÑной ÑедакÑией."> 164 <!-- Со ÑлÑÑем â Ñовно как rel=canonical ниже. ÐдÑÐµÑ Ð³Ð»Ð°Ð²Ð½Ð¾Ð¹ ÑигÑÑиÑÑÐµÑ Ð² 165 ÑеÑÑÑÑÑ Ð¼ÐµÑÑÐ°Ñ (canonical, og:url, Organization.url, WebSite.url в 166 воÑкеÑе); Google ÑвеÑÑÐµÑ Ð¸Ñ ÑÑÑокой, поÑÑÐ¾Ð¼Ñ Â«io» и «io/» Ð´Ð»Ñ Ð½ÐµÐ³Ð¾ 167 ÑазнÑе адÑеÑа. 168 Тег внÑÑÑи комменÑаÑÐ¸Ñ Ð½Ðµ пиÑем намеÑенно: grep и внеÑние ÑканеÑÑ Ð½Ðµ 169 оÑлиÑаÑÑ ÐµÐ³Ð¾ Ð¾Ñ Ð½Ð°ÑÑоÑÑего â на пÑевÑÑ PR #505 Ñакой комменÑаÑий Ñже 170 пÑоÑиÑалÑÑ ÐºÐ°Ðº вÑоÑой, пÑÑÑой canonical. --> 171 <meta property="og:url" content="https://discours.io/"> 172 <meta property="og:image" content="https://discours.io/images/opengraph-logo-black.png"> 173 <meta property="og:locale" content="ru_RU"> 174 <meta property="og:type" content="website"> 175 <meta property="fb:app_id" content="3004103336427826"> 176 <meta property="fb:pages" content="1430627470564984"> 177 178 <!-- SEO: Twitter Card (обновлÑеÑÑÑ Worker'ом Ð´Ð»Ñ ÑÑаÑей) --> 179 <meta name="twitter:card" content="summary"> 180 <meta name="twitter:title" content="ÐиÑкÑÑÑ â оÑкÑÑÑÑй жÑÑнал о кÑлÑÑÑÑе, наÑке и обÑеÑÑве"> 181 <meta name="twitter:description" content="ÐезавиÑÐ¸Ð¼Ð°Ñ Ð¼ÐµÐ´Ð¸Ð°Ð¿Ð»Ð°ÑÑоÑма о кÑлÑÑÑÑе, наÑке, иÑкÑÑÑÑве и обÑеÑÑве Ñ Ð¾ÑкÑÑÑой гоÑизонÑалÑной ÑедакÑией."> 182 <meta name="twitter:image" content="https://discours.io/images/opengraph-logo-black.png"> 183 184 <!-- SEO: RSS --> 185 <link rel="alternate" type="application/rss+xml" title="RSS ленÑа" href="/rss"> 186 <link rel="image_src" href="/images/opengraph-logo-black.png"> 187 188 <!-- ÐеÑолÑнÑе <title>/<meta> и заголовок RSS на английÑком Ð´Ð»Ñ /en. 189 ÐÑи ÑлеменÑÑ ÑиÑаÑÑÑÑ Ð±ÑаÑзеÑом и кÑаÑлеÑом до bootstrap Angular, 190 поÑÑÐ¾Ð¼Ñ ÑилÑÑÑ `t` здеÑÑ Ð½ÐµÐ¿Ñименим â он показал Ð±Ñ ÑÑÑÑÑ Ð¸Ð½ÑеÑполÑÑÐ¸Ñ 191 во вкладке. ÐодменÑем ÑÑÐ°Ð·Ñ Ð¿Ð¾Ñле Ñого, как ÑлеменÑÑ ÑазобÑÐ°Ð½Ñ Ð¿Ð°ÑÑеÑом; 192 lang Ñже вÑÑÑавлен ÑкÑипÑом вÑÑе. Ðа конкÑеÑной ÑÑÑаниÑе <title> и 193 опиÑÐ°Ð½Ð¸Ñ Ð²ÑÑ Ñавно пеÑезапиÑÐµÑ update-meta â ÑÑо знаÑение ÑолÑко Ð´Ð»Ñ 194 пеÑвой оÑÑиÑовки и Ð´Ð»Ñ ÑÑÑÐ°Ð½Ð¸Ñ Ð±ÐµÐ· ÑобÑÑвеннÑÑ Ð¼ÐµÑ. --> 195
195<script> 196 (function () { 197 if (document.documentElement.lang !== 'en') return; 198 199 var TITLE = 'Discours â an open journal on culture, science, and society'; 200 var DESC = 201 'An independent media platform on culture, science, art, and society with an open horizontal editorial board.'; 202 var KEYWORDS = 203 'journal, Discours, science, culture, art, society, articles, interviews, reports, research, community, literature, music, video, photography, painting, discours.io'; 204 205 document.title = TITLE; 206 var rss = document.querySelector('link[rel="alternate"][type="application/rss+xml"]'); 207 if (rss) rss.setAttribute('title', 'RSS feed'); 208 209 var META = [ 210 ['name', 'description', DESC], 211 ['name', 'keywords', KEYWORDS], 212 ['property', 'og:title', TITLE], 213 ['property', 'og:description', DESC], 214 ['property', 'og:locale', 'en_US'], 215 // ÐаÑинÑÐºÐ°Ñ ÑоÑма имени â ÑолÑко Ð´Ð»Ñ Ð°Ð½Ð³Ð»Ð¾ÑзÑÑной веÑÑии. Ð ÑÑÑÑкой 216 // вÑдаÑе Ð¸Ð¼Ñ ÑайÑа киÑиллиÑеÑкое, Ñм. комменÑаÑий Ñ og:site_name вÑÑе. 217 ['property', 'og:site_name', 'Discours'], 218 ['name', 'twitter:title', TITLE], 219 ['name', 'twitter:description', DESC], 220 ]; 221 for (var i = 0; i < META.length; i++) { 222 var el = document.querySelector('meta[' + META[i][0] + '="' + META[i][1] + '"]'); 223 if (el) el.setAttribute('content', META[i][2]); 224 } 225 })(); 226 </script>
226 227 228 <!-- SEO: Canonical (обновлÑеÑÑÑ Ð´Ð¸Ð½Ð°Ð¼Ð¸ÑеÑки ÑеÑез headCtrl.js) --> 229 <link rel="canonical" href="https://discours.io/"> 230 231 <!-- SEO: Organization (ÑÑаÑиÑно, на вÑÐµÑ ÑÑÑаниÑÐ°Ñ ). @id ÑвÑзÑÐ²Ð°ÐµÑ ÑÑда вÑе 232 Article/CollectionPage/Person (publisher, worksFor) â один Ñзел Knowledge 233 Graph вмеÑÑо конкÑÑиÑÑÑÑÐ¸Ñ Ð¾Ð¿Ð¸Ñаний. Ð Ð°Ð·Ð±Ð¾Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ Ð¿Ð¾Ð»Ñ Ð¸ замеÑÑ â 234 docs/seo-investigations/plan-2026-08-04.md, §2.7. ÐÐµÐ¹Ñ â 235 scripts/organization-jsonld.test.js (пÑÑи живÑе и не под ÑедиÑекÑом, 236 ÑкоÑÑ Ð½Ð° меÑÑе, ÑазмеÑÑ Ð»Ð¾Ð³Ð¾Ñипа ÑовпадаÑÑ Ñ Ñайлом). 237 238 ТÑи веÑи, без коÑоÑÑÑ Ð¿ÑÐ°Ð²ÐºÑ Ð»Ð¾Ð¼Ð°ÑÑ Ð¿Ð¾Ð²ÑоÑно: 239 1. logo коÑневой Organization и publisher.logo ÐÐУТРРArticle â ÑазнÑе 240 Ð¿Ð¾Ð»Ñ Ñ ÑазнÑми ÑÑебованиÑми. ÐдеÑÑ Ð³Ð¾Ð´Ð¸ÑÑÑ ÐºÐ²Ð°Ð´ÑаÑнÑй logo-small.png 241 (283Ã288); logo.png â OG-обложка 1200Ã630, не Ð¿Ð¾Ð´Ñ Ð¾Ð´Ð¸Ñ. РвоÑкеÑе и в 242 angular-json-ld.js publisher ÐÐÐ logo, и ÑÑо веÑно: они ÑÑÑлаÑÑÑÑ Ð¿Ð¾ @id. 243 2. Ðаждое ÑвойÑÑво довеÑÐ¸Ñ Ð²ÐµÐ´ÑÑ Ð½Ð° ÑÑÑаниÑÑ, коÑоÑÐ°Ñ ÐµÐ³Ð¾ дейÑÑвиÑелÑно 244 опиÑÑваеÑ. ÐÑавдоподобнÑй, но неÑелеванÑнÑй URL Ñ Ñже оÑÑÑÑÑÑÐ²Ð¸Ñ Ð¿Ð¾Ð»Ñ. 245 3. /help-us даÑÑ 301 на /help â ÑÑÑлка на ÑедиÑÐµÐºÑ Ð´ÐµÐ³ÑадиÑÑÐµÑ Ð² 246 «Page with redirect», поÑÑÐ¾Ð¼Ñ ownershipFundingInfo ÑказÑÐ²Ð°ÐµÑ Ð½Ð° /help. 247 4. Ð alternateName не должно бÑÑÑ Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð¿Ð¾Ð´Ð¾Ð±Ð½ÑÑ ÑоÑм. «Discours.io» 248 ÑÑоÑло здеÑÑ Ð´Ð¾ 18.08.2026 и ÑабоÑало пÑоÑив наÑ: Google вÑбиÑÐ°ÐµÑ 249 Ð¸Ð¼Ñ ÑайÑа из наÑÐ¸Ñ Ð¶Ðµ кандидаÑов, и Ð¼Ñ Ñами пÑедлагали ÐµÐ¼Ñ Ð°Ð´ÑÐµÑ 250 вмеÑÑо названиÑ. ÐÑÑаÑÑÑÑ Ð¾Ð´Ð½Ð° лаÑинÑÐºÐ°Ñ ÑоÑма â «Discours». --> 251
251<script type="application/ld+json"> 252 { 253 "@context": "https://schema.org", 254 "@type": "NewsMediaOrganization", 255 "@id": "https://discours.io/#organization", 256 "name": "ÐиÑкÑÑÑ", 257 "alternateName": ["Discours"], 258 "url": "https://discours.io/", 259 "logo": { 260 "@type": "ImageObject", 261 "url": "https://discours.io/images/logo-small.png", 262 "width": 283, 263 "height": 288 264 }, 265 "description": "ÐезавиÑимое издание Ñ Ð¾ÑкÑÑÑой гоÑизонÑалÑной ÑедакÑией о кÑлÑÑÑÑе, обÑеÑÑве, наÑке и иÑкÑÑÑÑве. С 2015 года более 1100 авÑоÑов из 80+ гоÑодов пÑбликÑÑÑ ÑÑаÑÑи, ÑÑÑе, лиÑеÑаÑÑÑÑ, мÑзÑкÑ, видео и изобÑажениÑ.", 266 "foundingDate": "2015", 267 "knowsLanguage": ["ru-RU", "en"], 268 "sameAs": [ 269 "https://t.me/discoursio", 270 "https://vk.com/discoursio", 271 "https://facebook.com/discoursio", 272 "https://twitter.com/discours_io", 273 "https://youtube.com/@discoursio", 274 "https://instagram.com/discours.io" 275 ], 276 "publishingPrinciples": "https://discours.io/dogma", 277 "ethicsPolicy": "https://discours.io/dogma", 278 "diversityPolicy": "https://discours.io/principles", 279 "correctionsPolicy": "https://discours.io/dogma", 280 "verificationFactCheckingPolicy": "https://discours.io/dogma", 281 "actionableFeedbackPolicy": "https://discours.io/guide#editing", 282 "missionCoveragePrioritiesPolicy": "https://discours.io/manifest", 283 "masthead": "https://discours.io/guide", 284 "ownershipFundingInfo": "https://discours.io/help", 285 "contactPoint": { 286 "@type": "ContactPoint", 287 "email": "[email protected]", 288 "contactType": "editorial", 289 "availableLanguage": "ru-RU" 290 } 291 } 292 </script>
292 293 294 <!-- Prerender status for Angular routes --> 295 <meta name="prerender-status-code" content="{{ $root.statusCode }}" ng-if="$root.statusCode"> 296 297 <!-- Favicon --> 298 <link rel="apple-touch-icon" sizes="180x180" href="/images/favicon/apple-touch-icon.png"> 299 <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon/favicon-32x32.png"> 300 <link rel="icon" type="image/png" sizes="192x192" href="/images/favicon/android-chrome-192x192.png"> 301 <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon/favicon-16x16.png"> 302 <link rel="manifest" href="/images/favicon/site.webmanifest"> 303 <link rel="mask-icon" href="/images/favicon/safari-pinned-tab.svg" color="#2e2e2e"> 304 <meta name="msapplication-TileColor" content="#ffffff"> 305 <meta name="msapplication-TileImage" content="/images/favicon/mstile-144x144.png"> 306 <meta name="theme-color" content="#ffffff"> 307 308 <!-- DNS prefetch only for media and GTM â preconnect removed per PageSpeed audit: 309 - media: пÑÑмÑе ÑÐ°Ð¹Ð»Ñ (аÑдио/докÑменÑÑ) Ñ media-домена нÑÐ¶Ð½Ñ Ð½Ðµ на каждой 310 ÑÑÑаниÑе, поÑÑÐ¾Ð¼Ñ Ð»Ñгкий dns-prefetch вмеÑÑо preconnect; домен завиÑÐ¸Ñ 311 Ð¾Ñ Ð·ÐµÑкалÑноÑÑи (MEDIA_DOMAIN из head-ÑкÑипÑа вÑÑе) 312 - GTM: loaded via requestIdleCallback after page load; preconnect expires (~10s) 313 before the request is made, wasting a TCP/TLS handshake --> 314
314<script> 315 (function () { 316 var l = document.createElement('link'); 317 l.rel = 'dns-prefetch'; 318 l.href = window.discours.MEDIA_DOMAIN; 319 document.head.appendChild(l); 320 })(); 321 </script>
321 322 <link rel="dns-prefetch" href="https://www.googletagmanager.com"> 323 <link rel="dns-prefetch" href="https://cloud.mave.digital"> 324 325 <!-- ÐÑиÑиÑеÑкие ÑÑиÑÑÑ: preload ÑазÑÑÐ²Ð°ÐµÑ ÑепоÑÐºÑ CSS â fonts (Lighthouse chain ~4.4s). 326 Early Hints в worker обеÑпеÑиваÑÑ Ð¿Ð°ÑаллелÑнÑÑ Ð·Ð°Ð³ÑÑзкÑ, но Lighthouse Ð¸Ñ Ð½Ðµ видиÑ. 327 Preload даÑÑ Ð³Ð°ÑанÑÐ¸Ñ Ð½Ð°Ñала загÑÑзки до паÑÑинга CSS, ÑÑкоÑÑÑ FCP на ~500ms. 328 Service Worker (CacheFirst) на повÑоÑнÑÑ Ð²Ð¸Ð·Ð¸ÑÐ°Ñ Ð¾Ð±ÑлÑÐ¶Ð¸Ð²Ð°ÐµÑ Ð¸Ð· кеÑа â preload 329 не вÑÐ·Ð¾Ð²ÐµÑ warning, Ñ.к. ÑÑиÑÑÑ ÑеалÑно иÑполÑзÑÑÑÑÑ Ð² @font-face. --> 330 <link rel="preload" href="/fonts/Muller/MullerRegular.woff2" as="font" type="font/woff2" crossorigin> 331 <link rel="preload" href="/fonts/Spectral/Spectral-Regular.woff2" as="font" type="font/woff2" crossorigin> 332 <!-- Muller Bold: заголовок H1 ÑÑаÑей и каÑÑоÑек â above-fold на каждой ÑÑÑаниÑе. 333 Preload ÑазÑÑÐ²Ð°ÐµÑ ÑепоÑÐºÑ HTMLâCSSâfont (PageSpeed: fonts ÑÑаÑÑовали на ~1Ñ Ð¿Ð¾Ð·Ð¶Ðµ CSS). --> 334 <link rel="preload" href="/fonts/Muller/Muller-Bold.woff2" as="font" type="font/woff2" crossorigin> 335 <!-- Muller Medium, Spectral Bold/Italic/SemiBold: не preload â secondary ÑÑиÑÑÑ, swap пÑиемлем; 336 каждÑй лиÑний preload конкÑÑиÑÑÐµÑ Ñ LCP hero-каÑÑинкой за полоÑÑ. --> 337 <!-- Iconfont: не preload â иконки не кÑиÑиÑÐ½Ñ Ð´Ð»Ñ LCP --> 338 <!-- FontAwesome: не preload â менÑ-иконки не нÑÐ¶Ð½Ñ Ð´Ð»Ñ Ð¿ÐµÑвого paint --> 339 340 <!-- footer.webp preload removed â loading overlay image is decorative, not LCP. 341 Preloading it competes with the hero article image (actual LCP element) for bandwidth. 342 The overlay image loads naturally from the <picture> element in body. --> 343 <!-- Logo preloads removed â small SVGs (~1-2 KB) that load fast without preload. 344 Each preload hint consumes a connection slot and competes with the LCP hero image. 345 On mobile with limited bandwidth, fewer preloads = faster LCP. --> 346 347 <!-- LCP hero-image preload инжекÑÐ¸Ñ Cloudflare Worker 348 (cloudflare/workers/src/rewriters.js: HomepageHeadHandler / ArticleHeadHandler). 349 Netlify edge functions ÑÐ´Ð°Ð»ÐµÐ½Ñ â инжекÑили дÑблиÑÑÑÑий preload Ñ 2560x, 350 коÑоÑой Ð½ÐµÑ Ð² Angular hero-preset, и давали warning в конÑоли. --> 351 352 <!-- CSS preload ÑбÑан: <link rel="stylesheet"> Ñже в <head> (ÑÑÑока ~268), 353 preload-scanner бÑаÑзеÑа Ð½Ð°Ñ Ð¾Ð´Ð¸Ñ ÐµÐ³Ð¾ мгновенно. ÐÑблиÑÑÑÑий preload вÑзÑвал 354 "preloaded but not used" warning пÑи ÑабоÑе Service Worker. --> 355 356 <!-- FontAwesome: Hide icons until font is loaded to prevent broken squares --> 357 <style>.fa,.fab,.far,.fas{visibility:hidden}.fa-loaded .fa,.fa-loaded .fab,.fa-loaded .far,.fa-loaded .fas{visibility:visible}</style> 358
358<script> 359 (function () { 360 if (document.fonts && document.fonts.load) { 361 Promise.all([ 362 document.fonts.load('900 1em "Font Awesome 5 Free"'), 363 document.fonts.load('400 1em "Font Awesome 5 Brands"'), 364 ]) 365 .then(function () { 366 document.documentElement.classList.add('fa-loaded'); 367 }) 368 .catch(function () { 369 document.documentElement.classList.add('fa-loaded'); 370 }); 371 setTimeout(function () { 372 document.documentElement.classList.add('fa-loaded'); 373 }, 3000); 374 } else { 375 document.documentElement.classList.add('fa-loaded'); 376 } 377 })(); 378 </script>
378 379 <!-- Pre-Angular auth detection: пÑедÑказÑваем compact header Ð´Ð»Ñ Ð·Ð°Ð»Ð¾Ð³Ð¸Ð½ÐµÐ½Ð½ÑÑ 380 полÑзоваÑелей на конÑенÑнÑÑ ÑÑÑаниÑÐ°Ñ (не главнаÑ). ÐÑедоÑвÑаÑÐ°ÐµÑ CLS-пÑÑжок 381 header'а Ñ 120px â 75px пÑи загÑÑзке Angular. УдалÑеÑÑÑ Ð² header.js. --> 382
382<script> 383 (function () { 384 try { 385 var ls = window.localStorage; 386 var p = window.location.pathname; 387 if ( 388 ls && 389 ls.getItem('ls.userId') && 390 ls.getItem('ls.authToken') && 391 p !== '/' && 392 p.indexOf('/edit/') !== 0 && 393 p.indexOf('/connect') !== 0 && 394 p.indexOf('/vpn') !== 0 && 395 p !== '/quotes' 396 ) { 397 document.documentElement.classList.add('auth-hint'); 398 } 399 // Ðа не-главнÑÑ ÑÑÑаниÑÐ°Ñ theme-menu не нÑжен â пÑÑÑем до Angular boot 400 // ÑÑÐ¾Ð±Ñ ~45px пÑÑÑого пÑоÑÑÑанÑÑва не иÑÑезало пÑи ng-show=false 401 if (p !== '/') { 402 document.documentElement.classList.add('hide-theme-menu'); 403 } 404 // Restore cached compact header height â ÑÑÑÑанÑÐµÑ 5px jump 405 // на ÑледÑÑÑÐ¸Ñ Ð·Ð°Ð³ÑÑÐ·ÐºÐ°Ñ Ð¿Ð¾Ñле пеÑвого измеÑÐµÐ½Ð¸Ñ Ð² header.js 406 var rawH = ls && ls.getItem('compactHeaderH'); 407 if (rawH) { 408 try { 409 var parsed = JSON.parse(rawH); 410 // ÐнвалидиÑÑем кÑÑ Ð¿Ñи Ñмене веÑÑии (deploy) или ÑеÑез 2 Ð´Ð½Ñ 411 var isVersionMatch = !parsed.v || parsed.v === '12.17.14'; 412 var isFresh = Date.now() - parsed.t < 172800000; // 48 ÑаÑов 413 var isValidRange = parsed.h >= 40 && parsed.h <= 200; 414 if (parsed && parsed.h && isVersionMatch && isFresh && isValidRange) { 415 document.documentElement.style.setProperty('--compact-header-h', parsed.h + 'px'); 416 } 417 } catch (_e) { 418 // Legacy ÑоÑÐ¼Ð°Ñ (plain number) â пÑименÑем и пеÑезапиÑем 419 if (/^\d+$/.test(rawH)) { 420 document.documentElement.style.setProperty('--compact-header-h', rawH + 'px'); 421 } 422 } 423 } 424 } catch (e) { 425 /* localStorage Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð½ÐµÐ´Ð¾ÑÑÑпен в private mode */ 426 } 427 })(); 428 </script>
428 429 430 <!-- JS preload hints REMOVED to free bandwidth for LCP hero image. 431 With `defer`, scripts download in parallel with HTML parsing automatically. 432 Preloading them steals bandwidth from the hero image on mobile connections, 433 delaying LCP by 200-500ms. The browser's priority scheduler handles defer 434 scripts well without preload hints. --> 435 436 <!-- Print styles: loaded only when printing (non-render-blocking) --> 437 <link rel="stylesheet" href="/styles/print.css" media="print"> 438 439 <!-- CSS: Non-critical vendor styles (async loading to avoid render blocking) --> 440 <!-- CSP: onload-обÑабоÑÑики Ð·Ð°Ð¼ÐµÐ½ÐµÐ½Ñ Ð½Ð° ÑкÑÐ¸Ð¿Ñ Ð½Ð¸Ð¶Ðµ (async-css клаÑÑ) --> 441 <!-- Slick, rn-carousel и rzslider â один Ñ ÐµÑиÑованнÑй Ñайл вмеÑÑо ÑÑÑÑ 442 <link> на /node_modules/... (пÑÑи без Ñ ÐµÑа нелÑÐ·Ñ ÐºÐµÑиÑоваÑÑ Ð½Ð°Ð´Ð¾Ð»Ð³Ð¾). 443 ÐÑÑоÑник: app/styles/vendor-async.scss. ÐейÑ: vendor-css-pipeline.test.js. --> 444 <link rel="stylesheet" href="/styles/vendor-async-805ff4f7.css" media="print" class="async-css"> 445 <!-- animate.css removed - only fadeIn was used, now in animation.scss (saves 71KB) --> 446 <!-- Notifications: base colors + layout ÑепеÑÑ Ð² partials/components/ui-notification.scss; 447 ÑÑаÑÑй vendor CSS ÑдалÑн вмеÑÑе Ñ angular-ui-notification npm package. --> 448 <!-- Loading bar: styles already in main.css via angular-loading-bar.scss --> 449 <!-- Font Awesome: now bundled as subset in main.css (~5KB vs ~40KB), saves 35KB --> 450 451 <!-- rzslider пеÑÐµÐµÑ Ð°Ð» в vendor-async вÑÑе (бÑл оÑделÑнÑм запÑоÑом). --> 452 <!-- CodeMirror CSS ÑбÑан оÑÑÑда: 10.9 ÐРна каждой ÑÑÑаниÑе Ñади ÑедакÑоÑа, 453 коÑоÑÑй оÑкÑÑваÑÑ ÐµÐ´Ð¸Ð½Ð¸ÑÑ. codemirror.css гÑÑÐ·Ð¸Ñ loadEditorScripts.js по 454 ÑÑебованиÑ; monokai.css не нÑжен вовÑе â html-editor-modal.js инжекÑÐ¸Ñ 455 ÑÐµÐ¼Ñ Ñам, но оÑклÑÑалÑÑ, Ð½Ð°Ð¹Ð´Ñ ÑÑÑ link[href*="monokai"]. --> 456 <!-- videogular ÑдалÑн в Phase 5 (2026-04-19); native HTML5 <video> не ÑÑебÑÐµÑ 457 дополниÑелÑного CSS â бÑаÑÐ·ÐµÑ Ð´Ð°ÑÑ ÑÑандаÑÑнÑе controls и layout. --> 458 459 <!-- ÐкÑиваÑÐ¸Ñ async CSS: заменÑÐµÑ inline onload-обÑабоÑÑики Ð´Ð»Ñ ÑовмеÑÑимоÑÑи Ñ nonce-based CSP. 460 window.__armAsyncCss пеÑеиÑполÑзÑеÑÑÑ ÑкÑипÑом secondary.css ниже â пÑи пÑавке 461 паÑÑеÑна (retry, ÑелемеÑÑиÑ, CSP-nonce) поведение менÑеÑÑÑ Ð² одном меÑÑе. --> 462
462<script> 463 (function () { 464 window.__armAsyncCss = function (link) { 465 if (link.sheet) { 466 link.media = 'all'; 467 return; 468 } 469 var activate = function () { 470 link.onload = link.onerror = null; 471 link.media = 'all'; 472 }; 473 link.onload = activate; 474 // onerror: еÑли CSS не загÑÑзилÑÑ â вÑÑ Ñавно пеÑеклÑÑаем media, 475 // ÑÑÐ¾Ð±Ñ Ð±ÑаÑÐ·ÐµÑ Ð¼Ð¾Ð³ показаÑÑ fallback и не завиÑал на media="print" 476 link.onerror = activate; 477 }; 478 var links = document.querySelectorAll('link.async-css'); 479 for (var i = 0; i < links.length; i++) { 480 window.__armAsyncCss(links[i]); 481 } 482 })(); 483 </script>
483 484 <!-- noscript CSS fallback ÑдалÑн: пÑиложение â Angular SPA, без JS не ÑабоÑаеÑ. 485 Ðлок добавлÑл ~800 Ð±Ð°Ð¹Ñ Ð² HTML без полÑзÑ. --> 486 487 <!-- Critical inline styles for first paint (CLS prevention). 488 ÐÐÐÐÐ: ÑÑÐ¾Ñ <style> блок ÐÐÐÐÐРбÑÑÑ ÐРвнеÑÐ½Ð¸Ñ <link> ÑÐ°Ð±Ð»Ð¸Ñ ÑÑилей! 489 ÐÑиÑина: CSS cascade â пÑи одинаковой ÑпеÑиÑиÑноÑÑи Ð¿Ð¾Ð±ÐµÐ¶Ð´Ð°ÐµÑ Ð¿Ð¾Ð·Ð´Ð½ÐµÐµ пÑавило. 490 ÐÑли inline-ÑÑили (напÑ. .tw\:w-full) идÑÑ ÐÐСÐÐ tailwind.css, они пеÑебиваÑÑ 491 responsive-ваÑианÑÑ (tw:md:w-7/12), Ð»Ð¾Ð¼Ð°Ñ grid-ÑаÑÐºÐ»Ð°Ð´ÐºÑ Ð½Ð° деÑкÑопе. 492 Preload scanner бÑаÑзеÑа найдÑÑ <link> Ñеги даже поÑле <style> блока. --> 493 <style>*,::after,::before{box-sizing:border-box}html{-webkit-text-size-adjust:100%;overflow-y:scroll;scrollbar-gutter:stable}@media (max-width:767px){html{overflow-y:auto;scrollbar-gutter:auto;overflow-x:hidden}}body{margin:0;background-color:#fff}.ng-cloak,[data-ng-cloak],[ng-cloak]{display:none!important}.ng-hide:not(.ng-hide-animate){display:none!important}html.has-ssr-content .loading-footer-overlay{display:none!important}html.has-ssr-content .main-content{min-height:auto}html.has-ssr-content .expo-item-title-image,html.has-ssr-content .open-post-title-image{opacity:1!important;transition:none!important}html.ssr-just-swapped img{opacity:1!important;transition:none!important}html.ssr-just-swapped .loading-footer-overlay{display:none!important}#ssr-about.ssr-hiding,#ssr-article.ssr-hiding,#ssr-author.ssr-hiding,#ssr-homepage.ssr-hiding,#ssr-tag.ssr-hiding{display:none!important}#ssr-about:not(.ssr-hiding)~ng-view,#ssr-article:not(.ssr-hiding)~ng-view,#ssr-author:not(.ssr-hiding)~ng-view,#ssr-homepage:not(.ssr-hiding)~ng-view,#ssr-tag:not(.ssr-hiding)~ng-view{display:none!important}html.has-ssr-content .author-post .article-image img{opacity:1!important;transition:none!important}#ssr-h1{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);border:0;white-space:nowrap}#ssr-tag .ssr-tag-header{text-align:center;margin-bottom:20px}#ssr-tag .ssr-tag-title{font-family:Spectral,Georgia,'Times New Roman',Times,serif;font-weight:400;font-size:42px;font-style:italic;text-align:center;margin:50px 0 16px;color:#000}@media (max-width:662px){#ssr-tag .ssr-tag-title{font-size:30px}}#ssr-tag .ssr-tag-count{font-weight:400;font-family:Muller,'Helvetica Neue',Helvetica,Arial,sans-serif;font-style:normal;font-size:14px;vertical-align:super}#ssr-tag .ssr-tag-desc{margin:0 auto 16px;max-width:700px;text-align:center}@media (max-width:767px){#ssr-tag .ssr-tag-desc{margin:0 5px 16px}}#ssr-tag .ssr-tag-stats{display:flex;align-items:center;justify-content:center;gap:30px;flex-wrap:wrap;min-height:40px}#ssr-tag .ssr-tag-stat{font-family:Muller,'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:14px;font-weight:500;color:#000;white-space:nowrap}#ssr-tag .ssr-tag-actions{display:flex;justify-content:center;align-items:center;gap:12px;margin-top:16px}#ssr-tag .ssr-tag-write{display:flex;justify-content:center;align-items:center;font-family:Muller,'Helvetica Neue',Helvetica,Arial,sans-serif;font-weight:500;font-size:15px;letter-spacing:-.01em;height:40px;padding:0 20px;border-radius:12px;background:#f7f7f8;color:#000;text-decoration:none;white-space:nowrap;box-sizing:border-box}#ssr-tag .ssr-tag-follow-reserve{display:inline-block;width:150px;height:40px}#ssr-tag .ssr-filter-bar{display:flex;align-items:center;justify-content:space-between;max-width:1280px;margin:20px auto 0;padding:12px 20px 16px;box-sizing:border-box;width:100%;background:#fff}@media (max-width:767px){#ssr-tag .ssr-filter-bar{display:none}}#ssr-tag .ssr-filter-tabs{display:flex;align-items:center;gap:24px}#ssr-tag .ssr-filter-tab{font-family:Muller,'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;color:#000;text-decoration:none;padding:4px 0;line-height:inherit}#ssr-tag .ssr-filter-tab--active{font-weight:700}#ssr-tag .ssr-filter-dropdowns{display:flex;align-items:center;gap:16px}#ssr-tag .ssr-filter-dropdown{font-family:Muller,'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:14px;color:#000;white-space:nowrap}#ssr-tag .ssr-filter-dropdown::after{content:' \25BE';opacity:.6}#ssr-tag .ssr-grid{display:flex;flex-wrap:wrap;margin-bottom:-30px}#ssr-tag .ssr-card{box-sizing:border-box;display:block;margin-bottom:50px;padding:0 15px;width:100%}@media (min-width:660px){#ssr-tag .ssr-card:nth-child(5n-2),#ssr-tag .ssr-card:nth-child(5n-3),#ssr-tag .ssr-card:nth-child(5n-4){width:33.33%}#ssr-tag .ssr-card:nth-child(5n),#ssr-tag .ssr-card:nth-child(5n-1){width:50%}}#ssr-tag .ssr-card__img-link{display:block;position:relative;overflow:hidden;margin-bottom:10px;aspect-ratio:100/65;background-color:#f6f6f6;text-decoration:none}@supports not (aspect-ratio:1){#ssr-tag .ssr-card__img-link{height:0;padding-bottom:65%}}#ssr-tag .ssr-card__img-link img{position:absolute;width:100%;height:100%;left:0;top:0;object-fit:cover;object-position:center center}#ssr-tag .ssr-card__category{font-size:12px;font-weight:700;letter-spacing:.15em;text-transform:uppercase;display:inline-block;line-height:1}#ssr-tag .ssr-card__title{font-family:Muller,'Helvetica Neue',Helvetica,Arial,sans-serif;font-weight:700;font-size:24px;line-height:1.2;margin:.4em 0 0}#ssr-tag .ssr-card__title a{color:inherit;text-decoration:none}#ssr-tag .ssr-card__author{font-family:Spectral,Georgia,'Times New Roman',Times,serif;font-size:16px;font-style:italic;margin-top:5px;
493display:block}#ssr-tag .ssr-loadmore-wrap{width:100%;padding:0 15px;box-sizing:border-box}@media (min-width:1200px){#ssr-tag .ssr-loadmore-inner{width:66.666%;margin-left:16.666%}}#ssr-tag .ssr-loadmore-inner{margin-top:40px;margin-bottom:20px;text-align:center}#ssr-tag .ssr-loadmore{display:inline-block;font-family:Spectral,Georgia,'Times New Roman',Times,serif;font-weight:400;font-size:26px;font-style:italic;color:#000;background:#fff;text-decoration:none;padding:6px 16px}@media (max-width:767px){#ssr-tag .ssr-loadmore{font-size:24px}}header{min-height:120px;margin-bottom:10px}header.is-short{min-height:90px}header.is-logo-only{min-height:60px;margin-bottom:0}header.is-force-compact{min-height:calc(var(--compact-header-h,65px) + 10px);margin-bottom:10px}html.auth-hint header:not(.is-logo-only){min-height:calc(var(--compact-header-h,65px) + 10px)!important}@media (prefers-reduced-motion:reduce){header{transition:none}}.header{position:fixed;top:0;width:100%;z-index:1002;background:#fff}@media (max-width:767px){header{min-height:80px}header.is-short{min-height:60px}header.is-logo-only{min-height:55px}}.skip-to-content{position:absolute;left:-9999px;top:auto;z-index:1100;padding:8px 16px;background:#2638d9;color:#fff;font-family:sans-serif;font-size:14px;text-decoration:none;border-radius:0 0 4px 0}.skip-to-content:focus{left:0;top:0}.main-content{display:block;min-height:calc(100vh - 490px)}html.defer-footer-top .main-content{min-height:100vh}html.defer-footer-top .theme-menu-container,html.has-ssr-content .theme-menu-container,html.hide-theme-menu .theme-menu-container{display:none!important}@media (min-width:768px){html.has-ssr-content.has-ssr-tag .theme-menu-wrapper{display:block!important;min-height:25px;margin-top:-50px;margin-bottom:20px}}@media (min-width:992px){html.has-ssr-content.has-ssr-tag .theme-menu-wrapper{margin-top:-15px}}@media (min-width:768px){html.auth-hint.has-ssr-content.has-ssr-tag .theme-menu-wrapper,html.has-ssr-content.has-ssr-tag header.is-force-compact~.theme-menu-wrapper{margin-top:0}}html.is-editor-page .footer-wrapper{display:none!important}.loading-footer-overlay{position:fixed;top:0;right:0;bottom:0;left:0;inset:0;z-index:999;display:flex;flex-direction:column;justify-content:flex-end;background:#fff;overflow:hidden}.loading-footer-overlay__content,.loading-footer-overlay__image,.loading-footer-overlay__punchline{opacity:0;transition:opacity .15s ease-out}.loading-footer-overlay.footer-loaded .loading-footer-overlay__content,.loading-footer-overlay.footer-loaded .loading-footer-overlay__image,.loading-footer-overlay.footer-loaded .loading-footer-overlay__punchline{opacity:1}.instant-hero-article__img{display:block;width:100%;height:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;z-index:2;color:transparent}.is-landscape .instant-hero-article__img{object-fit:cover;object-position:center}.is-portrait .instant-hero-article__img{object-fit:cover;object-position:center top}.expo-item-title-image-wrapper.is-image-loaded .instant-hero-article__img,.open-post-title-image-wrapper.is-image-loaded .instant-hero-article__img{display:none}.article--main-cover.is-image-loaded .instant-hero-homepage__img{display:none}.article{display:block;position:relative;width:100%;padding:0 15px;box-sizing:border-box}@media (max-width:767px){.article{padding:0}.article .article__inner{margin:0 15px}}.article.article--main-cover{overflow:hidden}@media (max-width:767px){.article.article--main-cover{margin-bottom:40px}}@media (min-width:992px){.article.article--main-cover{width:66.66%}}.article--main-cover .article__inner{position:relative}.article--main-cover .article__image-wrapper{display:block;position:relative;overflow:hidden;margin-bottom:0;padding-bottom:80%;height:0;background-color:#f6f6f6}@media (min-width:660px){.article--main-cover .article__image-wrapper{padding-bottom:54%}}.article--main-cover .article__image-inner{position:absolute;top:-2px;left:-2px;width:calc(100% + 4px);height:calc(100% + 4px);min-width:calc(100% + 4px);min-height:calc(100% + 4px)}.article--main-cover .article__image{position:absolute;top:-2px;left:-2px;width:calc(100% + 4px);height:calc(100% + 4px);min-width:calc(100% + 4px);min-height:calc(100% + 4px);object-fit:cover;object-position:center;color:transparent}@keyframes lcpShimmer{0%{background-position:-468px 0}100%{background-position:468px 0}}.article.article--main-cover:not(.is-image-loaded) .article__image-wrapper{background:linear-gradient(to right,#f6f6f6 0,#ececec 20%,#f6f6f6 40%,#f6f6f6 100%);background-size:800px 104px;animation:lcpShimmer 2.25s infinite ease-in-out}@media (prefers-reduced-motion:reduce){.article.article--main-cover:not(.is-image-loaded) .article__image-wrapper{animation:none}}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}@media (min-width:1300px){.container{width:1280px}}.tw\:flex{display:flex}.tw\:flex-wrap{flex-wrap:wrap}.tw\:flow-root{display:flow-root}.tw\:relative{position:relative}.tw\:block{display:block}.tw\:hidden{display:none}.tw\:w-full{width:100%}.tw\:w-3\/12{width:25%}.tw\:w-9\/12{width:75%}.tw\:h-auto{height:auto}.tw\:max-w-full{max-width:100%}.tw\:items-stretch{align-items:stretch}.tw\:mx-auto{margin-left:auto;margin-right:auto}.ds-container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.ds-container{width:750px}}@media (min-width:992px){.ds-container{width:970px}}@media (min-width:1200px){.ds-container{width:1170px}}@media (min-width:1300px){.ds-container{width:1280px}}.ds-content-col{padding-left:15px;padding-right:15px;min-width:0;flex-shrink:0;width:100%}@media (min-width:992px){.ds-content-col{width:58.333%;margin-left:16.667%}}.expo-item .audio-wrapper{position:relative;padding-top:30px}.expo-item .audio-container{display:flex;align-items:stretch;justify-content:space-between;gap:20px}.expo-item .audio-player-list{flex:1 50%;order:2;min-width:0}.expo-item .audio-img{order:3;margin-right:0;align-self:flex-start;flex:0 0 auto;width:320px;aspect-ratio:1/1;background-color:#e0e0e0}.expo-item .audio-img .ligthbox-img{display:block;width:100%;height:auto;margin-left:0}.ssr-video-carousel-frame{width:100%}.expo-video-carousel-frame{display:grid;grid-template-columns:minmax(0,1fr)}.expo-video-carousel-frame::before{content:'';grid-area:1/1;width:100%;aspect-ratio:16/9}.expo-video-carousel-frame>ul[rn-carousel]{grid-area:1/1;min-width:0}@media (max-width:991px){.expo-video-carousel-frame::before{width:calc(100% + 2 * var(--grid-gutter-half,15px));margin-left:calc(-1 * var(--grid-gutter-half,15px))}.ssr-video-carousel-frame{margin-left:calc(-1 * var(--grid-gutter-half,15px));margin-right:calc(-1 * var(--grid-gutter-half,15px));width:calc(100% + 2 * var(--grid-gutter-half,15px))}}@media (max-width:767px){.expo-item .audio-wrapper{padding-top:0}.expo-item .audio-container{flex-wrap:wrap;gap:10px}}@media (max-width:669px){.expo-item .audio-img{order:1;width:100%}}.tw\:row-flex{display:flex;flex-wrap:wrap;margin-left:-15px;margin-right:-15px}.tw\:gutter-half{padding-left:15px;padding-right:15px}.tw\:mt-\[30px\]{margin-top:30px}.tw\:mt-2\.5{margin-top:10px}.tw\:mt-20{margin-top:80px}.tw\:mt-section{margin-top:30px}.tw\:mb-5{margin-bottom:20px}.tw\:mb-section{margin-bottom:30px}.tw\:mb-section-lg{margin-bottom:50px}.tw\:pt-content-top{padding-top:100px}.tw\:sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);border:0;white-space:nowrap}@media (max-width:767.98px){.tw\:max-sm\:hidden{display:none!important}.tw\:max-sm\:inline{display:inline!important}}@media (min-width:768px){.tw\:sm\:hidden{display:none!important}.tw\:sm\:w-3\/12{width:25%}.tw\:sm\:w-4\/12{width:33.333333%}.tw\:sm\:w-5\/12{width:41.666667%}.tw\:sm\:w-6\/12{width:50%}.tw\:sm\:w-7\/12{width:58.333333%}.tw\:sm\:w-8\/12{width:66.666667%}.tw\:sm\:w-9\/12{width:75%}.tw\:sm\:w-10\/12{width:83.333333%}.tw\:sm\:w-11\/12{width:91.666667%}.tw\:sm\:ml-offset-2{margin-left:16.666667%}.tw\:sm\:ml-\[8\.333\%\]{margin-left:8.333%}.tw\:sm\:ml-\[16\.667\%\]{margin-left:16.667%}}@media (max-width:991.98px){.tw\:max-md\:hidden{display:none!important}}@media (min-width:992px){.tw\:md\:hidden{display:none!important}.tw\:md\:flex{display:flex}.tw\:md\:w-1\/2{width:50%}.tw\:md\:w-2\/12{width:16.666667%}.tw\:md\:w-3\/12{width:25%}.tw\:md\:w-4\/12{width:33.333333%}.tw\:md\:w-5\/12{width:41.666667%}.tw\:md\:w-6\/12{width:50%}.tw\:md\:w-7\/12{width:58.333333%}.tw\:md\:w-8\/12{width:66.666667%}.tw\:md\:w-10\/12{width:83.333333%}.tw\:md\:ml-offset-1{margin-left:8.333333%}.tw\:md\:ml-offset-2{margin-left:16.666667%}.tw\:md\:ml-offset-3{margin-left:25%}.tw\:md\:ml-offset-5{margin-left:41.666667%}.tw\:md\:ml-\[16\.667\%\]{margin-left:16.667%}.tw\:md\:mx-auto{margin-left:auto;margin-right:auto}}@media (min-width:1200px){.tw\:lg\:w-3\/12{width:25%}.tw\:lg\:w-5\/12{width:41.666667%}.tw\:lg\:w-7\/12{width:58.333333%}.tw\:lg\:w-8\/12{width:66.666667%}.tw\:lg\:w-9\/12{width:75%}.tw\:lg\:w-10\/12{width:83.333333%}.tw\:lg\:ml-offset-2{margin-left:16.666667%}.tw\:lg\:ml-offset-6{margin-left:50%}.tw\:lg\:inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.editor-loading-skeleton__body-col{width:100%!important;margin-left:0!important}}@media (min-width:992px) and (max-width:1199px){.editor-loading-skeleton__body-col{width:83.333%!important;margin-left:8.333%!important}}.open-post{display:flow-root}.expo-item .title,.open-post .title{font-family:Muller,'Helvetica Neue',Helvetica,Arial,sans-serif;font-weight:700;font-size:40px;line-height:44px;margin-bottom:25px;margin-left:0}@media (min-width:1200px){.expo-item .title,.open-post .title{margin-left:-130px}}@media screen and (max-width:767.98px){.expo-item .title,.open-post .title{font-size:25px;line-height:32px}}.expo-item .author-category,.open-post .author-category{position:relative;display:flex;justify-content:space-between}.expo-item .author-category::after,.open-post .author-category::after{position:absolute;z-index:-1;right:0;bottom:0;left:0;width:100%;height:7px;content:' ';background-color:#000}@media (max-width:767.98px){.expo-item .author-category::after,.open-post .author-category::after{height:0}.expo-item .author-category,.open-post .author-category{display:block}}.expo-item .author-category .category,.open-post .author-category .category{position:relative;bottom:8px}.expo-item .author-category .category a,.open-post .author-category .category a{font-weight:700;text-transform:lowercase;text-decoration:none;color:#000}.expo-item .author-category .author,.open-post .author-category .author{font-family:Spectral,Georgia,'Times New Roman',Times,serif;position:relative;padding-right:15px;padding-left:15px;height:18px;line-height:12px;bottom:-7px;left:-30px;border-right:7px solid #000;border-left:7px solid #000;vertical-align:bottom;background-color:#fff;margin-left:auto}.expo-item .author-category .author a,.open-post .author-category .author a{text-decoration:none;color:#000}@media (max-width:767px){.expo-item .author-category .category,.open-post .author-category .category{bottom:0}.expo-item .author-category .category a,.open-post .author-category .category a{color:#2638d9}.expo-item .author-category .author,.open-post .author-category .author{font-size:20px;margin-left:0;height:auto;border:none;padding:5px 0 0 0;left:0;line-height:1.1}}.expo-item .expo-item-title-image-wrapper,.open-post .open-post-title-image-wrapper{position:relative;width:calc(100% + 30px);margin-left:-15px;margin-bottom:25px;overflow:hidden;aspect-ratio:var(--cover-ratio,16 / 9);background:#f0f0f0}@supports not (aspect-ratio:1){.expo-item .expo-item-title-image-wrapper,.open-post .open-post-title-image-wrapper{height:0;padding-bottom:56.25%}}@media (min-width:768px){.expo-item .expo-item-title-image-wrapper,.open-post .open-post-title-image-wrapper{width:calc(100% + 20px);margin-left:-10px}}.expo-item .expo-item-title-image,.open-post .open-post-title-image{display:block;width:100%;height:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;color:transparent}.expo-item-title-image-wrapper.is-image-loaded .expo-item-title-image,.open-post-title-image-wrapper.is-image-loaded .open-post-title-image{opacity:1}.post-body,.post-body p{font-family:Spectral,Georgia,'Times New Roman',Times,serif;font-size:20px;line-height:27px;letter-spacing:-.01em}.post-body{contain:layout style}@media (max-width:500px){.post-body,.post-body p{font-size:18px;line-height:24px}}
493.expo-item .image-description,.open-post .image-description{font-size:13px;contain:layout style;min-height:1.2em}.footer-post hr{border-color:#000}.footer-post .post-meta-footer{display:flex;flex-wrap:wrap;align-items:center;gap:15px;font-family:Spectral,Georgia,'Times New Roman',Times,serif;margin:10px 0}.footer-post .post-meta-footer__author{display:flex;align-items:center;gap:15px}.footer-post .author-title a,.footer-post .author-title span{color:#000;font-weight:600;white-space:nowrap}.footer-post .date-time{flex-shrink:0;white-space:nowrap;margin:0 10px}@media (max-width:767.98px){.footer-post .post-meta-footer__author{width:100%;justify-content:space-between}.footer-post .date-time{margin:0}}</style> 494 495 <!-- CSS: Main styles (render-blocking). 496 ÐагÑÑжаÑÑÑÑ ÐÐСÐÐ inline <style> â внеÑние ÑÑили пеÑебиваÑÑ inline пÑи одинаковой 497 ÑпеÑиÑиÑноÑÑи, обеÑпеÑÐ¸Ð²Ð°Ñ ÐºÐ¾ÑÑекÑнÑÑ ÑабоÑÑ responsive-ваÑианÑов (tw:md:w-7/12 и дÑ.). 498 Preload scanner бÑаÑзеÑа наÑнÑÑ Ð·Ð°Ð³ÑÑÐ·ÐºÑ Ð´Ð¾ полного паÑÑинга inline-блока. 499 Early Hints в Link header (worker) обеÑпеÑиваÑÑ Ð¿ÑедзагÑÑÐ·ÐºÑ Ð½Ð° пеÑвом визиÑе. --> 500 <link rel="stylesheet" href="/styles/main-ced8eafc.css"> 501 <!-- Tailwind CSS v4: дизайн-ÑиÑÑема, поÑле main.css (ÑÑилиÑÑ Ð¿ÐµÑебиваÑÑ legacy) --> 502 <!-- Production: инлайн (~6 KB) ÑбиÑÐ°ÐµÑ render-blocking запÑоÑ. Dev: <link> Ð´Ð»Ñ hot reload --> 503 504 <style>/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,::backdrop,:after,:before{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial}}}:host,:root{--tw-color-gray-600:#696969;--tw-spacing:.25rem;--tw-text-xs:.75rem;--tw-text-xs--line-height:calc(1 / .75);--tw-text-sm:.875rem;--tw-text-sm--line-height:calc(1.25 / .875);--tw-text-lg:1.125rem;--tw-text-lg--line-height:calc(1.75 / 1.125);--tw-text-xl:1.25rem;--tw-text-xl--line-height:calc(1.75 / 1.25);--tw-font-weight-light:300;--tw-font-weight-bold:700;--tw-leading-relaxed:1.4;--tw-radius-md:4px;--tw-radius-lg:6px;--tw-radius-2xl:12px;--tw-color-error:#c62828;--tw-color-error-bg:#fce4e4;--tw-color-error-border:#e0b4b4;--tw-color-success:#2e7d32;--tw-color-muted:#555;--tw-spacing-section:30px;--tw-spacing-content-top:100px;--tw-spacing-section-lg:50px;--tw-spacing-section-xl:60px}.tw\:invisible{visibility:hidden}.tw\:sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.tw\:relative{position:relative}.tw\:m-0{margin:calc(var(--tw-spacing) * 0)}.tw\:mx-auto{margin-inline:auto}.tw\:-mt-2\.5{margin-top:calc(var(--tw-spacing) * -2.5)}.tw\:-mt-\[5px\]{margin-top:-5px}.tw\:-mt-section{margin-top:calc(var(--tw-spacing-section) * -1)}.tw\:mt-2{margin-top:calc(var(--tw-spacing) * 2)}.tw\:mt-2\.5{margin-top:calc(var(--tw-spacing) * 2.5)}.tw\:mt-4{margin-top:calc(var(--tw-spacing) * 4)}.tw\:mt-5{margin-top:calc(var(--tw-spacing) * 5)}.tw\:mt-6{margin-top:calc(var(--tw-spacing) * 6)}.tw\:mt-8{margin-top:calc(var(--tw-spacing) * 8)}.tw\:mt-10{margin-top:calc(var(--tw-spacing) * 10)}.tw\:mt-20{margin-top:calc(var(--tw-spacing) * 20)}.tw\:mt-\[30px\]{margin-top:30px}.tw\:mt-content-top{margin-top:var(--tw-spacing-content-top)}.tw\:mt-section{margin-top:var(--tw-spacing-section)}.tw\:mt-section-lg{margin-top:var(--tw-spacing-section-lg)}.tw\:mt-section-xl{margin-top:var(--tw-spacing-section-xl)}.tw\:row-flex{margin-left:calc(var(--grid-gutter-half,15px) * -1);margin-right:calc(var(--grid-gutter-half,15px) * -1);flex-wrap:wrap;align-items:flex-start;display:flex}.tw\:mr-section{margin-right:var(--tw-spacing-section)}.tw\:mb-0{margin-bottom:calc(var(--tw-spacing) * 0)}.tw\:mb-2\.5{margin-bottom:calc(var(--tw-spacing) * 2.5)}.tw\:mb-3{margin-bottom:calc(var(--tw-spacing) * 3)}.tw\:mb-4{margin-bottom:calc(var(--tw-spacing) * 4)}.tw\:mb-5{margin-bottom:calc(var(--tw-spacing) * 5)}.tw\:mb-6{margin-bottom:calc(var(--tw-spacing) * 6)}.tw\:mb-10{margin-bottom:calc(var(--tw-spacing) * 10)}.tw\:mb-section{margin-bottom:var(--tw-spacing-section)}.tw\:mb-section-lg{margin-bottom:var(--tw-spacing-section-lg)}.tw\:block{display:block}.tw\:flex{display:flex}.tw\:flow-root{display:flow-root}.tw\:hidden{display:none}.tw\:h-3\.5{height:calc(var(--tw-spacing) * 3.5)}.tw\:h-5{height:calc(var(--tw-spacing) * 5)}.tw\:h-9{height:calc(var(--tw-spacing) * 9)}.tw\:h-\[100px\]{height:100px}.tw\:h-\[180px\]{height:180px}.tw\:h-\[280px\]{height:280px}.tw\:h-auto{height:auto}.tw\:w-3\/5{width:60%}.tw\:w-3\/12{width:25%}.tw\:w-4\/5{width:80%}.tw\:w-9\/12{width:75%}.tw\:w-\[120px\]{width:120px}.tw\:w-full{width:100%}.tw\:max-w-full{max-width:100%}.tw\:gutter-half{padding-left:var(--grid-gutter-half,15px);padding-right:var(--grid-gutter-half,15px);flex:none;min-width:0}.tw\:shrink-0{flex-shrink:0}.tw\:cursor-pointer{cursor:pointer}.tw\:list-none{list-style-type:none}.tw\:flex-row{flex-direction:row}.tw\:flex-wrap{flex-wrap:wrap}.tw\:items-center{align-items:center}.tw\:justify-center{justify-content:center}.tw\:gap-4{gap:calc(var(--tw-spacing) * 4)}.tw\:gap-x-3{column-gap:calc(var(--tw-spacing) * 3)}.tw\:gap-y-2{row-gap:calc(var(--tw-spacing) * 2)}.tw\:overflow-hidden{overflow:hidden}.tw\:overflow-x-hidden{overflow-x:hidden}.tw\:rounded-2xl{border-radius:var(--tw-radius-2xl)}.tw\:rounded-lg{border-radius:var(--tw-radius-lg)}.tw\:rounded-md{border-radius:var(--tw-radius-md)}.tw\:border{border-style:var(--tw-border-style);border-width:1px}.tw\:border-error-border{border-color:var(--tw-color-error-border)}.tw\:bg-error-bg{background-color:var(--tw-color-error-bg)}.tw\:p-0{padding:calc(var(--tw-spacing) * 0)}.tw\:p-4{padding:calc(var(--tw-spacing) * 4)}.tw\:py-3{padding-block:calc(var(--tw-spacing) * 3)}.tw\:py-10{padding-block:calc(var(--tw-spacing) * 10)}.tw\:pt-2{padding-top:calc(var(--tw-spacing) * 2)}.tw\:pt-20{padding-top:calc(var(--tw-spacing) * 20)}.tw\:pb-20{padding-bottom:calc(var(--tw-spacing) * 20)}.tw\:pl-0{padding-left:calc(var(--tw-spacing) * 0)}.tw\:text-center{text-align:center}.tw\:text-left{text-align:left}.tw\:text-right{text-align:right}.tw\:align-baseline{vertical-align:baseline}.tw\:text-lg{font-size:var(--tw-text-lg);line-height:var(--tw-leading,var(--tw-text-lg--line-height))}.tw\:text-sm{font-size:var(--tw-text-sm);line-height:var(--tw-leading,var(--tw-text-sm--line-height))}.tw\:text-xl{font-size:var(--tw-text-xl);line-height:var(--tw-leading,var(--tw-text-xl--line-height))}.tw\:text-xs{font-size:var(--tw-text-xs);line-height:var(--tw-leading,var(--tw-text-xs--line-height))}.tw\:leading-relaxed{--tw-leading:var(--tw-leading-relaxed);line-height:var(--tw-leading-relaxed)}.tw\:font-bold{--tw-font-weight:var(--tw-font-weight-bold);font-weight:var(--tw-font-weight-bold)}.tw\:font-light{--tw-font-weight:var(--tw-font-weight-light);font-weight:var(--tw-font-weight-light)}.tw\:text-error{color:var(--tw-color-error)}.tw\:text-gray-600{color:var(--tw-color-gray-600)}.tw\:text-muted{color:var(--tw-color-muted)}.tw\:text-success{color:var(--tw-color-success)}@media not all and (min-width:1200px){.tw\:max-\[1200px\]\:hidden{display:none}}@media not all and (min-width:992px){.tw\:max-md\:block{display:block}.tw\:max-md\:hidden{display:none}}@media not all and (min-width:768px){.tw\:max-sm\:block{display:block}.tw\:max-sm\:hidden{display:none}.tw\:max-sm\:inline{display:inline}}@media (min-width:768px){.tw\:sm\:mx-auto{margin-inline:auto}.tw\:sm\:ml-offset-1{margin-left:8.333%}.tw\:sm\:ml-offset-2{margin-left:16.667%}.tw\:sm\:ml-offset-3{margin-left:25%}.tw\:sm\:ml-offset-4{margin-left:33.333%}.tw\:sm\:hidden{display:none}.tw\:sm\:w-1\/2{width:50%}.tw\:sm\:w-2\/12{width:16.6667%}.tw\:sm\:w-3\/12{width:25%}.tw\:sm\:w-4\/12{width:33.3333%}.tw\:sm\:w-5\/12{width:41.6667%}.tw\:sm\:w-6\/12{width:50%}.tw\:sm\:w-7\/12{width:58.3333%}.tw\:sm\:w-8\/12{width:66.6667%}.tw\:sm\:w-9\/12{width:75%}.tw\:sm\:w-10\/12{width:83.3333%}.tw\:sm\:w-11\/12{width:91.6667%}@media not all and (min-width:1200px){.tw\:sm\:max-\[1200px\]\:hidden{display:none}}}@media (min-width:992px){.tw\:md\:mt-content-top{margin-top:var(--tw-spacing-content-top)}.tw\:md\:ml-offset-1{margin-left:8.333%}.tw\:md\:ml-offset-2{margin-left:16.667%}.tw\:md\:ml-offset-3{margin-left:25%}.tw\:md\:hidden{display:none}.tw\:md\:w-1\/12{width:8.33333%}.tw\:md\:w-2\/12{width:16.6667%}.tw\:md\:w-3\/12{width:25%}.tw\:md\:w-4\/12{width:33.3333%}.tw\:md\:w-5\/12{width:41.6667%}.tw\:md\:w-6\/12{width:50%}.tw\:md\:w-7\/12{width:58.3333%}.tw\:md\:w-8\/12{width:66.6667%}.tw\:md\:w-9\/12{width:75%}.tw\:md\:w-10\/12{width:83.3333%}.tw\:md\:w-\[60\%\]{width:60%}}@media (min-width:1200px){.tw\:lg\:ml-offset-2{margin-left:16.667%}.tw\:lg\:inline{display:inline}.tw\:min-\[1200px\]\:hidden{display:none}.tw\:lg\:w-3\/12{width:25%}.tw\:lg\:w-5\/12{width:41.6667%}.tw\:lg\:w-7\/12{width:58.3333%}.tw\:lg\:w-8\/12{width:66.6667%}.tw\:lg\:w-9\/12{width:75%}.tw\:lg\:w-10\/12{width:83.3333%}}:root{--color-surface:#fff;--color-surface-alt:#fafafa;--color-on-surface:#000;--color-on-surface-muted:#555;--color-divider:#e0e0e0;--color-overlay:#00000080;--alpha-black-04:#0000000a;--alpha-black-05:#0000000d;--alpha-black-06:#0000000f;--alpha-black-07:#00000012;--alpha-black-08:#00000014;--alpha-black-10:#0000001a;--alpha-black-11:#0000001c;--alpha-black-12:#0000001f;--alpha-black-13:#00000021;--alpha-black-15:#00000026;--alpha-black-20:#0003;--alpha-black-25:#00000040;--alpha-black-30:#0000004d;--alpha-black-35:#00000059;--alpha-black-40:#0006;--alpha-black-45:#00000073;--alpha-black-50:#00000080;--alpha-black-55:#0000008c;--alpha-black-60:#0009;--alpha-black-65:#000000a6;--alpha-black-70:#000000b3;--alpha-black-75:#000000bf;--alpha-black-80:#000c;--alpha-black-85:#000000d9;--alpha-black-90:#000000e6;--alpha-white-07:#ffffff12;--alpha-white-09:#ffffff17;--alpha-white-10:#ffffff1a;--alpha-white-13:#ffffff21;--alpha-white-14:#ffffff24;--alpha-white-15:#ffffff26;--alpha-white-20:#fff3;--alpha-white-25:#ffffff40;--alpha-white-30:#ffffff4d;--alpha-white-35:#ffffff59;--alpha-white-40:#fff6;--alpha-white-45:#ffffff73;--alpha-white-50:#ffffff80;--alpha-white-55:#ffffff8c;--alpha-white-60:#fff9;--alpha-white-70:#ffffffb3;--alpha-white-75:#ffffffbf;--alpha-white-80:#fffc;--alpha-white-85:#ffffffd9;--alpha-white-90:#ffffffe6;--color-suggestion-add:#28a745;--color-suggestion-add-rgb:40,167,69;--color-suggestion-delete:#dc3545;--color-suggestion-delete-rgb:220,53,69;--color-suggestion-secondary:#4cb050;--color-suggestion-secondary-rgb:76,175,80;--color-suggestion-resolve:#e67e22;--color-suggestion-resolve-rgb:230,126,34;--color-comment-highlight:#ffb673;--color-comment-highlight-rgb:255,182,115;--color-format-change:#ffc107;--color-format-change-rgb:255,193,7;--color-search-match:#ea580c;--color-search-match-rgb:234,88,12;--color-messages-blue:#4a90d9;--color-messages-blue-rgb:74,144,217;--color-warning-accent:#f9a825;--color-warning-accent-rgb:249,168,37;--color-warning-gold:#b8860b;--color-warning-gold-rgb:184,134,11;--color-delete-text:#721c24;--color-delete-text-rgb:114,28,36;--grid-gutter:30px;--grid-gutter-half:15px;--container-width-sm:750px;--container-width-md:970px;--container-width-lg:1170px;--bp-xs:480px;--bp-sm:768px;--bp-md:992px;--bp-lg:1200px;--bp-xl:1300px;--bp-narrow:500px;--bp-mobile:600px;--bp-phablet:660px;--bp-tablet:991px;--bp-tablet-landscape:1024px;--btn-font-weight:normal;--btn-border-radius:4px;--btn-padding-x:12px;--btn-padding-y:6px;--btn-disabled-opacity:.65;--input-bg:#fff;--input-color:#000;--input-border:#ccc;--input-border-focus:#333;--input-border-radius:4px;--input-placeholder:#999;
504--input-auth-height:56px;--input-auth-border:#e9e9ee;--input-auth-border-focus:#000;--input-auth-border-error:#c62828;--input-auth-radius:2px;--input-auth-placeholder:#858585}[data-theme=dark]{--color-surface:#1a1a1a;--color-surface-alt:#242424;--color-on-surface:#e8e8e8;--color-on-surface-muted:#999;--color-divider:#333;--color-overlay:#000000b3;--alpha-black-04:#0000001a;--alpha-black-05:#0000001f;--alpha-black-06:#00000026;--alpha-black-07:#0000002e;--alpha-black-08:#0003;--alpha-black-11:#00000040;--alpha-black-12:#00000047;--alpha-black-13:#0000004d;--alpha-black-15:#00000059;--alpha-black-20:#0006;--alpha-black-25:#00000080;--alpha-black-35:#0000008c;--alpha-black-55:#000000b3;--alpha-black-70:#000000d9;--alpha-black-80:#000000e6;--alpha-white-25:#ffffff26;--alpha-white-30:#fff3;--alpha-white-40:#ffffff40;--alpha-white-60:#fff6;--alpha-white-80:#fff9;--alpha-white-90:#ffffffb3;--color-suggestion-add:#34d058;--color-suggestion-add-rgb:52,208,88;--color-suggestion-delete:#f97583;--color-suggestion-delete-rgb:249,117,131;--color-suggestion-secondary:#5cd65c;--color-suggestion-secondary-rgb:92,214,92;--color-suggestion-resolve:#f0a04b;--color-suggestion-resolve-rgb:240,160,75;--color-comment-highlight:#fc9;--color-comment-highlight-rgb:255,204,153;--color-format-change:#ffd54f;--color-format-change-rgb:255,213,79;--color-search-match:#f28c5e;--color-search-match-rgb:242,140,94;--color-messages-blue:#6db3f8;--color-messages-blue-rgb:109,179,248;--color-warning-accent:#fdd835;--color-warning-accent-rgb:253,216,53;--color-warning-gold:#d4a017;--color-warning-gold-rgb:212,160,23;--color-delete-text:#f5a0a8;--color-delete-text-rgb:245,160,168}@media (prefers-reduced-motion:reduce){:not([data-allow-motion]),:not([data-allow-motion]):after,:not([data-allow-motion]):before{scroll-behavior:auto!important;transition-duration:1ms!important;animation-duration:1ms!important;animation-iteration-count:1!important}}.ds-container{padding-left:var(--grid-gutter-half,15px);padding-right:var(--grid-gutter-half,15px);margin-left:auto;margin-right:auto}@media (min-width:768px){.ds-container{width:750px}}@media (min-width:992px){.ds-container{width:970px}}@media (min-width:1200px){.ds-container{width:1170px}}@media (min-width:1300px){.ds-container{width:1280px}}.ds-content-col{flex-shrink:0;width:100%;min-width:0;padding-left:15px;padding-right:15px}@media (min-width:992px){.ds-content-col{width:58.333%;margin-left:16.667%}}@layer components{.text-invert{color:#fff;background-color:#000}.wrapped{white-space:nowrap}@media (max-width:991px){.wrapped{white-space:normal;overflow-wrap:break-word;word-break:break-word}}.zoom-in{cursor:zoom-in}.big-button{box-sizing:border-box;cursor:pointer;color:#000;text-align:center;background:#fff;border:5px solid #000;flex-direction:column;justify-content:center;align-items:center;gap:8px;width:100%;height:177px;font-size:14px;display:flex;position:relative}.big-button:disabled{opacity:.5;cursor:not-allowed}.big-button.small{height:auto;padding:10px;line-height:1.3}.big-button.thin{border-width:1px}.big-button:after{content:" ";background-color:#fff;border-top:0;border-bottom:5px solid #000;border-left:5px solid #000;border-right:0;width:50px;height:50px;display:block;position:absolute;top:-5px;right:-5px}.big-button.small:after{width:10px;height:10px}.big-button.thin:after{border-width:1px;top:-1px;right:-1px}.big-button.drag-over{background-color:rgba(var(--color-tag-rgb),.08);border-style:dashed;border-color:#2638d9}.linklike{font:inherit;cursor:pointer;color:#000;background:linear-gradient(120deg,#000 0 100%) 0 93%/100% 2px no-repeat;border:0;margin:0;padding:0;font-family:Spectral,Georgia,Times New Roman,Times,serif;font-style:italic;text-decoration:none}.linklike:focus-visible,.linklike:hover{color:#fff;background-color:#000;background-size:100% 95%}.linklike:focus-visible{outline-offset:2px;outline:2px solid}.linklike:focus:not(:focus-visible){outline:0}.border-top-7px{border-top:7px solid #000}.border-right-7px{border-right:7px solid #000}.border-bottom-7px{border-bottom:7px solid #000}.border-left-7px{border-left:7px solid #000}.margin-top-20px{margin-top:20px}.margin-top-100px{margin-top:100px}.ds-btn{font-weight:var(--btn-font-weight,normal);text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;white-space:nowrap;padding:var(--btn-padding-y,6px) var(--btn-padding-x,12px);border-radius:var(--btn-border-radius,4px);-webkit-user-select:none;user-select:none;background-image:none;border:1px solid #0000;margin-bottom:0;font-size:18px;line-height:1.45;transition:background-color .15s ease-in-out,color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;display:inline-block}.ds-btn:focus-visible{box-shadow:0 0 0 2px var(--alpha-black-35);outline:0}.ds-btn:focus:not(:focus-visible){box-shadow:none;outline:0}.ds-btn:focus,.ds-btn:hover{color:#333;text-decoration:none}.ds-btn:active{background-image:none;outline:0;box-shadow:inset 0 3px 5px #00000020}.ds-btn.disabled,.ds-btn:disabled,.ds-btn[disabled],fieldset[disabled] .ds-btn{cursor:not-allowed;opacity:var(--btn-disabled-opacity,.65);box-shadow:none}a.ds-btn.disabled,a.ds-btn:disabled,fieldset[disabled] :is(a.ds-btn){pointer-events:none}.ds-btn-primary{color:#fff;background-color:#2638d9;border-color:#222fb8}.ds-btn-primary:focus,.ds-btn-primary:hover{color:#fff;background-color:#1e2cb3;border-color:#18238f}.ds-btn-primary:focus-visible{box-shadow:0 0 0 2px rgba(var(--color-tag-rgb),.4)}.ds-btn-primary.active,.ds-btn-primary:active{color:#fff;background-color:#1e2cb3;border-color:#18238f;box-shadow:inset 0 3px 5px #00000020}.ds-btn-default{color:#333;background-color:#fff;border-color:#ccc}.ds-btn-default:focus,.ds-btn-default:hover{color:#333;background-color:#e6e6e6;
504border-color:#adadad}.ds-btn-default.active,.ds-btn-default:active{color:#333;background-color:#e6e6e6;border-color:#adadad;box-shadow:inset 0 3px 5px #00000020}.ds-btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.ds-btn-success:focus,.ds-btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.ds-btn-success.active,.ds-btn-success:active{color:#fff;background-color:#449d44;border-color:#398439;box-shadow:inset 0 3px 5px #00000020}.ds-btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.ds-btn-danger:focus,.ds-btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.ds-btn-danger.active,.ds-btn-danger:active{color:#fff;background-color:#c9302c;border-color:#ac2925;box-shadow:inset 0 3px 5px #00000020}.ds-btn-warning{color:#fff;background-color:#e09b3d;border-color:#d48f31}.ds-btn-warning:focus,.ds-btn-warning:hover{color:#fff;background-color:#c9850f;border-color:#b47a0e}.ds-btn-warning.active,.ds-btn-warning:active{color:#fff;background-color:#ec971f;border-color:#d58512;box-shadow:inset 0 3px 5px #00000020}.ds-btn-link{color:#2638d9;background-color:#0000;border-color:#0000;border-radius:0;font-weight:400}.ds-btn-link:focus,.ds-btn-link:hover{color:#1c2aa3;background-color:#0000;border-color:#0000;text-decoration:underline}.ds-btn-link:disabled,.ds-btn-link[disabled]{color:#999}:is(.ds-btn-link:disabled,.ds-btn-link[disabled]):focus,:is(.ds-btn-link:disabled,.ds-btn-link[disabled]):hover{color:#999;text-decoration:none}.ds-btn-sm{border-radius:3px;padding:5px 10px;font-size:16px;line-height:1.5}.ds-btn-lg{border-radius:6px;padding:10px 16px;font-size:23px;line-height:1.333}.ds-input{width:100%;height:37px;color:var(--input-color,#000);background-color:var(--input-bg,#fff);border:1px solid var(--input-border,#ccc);border-radius:var(--input-border-radius,4px);box-shadow:none;background-image:none;padding:6px 12px;font-size:18px;line-height:1.45;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;display:block}.ds-input:hover:not(:disabled):not([readonly]){border-color:var(--input-border-focus,#333)}.ds-input:focus{border-color:var(--input-border-focus,#333);box-shadow:none;outline:0}.ds-input:focus-visible{outline-offset:-1px;box-shadow:none;outline:2px solid}.ds-input::placeholder{color:var(--input-placeholder,#999);opacity:1}.ds-input:disabled,.ds-input[readonly]{opacity:1;background-color:#eee}.ds-input:disabled{cursor:not-allowed}textarea.ds-input{height:auto}.has-error .ds-input{border-color:#a94442;box-shadow:inset 0 0 0 1px #a94442}.has-error .ds-input:focus,.has-error .ds-input:hover{border-color:#843534;box-shadow:inset 0 0 0 1px #843534}.ds-error-text{color:#a94442;font-size:var(--font-size-sm);margin-top:4px;display:block}.ds-btn-border{color:#000;cursor:pointer;background-color:#0000;border:1px solid #000;border-radius:0;padding:5px 10px;font-family:Muller,Helvetica Neue,Helvetica,Arial,sans-serif;font-size:12px;transition:background-color .15s ease-in-out,color .15s ease-in-out,border-color .15s ease-in-out}.ds-btn-border:hover{color:#fff;background-color:#000}.ds-btn-border.active:focus,.ds-btn-border:active,.ds-btn-border:active:focus,.ds-btn-border:focus{box-shadow:none;outline:0}.ds-btn-border:focus-visible{outline-offset:2px;outline:2px solid}.ds-btn-border:disabled{opacity:.35;cursor:not-allowed}.ds-btn-border.btn-lg{font-size:var(--font-size-base,16px)}.ds-btn-border.solid{border-width:3px}.ds-btn-border.full-width{text-align:center;width:100%}.ds-btn-border.active{color:#fff;background-color:#000}.ds-btn-border.btn-inverse{color:#fff;background-color:#000;border-color:#fff}.ds-btn-border.btn-inverse:active,.ds-btn-border.btn-inverse:focus,.ds-btn-border.btn-inverse:hover{color:#000;background:#fff}.ds-btn-border.btn-border-bold{border-width:2px}.ds-btn-border.media-move-btn{justify-content:center;align-items:center;min-width:40px;min-height:36px;padding:8px 12px;transition:background-color .2s,color .2s,border-color .2s;display:inline-flex}.ds-btn-border.media-move-btn:hover:not(:disabled){color:#fff;background-color:#000}.ds-btn-border.media-move-btn:hover:not(:disabled) i{color:#fff}.ds-btn-border.media-move-btn:disabled{opacity:.35;cursor:not-allowed;color:#999;border-color:#999}.ds-btn-border.media-move-btn:disabled i{color:#999}.ds-btn-border.media-move-btn i{font-size:var(--font-size-sm,14px);color:#000;line-height:1;display:inline-block}.ds-loadmore{color:#000;cursor:pointer;background-color:#fff;border:5px solid #000;padding:10px 20px;font-family:Muller,Helvetica Neue,Helvetica,Arial,sans-serif;font-size:27px;font-style:italic;font-weight:400;transition:color .3s ease-in-out,background-color .3s ease-in-out,border-color .3s ease-in-out;position:relative}.ds-loadmore:hover{color:#fff;background-color:#000}
504.ds-paper-button{cursor:pointer;letter-spacing:.12em;text-transform:uppercase;color:#000;background-color:#fff;border:5px solid #000;margin-right:10px;padding:10px;transition:all .3s ease-in-out;display:inline-block;position:relative}.ds-paper-button:after{content:" ";background-color:#fff;border-top:none;border-bottom:5px solid #000;border-left:5px solid #000;border-right:none;width:15px;height:15px;display:block;position:absolute;top:-5px;right:-5px}.ds-paper-button:hover{color:#fff;background-color:#000}.ds-paper-button:disabled{color:#ccc;cursor:not-allowed;border-color:#ccc}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}</style> 505 506 507 <!-- Secondary CSS: ÑÑили ÑÐµÐ´ÐºÐ¸Ñ ÑÑÑÐ°Ð½Ð¸Ñ (подкаÑÑÑ, /connect, /quotes, ÐÐ, 508 viewer-лайÑÐ±Ð¾ÐºÑ Ð¸ дÑ.), вÑнеÑеннÑе из main.css (Ñм. app/styles/secondary.scss). 509 Ðа маÑÑÑÑÑÐ°Ñ , где ÑÑи ÑÑили нÑÐ¶Ð½Ñ Ð´Ð»Ñ ÐÐÐ ÐÐÐÐ ÑендеÑа â блокиÑÑÑÑий <link> 510 (инаÑе FOUC); на оÑÑалÑнÑÑ (ÑÑаÑÑи, главнаÑ, Ñид) â async (media="print" 511 â onload â media="all"): Ñайл пÑогÑеваеÑÑÑ Ð² кÑÑ Ð´Ð»Ñ SPA-навигаÑии. 512 ÐÑи добавлении нового маÑÑÑÑÑа в secondary.scss â дополниÑÑ SECONDARY_SYNC_ROUTES 513 (пÑовеÑÑеÑÑÑ ÑеÑÑом scripts/secondary-sync-routes.test.js). 514 ÐÑознаннÑй ÑÑейдоÑÑ: link вÑÑавлÑеÑÑÑ ÑкÑипÑом, preload-scanner его не Ð²Ð¸Ð´Ð¸Ñ â 515 на sync-маÑÑÑÑÑÐ°Ñ Ð·Ð°Ð³ÑÑзка ÑÑаÑÑÑÐµÑ Ð½Ð° неÑколÑко деÑÑÑков Ð¼Ñ Ð¿Ð¾Ð·Ð¶Ðµ ÑÑаÑиÑеÑкого 516 <link>. СÑаÑиÑеÑкий ваÑÐ¸Ð°Ð½Ñ Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶ÐµÐ½: HTML один на вÑе маÑÑÑÑÑÑ, а ÑÑаÑиÑеÑкий 517 preload поднÑл Ð±Ñ Ð¿ÑиоÑиÑÐµÑ secondary на ÑÑаÑÑÑÑ Ð² ÑÑеÑб LCP-каÑÑинке. --> 518
518<script> 519 (function () { 520 // ÐÐ½ÐºÐµÑ (\/|$) обÑзаÑелен ÐºÐ°Ð¶Ð´Ð¾Ð¼Ñ Ð¿ÑÑи â без него /connect маÑÑил Ð±Ñ /connection. 521 // /edit/authors и /edit/profile â ÑÑÑаниÑÑ Ð½Ð°ÑÑÑоек пÑоÑÐ¸Ð»Ñ (settings-page 522 // в secondary); ÑедакÑÐ¾Ñ Ð¼Ð°ÑеÑиалов /edit/<type> ÑÑда ÐÐ Ð²Ñ Ð¾Ð´Ð¸Ñ: его модалки 523 // (inviteCollaborators) оÑкÑÑваÑÑÑÑ ÐºÐ»Ð¸ÐºÐ¾Ð¼, async-загÑÑзка ÑÑпеваеÑ. 524 var SECONDARY_SYNC_ROUTES = 525 /^\/(podcasts|connect|quotes|maps|almanac|newsletter|verify-email|login|register|messages|drafts|notifications|events|edit\/authors|edit\/profile)(\/|$)/; 526 var l = document.createElement('link'); 527 l.rel = 'stylesheet'; 528 l.href = '/styles/secondary-5e4678de.css'; 529 if (!SECONDARY_SYNC_ROUTES.test(location.pathname)) { 530 l.media = 'print'; 531 window.__armAsyncCss(l); 532 } 533 document.head.appendChild(l); 534 })(); 535 </script>
535 536 537 <!-- Remove no-js class when JS is available --> 538
538<script> 539 (function (h) { 540 h.className = h.className.replace(/\bno-js\b/, 'js'); 541 })(document.documentElement); 542 </script>
542 543 544 <!-- Overlay visibility controlled by siteLoading.js directive (adds .footer-loaded class) --> 545 </head> 546 547 <body class="{{ bodyClass }}"> 548 <!-- SVG-ÑпÑайÑ: ÑенÑÑализованнÑе иконки, иÑполÑзÑемÑе ÑеÑез <svg-icon name="..."> --> 549 <!-- SVG-ÑпÑайÑ: ÑенÑÑализованное Ñ Ñанение иконок. 550 ÐÑполÑзÑеÑÑÑ ÑеÑез <svg-icon name="article"></svg-icon> 551 или <svg class="icon"><use href="#icon-article"/></svg> --> 552<svg xmlns="http://www.w3.org/2000/svg" style="display:none"> 553 <!-- Тип конÑенÑа: ÑÑаÑÑÑ --> 554 <symbol id="icon-article" viewBox="0 0 24 24"> 555 <path d="M4 4.5C4 3.67157 4.67157 3 5.5 3H15.3787C15.7765 3 16.158 3.15804 16.4393 3.43934L19.5607 6.56066C19.842 6.84197 20 7.22346 20 7.62132V19.5C20 20.3284 19.3284 21 18.5 21H5.5C4.67157 21 4 20.3284 4 19.5V4.5Z" stroke="currentColor" stroke-width="1.5" fill="none"/> 556 <path d="M15 3V7.5C15 7.77614 15.2239 8 15.5 8H20" stroke="currentColor" stroke-width="1.5" fill="none"/> 557 <path d="M8 13H16M8 17H13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none"/> 558 </symbol> 559 560 <!-- Тип конÑенÑа: изобÑажение --> 561 <symbol id="icon-image" viewBox="0 0 24 24"> 562 <rect x="3" y="5" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.5" fill="none"/> 563 <path d="M3 15L7.5 11.5C8.32843 10.6716 9.67157 10.6716 10.5 11.5L15 16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none"/> 564 <path d="M12 14.5L14.5 12C15.3284 11.1716 16.6716 11.1716 17.5 12L21 15.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none"/> 565 <circle cx="8.5" cy="9.5" r="1.5" fill="currentColor"/> 566 </symbol> 567 568 <!-- Тип конÑенÑа: мÑзÑка --> 569 <symbol id="icon-music" viewBox="0 0 24 24"> 570 <path d="M9 18V6L21 4V16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/> 571 <circle cx="6" cy="18" r="3" stroke="currentColor" stroke-width="1.5" fill="none"/> 572 <circle cx="18" cy="16" r="3" stroke="currentColor" stroke-width="1.5" fill="none"/> 573 </symbol> 574 575 <!-- Тип конÑенÑа: видео --> 576 <symbol id="icon-video" viewBox="0 0 24 24"> 577 <rect x="3" y="5" width="14" height="14" rx="2" stroke="currentColor" stroke-width="1.5" fill="none"/> 578 <path d="M17 9L21 7V17L17 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/> 579 </symbol> 580 581 <!-- Тип конÑенÑа: лиÑеÑаÑÑÑа --> 582 <symbol id="icon-literature" viewBox="0 0 24 24"> 583 <path d="M4 19.5C4 18.837 4.26339 18.2011 4.73223 17.7322C5.20107 17.2634 5.83696 17 6.5 17H20" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/> 584 <path d="M6.5 2H20V22H6.5C5.83696 22 5.20107 21.7366 4.73223 21.2678C4.26339 20.7989 4 20.163 4 19.5V4.5C4 3.83696 4.26339 3.20107 4.73223 2.73223C5.20107 2.26339 5.83696 2 6.5 2Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/> 585 </symbol> 586 587 <!-- ÐалоÑка (ÑÐµÐºÐ±Ð¾ÐºÑ Ð² dropdown) --> 588 <symbol id="icon-check" viewBox="0 0 14 10"> 589 <path d="M1 5L5 9L13 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/> 590 </symbol> 591 592 <!-- СÑÑелка dropdown --> 593 <symbol id="icon-chevron-down" viewBox="0 0 11 7"> 594 <path d="M1 1L5.5 5.5L10 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/> 595 </symbol> 596 597 <!-- ÐÑоÑÐ¸Ð»Ñ --> 598 <symbol id="icon-profile" viewBox="0 0 15 16"> 599 <path d="M12.73 15.75H1.72C.53 15.75-.31 14.56.1 13.45 1.08 10.82 3.94 9.05 7.23 9.05c3.28 0 6.14 1.77 7.12 4.4.41 1.11-.43 2.3-1.62 2.3zM7.23 10.55c-2.62 0-4.97 1.41-5.72 3.42-.03.08.02.28.21.28h11.01c.21 0 .24-.2.21-.28-.75-2.01-3.1-3.42-5.71-3.42z" fill="currentColor"/> 600 <path d="M7.23 7.88c-2.07 0-3.75-1.77-3.75-3.94C3.48 1.77 5.16 0 7.23 0c2.07 0 3.75 1.77 3.75 3.94 0 2.17-1.68 3.94-3.75 3.94zm0-6.38c-1.24 0-2.25 1.1-2.25 2.44s1.01 2.44 2.25 2.44 2.25-1.1 2.25-2.44S8.47 1.5 7.23 1.5z" fill="currentColor"/> 601 </symbol> 602 603 <!-- ЧеÑновики --> 604 <symbol id="icon-drafts" viewBox="0 0 16 16"> 605 <path d="M13.44 0c-.61 0-1.22.23-1.69.7L1.32 11.13.03 14.96c-.17.51.35 1 .86.83l3.83-1.28L15.13 4.07c.93-.93.93-2.44 0-3.38C14.66.23 14.05 0 13.44 0zm-.06 1.56c.23 0 .45.09.63.26.34.35.34.91 0 1.25l-.52.52-1.25-1.25.52-.52c.17-.17.4-.26.62-.26zm-2.2 1.83l1.25 1.25-7.42 7.42-1.26-1.25 7.43-7.42zM2.69 11.88l1.25 1.26-1.51.72c-.13.06-.29.04-.39-.06-.
60511-.1-.14-.26-.08-.4l.73-1.52z" fill="currentColor"/> 606 </symbol> 607 608 <!-- ÐодпиÑки --> 609 <symbol id="icon-subscriptions" viewBox="0 0 17 15"> 610 <path d="M4.88 0C3.84 0 3 .84 3 1.88V3.75H1.88C.84 3.75 0 4.59 0 5.63v7.12C0 13.99 1.01 15 2.25 15h12c1.24 0 2.25-1.01 2.25-2.25V1.88C16.5.84 15.66 0 14.63 0H4.88zm0 1.5h9.75c.2 0 .37.17.37.38v10.87c0 .41-.34.75-.75.75H4.37c.08-.23.13-.49.13-.75V1.88c0-.21.17-.38.38-.38zM6.38 3c-.41 0-.75.34-.75.75s.34.75.75.75h6.75c.41 0 .75-.34.75-.75S13.54 3 13.13 3H6.38zM1.88 5.25H3v7.5c0 .41-.34.75-.75.75s-.75-.34-.75-.75V5.63c0-.21.17-.38.38-.38zm4.5.38c-.41 0-.75.34-.75.75s.34.75.75.75h6.75c.41 0 .75-.34.75-.75s-.34-.75-.75-.75H6.38zm0 2.62c-.41 0-.75.34-.75.75v2.63c0 .41.34.75.75.75h2.25c.41 0 .75-.34.75-.75V9c0-.41-.34-.75-.75-.75H6.38zm4.87 0c-.41 0-.75.34-.75.75s.34.75.75.75h1.88c.41 0 .75-.34.75-.75s-.34-.75-.75-.75H11.25zm0 2.63c-.41 0-.75.34-.75.75s.34.75.75.75h1.88c.41 0 .75-.34.75-.75s-.34-.75-.75-.75H11.25z" fill="currentColor"/> 611 </symbol> 612 613 <!-- ÐомменÑаÑии / ÑÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ --> 614 <symbol id="icon-notifications" viewBox="0 0 21 17"> 615 <path d="M3.88 0C3.03 0 2.32.15 1.74.45 1.17.75.73 1.19.44 1.76.15 2.34 0 3.05 0 3.88v2.65c0 .83.15 1.54.44 2.11.29.58.73 1.02 1.3 1.32.58.3 1.22.45 2.06.45v2.06c0 .36.09.64.26.85.18.21.43.32.75.32.23 0 .44-.06.62-.18.19-.12.42-.31.7-.56l2.78-2.48h2.24c.85 0 1.56-.15 2.14-.45.57-.3 1.01-.74 1.3-1.32.29-.57.44-1.28.44-2.11V3.88c0-.84-.15-1.54-.44-2.12-.29-.57-.73-1.01-1.3-1.31C12.71.15 12 0 11.15 0H3.88zm0 1.5h7.27c.83 0 1.44.2 1.82.6.38.4.57.99.57 1.78v2.65c0 .79-.2 1.39-.57 1.79-.38.4-.99.6-1.82.6H8.83c-.25 0-.45.02-.6.07-.16.06-.32.17-.48.35l-2.55 2.6V9.6c0-.26-.05-.43-.16-.54-.1-.1-.28-.16-.53-.16h-.63c-.84 0-1.44-.2-1.82-.6-.38-.4-.57-.99-.57-1.79V3.88c0-.79.19-1.39.57-1.78.38-.4.99-.6 1.82-.6zM16.1 3c.04.28.06.57.06.88V4.5h.11c.84 0 1.45.2 1.82.6.38.39.57.99.57 1.78v2.71c0 .8-.19 1.39-.57 1.79-.38.4-.99.6-1.82.6h-.63c-.25 0-.42.05-.53.16-.11.1-.16.29-.16.54v2.33l-2.54-2.6c-.17-.18-.33-.3-.48-.35-.16-.05-.36-.08-.6-.08-2.52 0-2.36 0-2.4-.01l-1.45 1.3c.31.09.8.2 1.53.2h2.23l2.78 2.48c.28.25.52.44.7.56.19.12.4.18.63.18.32 0 .57-.1.75-.31.18-.21.26-.5.26-.85v-2.06c.85 0 1.48-.15 2.06-.45.58-.3 1.01-.74 1.3-1.32.29-.57.44-1.28.44-2.11V6.88c0-.84-.15-1.54-.44-2.12-.3-.57-.73-1.01-1.3-1.31-.58-.3-1.29-.45-2.14-.45H16.1z" fill="currentColor"/> 616 </symbol> 617 618 <!-- ÐÑйÑи --> 619 <symbol id="icon-logout" viewBox="0 0 18 18"> 620 <path d="M12.93 0c-1.04 0-1.88.84-1.88 1.88s.84 1.87 1.88 1.87 1.87-.84 1.87-1.87S13.96 0 12.93 0zm-1.5 4.5c-.27 0-.53.06-.77.17L7.67 6.08c-.15.07-.26.18-.33.33L5.87 9.68c-.04.08-.07.16-.07.26 0 .31.25.56.56.56.23 0 .44-.14.52-.34v0l1.57-2.68.78-.28-.74 2.43.01 0c-.04.16-.08.33-.08.5 0 .71.41 1.33.99 1.65l0 0 2.92 2.13 1 3.56h0c.1.31.38.53.72.53.41 0 .75-.34.75-.75 0-.08-.02-.15-.04-.22h0l-.75-3.76c-.02-.12-.07-.24-.14-.35l-1.65-2.71 1-3.29-.02-.01c.06-.18.1-.36.1-.56 0-1.03-.84-1.88-1.88-1.88zm2.49 2.98l-.57 1.54.44.32c.04.03.07.05.12.07l2.73 1.04c.01.01.02.01.03.01l.02.01c.06.02.12.03.18.03.31 0 .56-.25.56-.56 0-.22-.13-.41-.32-.5l-2.44-1.28-.75-.68zM5.66 10.88c-.22-.02-.44.09-.55.29l-.93 1.76-1.33-.71c-.36-.19-.82-.06-1.01.31L.09 15.85c-.19.36-.05.82.32 1.01l.83.45c-.01-.26.05-.52.18-.75.26-.49.77-.8 1.33-.8.24 0 .48.06.7.18.11.06.22.13.31.22l.99-1.86 0 0 .23-.44.21-.39v0l.93-1.76c.15-.28.04-.61-.23-.76-.07-.04-.14-.05-.21-.06zm2.94 1.42l-.36 1.67-1.57 2.86-.03.06c-.06.11-.1.23-.1.36 0 .41.34.75.75.75.27 0 .51-.15.64-.37l2.54-3.96-1.87-1.37zm-5.8 4.21c-.29-.02-.58.13-.73.4-.2.37-.05.82.32 1.02.36.19.82.06 1.01-.31.19-.37.06-.82-.31-1.02-.09-.05-.18-.07-.29-.09z" fill="currentColor"/> 621 </symbol> 622 623 <!-- ÐаÑÑÑойки --> 624 <symbol id="icon-settings" viewBox="0 0 17 17"> 625 <path d="M7.15 0c-.5 0-.93.36-1.02.86L5.91 2.13c-.11.04-.23.09-.34.14L4.52 1.52c-.46-.32-1.01-.2-1.32.1L1.64 3.2c-.36.35-.4.91-.11 1.32l.74 1.05c-.05.11-.1.22-.14.34L.86 6.13C.36 6.21 0 6.64 0 7.15v2.21c0 .5.36.93.86 1.01l1.26.22c.04.11.09.23.14.34l-.74 1.05c-.29.41-.24.97.1 1.32l1.56 1.56c.41.4.97.37 1.32.11l1.05-.74c.11.05.23.1.34.14l.22 1.27c.08.49.51.85 1.02.85h2.21c.5 0 .93-.36 1.02-.86l.22-1.27c.11-.04.23-.09.34-.14l1.05.74c.31.23.9.31 1.32-.11l1.56-1.56c.36-.36.4-.91.11-1.32l-.74-1.05c.05-.11.1-.23.14-.34l1.27-.22c.49-.08.86-.51.86-1.02V7.15c0-.5-.36-.93-.86-1.02l-1.27-.22c-.04-.11-.09-.23-.14-.34l.74-1.05c.29-.41.24-.97-.11-1.32l-1.56-1.56c-.4-.39-.95-.38-1.32-.11l-1.
62505.74c-.11-.05-.23-.1-.34-.14L10.37.86C10.29.36 9.86 0 9.35 0H7.15zm.43 1.5h1.34c.02 0 .04.02.05.04l.29 1.68c.66.24 1.28.49 1.84.76l1.39-.98c.02-.01.04-.01.06.01l.95.95c.02.02.02.04.01.06l-.98 1.39c.28.6.54 1.22.76 1.84l1.68.29c.02 0 .04.02.04.05v1.34c0 .02-.02.04-.04.05l-1.68.29c-.22.63-.47 1.24-.76 1.84l.98 1.39c.01.02.01.04-.01.06l-.95.95c-.02.02-.04.02-.06.01l-1.39-.98c-.61.29-1.23.54-1.84.76l-.29 1.68c0 .03-.02.04-.05.04H7.58c-.02 0-.04-.02-.05-.04l-.29-1.68c-.6-.21-1.21-.46-1.84-.76l-1.39.98c-.02.01-.04.01-.06-.01l-.95-.95c-.02-.02-.02-.04-.01-.06l.98-1.39c-.29-.63-.55-1.24-.76-1.84l-1.68-.29C1.52 8.96 1.5 8.94 1.5 8.92V7.58c0-.02.02-.04.04-.05l1.68-.29c.22-.61.47-1.22.76-1.84l-.98-1.39c-.01-.02-.01-.04.01-.06l.95-.95c.02-.02.04-.02.06-.01l1.39.98c.58-.28 1.2-.53 1.84-.76l.29-1.68c0-.02.02-.04.05-.04zM8.25 4.88c-1.86 0-3.38 1.51-3.38 3.37 0 1.86 1.52 3.38 3.38 3.38s3.38-1.52 3.38-3.38c0-1.86-1.52-3.37-3.38-3.37zm0 1.5c1.04 0 1.88.84 1.88 1.87 0 1.04-.84 1.88-1.88 1.88s-1.88-.84-1.88-1.88c0-1.03.84-1.87 1.88-1.87z" fill="currentColor"/> 626 </symbol> 627 628 <!-- ÐомменÑаÑии (feed card) --> 629 <symbol id="icon-comments" viewBox="0 0 21 17"> 630 <path d="M3.88 0C3.03 0 2.32.15 1.74.45 1.17.75.73 1.19.44 1.76.15 2.34 0 3.05 0 3.88v2.65c0 .83.15 1.54.44 2.11.29.58.73 1.02 1.3 1.32.58.3 1.22.45 2.06.45v2.06c0 .36.09.64.26.85.18.21.43.32.75.32.23 0 .44-.06.62-.18.19-.12.42-.31.7-.56l2.78-2.48h2.24c.85 0 1.56-.15 2.14-.45.57-.3 1.01-.74 1.3-1.32.29-.57.44-1.28.44-2.11V3.88c0-.84-.15-1.54-.44-2.12-.29-.57-.73-1.01-1.3-1.31C12.71.15 12 0 11.15 0H3.88zm0 1.5h7.27c.83 0 1.44.2 1.82.6.38.4.57.99.57 1.78v2.65c0 .79-.2 1.39-.57 1.79-.38.4-.99.6-1.82.6H8.83c-.25 0-.45.02-.6.07-.16.06-.32.17-.48.35l-2.55 2.6V9.6c0-.26-.05-.43-.16-.54-.1-.1-.28-.16-.53-.16h-.63c-.84 0-1.44-.2-1.82-.6-.38-.4-.57-.99-.57-1.79V3.88c0-.79.19-1.39.57-1.78.38-.4.99-.6 1.82-.6zM16.1 3c.04.28.06.57.06.88V4.5h.11c.84 0 1.45.2 1.82.6.38.39.57.99.57 1.78v2.71c0 .8-.19 1.39-.57 1.79-.38.4-.99.6-1.82.6h-.63c-.25 0-.42.05-.53.16-.11.1-.16.29-.16.54v2.33l-2.54-2.6c-.17-.18-.33-.3-.48-.35-.16-.05-.36-.08-.6-.08-2.52 0-2.36 0-2.4-.01l-1.45 1.3c.31.09.8.2 1.53.2h2.23l2.78 2.48c.28.25.52.44.7.56.19.12.4.18.63.18.32 0 .57-.1.75-.31.18-.21.26-.5.26-.85v-2.06c.85 0 1.48-.15 2.06-.45.58-.3 1.01-.74 1.3-1.32.29-.57.44-1.28.44-2.11V6.88c0-.84-.15-1.54-.44-2.12-.3-.57-.73-1.01-1.3-1.31-.58-.3-1.29-.45-2.14-.45H16.1z" fill="currentColor"/> 631 </symbol> 632 633 <!-- ÐÑоÑмоÑÑÑ (feed card) --> 634 <symbol id="icon-eye" viewBox="0 0 20 14"> 635 <path d="M10 1C5.8 1 2.1 3.5.5 7c1.6 3.5 5.3 6 9.5 6s7.9-2.5 9.5-6c-1.6-3.5-5.3-6-9.5-6z" stroke="currentColor" stroke-width="1.5" fill="none"/> 636 <circle cx="10" cy="7" r="3" stroke="currentColor" stroke-width="1.5" fill="none"/> 637 </symbol> 638 639 <!-- ФилÑÑÑ (ÑлайдеÑÑ) --> 640 <symbol id="icon-filter-sliders" viewBox="0 0 24 20"> 641 <path d="M4.5 4.5H1C.45 4.5 0 4.05 0 3.5S.45 2.5 1 2.5h3.5c.55 0 1 .45 1 1s-.45 1-1 1z" fill="currentColor"/> 642 <path d="M23 4.5H10c-.55 0-1-.45-1-1s.45-1 1-1h13c.55 0 1 .45 1 1s-.45 1-1 1z" fill="currentColor"/> 643 <path d="M14.5 11H1c-.55 0-1-.45-1-1s.45-1 1-1h13.5c.55 0 1 .45 1 1s-.45 1-1 1z" fill="currentColor"/> 644 <path d="M23 11h-3.5c-.55 0-1-.45-1-1s.45-1 1-1H23c.55 0 1 .45 1 1s-.45 1-1 1z" fill="currentColor"/> 645 <path d="M6 17.5H1c-.55 0-1-.45-1-1s.45-1 1-1h5c.55 0 1 .45 1 1s-.45 1-1 1z" fill="currentColor"/> 646 <path d="M23 17.5H11.5c-.55 0-1-.45-1-1s.45-1 1-1H23c.55 0 1 .45 1 1s-.45 1-1 1z" fill="currentColor"/> 647 <path d="M9 20c-1.93 0-3.5-1.57-3.5-3.5S7.07 13 9 13s3.5 1.57 3.5 3.5S10.93 20 9 20zm0-5c-.83 0-1.5.67-1.5 1.5S8.17 18 9 18s1.5-.67 1.5-1.5S9.83 15 9 15z" fill="currentColor"/> 648 <path d="M17 13.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5zm0-5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z" fill="currentColor"/> 649 <path d="M7 7C5.07 7 3.5 5.43 3.5 3.5S5.07 0 7 0s3.5 1.57 3.5 3.5S8.93 7 7 7zm0-5c-.83 0-1.5.67-1.5 1.5S6.17 5 7 5s1.5-.67 1.5-1.5S7.83 2 7 2z" fill="currentColor"/> 650 </symbol> 651 652 <!-- СбÑÐ¾Ñ ÑилÑÑÑа (воÑонка + X) --> 653 <symbol id="icon-filter-reset" viewBox="0 0 24 24"> 654 <path d="M3 4H14L10 10V16L8 18V10L4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/> 655 <path d="M19 8L15 12M15 8L19 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/> 656 </symbol> 657 658 <!-- ÐакÑÑÑÑ (X) --> 659 <symbol id="icon-close" viewBox="0 0 24 24"> 660 <path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/> 661 </symbol> 662 663 <!-- VK (Font Awesome 5 ÑÑÐ¸Ð»Ñ â клаÑÑиÑеÑкий «Ðл знак) --> 664 <symbol id="icon-vk" viewBox="0 0 576 512">
665 <path fill="currentColor" d="M545 117.7c3.7-12.5 0-21.7-17.8-21.7h-58.9c-15 0-21.9 7.9-25.6 16.7 0 0-30 73.1-72.4 120.5-13.7 13.7-20 18.1-27.5 18.1-3.7 0-9.4-4.4-9.4-16.9V117.7c0-15-4.2-21.7-16.6-21.7h-92.6c-9.4 0-15 7-15 13.5 0 14.2 21.2 17.5 23.4 57.5v86.8c0 19-3.4 22.5-10.9 22.5-20 0-68.6-73.4-97.4-157.4-5.8-16.3-11.5-22.9-26.6-22.9H38.8c-16.8 0-20.2 7.9-20.2 16.7 0 15.6 20 93.1 93.1 195.5C160.4 378.1 229 416 291.4 416c37.5 0 42.1-8.4 42.1-22.9 0-66.8-3.4-73.1 15.4-73.1 8.7 0 23.7 3.5 58.7 34.1 40 40 46.6 57.9 69 57.9h58.9c16.8 0 25.3-8.4 20.4-25-11.2-34.9-86.9-106.7-90.3-111.5-8.7-11.2-6.2-16.2 0-26.2.1-.1 72-101.3 79.4-135.6z"/> 666 </symbol> 667 668 <!-- Telegram (Font Awesome 5 ÑÑÐ¸Ð»Ñ â бÑмажнÑй ÑамолÑÑ) --> 669 <symbol id="icon-telegram" viewBox="0 0 448 512"> 670 <path fill="currentColor" d="M446.7 98.6l-67.6 318.8c-5.1 22.5-18.4 28.1-37.3 17.5l-103-75.9-49.7 47.8c-5.5 5.5-10.1 10.1-20.7 10.1l7.4-104.9 190.9-172.5c8.3-7.4-1.8-11.5-12.9-4.1L117.8 284 16.2 252.2c-22.1-6.9-22.5-22.1 4.6-32.7L418.2 66.4c18.4-6.9 34.5 4.1 28.5 32.2z"/> 671 </symbol> 672 673 <!-- Twitter (Font Awesome 5 ÑÑилÑ) --> 674 <symbol id="icon-twitter" viewBox="0 0 512 512"> 675 <path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"/> 676 </symbol> 677 678 <!-- Facebook-f (Font Awesome 5 ÑÑилÑ) --> 679 <symbol id="icon-facebook" viewBox="0 0 320 512"> 680 <path fill="currentColor" d="M279.14 288l14.22-92.66h-88.91v-60.13c0-25.35 12.42-50.06 52.24-50.06h40.42V6.26S260.43 0 225.36 0c-73.22 0-121.08 44.38-121.08 124.72v70.62H22.89V288h81.39v224h100.17V288z"/> 681 </symbol> 682 683 <!-- Instagram (Font Awesome 5 ÑÑилÑ) --> 684 <symbol id="icon-instagram" viewBox="0 0 448 512"> 685 <path fill="currentColor" d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z"/> 686 </symbol> 687 688 <!-- YouTube (Font Awesome 5 ÑÑилÑ) --> 689 <symbol id="icon-youtube" viewBox="0 0 576 512"> 690 <path fill="currentColor" d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z"/> 691 </symbol> 692 693 <!-- Envelope (Font Awesome 5 Solid ÑÑилÑ) --> 694 <symbol id="icon-envelope" viewBox="0 0 512 512">
695 <path fill="currentColor" d="M502.3 190.8c3.9-3.1 9.7-.2 9.7 4.7V400c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V195.6c0-5 5.7-7.8 9.7-4.7 22.4 17.4 52.1 39.5 154.1 113.6 21.1 15.4 56.7 47.8 92.2 47.6 35.7.3 72-32.8 92.3-47.6 102-74.1 131.6-96.3 154-113.7zM256 320c23.2.4 56.6-29.2 73.4-41.4 132.7-96.3 142.8-104.7 173.4-128.7 5.8-4.5 9.2-11.5 9.2-18.9v-19c0-26.5-21.5-48-48-48H48C21.5 64 0 85.5 0 112v19c0 7.4 3.4 14.3 9.2 18.9 30.6 23.9 40.7 32.4 173.4 128.7 16.8 12.2 50.2 41.8 73.4 41.4z"/> 696 </symbol> 697</svg> 698 699 <!-- Add html classes for page-specific CSS (before Angular bootstrap) --> 700
700<script> 701 (function () { 702 var p = location.pathname; 703 // Article/expo pages: defer footer for CLS prevention 704 if (/^\/articles\/[^/]+\/[^/]+/.test(p) || /^\/expo\/[^/]+\/[^/]+\/[^/]+/.test(p)) { 705 document.documentElement.classList.add('defer-footer-top'); 706 } 707 // Editor pages: hide footer until Angular takes over (prevents footer flash) 708 if (/^\/edit\//.test(p)) { 709 document.documentElement.classList.add('is-editor-page'); 710 } 711 // ÐиÑнÑй кабинеÑ: ÑкÑÑваем ÑÑÑÐµÑ Ð½Ð° мобилÑнÑÑ Ð´Ð¾ Angular bootstrap 712 if ( 713 /^\/messages(\/|$)/.test(p) || 714 /^\/notifications(\/|$)/.test(p) || 715 /^\/drafts(\/|$)/.test(p) 716 ) { 717 document.documentElement.classList.add('hide-mobile-footer'); 718 } 719 })(); 720 </script>
720 721 <!-- Social selection popup --> 722 <social-selection></social-selection> 723 724 <!-- ÐлобалÑнÑй индикаÑÐ¾Ñ Â«API недоÑÑÑпен» â виден вÑем, в Ñ.Ñ. анонимам. 725 ÐоказÑваеÑÑÑ ÐºÐ¾Ð³Ð´Ð° circuit-breaker акÑивен (каÑкад 5xx/52x). 726 СкÑÑваеÑÑÑ Ð°Ð²ÑомаÑиÑеÑки пÑи воÑÑÑановлении. --> 727 <api-outage-banner></api-outage-banner> 728 729 <!-- Header (hidden when TipTap editor is active - it has its own editor-header) --> 730 <header ng-if="(!$root.statusCode || $root.isStubPage) && !$root.hideSiteHeader" ng-class="{'is-short': $root.isHeaderShort, 'is-expo': $root.isExpoPages, 'is-logo-only': $root.isLogoOnlyPage, 'is-force-compact': $root.isForceCompactHeader }"></header> 731 732 <!-- Feedback modal for editor mode (when site header is hidden, its own instance is gone) --> 733 <send-feedback-message ng-if="$root.hideSiteHeader"></send-feedback-message> 734 735 <!-- Login modal for editor mode (site header's instance is destroyed when hideSiteHeader=true) --> 736 <login-modal ng-if="$root.hideSiteHeader && $root.editorLoginShown" opened="$root.editorLoginShown" event-detail="$root.editorLoginDetail"></login-modal> 737 738 <!-- Route loading indicator --> 739 <route-loading-indicator></route-loading-indicator> 740 741 <!-- Loading footer overlay: visible when .ready class added --> 742 <div class="loading-footer-overlay do-animate" ng-cloak ng-if="$root.isRouteLoading && !$root.statusCode && !$root.isEditorLoading" role="status" aria-live="off" aria-busy="true" aria-label="ÐагÑÑзка ÑÑÑаниÑÑ" ng-attr-aria-label="{{ 'ÐагÑÑзка ÑÑÑаниÑÑ' | t }}"> 743 <!-- Centered punchline - always visible (default value set in siteLoading.js) --> 744 <div class="loading-footer-overlay__punchline" ng-bind-html="$root.loadingPunchline | trustedHtml"></div> 745 746 <!-- Footer image - sync decoding to prevent flicker --> 747 <!-- Responsive: 400w mobile, 640w tablet, 832w retina-mobile/laptop, 1280w desktop. 748 832w закÑÑÐ²Ð°ÐµÑ Ð´ÑÑÑ 640â1280: mobile DPR 1.75-2 (720-830 Ñиз.px) и ноÑÑбÑки 749 ÑанÑÑе каÑали 1280w (~102 KiB) вмеÑÑо ~42 KiB (PSI: â90 KiB на каждой ÑÑÑаниÑе). --> 750 <div class="loading-footer-overlay__image" aria-hidden="true"> 751 <picture> 752 <source type="image/webp" srcset="/images/footer-mobile.webp 400w, /images/footer-small.webp 640w, /images/footer-medium.webp 832w, /images/footer.webp 1280w" sizes="100vw"> 753 <!-- fetchpriority lowered from "high" to "low", decoding from "sync" to "async": 754 This is a decorative overlay image, NOT the LCP element. 755 High priority here competes with the hero article image for bandwidth. --> 756 <img src="/images/footer-small.webp" srcset="/images/footer-mobile.webp 400w, /images/footer-small.webp 640w, /images/footer-medium.webp 832w, /images/footer.webp 1280w" sizes="100vw" loading="eager" fetchpriority="low" decoding="async" width="1280" height="518" alt="" aria-hidden="true"> 757 </picture> 758 </div> 759 760 <!-- СÑаÑиÑнÑй ÑÑÑÐµÑ Ð´Ð»Ñ overlay â ÐРиÑполÑзÑем <site-footer>, 761 ÑÑÐ¾Ð±Ñ Ð½Ðµ ÑоздаваÑÑ Ð»Ð¸Ñний инÑÑÐ°Ð½Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñа на каждой навигаÑии. 762 СÑÑлки дÑблиÑÑÑÑ siteFooter.html Ð´Ð»Ñ ÐºÐ¾ÑÑекÑного оÑобÑÐ°Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° Ð¼Ð¾Ð±Ð¸Ð»ÐºÐ°Ñ . --> 763 <!-- Тег <footer>, а не <div>, â Ð´Ð»Ñ Markdown for Agents: конвеÑÑÐµÑ Cloudflare 764 ÑÑÐµÐ·Ð°ÐµÑ <header>/<footer>/<head>, но не div Ñ ÐºÐ»Ð°ÑÑом. Ðез Ñега ÑÑÐ¾Ñ 765 ÑÑаÑиÑнÑй блок Ñезжал в markdown-веÑÑÐ¸Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ ÑÑаÑÑи пеÑвÑм абзаÑем: 766 «РпÑоекÑе», «УÑаÑÑие», «РазделÑ», «ÐодпиÑка». ÐÐ°Ð¼ÐµÑ 06.09.2026 поÑле пÑавки: 767 1321 â 1157 Ñокенов Ñ ÑÑÑаниÑÑ Ð±ÐµÐ· Ñела â оÐ
767ºÐ¾Ð»Ð¾ 160 Ñокенов на ÑÑÑеÑ, оÑÑалÑное 768 Ñам JSON-LD, коÑоÑÑй конвеÑÑÐµÑ ÑÐ¾Ñ ÑанÑÐµÑ Ð½Ð°Ð¼ÐµÑенно. 769 ÐейÑ: scripts/markdown-noise-semantics.test.js --> 770 <div class="loading-footer-overlay__content"> 771 <div id="footer-overlay"> 772 <footer class="footer"> 773 <!-- ÐодпиÑи: ÑекÑÑ Ð² Ñеге, пеÑевод на ng-bind â не инÑеÑполÑÑией. 774 СÑÑÑÑ Ð¸Ð½ÑеÑполÑÑÐ¸Ñ Ð½Ðµ Ð²Ð¸Ð´Ð¸Ñ Ð½Ð¸ÐºÑо, кÑо не иÑполнÑÐµÑ JS. 775 Ð Ð°Ð·Ð±Ð¾Ñ Ð¸ гейÑ: scripts/static-footer-text.test.js --> 776 <div class="ds-container footer__inner"> 777 <div class="col footer__column footer__column--about"> 778 <p class="footer__heading" ng-bind="'РпÑоекÑе' | t">РпÑоекÑе</p> 779 <ul class="tw:list-none tw:pl-0"> 780 <li><a href="/manifest" ng-bind="'ÐаниÑеÑÑ' | t">ÐаниÑеÑÑ</a></li> 781 <li> 782 <a href="/guide" ng-bind="'Ðак ÑÑÑÑоен ÐиÑкÑÑÑ' | t">Ðак ÑÑÑÑоен ÐиÑкÑÑÑ</a> 783 </li> 784 <li class="tw:max-sm:hidden"> 785 <a href="/principles" ng-bind="'ÐÑинÑÐ¸Ð¿Ñ ÑообÑеÑÑва' | t">ÐÑинÑÐ¸Ð¿Ñ ÑообÑеÑÑва</a> 786 </li> 787 <li class="tw:max-sm:hidden"> 788 <a href="/dogma" ng-bind="'Ðогма оÑкÑÑÑой ÑедакÑии' | t">Ðогма оÑкÑÑÑой ÑедакÑии</a> 789 </li> 790 </ul> 791 </div> 792 <div class="col footer__column footer__column--participate"> 793 <p class="footer__heading" ng-bind="'УÑаÑÑие' | t">УÑаÑÑие</p> 794 <ul class="tw:list-none tw:pl-0"> 795 <li><a href="/help#idea" ng-bind="'ÐÑедложиÑÑ Ð¸Ð´ÐµÑ' | t">ÐÑедложиÑÑ Ð¸Ð´ÐµÑ</a></li> 796 <li><a href="/drafts" ng-bind="'СоздаÑÑ Ð¼Ð°ÑеÑиал' | t">СоздаÑÑ Ð¼Ð°ÑеÑиал</a></li> 797 <li><a href="/help" ng-bind="'ÐоддеÑжаÑÑ Ð¿ÑоекÑ' | t">ÐоддеÑжаÑÑ Ð¿ÑоекÑ</a></li> 798 <li> 799 <a href="https://forms.gle/GJ3Muqf1UjeCzyDXA" target="_blank" rel="noopener noreferrer" ng-bind="'ÐÑиÑоединиÑÑÑÑ Ðº ÑедакÑии' | t">ÐÑиÑоединиÑÑÑÑ Ðº ÑедакÑии</a> 800 </li> 801 </ul> 802 </div> 803 <div class="col footer__column footer__column--sections"> 804 <p class="footer__heading" ng-bind="'РазделÑ' | t">РазделÑ</p> 805 <ul class="tw:list-none tw:pl-0"> 806 <li><a href="/authors" ng-bind="'ÐвÑоÑÑ' | t">ÐвÑоÑÑ</a></li> 807 <li><a href="/tags" ng-bind="'ТемÑ' | t">ТемÑ</a></li> 808 <li><a href="/help#specials" ng-bind="'СпеÑпÑоекÑÑ' | t">СпеÑпÑоекÑÑ</a></li> 809 </ul> 810 </div> 811 <div class="col footer__column footer__column--subscription"> 812 <p class="footer__heading" ng-bind="'ÐодпиÑка' | t">ÐодпиÑка</p> 813 <!-- ÐлÑÑ Ñлово в Ñлово, Ñ : на RU ÑилÑÑÑ Ð²Ð¾Ð·Ð²ÑаÑÐ°ÐµÑ ÐµÐ³Ð¾ Ñам. --> 814 <p class="subscription-description" ng-bind="'ЧÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑаÑÑ ÑаÑÑÑÐ»ÐºÑ Ð»ÑÑÑÐ¸Ñ Ð¿ÑбликаÑий, 815 пÑоÑÑо ÑкажиÑе поÑÑÑ' | t"> 816 ЧÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑаÑÑ ÑаÑÑÑÐ»ÐºÑ Ð»ÑÑÑÐ¸Ñ Ð¿ÑбликаÑий, 817 пÑоÑÑо ÑкажиÑе поÑÑÑ 818 </p> 819 <!-- ФоÑма подпиÑки â в overlay Angular не иниÑиализиÑован, 820 инÑеÑакÑÐ¸Ð²Ð½Ð°Ñ ÑоÑма доÑÑÑпна в <site-footer> на загÑÑженной ÑÑÑаниÑе --> 821 </div> 822 </div> 823 </footer> 824 </div> 825 </div> 826 </div> 827 828 <!-- Editor loading skeleton: shown during editor lazy-load instead of blank white screen --> 829 <div class="editor-loading-skeleton do-animate" ng-cloak ng-if="$root.isEditorLoading && !$root.statusCode" role="status" aria-l
829ive="off" aria-busy="true" aria-label="ÐагÑÑзка ÑедакÑоÑа" ng-attr-aria-label="{{ 'ÐагÑÑзка ÑедакÑоÑа' | t }}"> 830 <!-- Skeleton header mimicking editor-header (fixed, 56px) --> 831 <div class="editor-loading-skeleton__header"> 832 <div class="editor-loading-skeleton__logo"> 833 <img src="/images/logo-mini.svg" alt="" width="26" height="28" aria-hidden="true"> 834 </div> 835 <div class="editor-loading-skeleton__header-spacer"></div> 836 <div class="editor-loading-skeleton__btn-placeholder"></div> 837 <div class="editor-loading-skeleton__btn-placeholder editor-loading-skeleton__btn-placeholder--wide"></div> 838 </div> 839 <!-- Skeleton body using same layout as real editor: 840 .container > wrapper(margin-top) > tw:sm:w-8/12 tw:md:ml-offset-2 841 Real editor: .discours-form has margin-top:-50px, row has margin-top:150px â net 100px --> 842 <div class="ds-container"> 843 <div class="tw:mt-content-top"> 844 <div class="editor-loading-skeleton__body-col tw:sm:w-8/12 tw:md:ml-offset-2"> 845 <!-- Title row: matches .title-input-row > .title-col/.cover-col with gutter padding --> 846 <div class="title-input-row tw:flex tw:flex-wrap"> 847 <div class="title-col tw:sm:w-8/12 tw:lg:w-9/12"> 848 <div class="editor-loading-skeleton__title-line"></div> 849 <div class="editor-loading-skeleton__title-line editor-loading-skeleton__title-line--short"></div> 850 </div> 851 <div class="cover-col tw:sm:w-4/12 tw:lg:w-3/12"> 852 <div class="editor-loading-skeleton__cover-placeholder"></div> 853 </div> 854 </div> 855 <!-- Editor body: shimmer lines mimicking text content --> 856 <div class="editor-loading-skeleton__editor-block"> 857 <div class="editor-loading-skeleton__text-line"></div> 858 <div class="editor-loading-skeleton__text-line editor-loading-skeleton__text-line--90"></div> 859 <div class="editor-loading-skeleton__text-line editor-loading-skeleton__text-line--75"></div> 860 <div class="editor-loading-skeleton__text-gap"></div> 861 <div class="editor-loading-skeleton__text-line"></div> 862 <div class="editor-loading-skeleton__text-line editor-loading-skeleton__text-line--85"></div> 863 <div class="editor-loading-skeleton__text-line editor-loading-skeleton__text-line--60"></div> 864 </div> 865 </div> 866 </div> 867 </div> 868 </div> 869 870 <!-- Theme menu --> 871 <div class="theme-menu-wrapper"> 872 <div class="theme-menu-container ds-container theme-menu-skeleton" ng-show="$root.isThemeMenuShowed && ($root.isRouteLoading || $root.statusCode)"> 873 <div class="theme-menu-skeleton__placeholder"></div> 874 </div> 875 <div class="theme-menu-container ds-container" ng-show="!$root.isRouteLoading && !$root.statusCode && $root.isThemeMenuShowed"> 876 <theme-menu></theme-menu> 877 </div> 878 </div> 879 880 <!-- Main banner (promotional) --> 881 <!-- No lazy-render: the component already defers its own display via $timeout (50-60s). 882 lazy-render adds .lazy-render-reveal animation whose transform: translateY(0) persists 883 (animation-fill-mode: both) and creates a new containing block, breaking 884 position: fixed on .main-banner inside â the banner jumps to the top instead of 885 sticking to the bottom of the viewport. --> 886 <div ng-if="!$root.statusCode && !$root.isStubPage"> 887 <main-banner></main-banner> 888 </div> 889 890 <!-- A11y: skip-to-content link â видима ÑолÑко пÑи ÑокÑÑе Ñ ÐºÐ»Ð°Ð²Ð¸Ð°ÑÑÑÑ --> 891 <a href="#main-content" class="skip-to-content" ng-bind="'ÐеÑейÑи к ÑодеÑжимомÑ' | t">ÐеÑейÑи к ÑодеÑжимомÑ</a> 892 893 <!-- Main app content --> 894 <main id="main-content" class="main-content" role="main"> 895 <!-- ================================================================ --> 896 <!-- Instant Hero: LCP optimization for article/expo pages --> 897 <!-- --> 898 <!-- HOMEPAGE: Worker injects complete hero card (#ssr-homepage) --> 899 <!-- into <main> â visible at FCP, removed when Angular renders. --> 900 <!-- Also has <link rel="preload"> for hero image network benefit. --> 901 <!-- --> 902 <!-- ARTICLE/EXPO: Uses MutationObserver to inject into Angular's --> 903 <!-- wrapper when it appears (these pages have different layouts). --> 904 <!-- ================================================================ --> 905
905<script> 906 (function () { 907 'use strict'; 908 909 var THUMBOR_BASE = 910 ((window.discours && window.discours.ASSETS_DOMAIN) || 'https://assets.discours.io') + 911 '/unsafe'; 912 var MEDIA_BASE = 913 (window.discours && window.discours.MEDIA_DOMAIN) || 'https://media.discours.io'; 914 var OBSERVER_TIMEOUT = 5000; 915 var path = location.pathname; 916 917 // ÐнимиÑованнÑй GIF ÑеÑайзиÑÑ Ð½ÐµÐ»ÑÐ·Ñ â Thumbor оÑдаÑÑ Ð½Ð° нÑм 502 918 // (замеÑÑ Ð¸ ÑÐ°Ð·Ð±Ð¾Ñ â в Ñапке app/scripts/shared/image-presets.js). 919 // ÐаÑÑеÑн подÑÑавлÑеÑÑÑ Ð½Ð° ÑбоÑке ÐРТÐÐÐ ÐРмодÑÐ»Ñ (webpack 920 // templateParameters), копии пÑавила здеÑÑ Ð½ÐµÑ â ÑаÑÑ Ð¾Ð´Ð¸ÑÑÑÑ Ð½ÐµÑемÑ. 921 var RE_DIRECT_MEDIA = new RegExp("\\.gif(?:[?#]|$)", 'i'); 922 function isDirectMedia(thumborId) { 923 return RE_DIRECT_MEDIA.test(thumborId); 924 } 925 926 // === Helper: URL каÑÑинки нÑжной ÑиÑÐ¸Ð½Ñ === 927 function imageUrl(thumborId, width) { 928 if (isDirectMedia(thumborId)) return MEDIA_BASE + '/' + thumborId; 929 return THUMBOR_BASE + '/' + width + 'x/' + thumborId; 930 } 931 932 // === Helper: generate srcset string === 933 // У GIF ваÑианÑов по ÑиÑине не бÑÐ²Ð°ÐµÑ â возвÑаÑаем '', Ñогда бÑаÑÐ·ÐµÑ Ð±ÐµÑÑÑ src. 934 function makeSrcset(thumborId, widths) { 935 if (isDirectMedia(thumborId)) return ''; 936 return widths 937 .map(function (w) { 938 return imageUrl(thumborId, w) + ' ' + w + 'w'; 939 }) 940 .join(', '); 941 } 942 943 // Homepage: Worker injects #ssr-homepage with complete hero card. 944 // No additional instant-hero injection needed here. 945 if (path === '/') return; 946 947 // ================================================================ 948 // ARTICLE / EXPO: MutationObserver approach (needs Angular DOM) 949 // ================================================================ 950 // Skip instant-hero injection when SSR content is present â 951 // the Worker already injected a complete article with hero image. 952 if (document.getElementById('ssr-article')) return; 953 954 var pageType = null; 955 if (/^\/articles\/[^/]+\/[^/]+/.test(path)) { 956 pageType = 'article'; 957 } else if (/^\/expo\/[^/]+\/[^/]+\/[^/]+/.test(path)) { 958 pageType = 'expo'; 959 } 960 if (!pageType) return; 961 962 // СеÑка hero и пÑавило ÐµÑ Ð¾Ð±Ñезки подÑÑавлÑÑÑÑÑ Ð½Ð° ÑбоÑке из единого 963 // модÑÐ»Ñ app/scripts/shared/image-presets.js (webpack templateParameters) â 964 // ÑÐ¾Ñ Ð¶Ðµ код иÑполнÑÑÑ Worker-preload (ArticleHeadHandler), Worker-SSR 965 // и Angular-ÑилÑÑÑ imageSrcset. Ðопии пÑавила здеÑÑ ÐÐТ: Ñовпадение 966 // гаÑанÑиÑовано ÑбоÑкой, инаÑе бÑаÑÐ·ÐµÑ Ð½Ðµ ÑмаÑÑÐ¸Ñ preload Ñ <img srcset> 967 // и ÑкаÑÐ°ÐµÑ hero-каÑÑÐ¸Ð½ÐºÑ Ð´Ð²Ð°Ð¶Ð´Ñ. 968 // 969 // Ðиже поÑвлÑÑÑÑÑ HERO_WIDTHS, heroWidthsFor() и heroBaseWidth(). 970 var HERO_WIDTHS = [320,640,960,1280,1920,2560]; 971var HERO_BASE_WIDTH = 960; 972var NATURAL_STEP_MIN_GAIN = 1.1; 973var MIN_SANE_NATURAL_WIDTH = 64; 974function normalizeNaturalWidth(naturalWidth) {
975 if (typeof naturalWidth !== 'number' || !isFinite(naturalWidth)) return 0; 976 var floored = Math.floor(naturalWidth); 977 return floored >= MIN_SANE_NATURAL_WIDTH ? floored : 0; 978} 979function trimWidthsToNatural(widths, naturalWidth) { 980 var natural = normalizeNaturalWidth(naturalWidth); 981 var max = widths[widths.length - 1]; 982 if (!natural || natural >= max) return widths.slice(); 983 984 var trimmed = widths.filter(function (w) { 985 return w <= natural; 986 }); 987 var top = trimmed[trimmed.length - 1]; 988 // top Ð½ÐµÑ Ð²Ð¾Ð²Ñе â оÑигинал мелÑÑе Ñамой мелкой ÑÑÑпени, Ñогда он и еÑÑÑ 989 // единÑÑвеннÑй ÑазÑмнÑй ваÑианÑ. 990 if (!top || natural >= top * NATURAL_STEP_MIN_GAIN) trimmed.push(natural); 991 return trimmed; 992} 993function pickBaseWidth(widths, preferred) { 994 var within = widths.filter(function (w) { 995 return w <= preferred; 996 }); 997 return within.length ? within[within.length - 1] : widths[0]; 998} 999function heroWidthsFor(naturalWidth) { 1000 return trimWidthsToNatural(HERO_WIDTHS, naturalWidth); 1001} 1002function heroBaseWidth(naturalWidth) {
1003 return pickBaseWidth(heroWidthsFor(naturalWidth), HERO_BASE_WIDTH); 1004} 1005 var HERO_SIZES = '(min-width: 768px) 66vw, 100vw'; 1006 var configs = { 1007 article: { 1008 dataId: 'preloaded-article', 1009 selector: '.open-post-title-image-wrapper', 1010 imgClass: 'instant-hero-article__img', 1011 imgSizes: HERO_SIZES, 1012 isLoaded: function (wrapper) { 1013 return wrapper.classList.contains('is-image-loaded'); 1014 }, 1015 getThumbor: function (data) { 1016 return data && data.thumborId; 1017 }, 1018 }, 1019 expo: { 1020 dataId: 'preloaded-article', 1021 selector: '.expo-item-title-image-wrapper', 1022 imgClass: 'instant-hero-article__img', 1023 imgSizes: HERO_SIZES, 1024 isLoaded: function (wrapper) { 1025 return wrapper.classList.contains('is-image-loaded'); 1026 }, 1027 getThumbor: function (data) { 1028 return data && data.thumborId; 1029 }, 1030 }, 1031 }; 1032 1033 var config = configs[pageType]; 1034 1035 function observeFor(tryInject) { 1036 if (tryInject()) return; 1037 var observer = new MutationObserver(function (mutations, obs) { 1038 if (tryInject()) { 1039 obs.disconnect(); 1040 } 1041 }); 1042 observer.observe(document.body, { childList: true, subtree: true }); 1043 setTimeout(function () { 1044 observer.disconnect(); 1045 }, OBSERVER_TIMEOUT); 1046 } 1047 1048 // Continuous aspect-ratio конÑейнеÑа обложки ÑÑÑаниÑÑ Ð¿ÑбликаÑии/ÑкÑпо 1049 // под ÑеалÑнÑй ratio каÑÑинки в [4:3, 16:9]. Ð¡Ð¾Ð²Ð¿Ð°Ð´Ð°ÐµÑ Ñ ngImageOnload.js. 1050 // 1051 // Scope: inline aspect-ratio ÑÑавиÑÑÑ Ð¢ÐÐЬÐÐ Ð´Ð»Ñ wrapper'ов обложки 1052 // ÑÑÑаниÑÑ Ð¿ÑбликаÑии (.open-post-title-image-wrapper) или ÑкÑпо 1053 // (.expo-item-title-image-wrapper). Ð Ñиде и дÑÑÐ³Ð¸Ñ Ð¼ÐµÑÑÐ°Ñ Ð¿Ð°ÑÐµÐ½Ñ img 1054 // Ð¸Ð¼ÐµÐµÑ Ñвои breakpoint-завиÑимÑе пÑавила aspect-ratio â пеÑепиÑÑваÑÑ 1055 // нелÑзÑ, layout Ñида поломаеÑÑÑ. 1056 function applyOrientation(img, wrapper) { 1057 if (!img || !wrapper) return; 1058 var w = img.naturalWidth; 1059 var h = img.naturalHeight; 1060 if (!w || !h) return; 1061 var ratio = w / h; 1062 if (ratio >= 1) { 1063 wrapper.classList.add('is-landscape'); 1064 } else if (ratio >= 0.75) { 1065 wrapper.classList.add('is-portrait'); 1066 } else { 1067 wrapper.classList.add('is-very-portrait'); 1068 } 1069 var isPostCoverWrapper = 1070 wrapper.classList && 1071 (wrapper.classList.contains('open-post-title-image-wrapper') || 1072 wrapper.classList.contains('expo-item-title-image-wrapper')); 1073 if (!isPostCoverWrapper) return; 1074 var clamped = Math.max(4 / 3, Math.min(16 / 9, ratio)); 1075 var value = clamped.toFixed(4); 1076 if (!wrapper.style.aspectRatio) { 1077 wrapper.style.aspectRatio = value; 1078 } 1079 if (!wrapper.style.getPropertyValue('--cover-ratio')) { 1080 wrapper.style.setProperty('--cover-ratio', value); 1081 } 1082 } 1083 1084 observeFor(function tryInject() { 1085 var dataEl = document.getElementById(config.dataId); 1086 if (!dataEl) return false; 1087 var wrapper = document.querySelector(config.selector); 1088 if (!wrapper) return false; 1089 if (config.isLoaded(wrapper)) return true; 1090 if (wrapper.querySelector('.' + config.imgClass)) return true; 1091 try { 1092 var data = JSON.parse(dataEl.textContent); 1093 var thumborId = config.getThumbor(data); 1094 if (!thumborId) return true; 1095 // coverImageWidth кладÑÑ Worker в #preloaded-article. Ðока его Ð
1095½ÐµÑ 1096 // (legacy-маÑеÑиал, оÑÐ²ÐµÑ Ð±ÐµÐ· ÑазмеÑов) â ÑеÑка оÑÑаÑÑÑÑ Ð¿Ð¾Ð»Ð½Ð¾Ð¹, 1097 // Ñо еÑÑÑ Ñовно пÑежнее поведение. 1098 var naturalWidth = data && data.coverImageWidth; 1099 var img = document.createElement('img'); 1100 img.className = config.imgClass; 1101 img.src = imageUrl(thumborId, heroBaseWidth(naturalWidth)); 1102 img.srcset = makeSrcset(thumborId, heroWidthsFor(naturalWidth)); 1103 img.sizes = config.imgSizes; 1104 img.alt = ''; 1105 img.loading = 'eager'; 1106 img.decoding = 'async'; 1107 img.fetchPriority = 'high'; 1108 img.onload = function () { 1109 applyOrientation(img, wrapper); 1110 }; 1111 wrapper.appendChild(img); 1112 // If image is served from cache, onload may have already fired 1113 if (img.complete && img.naturalWidth > 0) { 1114 applyOrientation(img, wrapper); 1115 } 1116 return true; 1117 } catch (e) { 1118 return true; 1119 } 1120 }); 1121 })(); 1122 </script>
1122 1123 1124 <!-- Route loading bar â shown during lazy controller loading --> 1125 <div class="route-loading-bar" ng-if="$root.lazyRouteLoading" ng-cloak> 1126 <div class="route-loading-bar__progress"></div> 1127 </div> 1128 1129 <ng-view autoscroll="true" ng-show="!$root.isRouteLoading" ng-cloak></ng-view> 1130 1131 <!-- SSR Content Cleanup: Remove pre-rendered content when Angular renders. 1132 For articles (#ssr-article): controller signals readiness via window.__removeSSR() 1133 after ng-include renders the article body. This prevents the flash where SSR is 1134 hidden before Angular content is painted (MutationObserver fires too early because 1135 the route template is inserted before ng-include loads postMainPart.html). 1136 For homepage (#ssr-homepage): MutationObserver detects Angular content insertion. --> 1137
1137<script> 1138 (function () { 1139 try { 1140 var ssr = 1141 document.getElementById('ssr-article') || 1142 document.getElementById('ssr-homepage') || 1143 document.getElementById('ssr-tag') || 1144 document.getElementById('ssr-author') || 1145 document.getElementById('ssr-about'); 1146 if (!ssr) return; 1147 1148 var removed = false; 1149 var isArticle = ssr.id === 'ssr-article'; 1150 var observer; 1151 var detachRetries = 0; 1152 var pinned = false; 1153 var handedOff = false; 1154 1155 // У ÑÑаÑÑÑ /не-HD Ñоликов YouTube maxresdefault Ð¼Ð¾Ð¶ÐµÑ Ð²ÐµÑнÑÑÑ Ð½Ðµ 1156 // оÑибкÑ, а ÑлÑжебнÑÑ ÐºÐ°ÑÑÐ¸Ð½ÐºÑ 120Ã90. Angular Ð»Ð¾Ð²Ð¸Ñ ÐµÑ Ð´Ð¸ÑекÑивой 1157 // ng-image-onload; SSR вне ng-view не компилиÑÑеÑÑÑ, поÑÑÐ¾Ð¼Ñ Ð´ÐµÐ»Ð°ÐµÑ 1158 // ÑÐ¾Ñ Ð¶Ðµ пеÑÐµÑ Ð¾Ð´ на гаÑанÑиÑованнÑй hqdefault Ñам. Ð Ð°Ð·Ð¼ÐµÑ ÐºÐ¾Ð½ÑейнеÑа 1159 // не менÑеÑÑÑ, Ñо еÑÑÑ fallback не ÑоздаÑÑ layout shift. 1160 (function armVideoPosterFallback() { 1161 var poster = ssr.querySelector('.ssr-video-poster-img[data-fallback-src]'); 1162 if (!poster) return; 1163 var fallbackSrc = poster.getAttribute('data-fallback-src'); 1164 if (!fallbackSrc) return; 1165 1166 function cleanup() { 1167 poster.removeEventListener('load', validatePoster); 1168 poster.removeEventListener('error', useFallback); 1169 } 1170 function useFallback() { 1171 cleanup(); 1172 poster.removeAttribute('data-fallback-src'); 1173 if (poster.src !== fallbackSrc) poster.src = fallbackSrc; 1174 } 1175 function validatePoster() { 1176 if (!poster.naturalWidth || poster.naturalWidth <= 120) useFallback(); 1177 else cleanup(); 1178 } 1179 1180 poster.addEventListener('load', validatePoster); 1181 poster.addEventListener('error', useFallback); 1182 if (poster.complete) validatePoster(); 1183 })(); 1184 1185 // ÐÐ°Ð´Ñ Ð¼Ð¾Ð¶ÐµÑ Ð½Ðµ наÑÑÑпиÑÑ ÐÐÐÐÐÐÐ. Ðока вкладка не композиÑÐ¸Ñ ÐºÐ°Ð´ÑÑ 1186 // â ÑÐ¾Ð½Ð¾Ð²Ð°Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ°, ÑвÑÑнÑÑое или полноÑÑÑÑ Ð¿ÐµÑекÑÑÑое окно â 1187 // requestAnimationFrame не вÑзÑваеÑÑÑ, Ñогда как setTimeout 1188 // пÑÐ¾Ð´Ð¾Ð»Ð¶Ð°ÐµÑ ÑабоÑаÑÑ (Ð·Ð°Ð¼ÐµÑ 26.08.2026 на пÑоде: Ñвежий rAF не 1189 // ÑÑабоÑал за минÑÑÑ, setTimeout(â¦, 0) ÑÑабоÑал в Ñом же кадÑе 1190 // кода). ÐоÑÑÐ¾Ð¼Ñ Ð²ÑÑ, ÑÑо ÐÐЯÐÐÐРпÑоизойÑи, ждÑÑ ÐºÐ°Ð´Ñа, но не 1191 // долÑÑе ÑÑока: в видимой вкладке вÑигÑÑÐ²Ð°ÐµÑ ÐºÐ°Ð´Ñ (~16 мÑ) и 1192 // поведение не менÑеÑÑÑ, в невидимой â ÑаймеÑ. 1193 // 1194 // Ðез ÑÑого ÑÑаÑÑÑ, оÑкÑÑÑÐ°Ñ Ð²Ð¾ вкладке без кадÑов, оÑÑавалаÑÑ Ñ 1195 // ÑеÑвеÑнÑм ÑендеÑом ÐÐÐСÐÐÐÐ: `removed` взводиÑÑÑ Ð´Ð¾ вÑбоÑа веÑки, 1196 // веÑка «обложка Ñже гоÑова» ÑÑ Ð¾Ð´Ð¸Ð»Ð° в doHandoff единÑÑвенной 1197 // доÑогой â ÑеÑез кадÑ, â и вÑе ÑледÑÑÑие Ð·Ð°Ñ Ð¾Ð´Ñ (Ñигнал 1198 // конÑÑоллеÑа, 3-ÑекÑнднÑй ÑÐ°Ð¹Ð¼ÐµÑ waitForContentAndRemoveSSR, 1199 // безÑÑловнÑй бÑкÑÑоп ниже) гаÑилиÑÑ Ð¿ÐµÑвой ÑÑÑокой removeSSR(): 1200 // `if (removed) return`. ЧиÑаÑÐµÐ»Ñ Ð½Ðµ видел ни комменÑаÑиев, ни 1201 // ÑемаÑок, ни блока «поддеÑжаÑÑ». 1202 // ÐейÑ: scripts/ssr-handoff-without-frames.test.js. 1203 function onFrameOrTimeout(fn, ms) { 1204 var done = false;
1205 function run() { 1206 if (done) return; 1207 done = true; 1208 fn(); 1209 } 1210 var timer = setTimeout(run, ms); 1211 requestAnimationFrame(function () { 1212 clearTimeout(timer); 1213 run(); 1214 }); 1215 } 1216 1217 // Узел ng-view, Ð·Ð°Ñ Ð²Ð°ÑеннÑй пÑи ÑазбоÑе докÑменÑа: ÑолбÑк Ð´Ð»Ñ Ð³ÐµÐ¹Ñа 1218 // ниже. Свежий запÑÐ¾Ñ Ð½Ð°Ð´Ñжнее, но еÑли он не наÑÑл Ñзла â лÑÑÑе 1219 // ÑпÑоÑиÑÑ Ð·Ð°Ñ Ð²Ð°ÑеннÑй, Ñем закÑÑÑÑ Ð³ÐµÐ¹Ñ Ð²Ñ Ð¾Ð»Ð¾ÑÑÑÑ. 1220 var capturedNgView = null; 1221 1222 /** 1223 * ÐоÑова ли ÑÑÑаниÑа к ÑнÑÑÐ¸Ñ ÑендеÑа по пÑизнакÑ, коÑоÑÑй Ð´Ð»Ñ Ð½ÐµÑ 1224 * главнÑй. ÐÐÐÐСТÐÐÐÐÐРопÑеделение кÑиÑеÑиÑ: его зовÑÑ Ð¸ веÑка 1225 * оÑпÑÑÐºÐ°Ð½Ð¸Ñ Ð¿Ð¸Ð½Ð°, и наблÑдаÑÐµÐ»Ñ Ð²ÑÑавок, и поллеÑ. 1226 * 1227 * ÐдаÑÑ Ð·Ð´ÐµÑÑ Ð¸Ð¼ÐµÐµÑ ÑмÑÑл ÑолÑко ÑегÑ. У ÑÑаÑÑи и авÑоÑа гоÑовноÑÑÑ 1228 * опÑеделÑÐµÑ hero-каÑÑинка â ÐµÑ Ð¶Ð´ÑÑ Ñам removeSSR(). У главной Ð³ÐµÐ¹Ñ 1229 * оÑкÑÑÑ, и Ð¿Ð¾Ð»Ð»ÐµÑ ÑÐ½Ð¸Ð¼Ð°ÐµÑ ÑÐµÐ½Ð´ÐµÑ Ð½Ð° пеÑвом же Ñике, ÑеÑез 100 мÑ: 1230 * оÑделÑнÑй кÑиÑеÑий Ð´Ð»Ñ Ð½ÐµÑ Ð±Ñл Ð±Ñ Ð¼ÑÑÑвÑм кодом Ñ Ð²Ð¸Ð´Ð¾Ð¼ 1231 * ÑабоÑаÑÑего. ÐÐ°Ð¼ÐµÑ 27.08.2026: на пÑоде воÑÐºÐµÑ #ssr-homepage вообÑе 1232 * не оÑдаÑÑ, веÑка не иÑполнÑеÑÑÑ. 1233 */ 1234 function contentGateOpen() { 1235 // СÑабилÑнÑй id ÑÐ¾Ñ ÑанÑеÑÑÑ Ð½Ð° веÑÑ Ð¶Ð¸Ð·Ð½ÐµÐ½Ð½Ñй Ñикл ÑкÑипÑа; ÑÑÑлка 1236 // `ssr` поÑле завеÑÑÑнного handoff Ñже Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¾Ð±Ð½Ñлена. 1237 // СÑÑаниÑÑ Â«Ð Ð¿ÑоекÑе» (#ssr-about): кÑиÑиÑеÑкой каÑÑинки неÑ, а 1238 // без гейÑа Ð¿Ð¾Ð»Ð»ÐµÑ ÑнÑл Ð±Ñ ÑеÑвеÑнÑй ÑекÑÑ Ð½Ð° пеÑвом Ñике â за 1239 // ÑекÑÐ½Ð´Ñ Ð´Ð¾ Ñого, как ленивÑй ÑоÑÑ Ð´Ð¾ÑиÑÑÐµÑ Ñвой. ÐдÑм `.ds-container` 1240 // Ñаблона about-ÑÑÑаниÑÑ Ð²Ð½ÑÑÑи ng-view (Ñ help-us ÑÑо не коÑенÑ, а 1241 // поÑомок â querySelector иÑÐµÑ Ð²Ð³Ð»ÑбÑ); ÑолбÑк по ÑаймеÑÑ Ð¾Ð±Ñий. 1242 if (ssrId === 'ssr-about') { 1243 var ngAbout = document.querySelector('ng-view, [ng-view]') || capturedNgView; 1244 return !!(ngAbout && ngAbout.querySelector('.ds-container')); 1245 } 1246 if (ssrId !== 'ssr-tag') return true; 1247 // Свежий запÑÐ¾Ñ ng-view: Ð·Ð°Ñ Ð²Ð°ÑеннÑй пÑи паÑÑинге Ñзел Ð¼Ð¾Ð¶ÐµÑ 1248 // ÑÑÑаÑеÑÑ, еÑли Angular его пеÑеÑоздаÑÑ, â Ñогда Ð³ÐµÐ¹Ñ Ð»Ð¾Ð¶Ð½Ð¾ веÑнÑл 1249 // Ð±Ñ false, и ни наблÑдаÑелÑ, ни Ð¿Ð¾Ð»Ð»ÐµÑ Ð½Ðµ ÑнÑли Ð±Ñ ÑÐµÐ½Ð´ÐµÑ Ð¿Ñи 1250 // гоÑовом Ñиде. ÐÐ°Ñ Ð²Ð°ÑеннÑй оÑÑаÑÑÑÑ ÑолбÑком. 1251 var ng = document.querySelector('ng-view, [ng-view]') || capturedNgView; 1252 return !!( 1253 ng && 1254 (ng.querySelector('.articles-container--tag-page .article > .article__inner') || 1255 ng.querySelector('.feed-empty')) 1256 ); 1257 } 1258 1259 /** 1260 * ÐодклÑÑÐ°ÐµÑ Ð½Ð°Ð±Ð»Ñдение за ÑеалÑнÑм Angular-конÑенÑом. ÐаблÑдаÑÐµÐ»Ñ 1261 * можно безопаÑно вклÑÑиÑÑ Ð¿Ð¾Ð²ÑоÑно поÑле disconnect(): ÑÑо нÑжно 1262 * ÑÐµÐ³Ñ Ð¿Ð¾Ñле ÑÑпеÑного retry ÑоÑÑа, поÑÐ¾Ð¼Ñ ÑÑо __pinSSR оÑклÑÑÐ°ÐµÑ 1263 * иÑÑ Ð¾Ð´Ð½ÑÑ Ð¿Ð¾Ð´Ð¿Ð¸ÑкÑ, пока ÑеÑвеÑÐ½Ð°Ñ Ð»ÐµÐ½Ñа пÑибиÑа повеÑÑ Ð¾Ñибки. 1264 */ 1265 function observeContentMutations() { 1266 var ng = document.querySelector('ng-view, [ng-view]') || capturedNgView; 1267 var container = ng ? ng.parentNode : null; 1268 if (!ng || !container) return false;
1269 if (ssrId === 'ssr-tag') capturedNgView = ng; 1270 1271 if (!observer) { 1272 observer = new MutationObserver(function (mutations, obs) { 1273 for (var i = 0; i < mutations.length; i++) { 1274 var added = mutations[i].addedNodes; 1275 for (var j = 0; j < added.length; j++) { 1276 var node = added[j]; 1277 if (node.nodeType === 1 && node.id !== ssrId && node.tagName !== 'SCRIPT') { 1278 if (contentGateOpen()) { 1279 obs.disconnect(); 1280 removeSSR(); 1281 return; 1282 } 1283 // Tag: конÑÐµÐ½Ñ ÐµÑÑ Ð½Ðµ гоÑов â пÑодолжаем наблÑдаÑÑ. 1284 // ÐРвÑзÑваем disconnect, ждÑм ÑледÑÑÑей мÑÑаÑии. 1285 } 1286 } 1287 } 1288 }); 1289 } 1290 1291 try { 1292 observer.observe(container, { childList: true }); 1293 // AngularJS вÑÑавлÑÐµÑ ÐºÐ¾Ð½ÑÐµÐ½Ñ ÐÐУТРРng-view (не заменÑÑ ÑлеменÑ). 1294 // ÐÐ»Ñ tag-ÑÑÑÐ°Ð½Ð¸Ñ Ð½Ñжен subtree, ÑÑÐ¾Ð±Ñ Ð²Ð¸Ð´ÐµÑÑ ÐºÐ¾Ð¼Ð¿Ð¸Ð»ÑÑÐ¸Ñ <tag-page>. 1295 observer.observe(ng, { 1296 childList: true, 1297 subtree: ssrId === 'ssr-tag', 1298 }); 1299 return true; 1300 } catch (e) { 1301 return false; 1302 } 1303 } 1304 1305 var reenterCycle = 0; 1306 // Ðжидание hero-каÑÑинки: наблÑдаÑÐµÐ»Ñ Ð¸ его ÑолбÑк живÑÑ ÑнаÑÑжи 1307 // removeSSR(), ÑÑÐ¾Ð±Ñ Ð¿Ð¾Ð²ÑоÑнÑй Ð·Ð°Ñ Ð¾Ð´ мог погаÑиÑÑ Ð¿Ñежнее ожидание, 1308 // а не подпиÑаÑÑÑÑ Ð¿Ð¾Ð²ÐµÑÑ Ð½ÐµÐ³Ð¾. 1309 var heroObs = null; 1310 var heroFallbackTimer = null; 1311 function stopHeroWatch() { 1312 if (heroObs) { 1313 try { 1314 heroObs.disconnect(); 1315 } catch (e) { 1316 /* ignored */ 1317 } 1318 heroObs = null; 1319 } 1320 if (heroFallbackTimer) { 1321 clearTimeout(heroFallbackTimer); 1322 heroFallbackTimer = null; 1323 } 1324 } 1325 /** 1326 * ÐовÑоÑнÑй Ð·Ð°Ñ Ð¾Ð´ поÑле оÑпÑÑÐºÐ°Ð½Ð¸Ñ Â«Ð¿ÑибиÑого» ÑендеÑа. 1327 * 1328 * `removed` взводиÑÑÑ Ð² ÐÐЧÐÐÐ handoff и назад не оÑпÑÑкаеÑÑÑ, а 1329 * пÑибиÑÑ ÑÐµÐ½Ð´ÐµÑ Ð¼Ð¾Ð³Ð»Ð¸ Ñже ÐÐСÐÐ Ñигнала конÑÑоллеÑа: ÑиÑаÑÐµÐ»Ñ ÑÑÑл 1330 * по ÑÑÑлке в Ñе ÑекÑндÑ, пока идÑÑ handoff, и новÑй ÑоÑÑ Ñпал. Тогда 1331 * __unpinSSR() возвÑаÑал true, но removeSSR() гаÑилÑÑ ÑобÑÑвенной 1332 * пеÑвой ÑÑÑокой `if (removed) return`, и ÑÑÑаниÑа навÑегда 1333 * оÑÑавалаÑÑ ÑеÑвеÑнÑм ÑендеÑом под 1334 * `#ssr-*:not(.ssr-hiding) ~ ng-view { display: none }`. Ðока handoff 1335 * не ÑоÑÑоÑлÑÑ, Ð·Ð°Ñ Ð¾Ð´ обÑзан пÑойÑи заново; повÑоÑнÑе обозÑеваÑели 1336 * безвÑÐµÐ´Ð½Ñ â doHandoff заÑиÑÑн `handedOff`. 1337 * ÐейÑ: scripts/ssr-pin-on-route-error.test.js. 1338 * 1339 * @param {boolean} [immediate] â ÑнÑÑÑ ÐРдожидаÑÑÑ ÐºÐ¾Ð½ÑенÑа. СÑÐ°Ð²Ð¸Ñ 1340 * ÑолÑко Ñмена адÑеÑа: ÑÐµÐ½Ð´ÐµÑ Ñам оÑноÑиÑÑÑ Ðº ÐÐ ÐÐÐÐÐ ÑÑÑаниÑе, и 1341 * ждаÑÑ Ð»ÐµÐ½ÑÑ Ð½Ð¾Ð²Ð¾Ð¹ беÑÑмÑÑленно â пока блок виÑÐ¸Ñ Ð±ÐµÐ· .ssr-hiding, 1342 * CSS деÑÐ¶Ð¸Ñ ng-view под display:none, и аваÑийнÑй ÑкÑан нового 1343 * ÑоÑÑа невидим. ÐÑпÑÑкание пина ÑÑого не пеÑедаÑÑ. 1344 */ 1345 function reenterRemoveSSR(immediate) { 1346 // Сбои ÑоÑÑа идÑÑ Ð¿Ð°Ñками: поколение гаÑÐ¸Ñ Ñики, оÑÑавÑиеÑÑ Ð¾Ñ
1347 // пÑоÑлÑÑ Ð·Ð°Ñ Ð¾Ð´Ð¾Ð², ÑÑÐ¾Ð±Ñ Ð¾Ð½Ð¸ не ÑнÑли ÑÐµÐ½Ð´ÐµÑ Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ñикла. 1348 waitThenRemoveSSR(immediate, ++reenterCycle); 1349 } 1350 1351 function waitThenRemoveSSR(immediate, cycle) { 1352 // Цикл ÑÑÑаÑел â Ð·Ð°Ñ Ð¾Ð´ бÑл пеÑезапÑÑен новÑм оÑпÑÑканием. 1353 if (cycle !== reenterCycle) return; 1354 // ÐÑпÑÑкание пина не должно Ð¾Ð±Ñ Ð¾Ð´Ð¸ÑÑ Ð³ÐµÐ¹Ñ Ð³Ð¾ÑовноÑÑи. У Ñега 1355 // ÑнÑÑие на пÑÑÑом ng-view Ð¾Ð±Ð½Ð°Ð¶Ð°ÐµÑ Ð¿ÑÑÑоÑÑ Ð²Ð¼ÐµÑÑо ленÑÑ, коÑоÑÑÑ 1356 // ÑиÑаÑÐµÐ»Ñ Ñже видел, â Ñа ÑÐ°Ð¼Ð°Ñ Ð²ÑпÑÑка, пÑоÑив коÑоÑой и заведено 1357 // ожидание (Ñ Ð¾ÑÑалÑнÑÑ Ð³ÐµÐ¹Ñ Ð¾ÑкÑÑÑ, Ñм. contentGateOpen вÑÑе). 1358 // ÐоÑле route-error наблÑдаÑÐµÐ»Ñ Ð¾ÑклÑÑÑн пÑибиÑием. ÐодклÑÑаем его 1359 // заново и вÑÑ Ð¾Ð´Ð¸Ð¼ без поллинга: позднÑÑ ÐºÐ°ÑÑоÑка Ñама завеÑÑÐ¸Ñ 1360 // handoff, а на навÑегда пÑÑÑом Ñиде не оÑÑанеÑÑÑ Ð²ÐµÑного ÑаймеÑа. 1361 // ÐовÑоÑÐ½Ð°Ñ Ð¿ÑовеÑка поÑле observe закÑÑÐ²Ð°ÐµÑ Ð³Ð¾Ð½ÐºÑ, еÑли каÑÑоÑка 1362 // поÑвилаÑÑ Ð¼ÐµÐ¶Ð´Ñ Ð¿ÐµÑвой пÑовеÑкой гейÑа и подпиÑкой. 1363 if (!immediate && !handedOff && !contentGateOpen()) { 1364 observeContentMutations(); 1365 if (contentGateOpen()) waitThenRemoveSSR(immediate, cycle); 1366 return; 1367 } 1368 if (!handedOff) { 1369 removed = false; 1370 detachRetries = 0; 1371 } 1372 removeSSR(); 1373 } 1374 1375 // ÐваÑийнÑй ÑкÑан ÑоÑÑа (route-error-screen.js) ÑиÑÑеÑÑÑ Ð²Ð½ÑÑÑÑ <ng-view>, 1376 // когда ÑÐ¾Ñ ÐµÑÑÑ Ð² DOM; на неÑобÑавÑемÑÑ ÑоÑÑе диÑекÑива ngView вÑÑÐµÐ·Ð°ÐµÑ 1377 // его (transclude: 'element'), и ÑкÑан допиÑÑваеÑÑÑ Ð² <main>. 1378 // ÐÑли в ÑÑÐ¾Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ ÑнÑÑÑ ÑеÑвеÑнÑй ÑендеÑ, ÑиÑаÑÐµÐ»Ñ Ð¿Ð¾Ð»ÑÑÐ¸Ñ Ð²Ð¼ÐµÑÑо 1379 // гоÑовой ÑÑÑаниÑÑ ÑÐµÑ Ð½Ð¸ÑеÑкÑÑ ÐºÐ°ÑÑоÑÐºÑ Â«Ðедленное Ñоединениеâ¦Â» â а 1380 // Googlebot иÑполнÑÐµÑ JS и в Ñнимке Ð²Ð¸Ð´Ð¸Ñ Ñовно еÑ. Так ÑекÑÑ ÑеÑÑаев 1381 // попал в ÑÐ½Ð¸Ð¿Ð¿ÐµÑ Ð¿ÑоÑÐ¸Ð»Ñ Ð² поиÑке (Ð·Ð°Ð¼ÐµÑ 26.08.2026). ÐоÑÑÐ¾Ð¼Ñ Ð¿Ñи 1382 // оÑибке ÑоÑÑа ÑеÑвеÑнÑй ÑÐµÐ½Ð´ÐµÑ ÐÐ ÐÐÐÐÐÐТСЯ: пока #ssr-* без .ssr-hiding, 1383 // CSS деÑÐ¶Ð¸Ñ ng-view под display:none, и аваÑийнÑй ÑкÑан не виден ни 1384 // ÑиÑаÑелÑ, ни индекÑаÑоÑÑ. СÑÑлки в SSR â обÑÑнÑе <a href>, навигаÑÐ¸Ñ 1385 // оÑÑаÑÑÑÑ ÑабоÑей. ÐÑклÑÑение â Ð²Ð¸Ð´Ñ 'lock' (403) и 'missing' (404): 1386 // Ñам бÑкенд Ñвно Ñказал, ÑÑо доÑÑÑпа Ð½ÐµÑ Ð¸Ð»Ð¸ ÑÑÑаниÑÑ Ð½Ðµ ÑÑÑеÑÑвÑеÑ, 1387 // показÑваÑÑ ÐºÐ¾Ð½ÑÐµÐ½Ñ Ð½ÐµÐ»ÑзÑ, и модÑÐ»Ñ Ñам зовÑÑ __removeSSR (Ñм. routes.js). 1388 // ÐозвÑаÑÐ°ÐµÑ true, еÑли ÑеÑвеÑнÑй ÑÐµÐ½Ð´ÐµÑ ÐРЯÐРСÐÐЧÐС на ÑкÑане, â 1389 // ÑолÑко Ñогда вÑзÑваÑÑÐµÐ¼Ñ ÐµÑÑÑ ÑÑо пÑибиваÑÑ. `removed` Ð´Ð»Ñ ÑÑого не 1390 // годиÑÑÑ: он взводиÑÑÑ Ð² наÑале handoff, когда Ñзел еÑÑ Ð²Ð¸Ð´ÐµÐ½. 1391 var ssrId = ssr.id; 1392 // ÐдÑеÑ, по коÑоÑÐ¾Ð¼Ñ Ð¿ÑиÑÑл докÑменÑ: ÑкÑÐ¸Ð¿Ñ Ð¸ÑполнÑеÑÑÑ Ð¿Ñи ÑазбоÑе 1393 // HTML, поÑÑÐ¾Ð¼Ñ Ð·Ð´ÐµÑÑ ÐµÑÑ Ð½ÐµÑ Ð½Ð¸ÐºÐ°ÐºÐ¾Ð¹ SPA-навигаÑии. СÑавнение Ñ Ð½Ð¸Ð¼ 1394 // заÑиÑÐ°ÐµÑ Ð¾Ñ Ð¿Ð¾Ð´Ð¼ÐµÐ½Ñ: поÑле пеÑÐµÑ Ð¾Ð´Ð° на дÑÑгой ÑоÑÑ ÑеÑвеÑнÑй ÑÐµÐ½Ð´ÐµÑ 1395 // оÑноÑиÑÑÑ Ðº ÐÐ ÐÐÐÐÐ ÑÑÑаниÑе, и оÑÑавиÑÑ ÐµÐ³Ð¾ â знаÑÐ¸Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°ÑÑ ÑÑжой 1396 // маÑеÑиал под новÑм адÑеÑом. 1397 var ssrPath = window.location.pathname; 1398 window.__pinSSR = function () { 1399 if (window.location.pathname !== ssrPath) { 1400 // ÐдÑÐµÑ ÑменилÑÑ â ÑÐµÐ½Ð´ÐµÑ Ð¾Ð¿Ð¸ÑÑÐ²Ð°ÐµÑ ÐÐ ÐÐÐЮЮ ÑÑÑаниÑÑ. Ðало Ñого
1401 // ÑÑо пÑибиваÑÑ ÐµÐ³Ð¾ нелÑзÑ: пока он виÑÐ¸Ñ Ð±ÐµÐ· .ssr-hiding, CSS 1402 // деÑÐ¶Ð¸Ñ ng-view под display:none, и аваÑийнÑй ÑкÑан нового ÑоÑÑа 1403 // оказалÑÑ Ð±Ñ Ð½ÐµÐ²Ð¸Ð´Ð¸Ð¼Ñм â ÑиÑаÑÐµÐ»Ñ ÑмоÑÑел Ð±Ñ ÑÑаÑÑй маÑеÑиал под 1404 // новÑм адÑеÑом. ÐоÑÑÐ¾Ð¼Ñ Ð·Ð´ÐµÑÑ ÑÐµÐ½Ð´ÐµÑ Ð¸Ð¼ÐµÐ½Ð½Ð¾ СÐÐÐÐÐТСЯ â и ÑÑазÑ, 1405 // без гейÑа гоÑовноÑÑи: ждаÑÑ Ð»ÐµÐ½ÑÑ ÐÐÐÐÐ ÑÑÑаниÑÑ Ð½ÐµÐ·Ð°Ñем, пока 1406 // на ÑкÑане виÑÐ¸Ñ ÑÐµÐ½Ð´ÐµÑ ÐÐ ÐÐÐÐÐ. 1407 pinned = false; 1408 reenterRemoveSSR(true); 1409 return false; 1410 } 1411 pinned = true; 1412 if (observer) { 1413 try { 1414 observer.disconnect(); 1415 } catch (e) { 1416 /* ignored */ 1417 } 1418 } 1419 // Ðжидание обложки гаÑим вмеÑÑе Ñ Ð½Ð°Ð±Ð»ÑдаÑелем вÑÑавок: пока ÑÐµÐ½Ð´ÐµÑ 1420 // пÑибиÑ, doHandoff вÑÑ Ñавно вÑÑ Ð¾Ð´Ð¸Ñ Ð¿Ð¾ `pinned`, а подпиÑка Ñ 1421 // subtree и Ñлежением за клаÑÑами пÑодолжала Ð±Ñ Ð·Ð²Ð°ÑÑ ÐºÐ¾Ð»Ð±Ñк на 1422 // каждÑÑ ÑÐ¼ÐµÐ½Ñ ÐºÐ»Ð°ÑÑа â и Ñовно в Ñе ÑекÑндÑ, когда ÑеÑÑ Ñже Ð¿Ð»Ð¾Ñ Ð°Ñ, 1423 // а Ð³Ð»Ð°Ð²Ð½Ð¾Ð¼Ñ Ð¿Ð¾ÑÐ¾ÐºÑ Ð¿ÑедÑÑÐ¾Ð¸Ñ ÑобÑаÑÑ ÑоÑÑ Ð·Ð°Ð½Ð¾Ð²Ð¾. ÐÑпÑÑкание пина 1424 // Ð·Ð°Ð²Ð¾Ð´Ð¸Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ðµ Ñ Ð½ÑÐ»Ñ (Ñм. reenterRemoveSSR). 1425 stopHeroWatch(); 1426 var live = document.getElementById(ssrId); 1427 return !!(live && live.parentNode && !live.classList.contains('ssr-hiding')); 1428 }; 1429 1430 // РоÑÑ Ð²ÑÑ-Ñаки загÑÑзилÑÑ (ÑиÑаÑÐµÐ»Ñ Ð¿ÐµÑеÑÑл по ÑÑÑлке, ÑеÑÑай ÑдалÑÑ). 1431 // ÐÑпÑÑкаем ÑÐµÐ½Ð´ÐµÑ Ð¸ Ñнимаем его обÑÑнÑм пÑÑÑм â инаÑе ng-view оÑÑалÑÑ Ð±Ñ 1432 // под display:none, и Ð½Ð¾Ð²Ð°Ñ ÑÑÑаниÑа оÑÑиÑовалаÑÑ Ð±Ñ Ð² невидимÑй конÑейнеÑ. 1433 window.__unpinSSR = function () { 1434 var pathChanged = window.location.pathname !== ssrPath; 1435 // $routeChangeSuccess пÑÐ¸Ñ Ð¾Ð´Ð¸Ñ Ð¸ без пÑедÑеÑÑвÑÑÑего route-error. 1436 // ÐÑли за вÑÐµÐ¼Ñ Ð·Ð°Ð³ÑÑзки адÑÐµÑ ÑменилÑÑ, SSR опиÑÑÐ²Ð°ÐµÑ Ð¿ÑежнÑÑ 1437 // ÑÑÑаниÑÑ Ð¸ обÑзан ÑйÑи немедленно â tag-Ð³ÐµÐ¹Ñ Ð½Ð¾Ð²Ð¾Ð¹ ÑÑÑаниÑÑ 1438 // заведомо никогда не оÑкÑоеÑÑÑ Ð¿Ð¾ ÑÑаÑÐ¾Ð¼Ñ ÐºÑиÑеÑиÑ. 1439 if (!pinned && !pathChanged) return false; 1440 pinned = false; 1441 reenterRemoveSSR(pathChanged); 1442 return true; 1443 }; 1444 1445 function removeSSR() { 1446 if (pinned) return; 1447 if (removed) return; 1448 if (!ssr) return; 1449 // AngularJS пÑи загÑÑзке Ð¼Ð¾Ð¶ÐµÑ ÐÐÐ ÐСÐÐÐÐТЬ #ssr-tag â Ð·Ð°Ñ Ð²Ð°ÑÐµÐ½Ð½Ð°Ñ Ð¿Ñи 1450 // паÑÑинге ÑÑÑлка `ssr` ÑÑÑаÑÐµÐ²Ð°ÐµÑ (оÑÑепленнÑй orphan), и Ñогда handoff 1451 // Ñнимал Ð±Ñ .ssr-hiding Ñ orphan'а Ð²Ñ Ð¾Ð»Ð¾ÑÑÑÑ, пока живой #ssr-tag оÑÑаÑÑÑÑ 1452 // видимÑм и деÑÐ¶Ð¸Ñ ng-view под display:none. ÐеÑезапÑаÑиваем живой Ñзел по 1453 // id (подÑвеÑждено замеÑом на пÑоде: .ssr-hiding на getElementById('ssr-tag') 1454 // мгновенно ÑаÑкÑÑÐ²Ð°ÐµÑ Ð»ÐµÐ½ÑÑ, reQueryWouldFix:true). 1455 var _liveSsr = document.getElementById(ssr.id); 1456 if (_liveSsr) ssr = _liveSsr; 1457 // #ssr-tag кÑаÑко оÑÑеплÑеÑÑÑ Ð¾Ñ DOM, пока AngularJS компилиÑÑÐµÑ 1458 // route-Ñаблон в <ng-view>. ÐÑли в ÑÑÐ¾Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð²Ð·Ð²ÐµÑÑи removed=true и 1459 // вÑйÑи по !parentNode â handoff блокиÑÑеÑÑÑ Ð½Ð°Ð²Ñегда: и observer, и 1460 // аваÑийнÑй setTimeout ниже поÑом Ð·Ð°Ð³Ð»Ð¾Ñ Ð½ÑÑ Ð½Ð° `if (removed) return`, 1461 // а гоÑовÑй Ñид оÑÑанеÑÑÑ Ð¿Ð¾Ð´ `#ssr-tag:not(.ssr-hiding) ~ ng-view 1462 // { display:none }`. ÐоÑÑÐ¾Ð¼Ñ Ð½Ð° оÑÑепленном Ñзле ÐРлаÑÑим removed, 1463 // а повÑоÑÑем на ÑледÑÑÑем кадÑе (Ñ Ð¾Ð³ÑаниÑением â ÑиналÑной 1464 // ÑÑÑÐ°Ñ Ð¾Ð²ÐºÐ¾Ð¹ ÑлÑÐ¶Ð¸Ñ 8-Ñек setTimeout ниже). 1465 if (!ssr.parentNode) { 1466 // Ð¢Ð¾Ñ Ð¶Ðµ Ð·Ð°Ð¿Ð°Ñ Ð¿Ð¾ кадÑам: без него Ñзел, оÑÑепленнÑй на Ð¼Ð¾Ð¼ÐµÐ½Ñ 1467 // вÑзова, во вкладке без кадÑов не пеÑеÑпÑаÑивалÑÑ Ð±Ñ Ð½Ð¸ÐºÐ¾Ð³Ð´Ð°. 1468 if (detachRetries++ < 120) onFrameOrTimeout(removeSSR, 50); 1469 return; 1470 } 1471 removed = true; 1472 if (observer) { 1473 try { 1474 observer.disconnect(); 1475 } catch (e) { 1476 /* ignored */ 1477 } 1478 } 1479 // ÐовÑй Ð·Ð°Ñ Ð¾Ð´ оÑменÑÐµÑ ÑабоÑÑ Ð¿ÑедÑдÑÑего. Сбои ÑоÑÑа идÑÑ Ð¿Ð°Ñками, и 1480 // каждÑй Ð·Ð°Ñ Ð¾Ð´ заводил СÐÐРнаблÑдаÑÐµÐ»Ñ Ð½Ð° ng-view â Ñ subtree и 1481 // Ñлежением за клаÑÑами, â а ÑÑаÑÑй оÑÑавалÑÑ Ð¿Ð¾Ð´Ð¿Ð¸Ñан до Ñвоего 1482 // пÑÑиÑекÑндного ÑолбÑка. ÐÐ°Ð¼ÐµÑ 27.08.2026: пÑÑÑ Ñбоев подÑÑд оÑÑавлÑли 1483 // ÑеÑÑÑ Ð¶Ð¸Ð²ÑÑ Ð½Ð°Ð±Ð»ÑдаÑелей и ÑÐµÐ¼Ñ ÑаймеÑов, Ñо еÑÑÑ Ð½Ð° каждÑÑ ÑÐ¼ÐµÐ½Ñ 1484 // клаÑÑа в поддеÑеве ÑÑаÑÑи главнÑй поÑок звал ÑеÑÑÑ ÐºÐ¾Ð»Ð±Ñков вмеÑÑо 1485 // одного. ÐаÑим здеÑÑ, а не в веÑке Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð½Ð¸Ð¶Ðµ: Ð·Ð°Ñ Ð¾Ð´, Ñ ÐºÐ¾ÑоÑого 1486 // обложки Ñже неÑ, ÑÑ Ð¾Ð´Ð¸Ñ ÐºÐ¾ÑоÑким пÑÑÑм в doHandoff и до Ñой веÑки 1487 // не Ð´Ð¾Ñ Ð¾Ð´Ð¸Ñ â пÑежнее ожидание пеÑежило Ð±Ñ Ð¸ его. 1488 1489 stopHeroWatch(); 1490 1491 // ÐлÑÑÐµÐ²Ð°Ñ Ð¸Ð´ÐµÑ: ÐÐ ÑкÑÑваем SSR (#ssr-article) до ÑÐµÑ Ð¿Ð¾Ñ, пока Angular'Ð½Ð°Ñ 1492 // hero-каÑÑинка не загÑÑзиÑÑÑ. Ðока SSR виден â ng-view ÑкÑÑÑ ÑеÑез CSS 1493 // (#ssr-article:not(.ssr-hiding) ~ ng-view { display: none }). 1494 // ÐÑаÑÐ·ÐµÑ Ð·Ð°Ð³ÑÑÐ¶Ð°ÐµÑ <img> внÑÑÑи display:none и ÑÑÑелÑÐµÑ load-ÑобÑÑиÑми, 1495 // поÑÑÐ¾Ð¼Ñ ng-image-onload ÑÑÐ°Ð²Ð¸Ñ is-loaded/is-image-loaded даже в ÑкÑÑÑом ng-view. 1496 // Ðак ÑолÑко hero гоÑов â ÑкÑÑваем SSR и показÑваем ng-view одним кадÑом: Ð½Ð¾Ð»Ñ Ð¼ÐµÑÑаниÑ. 1497 1498 var heroWrapperSelector = 1499 '.open-post-title-image-wrapper, .expo-item-title-image-wrapper'; 1500 var loadedWrapperSelector = 1501 '.open-post-title-image-wrapper.is-image-loaded, ' + 1502 '.expo-item-title-image-wrapper.is-image-loaded'; 1503 var imgSelector = 1504 '.open-post-title-image.is-loaded, ' + '.expo-item-title-image.is-loaded'; 1505 // Music has no hero-wrapper: its above-the-fold visual is the square 1506 // cover in Worker scaffold. Match the full scaffold, not a generic 1507 // `.audio-img`: article body is user content and may reuse that class. 1508 var audioCoverSelector = 1509 '.expo-item .audio-wrapper > .audio-container > .audio-img img'; 1510 var ssrHasAudioCover = !!ssr.querySelector(audioCoverSelector); 1511 1512 function getNgView() { 1513 return document.querySelector('ng-view, [ng-view]'); 1514 } 1515 1516 function hasHeroWrapper() { 1517 var ngView = getNgView(); 1518 if (!ngView) return document.querySelector(heroWrapperSelector); 1519 return ngView.querySelector(heroWrapperSelector); 1520 } 1521 1522 function isHeroReady() { 1523 var ngView = getNgView(); 1524 if (!ngView) { 1525 return ( 1526 document.querySelector(loadedWrapperSelector) || 1527 document.querySelector(imgSelector) 1528 ); 1529 } 1530 return ( 1531 ngView.querySelector(loadedWrapperSelector) || ngView.querySelector(imgSelector) 1532 ); 1533 } 1534 1535 function isAudioCoverReady() { 1536 if (!ssrHasAudioCover) return true; 1537 var ngView = getNgView(); 1538 if (!ngView) return false;
1539 var image = ngView.querySelector(audioCoverSelector); 1540 return !!(image && image.complete && image.naturalWidth > 0); 1541 } 1542 1543 function hasCriticalVisual() { 1544 return ssrHasAudioCover || !!hasHeroWrapper(); 1545 } 1546 1547 function isCriticalVisualReady() { 1548 return isAudioCoverReady() && (!hasHeroWrapper() || !!isHeroReady()); 1549 } 1550 1551 function doHandoff() { 1552 // ÐÑибиÑÑй ÑÐµÐ½Ð´ÐµÑ Ð½Ðµ Ñнимаем даже из Ñже запÑÑенного handoff: 1553 // removeSSR() лаÑÑÐ¸Ñ removed=true ÑанÑÑе, Ñем hero-обозÑеваÑÐµÐ»Ñ 1554 // (или его 5-Ñек ÑолбÑк) добеÑÑÑÑÑ ÑÑда, поÑÑÐ¾Ð¼Ñ Ð¾Ð´Ð½Ð¾Ð¹ пÑовеÑки 1555 // в removeSSR() недоÑÑаÑоÑно â окно Ð¼ÐµÐ¶Ð´Ñ Ð½Ð¸Ð¼Ð¸ ÑеалÑно. 1556 if (pinned) return; 1557 // ÐÑоÑой Ð·Ð°Ñ Ð¾Ð´ ÑÑаÑен и Ñеален: hero-обозÑеваÑÐµÐ»Ñ ÑÐ½Ð¸Ð¼Ð°ÐµÑ ÑÐµÐ½Ð´ÐµÑ 1558 // ÑанÑÑе ÑÑока, а 5-ÑекÑнднÑй ÑолбÑк ниже ÐРоÑменÑеÑÑÑ Ð¸ 1559 // пÑÐ¸Ñ Ð¾Ð´Ð¸Ñ Ñледом. Ð ÑÑÐ¾Ð¼Ñ Ð¼Ð¾Ð¼ÐµÐ½ÑÑ `ssr` Ñже обнÑлÑн, и 1560 // `ssr.classList` ÑонÑÐµÑ ÐºÐ¾Ð»Ð±Ñк ÑаймеÑа ÐÐ Ñого, как заведÑÑÑÑ 1561 // 600-Ð¼Ñ ÑбоÑка, â Ñогда `ssr-just-swapped` и `has-ssr-content` 1562 // оÑÑаÑÑÑÑ Ð½Ð° <html> навÑегда: Ñ Ð²ÑÐµÑ ÐºÐ°ÑÑинок наÑмеÑÑÑ Ð²ÑклÑÑен 1563 // fade-in, а овеÑлей загÑÑзки ÑоÑÑа болÑÑе не показÑваеÑÑÑ. 1564 if (handedOff) return; 1565 handedOff = true; 1566 // ssr-just-swapped: подавлÑÐµÑ fade-in каÑÑинок ÐÐСÐÐ swap 1567 // + подавлÑÐµÑ loading-footer-overlay (Ñм. CSS вÑÑе) 1568 document.documentElement.classList.add('ssr-just-swapped'); 1569 ssr.classList.add('ssr-hiding'); 1570 // has-ssr-content ÐÐ ÑдалÑем ÑÑÐ°Ð·Ñ â оÑÑавлÑем до 600ms timeout, 1571 // ÑÑÐ¾Ð±Ñ loading-footer-overlay оÑÑавалÑÑ ÑкÑÑÑ Ð¿Ð¾ÐºÐ° Angular ÑендеÑиÑÑÑ. 1572 // ssr-just-swapped â дополниÑелÑÐ½Ð°Ñ ÑÑÑÐ°Ñ Ð¾Ð²ÐºÐ° (Ñоже ÑкÑÑÐ²Ð°ÐµÑ overlay). 1573 onFrameOrTimeout(function () { 1574 if (ssr && ssr.parentNode) ssr.remove(); 1575 ssr = null; 1576 window.__removeSSR = null; 1577 }, 250); 1578 // УбиÑаем ÐÐРклаÑÑа ÑеÑез 600ms â Angular к ÑÑÐ¾Ð¼Ñ Ð¼Ð¾Ð¼ÐµÐ½ÑÑ Ñже оÑÑендеÑилÑÑ, 1579 // его ng-if ÑбÑал overlay из DOM. 1580 setTimeout(function () { 1581 document.documentElement.classList.remove('ssr-just-swapped'); 1582 document.documentElement.classList.remove('has-ssr-content'); 1583 document.documentElement.classList.remove('has-ssr-tag'); 1584 }, 600); 1585 } 1586 1587 // ÐÑли кÑиÑиÑеÑкой каÑÑинки Ð½ÐµÑ Ð¸Ð»Ð¸ она Ñже загÑÑжена â пеÑеклÑÑаем ÑÑазÑ. 1588 // ÐÐ»Ñ Music ждÑм именно Angular-обложкÑ: `$includeContentLoaded` 1589 // ÑообÑÐ°ÐµÑ Ð¾ Ñаблоне, но вложеннÑй `ng-if` поÑвлÑеÑÑÑ ÑледÑÑÑим digest. 1590 if (!hasCriticalVisual() || isCriticalVisualReady()) { 1591 onFrameOrTimeout(doHandoff, 250); 1592 return; 1593 } 1594 1595 // ÐдÑм загÑÑзки hero/аÑдиообложки ÑеÑез MutationObserver. 1596 // ÐаблÑдаем ÑÑабилÑного ÑодиÑÐµÐ»Ñ SSR и ng-view: AngularJS Ð¼Ð¾Ð¶ÐµÑ 1597 // замениÑÑ Ñам <ng-view> поÑле retry, и подпиÑка на ÑÑаÑÑй Ñзел 1598 // Ñогда пÑоÑпала Ð±Ñ Ð³Ð¾ÑовÑÑ ÐºÐ°ÑÑÐ¸Ð½ÐºÑ Ð´Ð¾ 5-ÑекÑндного fallback. 1599 // ÐÑежнее ожидание к ÑÑÐ¾Ð¼Ñ Ð¼Ð¾Ð¼ÐµÐ½ÑÑ Ñже погаÑено â на ÑÑаÑÑе Ð·Ð°Ñ Ð¾Ð´Ð°. 1600 heroObs = new MutationObserver(function () { 1601 if (isCriticalVisualReady()) { 1602 stopHeroWatch(); 1603 onFrameOrTimeout(doHandoff, 250); 1604 } 1605 }); 1606 heroObs.observe(ssr.parentNode || document.body, { 1607 childList: true, 1608 subtree: true, 1609 attributes: true, 1610 attributeFilter: ['class'], 1611 }); 1612 1613 // Fallback: не ждÑм беÑконеÑно â пеÑеклÑÑаем ÑеÑез 5 ÑекÑнд 1614 heroFallbackTimer = setTimeout(function () { 1615 stopHeroWatch(); 1616 doHandoff(); 1617 }, 5000); 1618 } 1619 1620 // Expose for controller-driven removal (MagazinePostCtrl, ExpoItemCtrl) 1621 window.__removeSSR = removeSSR; 1622 1623 // Homepage/Tag SSR: use MutationObserver (no controller signal available). 1624 // Article/Author SSR: controller calls window.__removeSSR() after content renders. 1625 var isAuthor = ssr.id === 'ssr-author'; 1626 if (!isArticle && !isAuthor) { 1627 if (observeContentMutations()) { 1628 // ÐÐ»Ñ tag-ÑÑÑÐ°Ð½Ð¸Ñ Ð¶Ð´Ñм Ð ÐÐÐЬÐЫРÑезÑлÑÑÐ°Ñ Ñида, а не пеÑвÑй вÑÑавленнÑй 1629 // node, пÑÑÑой host <article> или голÑÑ ÑапкÑ: либо ÑкомпилиÑованнÑе 1630 // каÑÑоÑки (.article > .article__inner), либо ÑиналÑное пÑÑÑое/оÑибоÑное 1631 // ÑоÑÑоÑние (.feed-empty). Skeleton 1632 // (.tag-skeleton â без .article) гоÑовноÑÑÑÑ ÐÐ ÑÑиÑаеÑÑÑ, инаÑе пÑи 1633 // медленном или ÑпавÑем Ñиде живÑе SSR-каÑÑоÑки ÑнÑлиÑÑ Ð±Ñ, обнажив 1634 // skeleton/пÑÑÑоÑÑ. Happy-path (resolve Ñ items) ÐРменÑеÑÑÑ: items 1635 // добавлÑÑÑÑÑ ÑÐ¸Ð½Ñ Ñонно в $onInit, а handoff ÑÑабаÑÑÐ²Ð°ÐµÑ ÑÑÐ°Ð·Ñ Ð¿Ð¾Ñле 1636 // загÑÑзки Ñаблона article-компоненÑа â когда каÑÑоÑка Ñже видима. 1637 // ÐÑиÑеÑий гоÑовноÑÑи живÑÑ Ð² одном меÑÑе â contentGateOpen вÑÑе. 1638 // ÐÑежде он бÑл опиÑан дваждÑ: здеÑÑ Ð¸ в веÑке оÑпÑÑÐºÐ°Ð½Ð¸Ñ Ð¿Ð¸Ð½Ð°, и 1639 // ÑаÑÑ Ð¾Ð¶Ð´ÐµÐ½Ð¸Ðµ Ð¼ÐµÐ¶Ð´Ñ ÐºÐ¾Ð¿Ð¸Ñми бÑло вопÑоÑом вÑемени. 1640 // ТаймеÑ-ÑÑÑÐ°Ñ Ð¾Ð²ÐºÐ° к MutationObserver. Ðа ÑÑжÑлом Ñ Ð¾Ð»Ð¾Ð´Ð½Ð¾Ð¼ Ð·Ð°Ñ Ð¾Ð´Ðµ observer 1641 // Ð¼Ð¾Ð¶ÐµÑ ÐÐ ÐÐУСТÐТЬ поÑвление конÑенÑа (каÑÑоÑки оÑÑиÑовалиÑÑ Ð´Ð¾ observe(), 1642 // либо мÑÑаÑÐ¸Ñ Ð½Ðµ доÑла), и Ñогда главнÑй поÑок ÐÐ ÐСТÐÐÐÐÐТ Ñ Ð³Ð¾Ñовой-но- 1643 // ÑкÑÑÑой ленÑой, пока ÑлÑÑÐ°Ð¹Ð½Ð°Ñ Ð¿Ð¾Ð·Ð´Ð½
1643ÑÑ Ð¼ÑÑаÑÐ¸Ñ (загÑÑзка каÑÑинки, ~3-5 c) 1644 // не дÑÑÐ½ÐµÑ Ð½Ð°Ð±Ð»ÑдаÑелÑ. Performance-пÑоÑÐ¸Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°Ð» именно ÑÑо: ~67% вÑемени 1645 // главнÑй поÑок idle. ÐÑоÑÑой setInterval пÑовеÑÑÐµÑ Ð¢ÐТ ÐÐ Ð³ÐµÐ¹Ñ Ð½ÐµÐ·Ð°Ð²Ð¸Ñимо Ð¾Ñ 1646 // мÑÑаÑий и ÑÐ½Ð¸Ð¼Ð°ÐµÑ SSR ÑÑазÑ, как ÑолÑко конÑÐµÐ½Ñ Ð³Ð¾Ñов. ÐÑеждевÑеменно 1647 // ÑÑабоÑаÑÑ Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ (ÑÑловие Ñо же, ÑÑо Ñ observer). СамооÑиÑÑка: по removed 1648 // или по лимиÑÑ Ñиков (â9 Ñ, ÑÑÑÑ Ð´Ð¾Ð»ÑÑе 8-Ñек бÑкÑÑопа). 1649 var _pollTicks = 0; 1650 var _pollId = setInterval(function () { 1651 if (removed || ++_pollTicks > 90) { 1652 clearInterval(_pollId); 1653 return; 1654 } 1655 if (contentGateOpen()) { 1656 clearInterval(_pollId); 1657 removeSSR(); 1658 } 1659 }, 100); 1660 } 1661 } 1662 1663 // Safety timeout: remove SSR even if controller/observer doesn't fire. 1664 // Article: 5Ñ (ждÑм hero-image), оÑÑалÑнÑе (Homepage/Tag/Author): 8Ñ 1665 // (ÑÑжÑлÑй конÑенÑ; на медленнÑÑ ÑÑÑÑойÑÑÐ²Ð°Ñ Angular route гÑÑзиÑÑÑ Ð´Ð¾Ð»Ð³Ð¾). 1666 // Тег â иÑклÑÑение: пÑÑÑÑе host-ÑлеменÑÑ ÐºÐ°ÑÑоÑек еÑÑ Ð½Ðµ даÑÑ ÑиÑаÑÐµÐ»Ñ 1667 // конÑенÑа, поÑÑÐ¾Ð¼Ñ ÐµÐ³Ð¾ SSR оÑÑаÑÑÑÑ Ð´Ð¾ каÑÑоÑки или Ñвного feed-empty. 1668 // Ðа slow-2g/2g ÑвелиÑиваем timeout â Angular гÑÑзиÑÑÑ Ð´Ð¾Ð»ÑÑе, SSR должен оÑÑаваÑÑÑÑ. 1669 var conn = navigator.connection || navigator.mozConnection; 1670 var isSlow = conn && (conn.effectiveType === 'slow-2g' || conn.effectiveType === '2g'); 1671 var ssrTimeout = isArticle ? (isSlow ? 10000 : 5000) : isSlow ? 15000 : 8000; 1672 setTimeout(function () { 1673 if (ssrId === 'ssr-tag' && !contentGateOpen()) return; 1674 // AngularJS temporarily replaces <ng-view> while compiling a cold 1675 // route. Removing an article/expo SSR block in that window exposes 1676 // a blank page until the controller can signal readiness. Keep the 1677 // useful server render; a ready controller still calls __removeSSR, 1678 // and the existing no-frame fallback remains active after that call. 1679 if (isArticle) { 1680 var liveNgView = document.querySelector('ng-view, [ng-view]'); 1681 var articleRouteReady = 1682 liveNgView && 1683 liveNgView.querySelector( 1684 '.open-post, .expo-item, ' + 1685 '.open-post-title-image-wrapper, .expo-item-title-image-wrapper, ' + 1686 '.auth-required-screen', 1687 ); 1688 if (!articleRouteReady) return; 1689 } 1690 removeSSR(); 1691 }, ssrTimeout); 1692 } catch (e) { 1693 // Safety net: SSR cleanup не должен кÑаÑиÑÑ Angular boot 1694 try { 1695 if (ssr && ssr.parentNode) ssr.remove(); 1696 } catch (_) { 1697 /* ignore */ 1698 } 1699 } 1700 })(); 1701 </script>
1701 1702 1703 <!-- SSR H1 Cleanup: ÑÐ½Ð¸Ð¼Ð°ÐµÑ #ssr-h1 воÑкеÑа, когда Angular оÑÑиÑовал Ñвой 1704 заголовок. ÐоÑÐµÐ¼Ñ Ð¾ÑделÑно Ð¾Ñ Ð¾ÑиÑÑки вÑÑе â в SsrH1Handler (rewriters.js). 1705 Тег заголовка ÑÑÑ Ð½Ð°Ð¿Ð¸Ñан Ñловом намеÑенно: ÑазмеÑка внÑÑÑи комменÑаÑÐ¸Ñ 1706 подделÑваеÑÑÑ Ð¿Ð¾Ð´ наÑÑоÑÑÑÑ Ð¸ ÑÐ±Ð¸Ð²Ð°ÐµÑ Ð¸ гÑепÑ, и внеÑние ÑканеÑÑ. --> 1707
1707<script> 1708 (function () { 1709 try { 1710 var ssrH1 = document.getElementById('ssr-h1'); 1711 if (!ssrH1) return; 1712 1713 // ÐаблÑдаем за Ð ÐÐÐТÐÐÐÐ ng-view, а не за ним Ñамим: AngularJS 1714 // пеÑеÑоздаÑÑ <ng-view> пÑи компилÑÑии, и подпиÑка на Ð·Ð°Ñ Ð²Ð°ÑеннÑй 1715 // Ñзел пеÑеÑÑаÑÑ Ð¿Ð¾Ð»ÑÑаÑÑ Ð¼ÑÑаÑии (пÑовеÑено в бÑаÑзеÑе 05.08.2026 â 1716 // #ssr-h1 оÑÑавалÑÑ Ð²ÑоÑÑм h1 пÑи гоÑовом Angular-заголовке). 1717 // РодиÑÐµÐ»Ñ <main> ÑÑаÑиÑен, замена ng-view видна как мÑÑаÑÐ¸Ñ Ð² нÑм. 1718 var host = ssrH1.parentNode; 1719 if (!host) return; 1720 1721 var obs; 1722 var timer; 1723 1724 function stop() { 1725 if (obs) { 1726 try { 1727 obs.disconnect(); 1728 } catch (e) { 1729 /* ignored */ 1730 } 1731 obs = null; 1732 } 1733 if (timer) { 1734 clearTimeout(timer); 1735 timer = null; 1736 } 1737 } 1738 1739 function dropIfAngularHasH1() { 1740 // Узел ng-view запÑаÑиваем каждÑй Ñаз заново â по Ñой же пÑиÑине. 1741 var ng = document.querySelector('ng-view, [ng-view]'); 1742 if (!ng || !ng.querySelector('h1')) return false; 1743 stop(); 1744 if (ssrH1 && ssrH1.parentNode) ssrH1.remove(); 1745 ssrH1 = null; 1746 return true; 1747 } 1748 1749 if (dropIfAngularHasH1()) return; 1750 1751 obs = new MutationObserver(function () { 1752 dropIfAngularHasH1(); 1753 }); 1754 obs.observe(host, { childList: true, subtree: true }); 1755 1756 // 60 Ñ, а не 15: на нагÑÑженной маÑине Angular оÑÑиÑовал заголовок 1757 // ÑеÑез 17 Ñ, и коÑоÑкий ÑаймаÑÑ Ð¾ÑÑавил Ð±Ñ Ð´Ð²Ð° h1 навÑегда. 1758 // ÐаблÑдаÑÐµÐ»Ñ Ð´ÑÑев (замеÑ: 5 вÑзовов на 105 паÑек мÑÑаÑий). 1759 timer = setTimeout(stop, 60000); 1760 } catch (e) { 1761 /* оÑиÑÑка h1 не должна меÑаÑÑ Ð·Ð°Ð³ÑÑзке Angular */ 1762 } 1763 })(); 1764 </script>
1764 1765 </main> 1766 1767 <!-- ÐÑавовÑе ÑÑÑлки без JS: пÑовеÑка бÑенда OAuth в Google ÑÑебÑÐµÑ Ð²Ð¸Ð´Ð¸Ð¼ÑÑ 1768 ÑÑÑÐ»ÐºÑ Ð½Ð° полиÑÐ¸ÐºÑ ÐºÐ¾Ð½ÑиденÑиалÑноÑÑи Ñ Ð³Ð»Ð°Ð²Ð½Ð¾Ð¹, а подвал <site-footer> 1769 ÑиÑÑÐµÑ Angular â без JS на главной видна ÑолÑко ÑÑÑлка «ÐеÑейÑи к 1770 ÑодеÑжимомÑ» (Ð·Ð°Ð¼ÐµÑ 28.09.2026). СÑаÑиÑеÑÐºÐ°Ñ ÐºÐ¾Ð¿Ð¸Ñ Ð² overlay загÑÑзки 1771 не годиÑÑÑ: overlay ÑкÑÑÑ Ð´Ð¾ ÑÑаÑÑа Angular, и ÑÑÑлка бÑла Ð±Ñ ÑолÑко 1772 в иÑÑ Ð¾Ð´Ð½Ð¸ÐºÐµ. С JS блок не оÑобÑажаеÑÑÑ Ð²Ð¾Ð²Ñе. --> 1773 <noscript> 1774 <p class="ds-container"> 1775 <a href="/terms-of-use" class="copyright-link">ÐÑавила ÑайÑа</a> · 1776 <a href="/privacy-policy" class="copyright-link">ÐолиÑика конÑиденÑиалÑноÑÑи</a> 1777 </p> 1778 </noscript> 1779 1780 <!-- Footer: hidden during route loading and in editor mode (hideSiteHeader) to prevent CLS --> 1781 <div class="footer-wrapper" ng-if="!$root.statusCode || $root.isStubPage" ng-show="!$root.isRouteLoading && !$root.hideSiteHeader" site-loading> 1782 <site-footer></site-footer> 1783 </div> 1784 1785 <!-- Music player (global audio player) --> 1786 <div ng-if="!$root.statusCode || $root.isStubPage"> 1787 <music-player></music-player> 1788 </div> 1789 1790 <!-- ================================================================== --> 1791 <!-- VENDOR SCRIPTS: Split into small bundles to reduce long tasks (TBT) --> 1792 <!-- Built by: npm run prebuild â scripts/build-vendor-libs.js --> 1793 <!-- ================================================================== --> 1794 1795 <!-- ================================================================== --> 1796 <!-- ALL vendor scripts use `defer` to unblock first paint (FCP). --> 1797 <!-- Defer guarantees: (1) parallel download during parsing, --> 1798 <!-- (2) execution in document order, (3) before DOMContentLoaded. --> 1799 <!-- This lets the browser paint HTML+CSS without waiting for ~3.5s --> 1800 <!-- of JS evaluation, dramatically improving FCP (5.7s â ~2s target). --> 1801 <!-- ================================================================== --> 1802 1803 <!-- JQUERY: Must execute first â Angular and other libs depend on it --> 1804
1804<script defer="defer" src="/scripts/vendor-jquery-7d17a96c.js"></script>
1804 1805 1806 <!-- ANGULAR CORE: angular.min.js only --> 1807
1807<script defer="defer" src="/scripts/vendor-angular-core-dd9e4fc5.js"></script>
1807 1808 1809 <!-- ANGULAR MODULES: route + sanitize + touch --> 1810
1810<script defer="defer" src="/scripts/vendor-angular-modules-53712019.js"></script>
1810 1811 1812 <!-- LAZYSIZES: Image lazy loading (standalone, no dependencies). 1813 Changed from defer to async â removes it from the DOMContentLoaded 1814 blocking chain, allowing Angular to bootstrap ~50ms faster. 1815 Native loading="lazy" handles most cases; lazysizes is supplementary. --> 1816
1816<script async src="/scripts/vendor-lazysizes-1ab9fa33.js"></script>
1816 1817 1818 <!-- UI PLUGINS: Slick carousel + Bootstrap jQuery plugins. 1819 Must stay defer (depends on jQuery being available globally). 1820 async would risk execution before jQuery, causing errors. --> 1821
1821<script defer="defer" src="/scripts/vendor-ui-deferred-fb1641de.js"></script>
1821 1822 1823 <!-- ================================================================== --> 1824 <!-- DEFERRED VENDOR SCRIPTS: Not needed for initial render --> 1825 <!-- Loaded with defer to avoid blocking first paint --> 1826 <!-- ================================================================== --> 1827 1828 <!-- Bootbox ÑдалÑн â заменÑн на dialog-lite.js (вÑÑÑоеннÑй в Webpack-бандл). 1829 ÐÑполÑзÑеÑÑÑ Ð²: ratingButtons (диалог оÑзÑва), captionEditorService (подпиÑÑ). --> 1830 1831 <!-- He.js removed: replaced with native htmlDecode utility in Webpack bundle (saves ~99KB) --> 1832 1833 <!-- ================================================================== --> 1834 <!-- APPLICATION: Webpack bundles (includes config.js for proxy URL replacement) --> 1835 <!-- All use `defer` (set by HtmlWebpackPlugin scriptLoading: 'defer') --> 1836 <!-- ================================================================== --> 1837 <!-- Note: config.js is now bundled in application-*.js to allow proxy to replace API URLs --> 1838 1839 <!-- Webpack bundles (auto-injected by HtmlWebpackPlugin) --> 1840 1841
1841<script defer="defer" src="/scripts/runtime-0b964bee.js"></script>
1841 1842 1843
1843<script defer="defer" src="/scripts/vendor-4feffd26.js"></script>
1843 1844 1845
1845<script defer="defer" src="/scripts/application-0d4abaf6.js"></script>
1845 1846 1847 1848 <!-- ================================================================== --> 1849 <!-- POST-APP SCRIPTS: Execute after Webpack bundles (need window.* functions) --> 1850 <!-- ================================================================== --> 1851 1852 <!-- Asteroid (DDP client for real-time WebSocket notifications) --> 1853 <!-- MUST be after Webpack bundles: initializeAsteroid опÑеделена в application bundle. 1854 С defer-ÑкÑипÑами вÑполнение идÑÑ Ð¿Ð¾ поÑÑÐ´ÐºÑ Ð´Ð¾ÐºÑменÑа â ÑÑÐ¾Ñ ÑкÑÐ¸Ð¿Ñ Ð·Ð°Ð¿ÑÑÑиÑÑÑ Ð¿Ð¾Ñле app bundle. 1855 CSP: onload-обÑабоÑÑик заменÑн на ÑкÑÐ¸Ð¿Ñ Ð½Ð¸Ð¶Ðµ Ð´Ð»Ñ ÑовмеÑÑимоÑÑи Ñ nonce-based CSP. --> 1856
1856<script defer="defer" src="/scripts/vendor-asteroid-a82aa62d.js" id="asteroid-script"></script>
1856 1857
1857<script> 1858 (function () { 1859 var s = document.getElementById('asteroid-script'); 1860 if (!s) return; 1861 s.addEventListener('load', function () { 1862 if (window.initializeAsteroid) window.initializeAsteroid(); 1863 }); 1864 })(); 1865 </script>
1865 1866 1867 <!-- Optional utilities loaded after main app (async â not needed for Angular bootstrap, 1868 removed from defer chain so they don't block DOMContentLoaded) --> 1869
1869<script async src="/scripts/utils/scheduleTasks.js"></script>
1869 1870 1871 <!-- Editor scripts loader: dynamically loads CodeMirror, Typograf --> 1872
1872<script async src="/scripts/utils/loadEditorScripts.js"></script>
1872 1873 1874 <!-- Manual Angular bootstrap 1875 All vendor and application scripts use `defer` and execute in order before 1876 DOMContentLoaded. Bootstrap uses setTimeout(0) to yield to the browser between 1877 script execution and Angular compilation, breaking the long task chain and 1878 improving TBT. Lazysizes is the only async script (no Angular dependency). --> 1879
1879<script> 1880 (function () { 1881 var maxAttempts = 50; // 5 seconds max (50 * 100ms) 1882 var attempt = 0; 1883 1884 function tryBootstrap() { 1885 attempt++; 1886 1887 // Check if Angular is available 1888 if (typeof angular === 'undefined' || !angular.module) { 1889 if (attempt < maxAttempts) { 1890 setTimeout(tryBootstrap, 100); 1891 } else { 1892 console.error('[Bootstrap] Angular not available after', maxAttempts * 100, 'ms'); 1893 } 1894 return; 1895 } 1896 1897 var rootEl = document.getElementById('discours-app'); 1898 if (!rootEl) { 1899 if (attempt < maxAttempts) { 1900 setTimeout(tryBootstrap, 100); 1901 } else { 1902 console.error('[Bootstrap] Element #discours-app not found'); 1903 } 1904 return; 1905 } 1906 // Avoid double bootstrap (e.g. hot reload or duplicate script run) 1907 try { 1908 if (angular.element(rootEl).data('$injector')) { 1909 return; 1910 } 1911 } catch (e) {} 1912 // Check if discoursApp module is available 1913 try { 1914 angular.module('discoursApp'); 1915 angular.bootstrap(rootEl, ['discoursApp']); 1916 } catch (e) { 1917 if (attempt === 1) { 1918 console.error('[Bootstrap] First attempt failed:', e && e.message ? e.message : e); 1919 } 1920 if (attempt < maxAttempts) { 1921 setTimeout(tryBootstrap, 100); 1922 } else { 1923 console.error('[Bootstrap] Failed after', attempt, 'attempts:', e); 1924 } 1925 } 1926 } 1927 1928 // Yield to main thread before Angular bootstrap to break the long task chain. 1929 // scheduler.yield() (when available) yields with higher priority than setTimeout(0), 1930 // ensuring the bootstrap task runs sooner while still allowing paint + input processing. 1931 function yieldThenBootstrap() { 1932 if (typeof scheduler !== 'undefined' && typeof scheduler.yield === 'function') { 1933 scheduler.yield().then(tryBootstrap); 1934 } else { 1935 setTimeout(tryBootstrap, 0); 1936 } 1937 } 1938 if (document.readyState === 'loading') { 1939 document.addEventListener('DOMContentLoaded', yieldThenBootstrap); 1940 } else { 1941 yieldThenBootstrap(); 1942 } 1943 })(); 1944 </script>
1944 1945 1946 <!-- Service Worker registration: caches fonts, CSS, JS, and Thumbor images + API responses 1947 for dramatically faster repeat visits and offline resilience. 1948 Registered after page load to avoid competing with initial resource loading. 1949 Periodic update check ensures users get new SW within 1 hour after deploy. --> 1950
1950<script> 1951 if ('serviceWorker' in navigator) { 1952 var hostname = window.location.hostname; 1953 var isLocalhost = 1954 hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]'; 1955 1956 window.addEventListener('load', function () { 1957 if (isLocalhost) { 1958 Promise.all([ 1959 navigator.serviceWorker.getRegistrations().then(function (registrations) { 1960 return Promise.all( 1961 registrations.map(function (registration) { 1962 return registration.unregister(); 1963 }), 1964 ); 1965 }), 1966 typeof caches !== 'undefined' 1967 ? caches.keys().then(function (keys) { 1968 return Promise.all( 1969 keys.map(function (key) { 1970 return caches.delete(key); 1971 }), 1972 ); 1973 }) 1974 : Promise.resolve(), 1975 ]) 1976 .catch(function () { 1977 /* SW cleanup error â не кÑиÑиÑно */ 1978 }) 1979 .finally(function () { 1980 try { 1981 var reloadKey = 'localhost-sw-reset'; 1982 if (navigator.serviceWorker.controller && !sessionStorage.getItem(reloadKey)) { 1983 sessionStorage.setItem(reloadKey, '1'); 1984 window.location.reload(); 1985 } 1986 } catch (e) { 1987 /* sessionStorage недоÑÑÑпен (пÑиваÑнÑй Ñежим) â пÑопÑÑкаем reload */ 1988 } 1989 }); 1990 return; 1991 } 1992 1993 // Флаг: пеÑезагÑÑзка Ñже запланиÑована (пÑедоÑвÑаÑÐ°ÐµÑ Ð´Ð²Ð¾Ð¹Ð½Ð¾Ð¹ reload) 1994 var swReloading = false; 1995 // Ðапоминаем наÑалÑнÑй конÑÑÐ¾Ð»Ð»ÐµÑ ÐÐ ÑегиÑÑÑаÑии. ÐÑли null â ÑÑо пеÑÐ²Ð°Ñ Ð°ÐºÑиваÑиÑ, 1996 // Ð±Ð°Ð½Ð½ÐµÑ Â«ÐбновиÑÑ» не нÑжен (полÑзоваÑÐµÐ»Ñ Ñже Ð²Ð¸Ð´Ð¸Ñ Ñвежий HTML). 1997 var hadController = !!navigator.serviceWorker.controller; 1998 // Флаг Ð´Ð»Ñ race condition: controllerchange Ð¼Ð¾Ð¶ÐµÑ ÑÑабоÑаÑÑ Ð´Ð¾ загÑÑзки sw-register.js. 1999 // sw-register.js пÑовеÑÑÐµÑ ÑÑÐ¾Ñ Ñлаг ÑÑÐ°Ð·Ñ Ð¿Ð¾Ñле иниÑиализаÑии и показÑÐ²Ð°ÐµÑ Ð±Ð°Ð½Ð½ÐµÑ. 2000 window.__swUpdatePending = false; 2001 2002 // Listener ÐÐÐÐÐРбÑÑÑ inline â еÑли вÑнеÑÑи в async-ÑкÑипÑ, controllerchange 2003 // Ð¼Ð¾Ð¶ÐµÑ ÑÑабоÑаÑÑ ÑанÑÑе, Ñем ÑкÑÐ¸Ð¿Ñ Ð·Ð°Ð³ÑÑзиÑÑÑ (new SW skipWaiting пÑи пеÑвом визиÑе). 2004 navigator.serviceWorker.addEventListener('controllerchange', function () { 2005 if (swReloading) return; 2006 if (!hadController) return; 2007 // showSwUpdateBanner опÑеделена в sw-register.js (загÑÑжаеÑÑÑ Ð½Ð¸Ð¶Ðµ) 2008 if (typeof window.__showSwUpdateBanner === 'function') { 2009 window.__showSwUpdateBanner(function () { 2010 swReloading = true; 2011 }); 2012 } else { 2013 // sw-register.js еÑÑ Ð½Ðµ загÑÑзилÑÑ â Ð±Ð°Ð½Ð½ÐµÑ Ð¿Ð¾ÐºÐ°Ð¶ÐµÐ¼ пÑи его load 2014 window.__swUpdatePending = true; 2015 } 2016 }); 2017 2018 // РегиÑÑÑаÑÐ¸Ñ SW и Ð±Ð°Ð½Ð½ÐµÑ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ â вÑнеÑÐµÐ½Ñ Ð² оÑделÑнÑй модÑлÑ. 2019 var s = document.createElement('script'); 2020 s.src = '/scripts/sw-register.js'; 2021 s.async = true; 2022 document.body.appendChild(s); 2023 }); 2024 } 2025 </script>
2025 2026 2027 <!-- Google Analytics (deferred with event buffering) --> 2028
2028<script> 2029 // Buffer dataLayer events before gtag.js loads to avoid missing early page events 2030 window.dataLayer = window.dataLayer || []; 2031 function gtag() { 2032 window.dataLayer.push(arguments); 2033 } 2034 window.gtag = gtag; 2035 2036 // Pre-configure GA before script loads (events are buffered in dataLayer) 2037 gtag('js', new Date()); 2038 gtag('config', 'G-LQ4B87H8C2', { 2039 transport_type: 'beacon', 2040 anonymize_ip: true, 2041 }); 2042 2043 // Load gtag.js after first user interaction to avoid TBT impact on INP. 2044 // GTM contributes ~172ms of main-thread work (two long tasks: 94ms + 78ms). 2045 // Deferring until interaction pushes this work to when the user is actively 2046 // engaged (expecting slight delay), not during passive page viewing. 2047 // The dataLayer buffer ensures no events are lost during this deferral. 2048 // Safety: 10s timeout guarantees GA loads even for passive visitors. 2049 (function () { 2050 var loaded = false; 2051 function loadGA() { 2052 if (loaded) return; 2053 loaded = true; 2054 removeInteractionListeners(); 2055 var s = document.createElement('script'); 2056 s.async = true; 2057 s.src = 'https://www.googletagmanager.com/gtag/js?id=G-LQ4B87H8C2'; 2058 document.head.appendChild(s); 2059 } 2060 2061 function scheduleGA() { 2062 if ('requestIdleCallback' in window) { 2063 requestIdleCallback(loadGA, { timeout: 5000 }); 2064 } else { 2065 setTimeout(loadGA, 4000); 2066 } 2067 } 2068 2069 // Listen for first user interaction (click, scroll, touch, keydown) 2070 var interactionEvents = ['click', 'scroll', 'touchstart', 'keydown']; 2071 function onFirstInteraction() { 2072 removeInteractionListeners(); 2073 scheduleGA(); 2074 } 2075 function removeInteractionListeners() { 2076 interactionEvents.forEach(function (evt) { 2077 window.removeEventListener(evt, onFirstInteraction); 2078 }); 2079 } 2080 2081 function init() { 2082 // Register interaction listeners 2083 interactionEvents.forEach(function (evt) { 2084 window.addEventListener(evt, onFirstInteraction, { once: true, passive: true }); 2085 }); 2086 // Safety: load GA after 10s even without interaction (passive visitors / bots) 2087 setTimeout(scheduleGA, 10000); 2088 } 2089 2090 // Start listening after page load (don't add listeners during initial parse) 2091 if (document.readyState === 'complete') { 2092 init(); 2093 } else { 2094 window.addEventListener('load', init); 2095 } 2096 })(); 2097 </script>
2097 2098 2099 <!-- Ðнопка «ÐавеÑÑ Â» â поÑвлÑеÑÑÑ Ð¿Ñи пÑокÑÑÑке > 2 ÑкÑанов --> 2100 <style>#back-to-top{align-items:center;justify-content:center;padding:0;line-height:0;transition:opacity .25s ease,transform .25s ease,background-color .2s ease,color .2s ease,border-color .2s ease,box-shadow .2s ease}#back-to-top svg{display:block;width:18px;height:18px}#back-to-top:hover{background:rgba(20,20,20,.92)!important;color:#fff!important;border-color:rgba(20,20,20,.92)!important;box-shadow:0 4px 14px rgba(0,0,0,.18)!important}@media (hover:hover) and (pointer:fine) and (min-width:768px){#back-to-top::before{content:'ÐавеÑÑ ';position:absolute;right:calc(100% + 10px);top:50%;transform:translateY(-50%) translateX(4px);padding:6px 10px;background:rgba(20,20,20,.92);color:#fff;font-size:13px;line-height:1;font-family:inherit;white-space:nowrap;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.15);opacity:0;pointer-events:none;transition:opacity .18s ease,transform .18s ease}#back-to-top:hover::before{opacity:1;transform:translateY(-50%) translateX(0)}}</style> 2101 <button id="back-to-top" type="button" aria-label="ÐавеÑÑ " ng-attr-aria-label="{{ 'ÐавеÑÑ ' | t }}" style="display:none;position:fixed;bottom:24px;right:24px;z-index:100;width:44px;height:44px;border-radius:50%;border:1px solid rgba(0,0,0,.15);background:rgba(255,255,255,.92);color:#333;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.1);opacity:0;transform:translateY(8px);backdrop-filter:blur(4px)"> 2102 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 2103 <path d="M12 19V5M5 12l7-7 7 7"/> 2104 </svg> 2105 </button> 2106
2106<script> 2107 (function () { 2108 var btn = document.getElementById('back-to-top'); 2109 if (!btn) return; 2110 var visible = false; 2111 var threshold = window.innerHeight * 2; 2112 var lastScrollY = window.scrollY; 2113 var isMobile = window.innerWidth < 768; 2114 2115 /* ÐобилÑнÑе оÑÑÑÑÐ¿Ñ */ 2116 if (isMobile) { 2117 btn.style.bottom = '15px'; 2118 btn.style.right = '15px'; 2119 } 2120 2121 function showBtn() { 2122 if (visible) return; 2123 visible = true; 2124 btn.style.display = 'flex'; 2125 requestAnimationFrame(function () { 2126 btn.style.opacity = '1'; 2127 btn.style.transform = 'translateY(0)'; 2128 }); 2129 } 2130 2131 function hideBtn() { 2132 if (!visible) return; 2133 visible = false; 2134 btn.style.opacity = '0'; 2135 btn.style.transform = 'translateY(8px)'; 2136 setTimeout(function () { 2137 btn.style.display = 'none'; 2138 }, 300); 2139 } 2140 2141 window.addEventListener( 2142 'scroll', 2143 function () { 2144 var scrollY = window.scrollY; 2145 var deepEnough = scrollY > threshold; 2146 var scrollingUp = scrollY < lastScrollY; 2147 lastScrollY = scrollY; 2148 2149 if (!deepEnough) { 2150 hideBtn(); 2151 return; 2152 } 2153 2154 /* Ðа деÑкÑопе â вÑегда видна, на мобилÑнÑÑ â ÑолÑко пÑи ÑкÑолле ввеÑÑ */ 2155 if (isMobile) { 2156 if (scrollingUp) { 2157 showBtn(); 2158 } else { 2159 hideBtn(); 2160 } 2161 } else { 2162 showBtn(); 2163 } 2164 }, 2165 { passive: true }, 2166 ); 2167 btn.addEventListener('click', function () { 2168 window.scrollTo({ top: 0, behavior: 'smooth' }); 2169 }); 2170 })(); 2171 </script>
2171 2172 </body> 2173</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.