/* ============================================================
   vasilenko.info — таблица стилей комплекта
   Собрана из 55 нумерованных блоков. Порядок значим:
   блок 1 задаёт токены, блок 40 «Движение» всегда последний —
   всё движение живёт внутри prefers-reduced-motion: no-preference.
   Раньше эти блоки лежали инлайном в каждой из 11 страниц:
   914 KB суммарно против 146 KB здесь.
   ============================================================ */

/* ---------- 1. Токены ----------
   Побайтово одинаков во всех шести файлах. Порядок групп зафиксирован
   спецификацией: поверхности → линии → чернила → синий → роли действия →
   статусы → ember → flash → стекло и тени → рубрики → шрифты → кегли →
   трекинг → отступы → длительности → кривые.
   Правило имён: токен без роли в имени не существует.
   --surface-* только фон, --ink* только текст, --rule* только линия 1 px,
   --signal-* только то, что обязано быть замечено, --blue-N только ступень.
   Альфа разрешена в двух местах: стекло липких панелей и тени. */
:root{
  color-scheme: light dark;

  /* ---- поверхности: холодный интерфейс ---- */
  --surface-void:#E5EAF3;    /* L*92,6 — фон <html> за телом страницы */
  --surface-bg:#EEF1F7;      /* L*95,1  b*−3,2 */
  --surface-bg-2:#E5EAF3;    /* L*92,6 */
  --surface-card:#FFFFFF;    /* L*100  */
  --surface-card-2:#F4F6FB;  /* L*96,9 */
  --surface-raised:#FFFFFF;  /* ховер карточки, поповер */
  --surface-sunken:#E9EFFD;  /* L*94,4 */
  /* заливка по краям обложки записи; в тёмной теме пуста, см. .article__figure::after */
  --cover-wash:#E5EAF3;
  --card-bd:rgba(23,37,71,.08);

  /* ---- линии ---- */
  --rule-hair:#E3E8F0;       /* L*91,8 — сетка, разделители */
  --rule:#D3DAE6;            /* L*86,9 — границы полей, таблиц */

  /* ---- чернила ---- */
  --ink:#191D24;             /* 16,90:1 на карточке */
  --ink-mute:#4C5464;        /* 7,61:1 */
  --ink-meta:#5A6272;        /* 6,13:1 */

  /* ---- синий: восемь настоящих ступеней, тон 292,4° ---- */
  --blue-900:#002B74;        /* L*20   13,14:1 на белом */
  --blue-800:#0442A6;        /* L*31    8,99:1 */
  --blue-700:#1B5AD7;        /* L*42    6,02:1 */
  --blue-600:#5775EF;        /* L*53    4,04:1 — НЕ текст */
  --blue-500:#8D98FE;        /* L*66 */
  --blue-300:#C7C7FE;        /* L*82 */
  --blue-200:#E7E6FF;        /* L*92 */
  --blue-100:#F3F2FE;        /* L*96 */

  /* ---- роли действия ---- */
  --action:var(--blue-700);
  --action-hover:var(--blue-800);   /* в светлой теме ховер ТЕМНЕЕ, а не ярче */
  --action-ink:#FFFFFF;
     под blue-700 полоса читалась не как ребро, а как чужая линия под
     иначе она совпала бы с лицом и ребро исчезло. */
  --action-wash:var(--blue-100);    /* подложка ховера */
  --action-wash-2:var(--blue-200);  /* подложка выбранного */
  --action-line:var(--blue-300);
  --action-ring:var(--blue-700);    /* кольцо фокуса */

  /* ---- статусы записей ---- */
  --signal-verified:#0B6B4C;        /* 6,52:1 — «перепроверено» */
  --signal-verified-wash:#DDF7EA;
  --signal-verified-line:#B1DEC9;
  --signal-evergreen:#8A5300;       /* 6,33:1 — «вечнозелёное» */
  --signal-evergreen-wash:#FFEEDD;
  --signal-evergreen-line:#EFCFB1;

  /* ---- единственный горячий ---- */
  --signal-ember:#ED3018;           /* L*52 C*90 H40 — заливка, НИКОГДА не текст */
  --signal-ember-ink:#B92E10;       /* 6,07:1 — текст и рамка ошибки */
  --signal-ember-wash:#FFE9E3;

  /* ---- подсветка кода: класс описывает роль лексемы, а не язык ----
     Контраст считан на --surface-card-2 (#F4F6FB / #1B2028) — на той
     поверхности, где листинг лежит на самом деле. Порог 4,5:1 держат все
     восемь ролей, комментарий в том числе: бледный комментарий — это текст,
     который решили не давать читать, а в листинге он часто главный.
     Раскраска проставляется на сервере при сохранении статьи; в браузере
     не выполняется ничего. Язык, которого движок не знает, не красится вовсе. */
  --ink-code-key:#93247E;   /* ключевое слово, директива     6,95:1 */
  --ink-code-str:#146A3F;   /* строка в кавычках             6,14:1 */
  --ink-code-num:#8E4B0C;   /* число                         6,14:1 */
  --ink-code-rem:#4E6157;   /* комментарий                   6,12:1 */
  --ink-code-nam:#293441;   /* имя, идентификатор, значение 11,69:1 */
  --ink-code-op:#5C6470;    /* оператор и пунктуация         5,53:1 */
  --ink-code-path:#2F5C86;  /* путь и адрес                  6,48:1 */
  --ink-code-fn:#00646E;    /* встроенная функция, команда   6,37:1 */

  /* ---- вспышка изменившегося значения ---- */
  --flash:#E7E6FF;                  /* ink на ней 13,81:1 */

  /* ---- стекло и тени (единственные места с альфой) ---- */
  --topbar-bg:rgba(255,255,255,.86);
  --shadow:0 1px 2px rgba(20,33,61,.05), 0 8px 20px -12px rgba(20,33,61,.18);
  --shadow-h:0 2px 4px rgba(20,33,61,.06), 0 18px 34px -18px rgba(20,33,61,.32);
  --shadow-flag:0 2px 10px rgba(16,24,40,.16);

  /* ---- рубрики: значение по умолчанию, ахроматичное ---- */
  --cat:var(--ink-meta); --cat-ink:var(--ink-mute); --cat-wash:var(--surface-card-2);

  /* ---- шрифты ---- */
  --f-sans:"Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans",
           "Liberation Sans", Arial, sans-serif;
  --f-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, Consolas, monospace;

  /* ---- кегли: арифметика до 20 px, Minor Third 1,2 от 24 px ---- */
  --fs-11:.6875rem; --fs-12:.75rem;  --fs-13:.8125rem; --fs-14:.875rem;
  --fs-15:.9375rem; --fs-16:1rem;    --fs-17:1.0625rem;--fs-19:1.1875rem;
  --fs-20:1.25rem;
  --fs-24:clamp(1.25rem, 1.1rem + .55vw, 1.5rem);        /* 20 → 24 */
  --fs-28:clamp(1.125rem,1.04rem + .4vw, 1.3125rem);     /* 18 → 21 */
  --fs-34:clamp(1.5rem,  1.15rem + 1.5vw, 2.125rem);     /* 24 → 34 */
  --fs-40:clamp(1.75rem, 1.25rem + 2vw,  2.5rem);        /* 28 → 40 */
  --fs-48:clamp(2rem,    1.4rem + 2.6vw, 3rem);          /* 32 → 48 */
  --fs-num:clamp(2.5rem, 1.9rem + 2.2vw, 3.25rem);       /* 40 → 52 */

  /* ---- трекинг: ровно семь значений на весь сайт ----
     Отрицательный только на дисплейных кеглях: кириллица на 12—18 px
     это частокол вертикалей, снятие 2 % бьёт по узнаванию словоформы. */
  --track-text:0;            /* ≤19 px */
  --track-title:-0.012em;    /* 20–24 px */
  --track-head:-0.018em;     /* 28–36 px */
  --track-display:-0.026em;  /* 40–56 px */
  --track-hero:-0.032em;     /* 60 px+ */
  --track-caps:0.073em;      /* прописные 11 px */
  --track-caps-13:0.055em;   /* прописные 13 px */

  /* ---- отступы: девять значений, доминанты 8 и 24 ---- */
  --s-1:2px;   /* хайрлайны и оптические сдвиги */
  --s-2:4px;
  --s-3:8px;   /* ДОМИНАНТА */
  --s-4:12px;
  --s-5:16px;
  --s-6:24px;  /* ДОМИНАНТА */
  --s-7:32px;
  --s-8:48px;
  --s-9:80px;

  /* ---- длительности ---- */
  --dur-0:0ms;      /* состояние, которое палец уже сделал */
  --dur-1:80ms;     /* микроотклик, нажатие */
  --dur-2:140ms;    /* цвет, граница, opacity */
  --dur-3:200ms;    /* панель, аккордеон, шкала */
  --dur-4:320ms;    /* крупный блок */
  --dur-flash:420ms;/* вспышка изменившегося значения */

  /* ---- кривые ---- */
  --ease-out-quart:cubic-bezier(.165,.84,.44,1);
  --ease-out-quint:cubic-bezier(.23,1,.32,1);
  --ease-in:cubic-bezier(.42,0,1,1);
  --ease-in-out-quint:cubic-bezier(.86,0,.07,1);
  --ease-back:cubic-bezier(.34,1.56,.64,1);   /* ровно одно применение на весь сайт */

  /* ---- скругления и раскладка: раздел 9, не трогаем ---- */
  --r:18px;
  --r-s:12px;
  --r-xs:9px;
  --wrap:1320px;
  --measure:min(100%, 700px);
}

/* ---- кольцо рубрик: одна светлота L*52, коридор бренда 272–312° вырезан ----
   Цвет рубрики никогда не попадает на кликабельное: синее — можно нажать,
   цветное — это про что. Тринадцатая рубрика ахроматична и служит нулём. */
[data-cat="seo"]  { --cat:#0C85B3; --cat-ink:#067097; --cat-wash:#E3F3FF; }
[data-cat="css"]  { --cat:#C4539B; --cat-ink:#B13788; --cat-wash:#FEECF6; }
[data-cat="color"]{ --cat:#9D64C0; --cat-ink:#894EAF; --cat-wash:#F8EDFE; }
[data-cat="text"] { --cat:#977909; --cat-ink:#7F6500; --cat-wash:#FAF0DC; }
[data-cat="dev"]  { --cat:#5A6272; --cat-ink:#3E4552; --cat-wash:#EDEFF3; }
[data-cat="work"] { --cat:#B76824; --cat-ink:#A25301; --cat-wash:#FEEEE3; }
[data-cat="ads"]  { --cat:#D24D71; --cat-ink:#BF2E5D; --cat-wash:#FDEDEF; }
[data-cat="money"]{ --cat:#358D3C; --cat-ink:#097923; --cat-wash:#E5F5E3; }
[data-cat="host"] { --cat:#0C889D; --cat-ink:#007385; --cat-wash:#D7F6FE; }
/* резерв: build #CD5748/#B93D32/#FDEDEA · food #6D8519/#587100/#F0F3DD
          unit #078D6C/#07765B/#DCF7EC · time #018B89/#047473/#D7F7F6 */

@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
  /* ---- поверхности: восемь ступеней, шаг ΔL*≈2,2 ---- */
  --surface-void:#050B15;    /* L*2,9 */
  --surface-bg:#0D121A;      /* L*5,4 */
  --surface-bg-2:#12171E;    /* L*7,5 */
  --surface-card:#161B23;    /* L*9,6 */
  --surface-card-2:#1B2028;  /* L*12,1 */
  --surface-raised:#20252D;  /* L*14,5 */
  --surface-sunken:#12171E;
  --cover-wash:transparent;
  --card-bd:#282C35;

  --rule-hair:#282C35;       /* L*18,0 */
  --rule:#353942;            /* L*23,9 */

  --ink:#E4E8EE;             /* 14,05:1 на карточке */
  --ink-mute:#A8B0BE;        /* 7,92:1 */
  --ink-meta:#9AA3B3;        /* 6,80:1 */

  --action:var(--blue-500);       /* 6,63:1 на карточке */
  --action-hover:var(--blue-300); /* 10,72:1 */
  --action-ink:#0D121A;           /* 7,20:1 */
  --action-wash:#1B2028;
  --action-wash-2:#25263D;
  --action-line:var(--blue-600);
  --action-ring:var(--blue-300);

  --signal-verified:#5FD3A3;        --signal-verified-wash:#122A20;  --signal-verified-line:#203F32;
  --signal-evergreen:#F0C063;       --signal-evergreen-wash:#312315; --signal-evergreen-line:#483623;
  --signal-ember:#FF6B4A;           /* 6,13:1 */
  --signal-ember-ink:#FF8264;       /* 7,10:1 */
  --signal-ember-wash:#3C211A;

  --ink-code-key:#F09AD4;   /*  8,01:1 */
  --ink-code-str:#74D0A2;   /*  8,79:1 */
  --ink-code-num:#E9AE73;   /*  8,39:1 */
  --ink-code-rem:#93A89B;   /*  6,48:1 */
  --ink-code-nam:#DAE0E7;   /* 12,30:1 */
  --ink-code-op:#A5ADB9;    /*  7,23:1 */
  --ink-code-path:#8FBEE8;  /*  8,33:1 — разведён с --ink-code-op: у прежнего
                                #9CB6D2 разница составляла ΔE 10,5 при пороге 11,
                                и «путь» с «оператором» сливались в тёмной теме */
  --ink-code-fn:#62CFDD;    /*  8,95:1 */

  --flash:#25263D;                  /* ink на ней 12,00:1 */

  --topbar-bg:rgba(13,18,26,.88);
  --shadow:0 1px 2px rgba(0,0,0,.45);
  --shadow-h:0 2px 10px rgba(0,0,0,.55);
  --shadow-flag:0 2px 10px rgba(0,0,0,.5);
  }
  :root:not([data-theme="light"]) [data-cat="seo"]  { --cat:#56C7FF; --cat-ink:#56C7FF; --cat-wash:#0D2836; }
  :root:not([data-theme="light"]) [data-cat="css"]  { --cat:#FF9CD7; --cat-ink:#FF9CD7; --cat-wash:#341F2C; }
  :root:not([data-theme="light"]) [data-cat="color"]{ --cat:#DDA6FD; --cat-ink:#DDA6FD; --cat-wash:#2D2233; }
  :root:not([data-theme="light"]) [data-cat="text"] { --cat:#DAB85D; --cat-ink:#DAB85D; --cat-wash:#2C2513; }
  :root:not([data-theme="light"]) [data-cat="dev"]  { --cat:#A8B0BE; --cat-ink:#A8B0BE; --cat-wash:#20252D; }
  :root:not([data-theme="light"]) [data-cat="work"] { --cat:#FAA96A; --cat-ink:#FAA96A; --cat-wash:#332216; }
  :root:not([data-theme="light"]) [data-cat="ads"]  { --cat:#FEA1B3; --cat-ink:#FEA1B3; --cat-wash:#371F24; }
  :root:not([data-theme="light"]) [data-cat="money"]{ --cat:#81CD7F; --cat-ink:#81CD7F; --cat-wash:#1B291A; }
  :root:not([data-theme="light"]) [data-cat="host"] { --cat:#0DCDEC; --cat-ink:#0DCDEC; --cat-wash:#042A31; }
}
:root[data-theme="dark"]{
  --surface-void:#050B15;
  --surface-bg:#0D121A;
  --surface-bg-2:#12171E;
  --surface-card:#161B23;
  --surface-card-2:#1B2028;
  --surface-raised:#20252D;
  --surface-sunken:#12171E;
  --cover-wash:transparent;
  --card-bd:#282C35;

  --rule-hair:#282C35;
  --rule:#353942;

  --ink:#E4E8EE;
  --ink-mute:#A8B0BE;
  --ink-meta:#9AA3B3;

  --action:var(--blue-500);
  --action-hover:var(--blue-300);
  --action-ink:#0D121A;
  --action-wash:#1B2028;
  --action-wash-2:#25263D;
  --action-line:var(--blue-600);
  --action-ring:var(--blue-300);

  --signal-verified:#5FD3A3;        --signal-verified-wash:#122A20;  --signal-verified-line:#203F32;
  --signal-evergreen:#F0C063;       --signal-evergreen-wash:#312315; --signal-evergreen-line:#483623;
  --signal-ember:#FF6B4A;
  --signal-ember-ink:#FF8264;
  --signal-ember-wash:#3C211A;

  --ink-code-key:#F09AD4;   /*  8,01:1 */
  --ink-code-str:#74D0A2;   /*  8,79:1 */
  --ink-code-num:#E9AE73;   /*  8,39:1 */
  --ink-code-rem:#93A89B;   /*  6,48:1 */
  --ink-code-nam:#DAE0E7;   /* 12,30:1 */
  --ink-code-op:#A5ADB9;    /*  7,23:1 */
  --ink-code-path:#8FBEE8;  /*  8,33:1 — разведён с --ink-code-op: у прежнего
                                #9CB6D2 разница составляла ΔE 10,5 при пороге 11,
                                и «путь» с «оператором» сливались в тёмной теме */
  --ink-code-fn:#62CFDD;    /*  8,95:1 */

  --flash:#25263D;

  --topbar-bg:rgba(13,18,26,.88);
  --shadow:0 1px 2px rgba(0,0,0,.45);
  --shadow-h:0 2px 10px rgba(0,0,0,.55);
  --shadow-flag:0 2px 10px rgba(0,0,0,.5);
}
:root[data-theme="dark"] [data-cat="seo"]  { --cat:#56C7FF; --cat-ink:#56C7FF; --cat-wash:#0D2836; }
:root[data-theme="dark"] [data-cat="css"]  { --cat:#FF9CD7; --cat-ink:#FF9CD7; --cat-wash:#341F2C; }
:root[data-theme="dark"] [data-cat="color"]{ --cat:#DDA6FD; --cat-ink:#DDA6FD; --cat-wash:#2D2233; }
:root[data-theme="dark"] [data-cat="text"] { --cat:#DAB85D; --cat-ink:#DAB85D; --cat-wash:#2C2513; }
:root[data-theme="dark"] [data-cat="dev"]  { --cat:#A8B0BE; --cat-ink:#A8B0BE; --cat-wash:#20252D; }
:root[data-theme="dark"] [data-cat="work"] { --cat:#FAA96A; --cat-ink:#FAA96A; --cat-wash:#332216; }
:root[data-theme="dark"] [data-cat="ads"]  { --cat:#FEA1B3; --cat-ink:#FEA1B3; --cat-wash:#371F24; }
:root[data-theme="dark"] [data-cat="money"]{ --cat:#81CD7F; --cat-ink:#81CD7F; --cat-wash:#1B291A; }
:root[data-theme="dark"] [data-cat="host"] { --cat:#0DCDEC; --cat-ink:#0DCDEC; --cat-wash:#042A31; }

/* ---------- 2. Сброс и база ----------
   Побайтово одинаков во всех шести файлах.
   Кегль базы 16 px и интерлиньяж 1.5: каталог из 126 инструментов —
   задача про плотность, а не про воздух. 1.65 остаётся прозе.
   Объявление свойств OpenType из общего правила убрано: kern и liga
   включены по умолчанию, а такое объявление стирает список целиком
   у любого правила ниже. Весь OpenType ведём через font-variant-*.
   Трекинг из общего правила заголовков убран: он назначается по полосам. */
*,*::before,*::after{box-sizing:border-box}
html{scrollbar-gutter:stable;-webkit-text-size-adjust:100%;background:var(--surface-void)}
body{
  margin:0;
  background:var(--surface-bg);
  color:var(--ink);
  font-family:var(--f-sans);
  font-size:var(--fs-16);
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-wrap:break-word;
}
img,svg{max-width:100%;display:block}
img{height:auto}
svg[viewBox="0 0 24 24"]{width:1.15em;height:1.15em;flex:0 0 auto;display:inline-block;vertical-align:-.18em}
h1,h2,h3,h4{margin:0;line-height:1.15;text-wrap:balance;color:var(--ink)}
p{margin:0}
a{color:var(--action);text-decoration:none}
ul,ol{margin:0;padding:0}
button{font:inherit;color:inherit}
:focus-visible{outline:2px solid var(--action-ring);outline-offset:3px;border-radius:5px}
.prose,.entry-content{font-size:clamp(1.0625rem,1.02rem + .2vw,1.1875rem);line-height:1.65}

/* Целевая раскладка OpenType, раздел 4.5. Все числа интерфейса — табличные,
   чтобы при live-пересчёте не дрожала строка; технический вывод — ещё и
   перечёркнутый ноль без лигатур, потому что его копируют; числа внутри
   бегущего текста — пропорциональные. */
.result__num,.calcpanel__rows b,.calcpanel__inp,.subline b,.facts b,
.count b,.chip__n,.intro__stat b,.bescale__legend b,.meta__date,
.drow__year,.pager a,.pager span,.foot .num,.catsec__n{
  font-variant-numeric: tabular-nums;
}
.mono,code,pre,.out,.path,.tcard__f{
  font-variant-numeric: tabular-nums slashed-zero;
  font-variant-ligatures: none;
}
.prose,.entry-content,.calcpanel__say,.calcpanel__hint{
  font-variant-numeric: proportional-nums;
}

.visually-hidden{
  position:absolute!important;width:1px;height:1px;margin:-2px;padding:0;border:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;
}
.skip-link{
  position:absolute;left:var(--s-4);top:-100px;z-index:99;
  background:var(--action);color:var(--action-ink);
  padding:var(--s-4) var(--s-6);border-radius:var(--r-xs);font-weight:600;
}
.skip-link:focus{top:var(--s-4)}

.wrap{max-width:var(--wrap);margin-inline:auto;padding-inline:clamp(16px, 2.6vw, 32px)}
main{display:block}

/* ---------- 3. Верхняя панель ----------
   Раскладка выбрана владельцем по hero-css: разделы капсулой, поиск
   открытым полем с круглой кнопкой. Отсюда — всё, кроме языка.
   — рельс разделов: одна утопленная дорожка вместо пяти коробок,
     текущий пункт лежит на приподнятой плашке и помечен насечкой
     в 18 px — той же линейкой, что стоит над H1;
   — поиск не свёрнут в кнопку: он открыт, потому что это главный вход
     в каталог, и стоит там, где его ищут, — между разделами и
     служебными переключателями;
   — язык и тема собраны в одну служебную капсулу. Причина не в красоте:
     ряд из пяти самостоятельных предметов (бренд, рельс, поиск, язык,
     тема) отбирает ширину именно у поля поиска — единственного
     эластичного элемента ряда, и первым это видно на 1140—1300 px.
     Капсула держит язык и тему за один предмет: ряд остаётся из
     четырёх, как в hero-css, а поле не сжимается.
   Без JS работает всё: поиск — обычная GET-форма, язык — <details>
   с настоящими ссылками, рельс — список ссылок, is-stuck не появляется.
   Высота панели на десктопе ровно 75 px (14 + 46 + 14 + 1) — под это число
   посчитаны --sticky-top и scroll-padding в блоке 31а и top у .catnav. */
#topbar-sentinel{position:absolute;top:0;left:0;width:1px;height:1px;pointer-events:none}
.topbar{
  position:sticky;top:0;z-index:40;
  background:var(--topbar-bg);
  border-bottom:1px solid var(--rule-hair);
  -webkit-backdrop-filter:saturate(1.6) blur(14px);
          backdrop-filter:saturate(1.6) blur(14px);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .topbar{background:var(--surface-card)}
}
/* страница уехала вниз — линия под шапкой набирает вес. В тёмной теме
   отделяет именно ступень линии: тень на #0D121A не видна. */
.topbar.is-stuck{border-bottom-color:var(--rule);box-shadow:var(--shadow)}
/* пока панель занимает больше одного ряда, она не липнет,
   чтобы не съедать высоту первого экрана */
@media (max-width:1139px){.topbar{position:static}}
/* три ряда на телефоне, два на планшете, один от 1140 px — блок 21 */
.topbar__inner{
  display:grid;
  grid-template-columns:minmax(0,1fr) auto;
  grid-template-areas:"brand tools" "search search" "nav nav";
  align-items:center;
  gap:var(--s-3) var(--s-5);
  /* Стороны названы по отдельности, а не сокращённым padding-block:
     поджатая шапка ниже задаёт физические padding-top/bottom, и спор
     логического семейства с физическим решается порядком в файле,
     а не весом селектора. Одно семейство на оба правила — и спора нет. */
  padding-top:14px;
  padding-bottom:14px;
}
/* Страница уехала вниз — панель поджимается: 14 полей превращаются в 8,
   знак ужимается на десятую. Меньше нельзя: пункт меню и так ровно
   36 пикселей, а вместе с полями это минимальная площадь для пальца.

   Стороны названы по отдельности, тем же семейством, что и в базовом
   правиле: переход по сокращённому padding-block браузер не отрабатывает,
   а смешивать логическое с физическим нельзя — они спорят порядком
   в файле, а не весом селектора. */
.topbar.is-stuck .topbar__inner{padding-top:8px;padding-bottom:8px}
.topbar.is-stuck .brand__mark{transform:scale(.9)}
.brand{grid-area:brand;display:flex;align-items:center;gap:var(--s-3);min-height:44px;min-width:0}
.brand__mark{flex:0 0 auto;color:var(--ink)}
.brand__text{display:flex;flex-direction:column;gap:var(--s-1);min-width:0}
.brand__name{font-size:var(--fs-17);font-weight:620;line-height:1.3;color:var(--ink);white-space:nowrap}
.brand:hover .brand__name{color:var(--action)}

/* рельс разделов: утопленная дорожка, активный пункт — приподнятая плашка.
   Дорожка сжимается по содержимому: полоса во всю ширину с пятью
   пунктами слева читалась как пустая. */
.mainnav{grid-area:nav;min-width:0}
.mainmenu{
  list-style:none;display:flex;align-items:center;gap:var(--s-1);min-width:0;
  padding:var(--s-2) 14px var(--s-2) var(--s-2);border-radius:100px;
  background:var(--surface-sunken);border:1px solid var(--rule-hair);
  overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;
}
.mainmenu::-webkit-scrollbar{display:none}
.mainmenu li{flex:0 0 auto}
.mainmenu a{
  display:inline-flex;align-items:center;min-height:44px;padding:0 var(--s-4);
  border-radius:100px;white-space:nowrap;
  font-size:var(--fs-14);font-weight:600;line-height:1.4;color:var(--ink-meta);
}
.mainmenu a:hover{background:var(--action-wash);color:var(--action)}
/* плашка вместо заливки цветом: в тёмной теме её отделяет ступень
   светлоты, а не тень, поэтому к тени добавлен внутренний борт */
.mainmenu a[aria-current="page"]{
  position:relative;background:var(--surface-card);color:var(--ink);
  box-shadow:var(--shadow), inset 0 0 0 1px var(--card-bd);
}
/* насечка под текущим пунктом: та же линейка, что над H1, ширина 18 px */
.mainmenu a[aria-current="page"]::after{
  content:"";position:absolute;left:50%;bottom:7px;
  width:18px;height:2px;margin-left:-9px;border-radius:2px;
  background:var(--action);
}
@media (max-width:699px){
  .mainmenu a{padding:0 var(--s-3);font-size:var(--fs-13)}
}
/* Пять разделов в строку на 375 px не встают, но и в бургер не прячутся:
   дорожка прокручивается, край растворяется маской — как в .catnav.
   Порог 479 px не выдуман и замерен: в этом кегле дорожке нужно 447 px
   (пять пунктов, поля 6 и 14, два борта), а колонка на таких экранах
   равна ширине окна минус 32 px полей — равенство наступает ровно там.
   Раньше здесь стояло 453: число считалось до того, как дорожка получила
   концевое поле в 14 px, и полоса 454—478 прокручивалась уже без маски.
   Выше порога дорожка помещается целиком и жмётся по содержимому — маска
   там съедала бы правый край капсулы вместе с «Об авторе». */
@media (max-width:478px){
  .mainmenu{
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 12px),transparent);
            mask-image:linear-gradient(90deg,#000 calc(100% - 12px),transparent);
  }
}

/* поиск: открытое поле, круглая кнопка отправки внутри пилюли.
   Кнопка 34 px, но её область нажатия дотянута псевдоэлементом до 44 —
   рисунок ряда не ломается, палец не промахивается. */
.topsearch{
  grid-area:search;min-width:0;
  display:flex;align-items:center;gap:var(--s-3);
  height:44px;padding:0 var(--s-2) 0 var(--s-4);
  background:var(--surface-card);border:1px solid var(--rule-hair);border-radius:100px;
}
.topsearch:focus-within{border-color:var(--action);box-shadow:0 0 0 3px var(--action-wash-2)}
.topsearch__ic{flex:0 0 auto;width:18px;height:18px;color:var(--ink-meta)}
.topsearch__field{
  flex:1 1 0;min-width:0;height:100%;background:none;border:0;
  font:inherit;font-size:var(--fs-15);font-weight:500;color:var(--ink);
}
.topsearch__field::placeholder{color:var(--ink-meta)}
.topsearch__field::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}
.topsearch__field:focus{outline:none}
.topsearch__btn{
  position:relative;flex:0 0 34px;height:34px;display:grid;place-items:center;
  background:var(--action);color:var(--action-ink);
  border:0;border-radius:100px;cursor:pointer;
}
.topsearch__btn::after{
  content:"";position:absolute;top:50%;left:50%;
  width:44px;height:44px;margin:-22px 0 0 -22px;
}
.topsearch__btn svg{width:16px;height:16px}
.topsearch__btn:hover{background:var(--action-hover)}

/* служебная капсула: язык и тема в одной коробке вместо двух отдельных.
   Меню языка всплывает наружу, поэтому overflow здесь не режется. */
.topbar__tools{
  grid-area:tools;justify-self:end;
  display:flex;align-items:center;gap:var(--s-1);height:46px;padding:var(--s-1);
  background:var(--surface-sunken);border:1px solid var(--rule-hair);border-radius:100px;
}
.iconbtn{
  width:42px;height:42px;display:grid;place-items:center;
  background:none;border:0;border-radius:100px;cursor:pointer;color:var(--ink-mute);
}
.iconbtn:hover{background:var(--action-wash);color:var(--action)}
.iconbtn svg{width:20px;height:20px}
.iconbtn .ico-sun{display:none}
:root[data-theme="dark"] .iconbtn .ico-sun{display:block}
:root[data-theme="dark"] .iconbtn .ico-moon{display:none}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .iconbtn .ico-sun{display:block}
  :root:not([data-theme="light"]) .iconbtn .ico-moon{display:none}
}

/* ---------- 4. Служебные строки ----------
   Раньше между шапкой и заголовком стояли три строки подряд: крошки,
   .statusbar и .meta. «Калькулятор» встречался дважды, «Реклама и трафик»
   дважды — дублировался не только вид, но и смысл.
   Осталась одна строка — крошки: они несут и путь, и тип, и рубрику,
   и они же размечены BreadcrumbList. Всё, что их повторяло, убрано;
   состояние, маршрут и дата собраны в одну .subline под заголовком. */
.crumbs-nav{padding-top:var(--s-6)}
.crumbs{
  list-style:none;display:flex;align-items:center;flex-wrap:nowrap;
  overflow-x:auto;scrollbar-width:none;
  font-size:var(--fs-14);font-weight:500;line-height:1.45;color:var(--ink-meta);
  -webkit-mask-image:linear-gradient(90deg,#000 88%,transparent);
          mask-image:linear-gradient(90deg,#000 88%,transparent);
}
.crumbs::-webkit-scrollbar{display:none}
.crumbs li{display:flex;align-items:center;white-space:nowrap}
.crumbs li+li::before{
  content:"";flex:0 0 auto;width:5px;height:5px;margin:0 var(--s-4);
  border-right:1.5px solid var(--rule);border-top:1.5px solid var(--rule);transform:rotate(45deg);
}
.crumbs a{color:var(--ink-meta)}
.crumbs a:hover{color:var(--action)}
.crumbs [aria-current="page"]{color:var(--ink-mute);font-weight:600}
/* точка рубрики стоит перед ссылкой, а не внутри неё:
   цвет рубрики не попадает на кликабельное (правило 3.7-1) */
.crumbs .chip__dot{margin-right:var(--s-3)}
@media (min-width:900px){.crumbs{-webkit-mask-image:none;mask-image:none;flex-wrap:wrap}}

/* воздух между шапкой и заголовком: на его месте стояли две лишние строки */
main>.intro:first-child,main>.cat-head:first-child{margin-top:var(--s-7)}

/* Единственная мета-строка страницы. Стоит под H1 и привязана к нему,
   а не висит над ним отдельной полосой. Несёт только то, чего нет ни
   в крошках, ни в карточке фактов, ни в лиде: маршрут, состояние, дату. */
.subline{
  margin-top:var(--s-4);
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2) 0;
  font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-meta);
}
.subline>*+*::before{
  content:"";display:inline-block;width:3px;height:3px;border-radius:50%;
  background:var(--rule);margin:0 var(--s-4);vertical-align:.24em;
}
.subline time,.subline b{color:var(--ink-mute);font-variant-numeric:tabular-nums}
@media (max-width:519px){
  .subline{gap:var(--s-1) var(--s-5)}
  .subline>*+*::before{display:none}
}
/* маршрут инструмента: моно 12 px. Читается как адрес, а не как заголовок. */
.path{
  font-family:var(--f-mono);font-size:var(--fs-12);line-height:1.45;color:var(--ink-meta);
  font-variant-numeric:tabular-nums slashed-zero;font-variant-ligatures:none;
}
.dot-ok{
  display:inline-block;width:7px;height:7px;border-radius:50%;
  background:var(--signal-verified);margin-right:var(--s-3);vertical-align:.06em;
}

/* ---------- 5. Общая карточка ---------- */
.card{
  background:var(--surface-card);
  border:1px solid var(--card-bd);
  border-radius:var(--r);
  box-shadow:var(--shadow);
  padding:clamp(16px, 2.2vw, 24px);
}
.section{margin-top:clamp(24px, 3.4vw, 48px)}
.section__head{
  display:flex;align-items:baseline;justify-content:space-between;gap:8px 24px;
  flex-wrap:wrap;margin-bottom:16px;
}
.section__head h2,.section>h2{font-size:var(--fs-24);font-weight:640;letter-spacing:var(--track-title);line-height:1.22}
.section__head p{font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-meta);max-width:52ch}
.section__more{font-size:var(--fs-14);font-weight:600;color:var(--action);white-space:nowrap}
.section__more:hover{text-decoration:underline;text-underline-offset:4px}

/* ---------- 6. Вводный блок главной ----------
   Сетка .intro с заголовком и лидом работает на главной. Каталогу из
   этого блока нужны два предмета: строка счётчиков .intro__stat,
   привязанная там к H1 каталога, и .findbox — карточка поиска;
   своя шапка раздела у каталога в блоке 31в. */
.intro{margin-top:var(--s-5);display:grid;gap:var(--s-6)}
.intro>*{min-width:0}
@media (min-width:960px){
  .intro{grid-template-columns:minmax(0,1.25fr) minmax(0,.95fr);gap:var(--s-7);align-items:start}
}
.intro h1{font-size:var(--fs-40);font-weight:620;letter-spacing:var(--track-display);line-height:1.1;max-width:19ch}
.intro__lead{
  margin-top:var(--s-5);color:var(--ink-mute);
  font-size:var(--fs-17);font-weight:400;line-height:1.62;max-width:52ch;
}
.intro__stat{
  margin-top:var(--s-6);display:flex;flex-wrap:wrap;gap:var(--s-5) var(--s-7);
  padding-top:var(--s-5);border-top:1px solid var(--rule-hair);
  font-size:var(--fs-13);font-weight:500;color:var(--ink-meta);line-height:1.5;
}
.intro__stat b{
  display:block;font-family:var(--f-mono);font-size:var(--fs-20);font-weight:400;color:var(--ink);
  letter-spacing:var(--track-title);line-height:1.25;
}
.findbox{background:linear-gradient(165deg, var(--surface-sunken), var(--surface-card) 68%);border-color:var(--action-line)}
.findbox h2{font-size:var(--fs-20);font-weight:640;letter-spacing:var(--track-title);line-height:1.25}
.findbox p{margin-top:var(--s-3);font-size:var(--fs-15);font-weight:500;line-height:1.55;color:var(--ink-mute);max-width:44ch}
.findbox__form{margin-top:var(--s-5);display:flex;gap:var(--s-3)}
.findbox__field{
  flex:1 1 0;min-width:0;height:48px;background:var(--surface-card);
  border:1px solid var(--rule);border-radius:var(--r-xs);
  padding:0 var(--s-5);font:inherit;font-size:var(--fs-16);color:var(--ink);
}
.findbox__field::placeholder{color:var(--ink-meta)}
.findbox__field:focus{border-color:var(--action);outline:none;box-shadow:0 0 0 3px var(--action-wash-2)}
.findbox__btn{
  flex:0 0 auto;min-height:48px;padding:var(--s-4) var(--s-5);background:var(--action);color:var(--action-ink);
  border:0;
  border-radius:var(--r-xs);cursor:pointer;font-weight:600;font-size:var(--fs-15);
  display:inline-flex;align-items:center;gap:var(--s-3);
}
.findbox__btn svg{width:17px;height:17px}
.findbox__btn:hover{background:var(--action-hover)}
.findbox__btn:active{transform:translateY(var(--s-1))}
/* Карточка объявлена контейнером: подсказки под полем должны знать
   ширину САМОЙ КАРТОЧКИ, а не окна. Ширина окна тут ничего не решает —
   в две колонки карточке достаётся то 300 px, то 400, и на 1200 px
   английские «shadow color password» ломались ровно так же, как на 1024.
   Гоняться за этим медиазапросами значит подбирать числа под каждый
   язык; контейнерный запрос спрашивает у карточки напрямую. */
.findbox{container-type:inline-size}
.findbox__hint{margin-top:var(--s-4);display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-3);font-size:var(--fs-13);font-weight:500;color:var(--ink-meta)}
/* Тесно — подпись уходит на свою строку целиком, и три подсказки встают
   в ряд под ней. Ломаться на «две сверху, одна снизу» им больше не с чего:
   подпись занимала до сотни пикселей ряда и забирала место у третьей. */
@container (max-width:380px){
  .findbox__hint>.findbox__hint-l{flex:1 0 100%}
}
.findbox__hint a{
  display:inline-flex;align-items:center;min-height:44px;padding:var(--s-3) var(--s-5);
  background:var(--surface-card);border:1px solid var(--rule);border-radius:100px;
  font-size:var(--fs-13);font-weight:600;color:var(--ink-mute);
}
.findbox__hint a:hover{color:var(--action);border-color:var(--action-line);background:var(--action-wash)}

/* ---------- 7. Рубрики лентой чипов ---------- */
.chips{margin-top:clamp(24px,3vw,32px)}
.chips__head{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:8px 24px;margin-bottom:12px}
.chips__head h2{font-size:var(--fs-20);font-weight:640;letter-spacing:var(--track-title);line-height:1.25}
.chips__head p{font-size:var(--fs-13);font-weight:500;color:var(--ink-meta)}
.chips__list{list-style:none;display:flex;flex-wrap:wrap;gap:8px}
.chip{
  display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:8px 16px;
  background:var(--surface-card);border:1px solid var(--card-bd);border-radius:100px;
  box-shadow:var(--shadow);
  font-size:var(--fs-15);font-weight:600;color:var(--ink-mute);line-height:1.4;
}
.chip:hover{color:var(--action);border-color:var(--action-line);transform:translateY(-2px);box-shadow:var(--shadow-h)}
.chip__n{
  font-family:var(--f-mono);font-size:var(--fs-12);font-weight:400;color:var(--ink-meta);
  background:var(--surface-card-2);border-radius:100px;padding:2px 8px;min-width:22px;text-align:center;
}
.chip:hover .chip__n{background:var(--action-wash);color:var(--action)}
.chip--sm{font-size:var(--fs-14);font-weight:500;padding:8px 12px;min-height:44px}
.chip[aria-current="page"]{border-color:var(--action);color:var(--action)}
/* точка рубрики: единственный цвет рубрики, попадающий внутрь кликабельного —
   она не окрашивает ни текст, ни фон, ни рамку ссылки */
.chip__dot{width:6px;height:6px;border-radius:50%;background:var(--cat);flex:0 0 auto}

/* ---------- 8. Обложки ---------- */
/* В теме превью берётся первой картинкой записи:
   — широкая картинка кадрируется по aspect-ratio (slice), см. .cover;
   — узкий скриншот ставится в рамку на тонированном поле, чтобы слабая
     картинка не разваливала карточку;
   — для восьми записей без картинок рисуется обложка по рубрике (.cover--gen). */
.cover{
  --h:214;
  --c-bg:hsl(var(--h), 58%, 93%);
  --c-bg2:hsl(var(--h), 52%, 86%);
  --c-1:hsl(var(--h), 58%, 52%);
  --c-2:hsl(var(--h), 42%, 70%);
  --c-ink:hsl(var(--h), 34%, 34%);
  --c-panel:var(--surface-card);
  --c-line:hsl(var(--h), 30%, 82%);
  position:relative;
  aspect-ratio:16/10;
  background:var(--c-bg);
  overflow:hidden;
  border-bottom:1px solid var(--card-bd);
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .cover{
    --c-bg:hsl(var(--h), 24%, 14%);
    --c-bg2:hsl(var(--h), 26%, 19%);
    --c-1:hsl(var(--h), 52%, 58%);
    --c-2:hsl(var(--h), 30%, 40%);
    --c-ink:hsl(var(--h), 28%, 74%);
    --c-panel:hsl(var(--h), 18%, 11%);
    --c-line:hsl(var(--h), 18%, 28%);
  }
}
:root[data-theme="dark"] .cover{
  --c-bg:hsl(var(--h), 24%, 14%);
  --c-bg2:hsl(var(--h), 26%, 19%);
  --c-1:hsl(var(--h), 52%, 58%);
  --c-2:hsl(var(--h), 30%, 40%);
  --c-ink:hsl(var(--h), 28%, 74%);
  --c-panel:hsl(var(--h), 18%, 11%);
  --c-line:hsl(var(--h), 18%, 28%);
}
.cover>svg{width:100%;height:100%;display:block}
.cover__flag{
  position:absolute;left:12px;bottom:12px;z-index:2;
  display:inline-flex;align-items:center;gap:8px;
  padding:4px 12px;border-radius:100px;
  background:var(--surface-card);border:1px solid var(--signal-verified-line);color:var(--signal-verified);
  box-shadow:var(--shadow-flag);
  font-size:var(--fs-12);font-weight:550;line-height:1.45;
}
.cover__flag svg{width:13px;height:13px}
.cover__flag--ever{border-color:var(--signal-evergreen-line);color:var(--signal-evergreen)}

/* ---------- 9. Сетка записей ---------- */
.feed{display:grid;gap:var(--s-5)}
@media (min-width:700px){.feed{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:1080px){.feed--3{grid-template-columns:repeat(3,minmax(0,1fr))}}

.post{
  position:relative;display:flex;flex-direction:column;padding:0;overflow:hidden;
}
.post:hover{box-shadow:var(--shadow-h);transform:translateY(-3px);border-color:var(--action-line)}
.post:hover .cover>svg{transform:scale(1.035)}
/* Обложка-картинка приближается так же, как рисованная: приём один,
   и карточка не должна вести себя по-разному от того, чем занята. */
.post:hover img.cover{transform:scale(1.035)}
img.cover{width:100%;height:auto;object-fit:cover;transform-origin:center}
.post:has(a:focus-visible){box-shadow:var(--shadow-h);border-color:var(--action)}
.post__body{
  display:flex;flex-direction:column;flex:1 1 auto;
  padding:var(--s-5);
}
.post__title{
  margin-top:var(--s-3);
  font-size:var(--fs-19);
  line-height:1.3;font-weight:620;letter-spacing:var(--track-text);
  max-width:32ch;                 /* строка не растягивается на широком экране */
}
.post__title a{color:var(--ink)}
.post__title a::after{content:"";position:absolute;inset:0}
.post:hover .post__title a{color:var(--action)}
.post__deck{
  margin-top:var(--s-3);color:var(--ink-mute);
  font-size:var(--fs-15);font-weight:500;line-height:1.55;text-wrap:pretty;max-width:44ch;
}
.post__foot{
  margin-top:auto;padding-top:var(--s-5);
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--s-2) var(--s-4);
  font-size:var(--fs-13);font-weight:500;color:var(--ink-meta);
}
.post__foot span{display:inline-flex;align-items:center;gap:var(--s-3)}
.post__foot svg{width:14px;height:14px;color:var(--ink-meta)}
.post__go{margin-left:auto;display:inline-flex;align-items:center;gap:var(--s-3);font-weight:600;color:var(--ink-meta)}
.post:hover .post__go{color:var(--action)}
.post__go svg{width:14px;height:14px;color:currentColor}
.post:hover .post__go svg{transform:translateX(3px)}

/* мета: рубрика • дата (над заголовком) */
.meta{display:flex;align-items:center;flex-wrap:wrap;gap:0;font-size:var(--fs-13);line-height:1.4}
/* Надзаголовок над H1 — цветом рубрики. Цвет рубрики никогда
   не попадает на кликабельное, поэтому ссылка с тем же классом
   возвращается к синему. */
.meta__cat{font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);text-transform:uppercase;line-height:1.4;color:var(--cat-ink)}
a.meta__cat{color:var(--action)}
a.meta__cat:hover{text-decoration:underline;text-underline-offset:3px}
.meta__sep{width:3px;height:3px;border-radius:50%;background:var(--rule);margin:0 var(--s-3);flex:0 0 auto}
.meta__date{color:var(--ink-meta);font-size:var(--fs-12);font-weight:550}

/* ---------- 10. (свободен) ----------
   Здесь жила «крупная первая запись»: большая карточка верхнего поста
   в ленте — обложка слева, значок, заголовок, подводка и призыв.
   Разметка её не использовала нигде: ни в шаблонах, ни в данных
   инструментов, ни в отдаче сервера — проверено по всем страницам.
   Правила убраны, номер оставлен пустым: перенумеровывать сорок с лишним
   блоков ради одного дороже, чем оставить эту строку. */

/* ---------- 11. Значок статуса записи ---------- */
.tag{
  display:inline-flex;align-items:center;gap:var(--s-3);padding:var(--s-2) var(--s-4);border-radius:100px;
  font-size:var(--fs-12);font-weight:550;line-height:1.45;
  background:var(--signal-verified-wash);border:1px solid var(--signal-verified-line);color:var(--signal-verified);
}
.tag svg{width:13px;height:13px}
/* «В минусе» берёт ember-ink, а не evergreen: один цвет не может
   означать «вот ваш ответ» и «дело плохо» одновременно. */
.tag--bad{background:var(--signal-ember-wash);border-color:var(--signal-ember-ink);color:var(--signal-ember-ink)}
.tag--plain{background:var(--surface-card-2);border-color:var(--rule-hair);color:var(--ink-meta)}

/* ---------- 12. Компактный список записей ---------- */
.digest{padding:clamp(16px,1.8vw,24px) clamp(12px,1.6vw,16px)}
.digest__head{
  display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:var(--s-3) var(--s-6);
  padding-inline:var(--s-3);margin-bottom:var(--s-3);
}
.digest__head h2{font-size:var(--fs-20);font-weight:640;letter-spacing:var(--track-title);line-height:1.25}
.digest__head p{font-size:var(--fs-13);font-weight:500;color:var(--ink-meta)}
.dlist{list-style:none;display:flex;flex-direction:column}
.drow{
  position:relative;display:grid;
  grid-template-columns:44px minmax(0,1fr);
  align-items:baseline;gap:var(--s-1) var(--s-4);
  padding:var(--s-3);border-radius:10px;
  border-top:1px solid var(--rule-hair);
}
.dlist>.drow:first-child{border-top:0}
.drow:hover{background:var(--surface-card-2)}
/* Год — технический вывод: моно, табличные цифры (блок 2). */
.drow__year{font-size:var(--fs-12);font-family:var(--f-mono);font-weight:400;color:var(--ink-meta)}
.drow__title{
  font-size:var(--fs-15);font-weight:600;line-height:1.4;color:var(--ink);
  letter-spacing:var(--track-text);max-width:62ch;
}
.drow:hover .drow__title{color:var(--action)}
.drow__title::after{content:"";position:absolute;inset:0;border-radius:10px}
.drow__meta{
  grid-column:2;display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2) var(--s-4);
  font-size:var(--fs-12);font-weight:550;color:var(--ink-meta);
}
.drow__meta span{display:inline-flex;align-items:center;gap:var(--s-2)}
.drow__meta svg{width:12px;height:12px}
.drow__cat{font-weight:600;letter-spacing:var(--track-caps);text-transform:uppercase;font-size:var(--fs-11);color:var(--ink-meta);line-height:1.4}
@media (min-width:820px){
  .drow{grid-template-columns:52px minmax(0,1fr) auto;gap:0 var(--s-5);align-items:center;padding:var(--s-3);min-height:44px}
  .drow__meta{grid-column:auto;justify-content:flex-end;flex-wrap:nowrap;gap:var(--s-5);white-space:nowrap}
}
.more{margin-top:var(--s-4)}
.more>summary{
  list-style:none;cursor:pointer;
  display:flex;align-items:center;justify-content:center;gap:var(--s-3);
  min-height:46px;padding:var(--s-4) var(--s-5);border-radius:var(--r-xs);
  background:var(--surface-card-2);border:1px solid var(--rule-hair);
  font-size:var(--fs-15);font-weight:600;color:var(--ink-mute);
}
.more>summary::-webkit-details-marker{display:none}
.more>summary:hover{background:var(--action-wash);color:var(--action);border-color:var(--action-line)}
.more>summary svg{width:16px;height:16px}
.more[open]>summary svg{transform:rotate(180deg)}
.more[open]>summary .more__off{display:none}
.more:not([open])>summary .more__on{display:none}
.more__inner{padding-top:var(--s-3)}

/* ---------- 13. Инструменты — широкая секция ---------- */
.tools{background:linear-gradient(160deg, var(--surface-sunken), var(--surface-card) 62%);border-color:var(--action-line)}
.tools__head{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:var(--s-3) var(--s-6)}
.tools__head h2{font-size:var(--fs-24);font-weight:640;letter-spacing:var(--track-title);line-height:1.22}
.tools__badge{font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-meta);line-height:1.4}
.tools__lead{margin-top:var(--s-3);color:var(--ink-mute);font-size:var(--fs-15);font-weight:500;line-height:1.55;max-width:64ch}
.tools__grid{margin-top:var(--s-6);display:grid;gap:var(--s-5)}
@media (min-width:720px){.tools__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
.tool{
  position:relative;display:flex;flex-direction:column;
  background:var(--surface-card);border:1px solid var(--card-bd);border-radius:var(--r-s);padding:var(--s-5);
}
.tool:hover{box-shadow:var(--shadow-h);transform:translateY(-2px);border-color:var(--action-line)}
.tool__ico{
  position:relative;
  width:44px;height:44px;border-radius:12px;background:var(--cat-wash);color:var(--cat-ink);
  display:grid;place-items:center;margin-bottom:var(--s-4);
}
.tool__ico svg{width:23px;height:23px}
.tool h3{font-size:var(--fs-17);font-weight:620;letter-spacing:var(--track-text);line-height:1.3}
.tool h3 a{color:var(--ink)}
.tool h3 a::after{content:"";position:absolute;inset:0;border-radius:var(--r-s)}
.tool:hover h3 a{color:var(--action)}
.tool p{margin-top:var(--s-3);font-size:var(--fs-14);font-weight:500;line-height:1.5;color:var(--ink-mute)}
.tool__go{margin-top:var(--s-5);font-size:var(--fs-12);font-weight:550;color:var(--ink-meta);display:flex;align-items:center;gap:var(--s-3)}
.tool:hover .tool__go{color:var(--action)}
.tool__go svg{width:13px;height:13px}
.tool:hover .tool__go svg{transform:translateX(3px)}

/* ---------- 14. Подписка ---------- */
.subs{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--s-5) var(--s-7)}
.subs__txt{max-width:56ch}
.subs h2{font-size:var(--fs-28);font-weight:640;letter-spacing:var(--track-title);line-height:1.25}
.subs p{margin-top:var(--s-3);color:var(--ink-mute);font-size:var(--fs-15);font-weight:500;line-height:1.55}
.subs__links{display:flex;flex-wrap:wrap;gap:var(--s-3)}
.subs__links a{
  display:inline-flex;align-items:center;gap:var(--s-3);min-height:46px;padding:var(--s-4) var(--s-5);
  border-radius:var(--r-xs);font-size:var(--fs-15);font-weight:600;
  background:var(--surface-card-2);border:1px solid var(--rule-hair);color:var(--ink-mute);
}
.subs__links a:hover{background:var(--action-wash);color:var(--action);border-color:var(--action-line)}
.subs__links a.is-primary{
  background:var(--action);color:var(--action-ink);border-color:transparent;
}
.subs__links a.is-primary:hover{background:var(--action-hover);color:var(--action-ink)}
.subs__links a.is-primary:active{transform:translateY(var(--s-1))}
.subs__links svg{width:18px;height:18px}

/* ---------- 15. Пагинация ---------- */
.pager{margin-top:var(--s-6);display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-3);justify-content:center}
.pager a,.pager span{
  min-width:46px;min-height:46px;padding:var(--s-4) var(--s-5);gap:var(--s-3);
  display:inline-flex;align-items:center;justify-content:center;
  border-radius:var(--r-xs);font-weight:600;font-size:var(--fs-15);
  background:var(--surface-card);border:1px solid var(--card-bd);color:var(--ink-mute);
  box-shadow:var(--shadow);
}
.pager a:hover{color:var(--action);border-color:var(--action-line)}
.pager [aria-current="page"]{background:var(--action);color:var(--action-ink);border-color:transparent}
.pager__gap{background:none!important;box-shadow:none!important;border:0!important;color:var(--ink-meta)!important;min-width:auto;padding:var(--s-4) var(--s-1)}
.pager svg{width:15px;height:15px}

/* ---------- 16. Плашки состояния ---------- */
.notice{
  display:flex;gap:var(--s-4);align-items:flex-start;
  background:var(--surface-card-2);border:1px solid var(--rule-hair);border-radius:var(--r-s);
  padding:var(--s-5);font-size:var(--fs-15);font-weight:500;line-height:1.55;color:var(--ink-mute);
}
/* Текст в плашке обязан уметь сжиматься. У флекс-элемента min-width
   равен auto, то есть он не становится уже самого длинного слова, —
   а в разборе примера сюда попадает хеш SHA-512 в 128 знаков без
   единого пробела. Он распирал плашку до 541 px, и на экране 320 px
   вбок уезжала вся страница. */
.notice>:not(.notice__ico){min-width:0;overflow-wrap:anywhere}
.notice__ico{flex:0 0 auto;color:var(--ink-meta);margin-top:var(--s-1)}
.notice__ico svg{width:22px;height:22px}
.notice b{color:var(--ink);font-weight:620}
.notice--ok{background:var(--signal-verified-wash);border-color:var(--signal-verified-line)}
.notice--ok .notice__ico{color:var(--signal-verified)}

/* ---------- 17. Подвал ---------- */
.site-foot{margin-top:clamp(32px,4vw,48px);background:var(--surface-bg-2);padding-block:0 24px}
.foot__grid{display:grid;gap:24px}
@media (min-width:700px){.foot__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:1000px){.foot__grid{grid-template-columns:1.5fr 1fr 1fr 1.1fr}}
.foot h2{font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);text-transform:uppercase;line-height:1.4;color:var(--ink-meta);margin-bottom:12px}
.foot ul{list-style:none;display:flex;flex-direction:column;gap:2px}
.foot li a{display:inline-flex;align-items:center;gap:8px;min-height:44px;color:var(--ink-mute);font-size:var(--fs-15);font-weight:500}
.foot li a:hover{color:var(--action)}
.foot li a svg{width:16px;height:16px;color:var(--ink-meta);flex:0 0 auto}
.foot li a:hover svg{color:var(--action)}
.foot__about{color:var(--ink-mute);font-size:var(--fs-15);font-weight:500;line-height:1.6;max-width:40ch}
.foot .num{font-family:var(--f-mono);font-size:var(--fs-12);font-weight:400;color:var(--ink-meta)}
.foot__bottom{
  margin-top:24px;padding-top:16px;border-top:1px solid var(--rule-hair);
  display:flex;flex-wrap:wrap;gap:8px 24px;align-items:center;justify-content:space-between;
  font-size:var(--fs-13);font-weight:500;color:var(--ink-meta);
}
.foot__bottom a{color:var(--ink-meta)}
.foot__bottom a:hover{color:var(--action)}
.totop{
  display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:8px 16px;
  border-radius:100px;border:1px solid var(--rule);background:var(--surface-card);
  font-size:var(--fs-13);font-weight:600;color:var(--ink-mute);
}
.totop:hover{color:var(--action);border-color:var(--action-line)}
.totop svg{width:14px;height:14px}

/* ---------- 18. Соседние записи ---------- */
.prevnext{display:grid;gap:var(--s-5);margin-top:clamp(24px,3vw,32px)}
@media (min-width:700px){.prevnext{grid-template-columns:repeat(2,minmax(0,1fr))}}
.pn{
  position:relative;display:flex;flex-direction:column;gap:var(--s-3);
  background:var(--surface-card);border:1px solid var(--card-bd);border-radius:var(--r-s);
  padding:var(--s-5);box-shadow:var(--shadow);
}
.pn:hover{box-shadow:var(--shadow-h);border-color:var(--action-line);transform:translateY(-2px)}
.pn__label{
  font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);text-transform:uppercase;
  line-height:1.4;color:var(--ink-meta);
  display:flex;align-items:center;gap:var(--s-3);
}
.pn__label svg{width:13px;height:13px}
.pn__title{font-size:var(--fs-15);font-weight:600;line-height:1.4;color:var(--ink);letter-spacing:var(--track-text)}
.pn__title::after{content:"";position:absolute;inset:0;border-radius:var(--r-s)}
.pn:hover .pn__title{color:var(--action)}
.pn--next{text-align:right;align-items:flex-end}
.pn--empty{background:var(--surface-card-2);box-shadow:none;border-style:dashed;justify-content:center}
.pn--empty:hover{transform:none;box-shadow:none;border-color:var(--rule)}
.pn--empty .pn__title{color:var(--ink-meta);font-weight:500}

/* ---------- 19. Доступность и печать ----------
   Блока «prefers-reduced-motion: reduce» здесь больше нет: движение включается
   опт-ином в блоке 40, поэтому при сбое или частичной загрузке CSS человек
   получает статику, а не тряску. */
@media (prefers-contrast: more){
  :root,:root[data-theme]{--card-bd:var(--rule);--ink-meta:var(--ink-mute)}
}
@media print{
  /* .railnav и .jump — навигация поверх страницы. На экране их видимость
     решают ширины окна, а лист бумаги узкий: без этой строки кнопка
     перехода отпечаталась бы поверх текста. */
  .topbar,.site-foot,.pager,.prevnext,.crumbs-nav,.tools,.skip-link,.chips,.subs,.findbox,.progress,
  .railnav,.jumpbar{display:none!important}
  body{background:#fff;color:#000;font-size:12pt}
  .card{box-shadow:none;border:0;padding:0}
  .cover{display:none}
  .tick::before{display:none}
  .entry-content{max-width:none}
  .entry-content a[href^="http"]::after{content:" (" attr(href) ")";font-size:9pt;color:#444}
}


/* =========================================================
   20—31г. Общий слой всех шести страниц: дополнение к системе
   для каталога инструментов. Токены, шапка, крошки, служебная
   строка, карточки и подвал наследуются из блоков 1—19.
   Новое в t2: .chip__dot, значок «равно» у калькуляторов,
   .tick (насечка), .result вместо .calcpanel__big.
   ========================================================= */

/* ---------- 20. Иконка-примитив ---------- */
.ic{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

/* ---------- 21. Раскладка шапки по ширинам ----------
   До 640 px — три ряда: бренд и служебная капсула, поиск, рельс разделов.
   Поле поиска получает свой ряд целиком: на 375 px оно иначе делит
   строку с брендом и обрезается до трёх букв плейсхолдера.
   640—1139 px — два ряда: бренд · поиск · капсула, рельс снизу.
   От 1140 px — один ряд высотой ровно 75 px. Порог не круглый и не
   выдуман: рельсу из пяти пунктов в мелком кегле нужно 455 px, а его
   колонка равна ширине окна минус 669 (бренд, поиск 300, капсула, три
   щели). Равенство наступает на 1124 px, ближайший безопасный порог —
   1140. Ниже честнее отдать рельсу отдельный ряд, чем прокручивать его
   внутри капсулы. Порядок колонок важен: рельс сидит в 1fr (маска края
   иначе съедает последний пункт), поиск — в minmax(200,300) и прижат
   к капсуле. Так свободная ширина уходит рельсу, а не в щель
   у правого края. */
@media (min-width:640px){
  .topbar__inner{
    grid-template-columns:auto minmax(0,1fr) auto;
    grid-template-areas:"brand search tools" "nav nav nav";
  }
  .topsearch{justify-self:end;width:100%;max-width:420px}
}
@media (min-width:1140px){
  .topbar__inner{
    grid-template-columns:auto minmax(0,1fr) minmax(200px,300px) auto;
    grid-template-areas:"brand nav search tools";
    gap:var(--s-4);
  }
  /* высота пункта задана жёстко: 36 + 8 полей дорожки + 2 борта = 46,
     плюс 14 + 14 полей панели и 1 линия = ровно 75 px.
     Поля пункта здесь ужаты до 8 px: это единственная ширина, где
     рельс, поиск и капсула делят один ряд, и отдавать приходится
     самому дешёвому — воздуху внутри пункта, а не полю поиска. */
  .mainmenu a{height:36px;min-height:0;padding:0 var(--s-3)}
  .mainmenu a[aria-current="page"]::after{bottom:5px}
  .topsearch{height:42px;max-width:320px}
}
@media (min-width:1300px){
  .topbar__inner{gap:var(--s-5)}
  .mainmenu a{padding-inline:var(--s-5);font-size:var(--fs-15)}
}
/* совсем узкие экраны: имя сайта уступает место переключателям */
@media (max-width:359px){.brand__name{display:none}}

/* ---------- 22. Переключатель языка ----------
   Живёт внутри служебной капсулы вместе с темой: своей рамки и своего
   фона у него больше нет, границу держит капсула. Меню всплывает из
   капсулы наружу, поэтому у .topbar__tools нет overflow:hidden. */
.lang{position:relative;display:flex;align-items:center;height:42px}
.lang__btn{
  list-style:none;display:inline-flex;align-items:center;gap:var(--s-3);
  height:100%;padding:0 var(--s-4);cursor:pointer;
  background:none;border:0;border-radius:100px;
  font-size:var(--fs-13);font-weight:600;letter-spacing:var(--track-caps-13);color:var(--ink-mute);
}
.lang__btn::-webkit-details-marker{display:none}
.lang__btn::marker{content:""}
.lang__btn:hover{background:var(--action-wash);color:var(--action)}
.lang[open] .lang__btn{background:var(--action-wash-2);color:var(--action)}
.lang__btn>svg{width:17px;height:17px}
.lang__chev{width:12px!important;height:12px!important}
.lang[open] .lang__chev{transform:rotate(180deg)}
/* глобус уступает место буквам кода языка везде, где ряд узкий:
   на телефоне и в одноряднoй раскладке до 1300 px, где ширину
   у капсулы забирает поле поиска */
/* специфичность здесь не украшение: базовое правило блока 2
   svg[viewBox="0 0 24 24"] сильнее одиночного класса и вернуло бы
   глобусу display:inline-block */
@media (max-width:519px),(min-width:1140px) and (max-width:1299px){
  .lang__btn{gap:var(--s-2);padding:0 var(--s-3)}
  .lang__btn>.lang__globe{display:none}
}
.lang__menu{
  position:absolute;right:calc(var(--s-1) * -1);top:calc(100% + var(--s-4));z-index:60;min-width:206px;
  background:var(--surface-raised);border:1px solid var(--card-bd);border-radius:var(--r-s);
  box-shadow:var(--shadow-h);padding:var(--s-3);
}
.lang__menu a{
  display:flex;align-items:center;gap:var(--s-3);min-height:44px;padding:var(--s-3) var(--s-4);
  border-radius:var(--r-xs);font-size:var(--fs-15);font-weight:500;color:var(--ink-mute);
}
.lang__menu a:hover{background:var(--action-wash);color:var(--action)}
.lang__menu a[aria-current="true"]{color:var(--action);font-weight:600;background:var(--action-wash-2)}
.lang__code{margin-left:auto;font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);color:var(--ink-meta)}
.lang__menu a:hover .lang__code,.lang__menu a[aria-current="true"] .lang__code{color:var(--action)}

/* ---------- 23. Карточка инструмента ---------- */
.tgrid{display:grid;gap:var(--s-4);grid-template-columns:repeat(auto-fill,minmax(min(100%,258px),1fr))}
.tgrid--wide{grid-template-columns:repeat(auto-fit,minmax(min(100%,306px),1fr))}
.tcard{
  position:relative;display:flex;gap:var(--s-4);align-items:flex-start;
  background:var(--surface-card);border:1px solid var(--card-bd);border-radius:var(--r-s);
  padding:var(--s-4) var(--s-5);box-shadow:var(--shadow);
}
.tcard:hover{box-shadow:var(--shadow-h);transform:translateY(-2px);border-color:var(--action-line)}
.tcard:has(a:focus-visible){border-color:var(--action);box-shadow:var(--shadow-h)}
/* Плитка иконки: глиф берёт --cat-ink, а не --cat, — так он проходит
   по текстовой норме 4,5:1, а не по графической 3:1. */
.tcard__ico{
  position:relative;
  flex:0 0 auto;width:40px;height:40px;border-radius:12px;display:grid;place-items:center;
  background:var(--cat-wash);color:var(--cat-ink);
}
.tcard__ico svg{width:22px;height:22px}
.tcard__b{min-width:0;flex:1 1 auto;display:flex;flex-direction:column}
.tcard__name{font-size:var(--fs-15);font-weight:600;line-height:1.4;letter-spacing:var(--track-text)}
.tcard__name a{color:var(--ink)}
.tcard__name a::after{content:"";position:absolute;inset:0;border-radius:var(--r-s)}
.tcard:hover .tcard__name a{color:var(--action)}
/* описание инструмента — проза, а не вывод прибора: моно снят. */
.tcard__desc{margin-top:var(--s-2);font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-mute);text-wrap:pretty}
.tcard__f{
  margin-top:var(--s-3);align-self:flex-start;max-width:100%;
  font-family:var(--f-mono);font-size:var(--fs-12);font-weight:400;line-height:1.55;color:var(--ink-meta);
  background:var(--surface-card-2);border:1px solid var(--rule-hair);border-radius:8px;padding:var(--s-2) var(--s-3);
  overflow-wrap:break-word;   /* формулу лучше перенести, чем обрезать */
}
.tcard__s{margin-top:auto;padding-top:var(--s-3);display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap}
.tcard__open{display:inline-flex;align-items:center;gap:var(--s-3);font-size:var(--fs-12);font-weight:550;color:var(--ink-meta)}
.tcard__open svg{width:13px;height:13px}
.tcard:hover .tcard__open{color:var(--action)}
.tcard:hover .tcard__open svg{transform:translateX(3px)}
.tcard--live{border-color:var(--signal-verified-line)}
.tcard--live:hover{border-color:var(--signal-verified)}
.tcard--soon{background:var(--surface-card-2);box-shadow:none}
.tcard--soon .tcard__ico{background:var(--surface-card);color:var(--ink-meta);border:1px solid var(--rule-hair)}
.tcard--soon:hover{box-shadow:var(--shadow)}
/* Тип записи больше не спорит с рубрикой за цвет: плитка говорит
   «про что», а «калькулятор» помечен знаком равенства — форма,
   а не краска. Знак — настоящий элемент с доступным именем, а не
   фоновый градиент, которого для скринридера не существует. */
.kindmark{
  position:absolute;right:calc(var(--s-1) * -1);bottom:calc(var(--s-1) * -1);
  width:16px;height:16px;border-radius:50%;display:grid;place-items:center;
  background:var(--surface-card);border:1px solid var(--rule);color:var(--ink-meta);
  font-family:var(--f-mono);font-size:var(--fs-11);font-weight:400;line-height:1;
}
.tcard--soon .kindmark{background:var(--surface-card-2)}

/* ---------- 24. Категория каталога ---------- */
.catsec{margin-top:clamp(24px,3.2vw,48px)}
.catsec__head{
  display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap;
  padding-bottom:var(--s-4);margin-bottom:var(--s-5);border-bottom:1px solid var(--rule-hair);
}
.catsec__ico{
  flex:0 0 auto;width:36px;height:36px;border-radius:10px;display:grid;place-items:center;
  background:var(--cat-wash);color:var(--cat-ink);
}
.catsec__ico svg{width:20px;height:20px}
.catsec h2{font-size:var(--fs-20);font-weight:640;letter-spacing:var(--track-title);line-height:1.25}
.catsec__n{
  font-family:var(--f-mono);font-size:var(--fs-12);font-weight:400;color:var(--ink-meta);
  background:var(--surface-card-2);border:1px solid var(--rule-hair);border-radius:100px;padding:var(--s-1) var(--s-3);
}
.catsec__note{flex:1 1 220px;font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-meta);text-align:right}
@media (max-width:759px){.catsec__note{flex-basis:100%;text-align:left}}

/* ---------- 25. Липкая навигация по каталогу ---------- */
.catnav{
  position:sticky;top:0;z-index:26;margin-top:var(--s-6);padding:var(--s-3);
  display:flex;align-items:center;gap:var(--s-3);
  background:var(--topbar-bg);border:1px solid var(--card-bd);border-radius:var(--r-s);
  box-shadow:var(--shadow);
  -webkit-backdrop-filter:saturate(1.6) blur(12px);
          backdrop-filter:saturate(1.6) blur(12px);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .catnav{background:var(--surface-card)}
}
/* липкая панель встаёт вплотную под верхнюю: её высота ровно 75 px */
@media (min-width:1140px){.catnav{top:75px}}
/* Вертикальный запас у прокручиваемой полосы и втягивание его обратно
   отрицательным отступом. Причина не в красоте: overflow-x:auto по
   спецификации делает auto и вертикальную ось тоже, а высота полосы была
   ровно в высоту чипа — 44 px без полей. Из-за этого подъём чипа на 2 px
   при наведении и его тень обрезались краем прокрутки. Запас 8 px
   помещается внутрь собственных полей .catnav, поэтому высота полосы
   в раскладке не меняется ни на пиксель. */
.catnav__list{
  list-style:none;display:flex;gap:var(--s-3);flex:0 1 auto;min-width:0;padding-right:var(--s-5);
  padding-block:var(--s-3);margin-block:calc(var(--s-3) * -1);
  overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 12px),transparent);
          mask-image:linear-gradient(90deg,#000 calc(100% - 12px),transparent);
}
.catnav__list::-webkit-scrollbar{display:none}
.catnav__list li{flex:0 0 auto}
.catnav .chip{box-shadow:none;background:var(--surface-card-2);white-space:nowrap;padding:var(--s-3) var(--s-4);font-size:var(--fs-13)}
/* активный чип: подчёркивание 2 px цветом рубрики, заливка остаётся служебной */
.catnav .chip[aria-current]{
  background:var(--surface-card);color:var(--ink);border-color:var(--card-bd);
  box-shadow:inset 0 -2px 0 0 var(--cat);
}
.catfind{display:none;position:relative;flex:0 0 236px;margin-left:auto}
@media (min-width:980px){.catfind{display:block}}
.catfind__f{
  width:100%;height:44px;border-radius:100px;border:1px solid var(--rule-hair);
  background:var(--surface-card);padding:0 var(--s-5) 0 var(--s-8);font:inherit;font-size:var(--fs-14);color:var(--ink);
}
.catfind__f::placeholder{color:var(--ink-meta)}
.catfind__f:focus{border-color:var(--action);outline:none;box-shadow:0 0 0 3px var(--action-wash-2)}
.catfind__i{
  position:absolute;left:var(--s-5);top:50%;transform:translateY(-50%);
  width:16px!important;height:16px!important;color:var(--ink-meta);pointer-events:none;
}

/* ---------- 26. Панель калькулятора ---------- */
.calcpanel{margin-top:var(--s-6);display:grid;gap:var(--s-6)}
@media (min-width:860px){
  .calcpanel{grid-template-columns:minmax(0,1fr) minmax(0,.92fr);gap:var(--s-6);align-items:start}
}
.calcpanel>*{min-width:0}
.calcpanel__form{display:flex;flex-direction:column;gap:var(--s-5)}
.calcpanel__row{display:flex;flex-direction:column;gap:var(--s-3)}
.calcpanel__row label{font-size:var(--fs-13);font-weight:600;color:var(--ink-mute)}
/* Поле, в котором цифры моноширинные, читается как прибор ещё до того,
   как в него что-то введено. Вес 400: набор, а не заголовок. */
.calcpanel__inp{
  height:48px;border:1px solid var(--rule);border-radius:var(--r-xs);background:var(--surface-card);
  padding:0 var(--s-5);font-family:var(--f-mono);font-size:var(--fs-16);font-weight:400;color:var(--ink);
}
.calcpanel__inp:focus{border-color:var(--action);outline:none;box-shadow:0 0 0 3px var(--action-wash-2)}
.calcpanel__out{background:var(--surface-card-2);border:1px solid var(--rule-hair);border-radius:var(--r-s);padding:var(--s-6)}
/* Все инструменты считают вживую: кнопки «посчитать» нет ни у одного,
   кроме сжатия картинок. Значит смотреть на результат надо в тот момент,
   когда правишь поле, — а он стоял вверху правой колонки и уезжал за верх
   экрана на первой же длинной форме. Липкость держит его на месте.
   Отступ сверху — высота липкой шапки плюс тот же зазор, что у якорей.
   Работает только вместе с align-items:start у .calcpanel: растянутому
   на всю высоту ряда элементу ехать некуда, и sticky ничего не делает.
   Если результат сам выше экрана, липкость молча отключается — он просто
   прокручивается, и это верное поведение, а не поломка. */
/* Липким становится только прямой ребёнок панели — то есть колонка
   результата. Тот же класс встречается и внутри формы, и в разделах ниже
   как обычная карточка; там прилипать нечему и незачем. */
@media (min-width:860px){
  .calcpanel>.calcpanel__out{position:sticky;top:calc(var(--sticky-top) + var(--s-5))}
}
.calcpanel__lbl{font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);text-transform:uppercase;line-height:1.4;color:var(--ink-meta)}

/* Число-результат. Ни один продукт, чья задача — число, не берёт вес
   выше 510 на дисплейном кегле: банк показывает баланс регуляром.
   Авторитет даёт кегль и контраст, а не масса штриха, поэтому цвет
   здесь --ink. Единица — второй гарнитурой: другой кегль и другая
   текстура читаются как подпись к величине ещё до чтения. */
.result{
  display:flex;align-items:baseline;gap:.24em;
  font-size:var(--fs-num);line-height:1;margin-top:var(--s-3);
}
.result__num{
  font-weight:550;
  letter-spacing:var(--track-display);
  color:var(--ink);
  border-radius:6px;padding-inline:.06em;margin-inline:-.06em;
}
.result__unit{
  font-family:var(--f-mono);font-size:.40em;font-weight:400;
  letter-spacing:var(--track-text);color:var(--ink-meta);
}
/* Строки уходят на 8 px в поля панели: подсветка зависимой строки
   должна быть шире текста, иначе она читается как выделение слова. */
.calcpanel__rows{
  margin-top:var(--s-5);margin-inline:calc(var(--s-3) * -1);
  display:flex;flex-direction:column;border-top:1px solid var(--rule);
}
.calcpanel__rows div{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-5);
  padding:var(--s-3) var(--s-3);border-radius:6px;
  border-bottom:1px solid var(--rule);font-size:var(--fs-14);font-weight:500;color:var(--ink-meta);
}
.calcpanel__rows b{color:var(--ink);font-weight:550;text-align:right}
/* Курсор в поле — и подсвечиваются ровно те строки, которые от него
   зависят. Интерактив, который учит формуле, а не украшает её. */
.calcpanel:has(#f-budget:focus-visible) [data-dep~="budget"],
.calcpanel:has(#f-cpc:focus-visible)    [data-dep~="cpc"],
.calcpanel:has(#f-cr:focus-visible)     [data-dep~="cr"],
.calcpanel:has(#f-rev:focus-visible)    [data-dep~="rev"]{background-color:var(--flash)}
.calcpanel__hint{margin-top:var(--s-5);font-size:var(--fs-13);font-weight:500;line-height:1.55;color:var(--ink-meta)}
.calcpanel__say{font-size:var(--fs-15);font-weight:500;line-height:1.55;color:var(--ink-mute)}

/* ---------- 27. Входы в каталоги ---------- */
.hubs{display:grid;gap:var(--s-5)}
@media (min-width:800px){.hubs{grid-template-columns:repeat(2,minmax(0,1fr))}}
.hub{
  position:relative;display:flex;flex-direction:column;
}
.hub:hover{box-shadow:var(--shadow-h);transform:translateY(-2px);border-color:var(--action-line)}
.hub__ico{
  position:relative;
  width:46px;height:46px;border-radius:12px;display:grid;place-items:center;margin-bottom:var(--s-5);
  background:var(--cat-wash);color:var(--cat-ink);
}
.hub__ico svg{width:25px;height:25px}
.hub h3{font-size:var(--fs-20);font-weight:640;letter-spacing:var(--track-title);line-height:1.25}
.hub h3 a{color:var(--ink)}
.hub h3 a::after{content:"";position:absolute;inset:0;border-radius:var(--r)}
.hub:hover h3 a{color:var(--action)}
.hub p{margin-top:var(--s-3);font-size:var(--fs-15);font-weight:500;line-height:1.55;color:var(--ink-mute);max-width:46ch}
.hub__list{margin-top:var(--s-5);list-style:none;display:flex;flex-wrap:wrap;gap:var(--s-3)}
.hub__list li{
  display:inline-flex;align-items:center;gap:var(--s-3);
  font-size:var(--fs-12);font-weight:550;color:var(--ink-mute);background:var(--surface-card-2);
  border:1px solid var(--rule-hair);border-radius:100px;padding:var(--s-2) var(--s-4);
}
.hub__go{margin-top:auto;padding-top:var(--s-5);display:inline-flex;align-items:center;gap:var(--s-3);
  font-size:var(--fs-14);font-weight:600;color:var(--action)}
.hub__go svg{width:14px;height:14px}
.hub:hover .hub__go svg{transform:translateX(3px)}

/* ---------- 28. Почему в браузере ---------- */
.why{display:grid;gap:var(--s-5)}
@media (min-width:760px){.why{grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-6)}}
.why__i{
  width:38px;height:38px;border-radius:12px;display:grid;place-items:center;margin-bottom:var(--s-4);
  background:var(--action-wash);color:var(--action);
}
.why__i svg{width:21px;height:21px}
.why h3{font-size:var(--fs-17);font-weight:620;letter-spacing:var(--track-text);line-height:1.3}
.why p{margin-top:var(--s-3);font-size:var(--fs-14);font-weight:500;line-height:1.5;color:var(--ink-mute)}

/* ---------- 29. Строка-переход в конце блока ---------- */
.endrow{
  margin-top:var(--s-5);display:flex;flex-wrap:wrap;gap:var(--s-4) var(--s-5);align-items:center;
  justify-content:space-between;padding:var(--s-4) var(--s-5);border-radius:var(--r-s);
  background:var(--surface-card-2);border:1px solid var(--rule-hair);
  font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-mute);
}
.endrow a{
  display:inline-flex;align-items:center;gap:var(--s-3);min-height:44px;padding:var(--s-3) var(--s-5);
  border-radius:var(--r-xs);background:var(--surface-card);border:1px solid var(--action-line);
  color:var(--action);font-size:var(--fs-14);font-weight:600;white-space:nowrap;
}
.endrow a:hover{background:var(--action);color:var(--action-ink);border-color:transparent}
.endrow svg{width:15px;height:15px}

/* ---------- 30. Тап-зоны строчных ссылок ----------
   Ссылка остаётся строчной и стоит на той же базовой линии — растёт
   только область нажатия. У крошек её растят и полоса, и ссылки внутри:
   у .crumbs есть overflow-x, поэтому 44 px надо уместить в её поля,
   иначе полоса начнёт прокручиваться ещё и по вертикали. */
.section__more{padding:var(--s-4) var(--s-2);margin:calc(var(--s-4) * -1) calc(var(--s-2) * -1)}
.crumbs{padding-block:var(--s-4);margin-block:calc(var(--s-4) * -1)}
.crumbs a{display:inline-flex;align-items:center;padding-block:var(--s-4);margin-block:calc(var(--s-4) * -1)}

/* ---------- 30а. Флагманская карточка .tool из блока 13 ---------- */
.tool__s{margin-top:12px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}

/* ---------- 30б. Легенда типов в тонированной секции .tools ----------
   Цвет больше не различает инструмент и калькулятор: цвет несёт рубрику,
   тип показывает знак в углу плитки. */
.tools__kinds{
  margin-top:var(--s-5);display:flex;flex-wrap:wrap;gap:var(--s-3) var(--s-5);
  font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);
  text-transform:uppercase;line-height:1.4;color:var(--ink-meta);
}
.tools__kinds span{display:inline-flex;align-items:center;gap:var(--s-3)}
.tools__kinds i{
  width:16px;height:16px;flex:0 0 auto;display:grid;place-items:center;
  font-family:var(--f-mono);font-size:var(--fs-11);font-weight:400;line-height:1;font-style:normal;
}
.tools__kinds .is-cat{border-radius:5px;background:var(--cat-wash);border:1px solid var(--cat)}
.tools__kinds .is-calc{border-radius:50%;background:var(--surface-card);border:1px solid var(--rule);color:var(--ink-meta)}

/* ---------- 31. Поиск: счётчик и пустой результат ---------- */
.count{margin-top:var(--s-4);font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-meta)}
.count b{color:var(--ink-mute);font-family:var(--f-mono);font-weight:400}
[hidden]{display:none!important}

/* На печати прячем то, что на бумаге бессмысленно: полосу разделов,
   выбор языка, поиск. А вот поля ввода оставляем — раньше пряталась
   вся .calcpanel__form, и на лист уходил результат без единого
   исходного числа: «120 %» неизвестно от чего. Прячем внутри неё
   только кнопки и подсказки, они на бумаге не нужны. */
@media print{
  .catnav,.lang,.findbox,.catfind{display:none!important}
  .calcpanel__actions,.calcpanel__help,.fieldgo .btn{display:none!important}
  .calcpanel__inp,.calcpanel__form input,.calcpanel__form select,.calcpanel__form textarea{
    border:0;padding:0;background:none;color:#000;font-weight:600;
  }
  .calcpanel__form{break-inside:avoid}
}

/* ---------- 31а. Якоря под липкими панелями ----------
   Единственное место, где задан отступ якоря: наследованные 96 px из
   блока 2 перекрыты здесь, у .catsec и у заголовков статьи своего
   scroll-margin больше нет — иначе отступы складывались. */
html{--sticky-top:75px;--sticky-nav:60px}
html{scroll-padding-top:var(--s-4)}
html:has(.catnav){scroll-padding-top:calc(var(--sticky-nav) + var(--s-4))}
@media (min-width:1140px){
  html{scroll-padding-top:calc(var(--sticky-top) + var(--s-4))}
  html:has(.catnav){scroll-padding-top:calc(var(--sticky-top) + var(--sticky-nav) + var(--s-4))}
}

/* ---------- 31б. Подписи внутри иллюстраций ----------
   --c-1 из блока 8 — декоративный акцент: на светлой обложке он даёт
   около 2,4:1. Для фигур этого хватает, для подписей нет. */
.cover{--c-1-ink:hsl(var(--h), 62%, 28%)}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .cover{--c-1-ink:hsl(var(--h), 52%, 58%)}
}
:root[data-theme="dark"] .cover{--c-1-ink:hsl(var(--h), 52%, 58%)}

/* ---------- 31в. Шапка раздела, карточка фактов, соседние рубрики ---------- */
.cat-head{margin-top:var(--s-5);display:grid;gap:var(--s-6)}
/* Две колонки — с 1060, а не с 900.
   Правая колонка тут не украшение: в ней стоит карточка поиска с полем,
   кнопкой и подсказками. При доле .85fr от 900 px ей достаётся 288 px —
   в поле не влезал даже плейсхолдер («shadow, json, robot» с обрезанным
   хвостом), а три подсказки ломались на два ряда: две сверху, третья
   одна снизу. По-английски хуже, чем по-русски: слова длиннее.
   1060 — та ширина, на которой правой колонке достаётся около 330 px,
   и всё содержимое карточки встаёт в один ряд. Ниже — одна колонка,
   карточка во всю ширину, как это давно и происходит на планшете. */
@media (min-width:1060px){
  .cat-head{grid-template-columns:minmax(0,1.5fr) minmax(0,.85fr);gap:var(--s-7);align-items:start}
}
.cat-head>*{min-width:0}
.cat-head h1{font-size:var(--fs-48);font-weight:600;line-height:1.06;letter-spacing:var(--track-hero)}
.cat-head__kicker{
  font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);
  text-transform:uppercase;line-height:1.4;color:var(--cat-ink);
}

/* Вторая колонка шапки раздела бывает занята по-разному: на витринах в ней
   поиск, в блоге и рубриках — карточка фактов, а на посадочной инструмента
   второго ребёнка нет вовсе, и колонка стояла пустой — подводка ужималась
   в первую, а справа зияло четыреста сорок точек.
   Условие поэтому считает детей, а не ищет знакомый класс: сначала здесь
   стояло :not(:has(.facts)), и витрины схлопнулись вместе с поиском. */
.cat-head:not(:has(> * + *)){grid-template-columns:1fr}
.cat-head__lead{
  margin-top:var(--s-5);color:var(--ink-mute);
  font-size:var(--fs-17);font-weight:400;line-height:1.62;max-width:90ch;text-wrap:pretty;
}
.facts{display:flex;flex-direction:column;gap:0;padding:var(--s-3) var(--s-5)}
.facts div{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-5);
  padding:var(--s-4) 0;border-top:1px solid var(--rule-hair);font-size:var(--fs-14);font-weight:500;color:var(--ink-meta);
}
.facts div:first-child{border-top:0}
/* min-width:0 и перенос в любом месте нужны из-за содержимого: сюда
   попадают хеш SHA-512 в 128 знаков и закодированный адрес — строки
   без пробелов, которые флексу нечем разорвать. Без этих двух свойств
   значение распирало строку до 580 px и на телефоне вся страница
   уезжала вбок. Перенос «где угодно» здесь уместен: у шестнадцатеричной
   строки нет слов, которые можно испортить переносом. */
.facts b{
  color:var(--ink);font-family:var(--f-mono);font-weight:400;font-size:var(--fs-16);
  min-width:0;overflow-wrap:anywhere;text-align:right;
}
.facts .is-ok{color:var(--signal-verified)}
/* на телефоне чипы под лидом едут одной строкой, а не занимают два ряда */
@media (max-width:699px){
  .cat-head .chips__list{
    flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 12px),transparent);
            mask-image:linear-gradient(90deg,#000 calc(100% - 12px),transparent);
  }
  .cat-head .chips__list::-webkit-scrollbar{display:none}
  .cat-head .chips__list>*{flex:0 0 auto}
}
.next-cats{margin-top:var(--s-5)}
.next-cats h2{font-size:var(--fs-20);font-weight:640;letter-spacing:var(--track-title);line-height:1.25;margin-bottom:var(--s-4)}
.next-cats p{font-size:var(--fs-14);font-weight:500;line-height:1.55;color:var(--ink-mute);max-width:60ch;margin-bottom:var(--s-5)}

/* ---------- 31г. Насечка ----------
   Линейка с делениями высотой 8 px, стоит везде, где на странице
   начинается измеримое. Высокие деления 8 px через 32 px, короткие
   4 px через 8 px — тот же шаг, что доминанта отступов, поэтому
   линейка совпадает с ритмом сетки. Ноль изображений, ноль JS,
   стоимость сто двадцать шестого экземпляра — ноль.
   Насечка не кликается и не анимируется. */
.tick{--tick-c:var(--cat, var(--ink-meta))}
.tick::before{
  content:"";
  display:block;
  width:var(--tick-w, 72px);
  height:8px;
  margin-bottom:var(--s-4);
  background-repeat:no-repeat;
  background-image:
    repeating-linear-gradient(90deg, var(--tick-c) 0 2px, transparent 2px 32px),
    repeating-linear-gradient(90deg,
      color-mix(in srgb, var(--tick-c) 55%, transparent) 0 2px, transparent 2px 8px);
  background-size:100% 8px, 100% 4px;
  background-position:0 0, 0 0;
}
/* Насечка перестала быть отдельной висящей строкой. У заголовка страницы
   она идёт по всей ширине его колонки и служит верхней кромкой блока —
   тем же способом, каким она уже стоит у панели результата и у подвала.
   Зазор до строки 8 px вместо 12: линейка принадлежит заголовку,
   а не висит над ним сама по себе. */
.intro h1.tick,.cat-head h1.tick{--tick-w:100%}
h1.tick::before,h2.tick::before{margin-bottom:var(--s-3)}
.calcpanel__out.tick{--tick-w:100%;--tick-c:var(--ink-meta)}
.calcpanel__out.tick::before{margin-bottom:var(--s-5)}
.site-foot.tick{--tick-w:100%;--tick-c:var(--rule)}
.site-foot.tick::before{margin-bottom:var(--s-6)}

/* ---------- 32. Кнопки ---------- */
/* Плоская заливка, ровные поля, нажатие — сдвигом вниз на два пикселя.
   Перехода нет вообще: это не анимация, это геометрия.
   Ховер уходит вниз по лестнице синего (контраст растёт до 8,99:1),
   а не осветляется фильтром, который контраст снижал.

   Раньше здесь была «губа» — нижняя рамка в четыре пикселя цветом на две
   ступени темнее лица, а поля стояли 12 и 8, чтобы сумма «нижнее поле плюс
   губа» совпала с верхним полем и высота не менялась при нажатии. Плата
   была двойная: надпись вставала по центру ВСЕЙ коробки, а глаз меряет её
   по лицу — и сидела на два пикселя ниже; сама же полоса читалась не как
   ребро, а как чужая линия под кнопкой, особенно в тёмной теме.
   Теперь рамка со всех сторон одна, поля равны, надпись по центру, а
   высота при нажатии не меняется просто потому, что менять нечего.
   Заодно кнопка поиска сравнялась по росту с полем рядом: губа делала её
   на три пикселя выше. */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-3);
  min-height:46px;padding-inline:var(--s-6);
  padding-block:var(--s-3);
  border:1px solid transparent;
  border-radius:var(--r-xs);
  font:inherit;font-size:var(--fs-15);font-weight:600;cursor:pointer;text-align:center;
  background:var(--action);color:var(--action-ink);
}
.btn:hover{background:var(--action-hover)}
.btn:active{transform:translateY(var(--s-1))}
.btn svg{width:16px!important;height:16px!important;flex:0 0 auto}
.btn--ghost{background:var(--surface-card);color:var(--ink-mute);border-color:var(--rule)}
.btn--ghost:hover{background:var(--surface-card);color:var(--action);border-color:var(--action-line)}
/* Единственное место на всём сайте, где появляется --signal-ember.
   Отметка «скопировано» жила в цвете губы; губы больше нет, и её место
   занимает кольцо. Именно box-shadow, а не outline: outline на кнопке
   занят кольцом фокуса, и два кольца спорили бы за один контур.
   Тень раскладку не двигает — высота кнопки остаётся прежней. */
.btn.is-ok{box-shadow:0 0 0 2px var(--signal-ember)}

/* ---------- 33. Расширения панели калькулятора ---------- */
/* Базовый .calcpanel описан в блоке 26. Здесь — то, чего нет
   в демо на главной: единицы, подсказки, ошибки ввода, валюта,
   кнопки и шкала точки безубыточности. */
@media (min-width:520px){
  .calcpanel__form--2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-5);align-content:start}
  .calcpanel__form--2 .calcpanel__row--wide{grid-column:1 / -1}
}
.calcpanel__wrap{position:relative;display:flex;align-items:center;min-width:0}
.calcpanel__wrap .calcpanel__inp{width:100%;min-width:0;padding-right:var(--s-8)}
.calcpanel__unit{
  position:absolute;right:14px;font-family:var(--f-mono);font-size:var(--fs-15);font-weight:400;
  color:var(--ink-meta);pointer-events:none;
}
.calcpanel__help{font-size:var(--fs-12);font-weight:550;line-height:1.45;color:var(--ink-meta);font-variant-numeric:proportional-nums}
/* Ошибка ввода и результат расчёта больше не одного цвета: один тон
   не может означать «вот ваш ответ» и «вы ввели неверно». */
.calcpanel__err{font-size:var(--fs-12);line-height:1.45;font-weight:600;color:var(--signal-ember-ink);display:none}
/* Сообщение от сервера показывается по признаку data-live, который ставит
   assets/tool-fetch.js, когда пишет туда текст.
   Не по «непустому» узлу: у двух десятков инструментов текст ошибки заранее
   вписан в разметку как заготовка, и правило :not(:empty) показывало их все
   разом и навсегда. Проверено — так и вышло, пришлось откатывать.
   Заготовки внутри строки поля остаются на прежнем правиле is-bad ниже. */
.calcpanel__err[data-live]{display:block}
.calcpanel__row.is-bad .calcpanel__inp{border-color:var(--signal-ember-ink);background:var(--signal-ember-wash)}
.calcpanel__row.is-bad .calcpanel__inp:focus{box-shadow:0 0 0 3px var(--signal-ember-wash)}
.calcpanel__row.is-bad .calcpanel__help{display:none}
.calcpanel__row.is-bad .calcpanel__err{display:block}

.calcpanel__group{border:0;margin:0;padding:0;min-inline-size:0}
.calcpanel__group>legend{padding:0;margin-bottom:var(--s-3);font-size:var(--fs-13);font-weight:600;color:var(--ink-mute)}
/* У fieldset сняты и рамка, и отступы — значит сам он детей ничем не
   разделяет, и ряд кнопок прилипал к полям сверху и к подписи снизу.
   Зазор берём тот же, что между строками формы, чтобы группа внутри
   панели не выглядела плотнее остальной панели. Первый после legend
   исключён: у legend уже есть свой margin-bottom, иначе сложатся два. */
.calcpanel__group>*+*{margin-top:var(--s-5)}
.calcpanel__group>legend+*{margin-top:0}
/* position:relative — не ради смещения, а чтобы удержать спрятанные
   переключатели: они absolute, и без позиционированного предка их держал
   бы сам экран.
   Селектор по типу — не украшение, а вес. Ниже по файлу стоит
   .cform input{width:100%;min-height:44px} ровно той же специфичности,
   и по порядку он выигрывал: спрятанный переключатель получал сто
   процентов ширины и сорок четыре точки роста. Сто процентов он мерил
   ОТ СТРАНИЦЫ и торчал за её правый край — телефон получал прокрутку
   вбок на сорок точек. Проверено на сборщике меток и на проверке
   регулярных выражений; внутри сегмента бывают только radio и checkbox. */
.calcpanel__seg{position:relative;display:flex;flex-wrap:wrap;gap:var(--s-3)}
.calcpanel__seg input[type="radio"],
.calcpanel__seg input[type="checkbox"]{
  position:absolute;width:1px;height:1px;min-height:0;opacity:0;pointer-events:none
}
.calcpanel__seg label{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-3);
  min-width:56px;min-height:44px;padding:var(--s-3) var(--s-5);border-radius:100px;
  background:var(--surface-card-2);border:1px solid var(--rule-hair);
  font-size:var(--fs-14);font-weight:600;color:var(--ink-mute);cursor:pointer;
}
.calcpanel__seg label:hover{color:var(--action);border-color:var(--action-line)}
.calcpanel__seg input:checked+label{background:var(--action-wash-2);border-color:var(--action);color:var(--action)}
.calcpanel__seg input:focus-visible+label{outline:2px solid var(--action-ring);outline-offset:3px}
.calcpanel__sign{font-family:var(--f-mono);font-size:var(--fs-16);font-weight:400}

.calcpanel__actions{display:flex;flex-wrap:wrap;gap:var(--s-4);align-items:center}
.calcpanel__live{font-size:var(--fs-12);font-weight:550;line-height:1.45;color:var(--ink-meta);max-width:32ch}
.calcpanel__state{margin-top:var(--s-4);display:flex;flex-wrap:wrap;gap:var(--s-3);align-items:center}
.calcpanel__say{margin-top:var(--s-4);font-size:var(--fs-15);font-weight:500;line-height:1.55;color:var(--ink-mute);max-width:46ch;font-variant-numeric:proportional-nums}

.calcpanel__be{margin-top:var(--s-6);padding-top:var(--s-5);border-top:1px solid var(--rule)}
.calcpanel__be h3{font-size:var(--fs-17);font-weight:620;line-height:1.3;color:var(--ink)}
.calcpanel__be>p{margin-top:var(--s-3);font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-meta);max-width:46ch}
/* Дорожка — буквально линейка, по которой ползёт отметка нулевой
   точки: здесь фирменная деталь смыкается с продуктом. Исключение
   к запрету анимации width/left: оба элемента абсолютны внутри
   дорожки фиксированной высоты, layout не пересчитывается. */
.bescale{
  position:relative;margin-top:var(--s-6);height:16px;
  background-repeat:no-repeat;
  background-image:
    repeating-linear-gradient(90deg, var(--rule) 0 2px, transparent 2px 32px),
    repeating-linear-gradient(90deg,
      color-mix(in srgb, var(--rule) 55%, transparent) 0 2px, transparent 2px 8px);
  background-size:100% 8px, 100% 4px;
  background-position:0 8px, 0 12px;
}
.bescale__fill{position:absolute;top:0;left:0;height:4px;width:0;border-radius:100px;background:var(--action)}
.bescale__mark{position:absolute;top:-2px;left:0;height:20px;width:2px;border-radius:2px;background:var(--ink)}
.bescale__legend{margin-top:var(--s-4);display:flex;flex-wrap:wrap;gap:var(--s-3) var(--s-6);font-size:var(--fs-12);font-weight:550;color:var(--ink-meta)}
.bescale__legend span{display:inline-flex;align-items:center;gap:var(--s-3)}
.bescale__legend b{color:var(--ink-mute);font-family:var(--f-mono);font-weight:400;font-variant-numeric:tabular-nums}
.bescale__key{width:10px;height:10px;border-radius:3px;flex:0 0 auto}
.bescale__key--now{background:var(--action)}
.bescale__key--zero{background:var(--ink)}

/* ---------- 34. Инструкция, формула, пример, вопросы ---------- */
.steps{list-style:none;counter-reset:st;display:grid;gap:var(--s-4);margin-top:var(--s-5)}
@media (min-width:820px){.steps{grid-template-columns:repeat(2,minmax(0,1fr))}}
.steps li{
  display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--s-1) var(--s-5);align-items:start;
  padding:var(--s-5) var(--s-5);border-radius:var(--r-s);background:var(--surface-card-2);border:1px solid var(--rule-hair);
}
.steps li::before{
  counter-increment:st;content:counter(st);grid-row:1 / span 2;
  width:30px;height:30px;border-radius:50%;
  background:var(--surface-card);border:1px solid var(--rule);color:var(--ink-meta);
  display:grid;place-items:center;font-family:var(--f-mono);font-size:var(--fs-14);font-weight:400;
  font-variant-numeric:tabular-nums;
}
.steps b{font-size:var(--fs-15);font-weight:620;line-height:1.35;color:var(--ink);align-self:center}
.steps p{margin-top:var(--s-3);font-size:var(--fs-14);font-weight:500;line-height:1.5;color:var(--ink-mute)}

.formula{
  margin-top:var(--s-5);padding:var(--s-5) var(--s-6);border-radius:var(--r-s);
  background:var(--surface-card-2);border:1px solid var(--rule-hair);
  overflow-x:auto;-webkit-overflow-scrolling:touch;
}
.formula p{
  font-family:var(--f-mono);font-size:clamp(.875rem,.8rem + .4vw,1.0625rem);
  line-height:1.7;font-weight:400;color:var(--ink-mute);white-space:nowrap;
  font-variant-numeric:tabular-nums slashed-zero;font-variant-ligatures:none;
}
.formula b{color:var(--ink);font-weight:400}
.legend{list-style:none;margin-top:var(--s-5);display:grid;gap:var(--s-3)}
.legend li{display:flex;gap:var(--s-4);font-size:var(--fs-14);font-weight:500;line-height:1.5;color:var(--ink-mute)}
/* Термин и пояснение — один поток, а не два соседних предмета флексбокса.
   Пока термины были короткими («Расход», «Клики»), разница не читалась;
   на «Неразрывный пробел» жирная часть переносилась внутри своей колонки,
   а пояснение вставало рядом столбиком и налезало на неё. */
.legend li>span{min-width:0}
.legend li::before{
  content:"";flex:0 0 auto;width:6px;height:6px;border-radius:50%;
  background:var(--ink-meta);margin-top:.62em;
}
.legend b{color:var(--ink);font-weight:620}

/* position:relative тут не для смещения, а чтобы удержать потомков.
   Внутри таблицы попадаются подписи .visually-hidden — они absolute, и
   без позиционированного предка их держал бы сам экран: подпись вставала
   по своему месту в ТАБЛИЦЕ шириной семьсот точек, то есть мимо страницы,
   и телефон получал лишние пятнадцать точек прокрутки вбок. Прокрутка
   обёртки её не обрезала: absolute с внешним предком уходит из-под
   обрезки. Проверено на матрице ДРР. */
.exam{position:relative;margin-top:var(--s-5);overflow-x:auto;-webkit-overflow-scrolling:touch}
.exam table{border-collapse:collapse;width:100%;min-width:400px;font-size:var(--fs-15)}
.exam caption{
  text-align:left;padding-bottom:var(--s-4);
  font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-meta);
}
.exam th,.exam td{padding:var(--s-4) var(--s-5);text-align:left;border-top:1px solid var(--rule-hair);vertical-align:baseline}
.exam thead th{
  border-top:0;font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-meta);
}
.exam th:last-child,.exam td:last-child{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.exam tbody th{font-weight:500;color:var(--ink-mute)}
.exam tbody td{color:var(--ink);font-family:var(--f-mono);font-weight:400;font-variant-numeric:tabular-nums slashed-zero}
.exam tbody tr:hover{background:var(--surface-card-2)}
.exam .is-total th,.exam .is-total td{border-top:2px solid var(--rule)}
.exam .is-total th{color:var(--ink);font-weight:620}
.exam .is-total td{color:var(--ink);font-size:var(--fs-17)}

.faq{list-style:none;display:grid;gap:var(--s-4);margin-top:var(--s-5)}
.faq__i{background:var(--surface-card-2);border:1px solid var(--rule-hair);border-radius:var(--r-s)}
.faq__i>summary{
  list-style:none;cursor:pointer;display:flex;align-items:center;gap:var(--s-5);
  min-height:52px;padding:var(--s-5) var(--s-5);
  font-size:var(--fs-16);font-weight:600;line-height:1.4;color:var(--ink);
}
.faq__i>summary::-webkit-details-marker{display:none}
.faq__i>summary::marker{content:""}
.faq__i>summary:hover{color:var(--action)}
.faq__i>summary::after{
  content:"";margin-left:auto;flex:0 0 auto;width:10px;height:10px;
  border-right:2px solid var(--ink-meta);border-bottom:2px solid var(--ink-meta);
  transform:rotate(45deg) translate(-3px,-3px);
}
.faq__i>summary:hover::after{border-color:var(--action)}
.faq__i[open]>summary::after{transform:rotate(225deg) translate(-3px,-3px)}
.faq__a{padding:0 var(--s-5) var(--s-5);font-size:var(--fs-15);font-weight:500;line-height:1.6;color:var(--ink-mute);max-width:78ch;font-variant-numeric:proportional-nums}
.faq__a>*+*{margin-top:.85em}
.faq__a code{
  font-family:var(--f-mono);font-size:.9em;background:var(--surface-card);
  border:1px solid var(--rule-hair);border-radius:5px;padding:.06em .34em;
  font-variant-numeric:tabular-nums slashed-zero;font-variant-ligatures:none;
}

/* ---------- 35. Оглавление и текстовая колонка ---------- */
.toc{
  margin:clamp(20px,2.4vw,28px) clamp(18px,2.4vw,32px) 0;
  padding:var(--s-5) var(--s-5);border-radius:var(--r-s);
  background:var(--surface-card-2);border:1px solid var(--rule-hair);
}
.toc h2{font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-meta)}
.toc ol{list-style:none;margin-top:var(--s-4);display:flex;flex-wrap:wrap;gap:var(--s-3);counter-reset:toc}
.toc a{
  display:inline-flex;align-items:center;gap:var(--s-3);min-height:44px;padding:var(--s-3) var(--s-5);
  border-radius:100px;background:var(--surface-card);border:1px solid var(--rule);
  font-size:var(--fs-14);font-weight:600;color:var(--ink-mute);
}
.toc a::before{
  counter-increment:toc;content:counter(toc);
  font-size:var(--fs-12);font-family:var(--f-mono);color:var(--ink-meta);font-variant-numeric:tabular-nums;
}
.toc a:hover{color:var(--action);border-color:var(--action-line)}
/* тот же список, но отдельной карточкой, а не внутри статьи */
.toc--flat{margin:clamp(20px,2.4vw,28px) 0 0}

.entry-content{
  margin-top:clamp(20px,2.4vw,28px);
  padding-inline:clamp(18px,2.4vw,32px);
  display:grid;
  grid-template-columns:
    [wide-start] minmax(0,1fr)
    [text-start] minmax(0,700px)
    [text-end] minmax(0,1fr) [wide-end];
}
.entry-content>*{grid-column:text}
.entry-content>figure,.entry-content>pre{grid-column:wide;justify-self:center;width:min(100%,940px)}
.entry-content>*+*{margin-top:1.15em}
.entry-content p{line-height:1.65;color:var(--ink);text-wrap:pretty;font-variant-numeric:proportional-nums}
/* ── Таблицы в записях ──────────────────────────────────────────────
   Стилей у них не было вовсе: <table> из статьи выходил голым, без рамок
   и с колонками по содержимому. Правила навешаны на САМИ элементы, а не
   на классы: таблицу вставляют откуда придётся, и она обязана выглядеть
   верно без единого класса. Обёртка .table-container поддержана, но
   не обязательна — прокрутка включается и у голой таблицы.

   Таблица шире колонки текста: как figure и pre, она занимает широкую
   полосу — в узкие 700 px пять столбцов не помещаются. */
.entry-content>table,
.entry-content>.table-container{
  grid-column:wide;justify-self:center;
}
.entry-content>.table-container{
  width:min(100%,940px);
  overflow-x:auto;-webkit-overflow-scrolling:touch;
}
.entry-content table{
  /* separate, а не collapse: при схлопывании браузер выбрасывает
     скругление у ячеек, и подложка шапки получалась с прямыми углами.
     Разделители при этом не удваиваются — граница задана только сверху. */
  border-collapse:separate;border-spacing:0;width:100%;
  font-size:var(--fs-15);line-height:1.5;
}
/* Ширину голой таблицы задаём ПОСЛЕ общего width:100% — иначе он её
   перебивает, и таблица растягивается на всю широкую полосу (замерено:
   1138 px против положенных 940). */
.entry-content>table{max-width:min(100%,940px)}
/* На узком экране голая таблица прокручивается сама. overflow к боксу
   display:table не применяется вовсе — приходится делать его блоком.
   В обёртке .table-container этого не нужно: там прокручивается обёртка,
   а таблица остаётся таблицей. */
@media (max-width:760px){
  .entry-content>table{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch}
}
.entry-content th,.entry-content td{
  padding:var(--s-4) var(--s-5);text-align:left;vertical-align:top;
  border-top:1px solid var(--rule-hair);
}
.entry-content thead th{
  background:var(--surface-card-2);
  border-top:0;border-bottom:1px solid var(--rule);
  font-size:var(--fs-13);font-weight:620;color:var(--ink-mute);
  letter-spacing:var(--track-caps);text-transform:uppercase;white-space:nowrap;
}
.entry-content thead th:first-child{border-start-start-radius:var(--r-xs)}
.entry-content thead th:last-child{border-start-end-radius:var(--r-xs)}
.entry-content tbody tr:hover{background:var(--surface-card-2)}
.entry-content td small{display:block;margin-top:2px;font-size:var(--fs-13);color:var(--ink-meta)}
/* Числа и цены — моноширинные цифры: столбец выравнивается по разряду. */
.entry-content td:has(> :only-child):not(:has(a)){font-variant-numeric:tabular-nums}
.entry-content caption{
  caption-side:top;text-align:left;padding-bottom:var(--s-4);
  font-size:var(--fs-13);font-weight:500;color:var(--ink-meta);
}

/* Метки состояния в ячейках. Три вида — их и присылают чаще всего:
   идёт, ждёт, отменено. Цвета берём из сигнальных токенов сайта,
   чтобы «активен» здесь был того же зелёного, что «работает» в каталоге. */
.entry-content .badge{
  display:inline-block;padding:2px 10px;border-radius:100px;
  font-size:var(--fs-13);font-weight:600;line-height:1.5;white-space:nowrap;
  background:var(--surface-card-2);border:1px solid var(--rule-hair);color:var(--ink-mute);
}
.entry-content .badge.active,.entry-content .badge.ok{
  background:var(--signal-verified-wash);border-color:var(--signal-verified-line);color:var(--signal-verified);
}
.entry-content .badge.pending,.entry-content .badge.wait{
  background:var(--signal-ember-wash);border-color:var(--signal-ember-ink);color:var(--signal-ember-ink);
}
.entry-content .badge.cancelled,.entry-content .badge.off{
  background:var(--surface-card-2);border-color:var(--rule);color:var(--ink-meta);text-decoration:line-through;
}

.entry-content h2{font-size:clamp(1.5rem,1.28rem + .8vw,1.75rem);font-weight:620;letter-spacing:var(--track-title);line-height:1.22;margin-top:1.9em;margin-bottom:-.35em}
.entry-content h3{font-size:clamp(1.25rem,1.14rem + .4vw,1.375rem);line-height:1.3;font-weight:620;letter-spacing:var(--track-title);margin-top:1.7em;margin-bottom:-.4em}
.entry-content a{
  color:var(--action);text-decoration:underline;text-decoration-thickness:1px;
  text-underline-offset:.2em;text-decoration-color:var(--action-line);
}
.entry-content a:hover{text-decoration-color:currentColor}
.entry-content ul,.entry-content ol{display:flex;flex-direction:column;gap:.5em;padding-left:1.5em}
.entry-content ul{list-style:none;padding-left:0}
.entry-content ul li{position:relative;padding-left:1.5em}
.entry-content ul li::before{content:"";position:absolute;left:.35em;top:.72em;width:6px;height:6px;border-radius:50%;background:var(--action);opacity:.8}
.entry-content ol li{padding-left:.3em}
.entry-content ol li::marker{color:var(--action);font-weight:600}
.entry-content blockquote{
  margin-block:1.6em;padding:var(--s-1) 0 var(--s-1) var(--s-6);border-left:3px solid var(--action);color:var(--ink-mute);
}
.entry-content pre{
  padding:var(--s-5) var(--s-6);border-radius:var(--r-s);
  overflow-x:auto;font-family:var(--f-mono);font-size:var(--fs-13);line-height:1.55;
  font-variant-numeric:tabular-nums slashed-zero;font-variant-ligatures:none;
  white-space:pre;-webkit-overflow-scrolling:touch;
}
/* Поверхность кода объявлена через :where(), поэтому её вес равен нулю
   и правило блока 1 «на бумаге код возвращается на холодное» выигрывает
   без единого нового хекса. Вне бумажной области работает эта строка. */
.entry-content :where(pre,code){background:var(--surface-card-2);border:1px solid var(--rule-hair);color:var(--ink)}
.entry-content pre code{background:none;padding:0;font-size:inherit;border:0}
.entry-content code{
  font-family:var(--f-mono);font-size:.9em;
  border-radius:5px;padding:.06em .34em;
  font-variant-numeric:tabular-nums slashed-zero;font-variant-ligatures:none;
  box-decoration-break:clone;-webkit-box-decoration-break:clone;
}
.entry-content figure{margin:1.9em 0}
.entry-content figure .cover{border-radius:var(--r-s);border:1px solid var(--card-bd);aspect-ratio:16/7}
.entry-content figcaption{margin-top:var(--s-4);font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-meta);max-width:70ch}
.entry-content strong{font-weight:620}
.entry-content a.is-ext::after{
  content:"";display:inline-block;width:.62em;height:.62em;margin-left:.28em;
  background:currentColor;opacity:.55;
  -webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17 17 7M9 7h8v8"/></svg>');
          mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17 17 7M9 7h8v8"/></svg>');
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain;
}
/* та же колонка, но внутри карточки с собственными полями и по левому краю */
.entry-content--flat{
  margin-top:0;padding-inline:0;
  grid-template-columns:[wide-start text-start] minmax(0,74ch) [text-end] minmax(0,1fr) [wide-end];
}
.entry-content--flat>:first-child{margin-top:0}

/* ---------- 36. Карточки каталога внутри тонированной секции ---------- */
/* Обёртка .tools описана в блоке 13 ядра, карточки .tcard — в блоке 23,
   легенда цветов — в блоке 30б. Здесь только связка сетки с секцией. */
.tools .tgrid{margin-top:var(--s-6)}

/* --- посадочная одного калькулятора --- */
.cat-head .facts b{font-size:var(--fs-15)}
/* На узком экране карточку фактов убираем: поля калькулятора должны попадать
   в первый экран. Состояние и дата при этом не теряются — они в .subline
   под заголовком, которая видна на всех ширинах. */
@media (max-width:899px){.cat-head .facts{display:none}}

/* ---------- 37. Мелочь посадочной ---------- */
/* .path переехал в общий блок 4: теперь он часть .subline под H1. */
/* Вводный абзац раздела. Был инлайновым style в шести местах. */
.sec__lead{
  margin-top:var(--s-3);
  font-size:var(--fs-15);font-weight:500;line-height:1.55;
  color:var(--ink-mute);max-width:66ch;font-variant-numeric:proportional-nums;
}
/* Точка рубрики в крошках: третий из пяти разрешённых выходов цвета.
   Ставится на ссылку, а не на li: у li уже занят ::before под шеврон. */
.crumbs .is-cat a::before{
  content:"";display:inline-block;flex:0 0 auto;
  width:6px;height:6px;border-radius:50%;background:var(--cat);
  margin-right:var(--s-3);
}
/* Кнопка «Скопировать расчёт» держит подпись одной ширины,
   чтобы строка действий не перекладывалась при смене текста. */
#copy-lab{display:inline-block;min-width:15ch;text-align:left}

/* ---------- 38. Подсветка кода ----------
   Раскраска проставляется заранее, на сервере: движок токенизирует
   листинг один раз при сохранении статьи и кладёт готовые span'ы
   в постраничный кеш. В браузере не выполняется ничего. Prism и
   highlight.js отпадают дважды: это внешние запросы, запрещённые
   разделом 1 спецификации, и это разбор кода на клиенте ради того,
   что уже известно на сервере.

   Класс описывает не язык, а роль лексемы: восемь ролей на все языки
   сразу. Заводить .lang-apache и .lang-shell не за чем — директива
   Apache и ключевое слово оболочки играют в тексте одну роль, и красить
   их надо одинаково. Языка, которого движок не знает, он не трогает
   вовсе: листинг выходит одноцветным, как сегодня, и это честнее
   угаданной раскраски.

   Цвета измерены по WCAG на --surface-card-2 — на той поверхности,
   где листинг лежит на самом деле (светлая #F4F6FB, тёмная #1B2028),
   а не на карточке. Худшая пара таблицы — 5,53:1 в светлой и 6,48:1
   в тёмной; порог 4,5:1 держат все восемь ролей, комментарий в том
   числе. Бледный комментарий — это текст, который решили не давать
   читать, а в листинге он часто главный.

   Тон каждой роли выведен наружу из коридора бренда 272—312° CIELCH:
   ни одна лексема не притворяется ссылкой. Светлота держится там же,
   где у --signal-*: L*36—40 в светлой теме, L*67—77 в тёмной — палитра
   стоит рядом с сигналами, а не поверх действия. Тёмная тема
   переопределяется теми же тремя селекторами, что и в блоке 1.

   Ни font-weight, ни font-style роли не меняют: из моношрифта загружено
   ровно одно начертание (JetBrains Mono 400), остальное браузер стал бы
   синтезировать. Роли различает только цвет. */

/* Подпись стоит НАД листингом, а не под ним: человеку надо знать, куда
   он это вставляет, раньше, чем он начнёт читать. Имя файла набрано той
   же линией, что и сам код, пояснение — прописными 11 px, как все
   служебные строки сайта. Правило снято с .entry-content: код едет
   на страницы инструментов, где текстовой колонки нет вовсе. */
.code__cap{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s-2) var(--s-4);
  margin:0 0 var(--s-3);
}
.entry-content .code__cap{max-width:none}
.code__file{
  font-family:var(--f-mono);font-size:var(--fs-13);font-weight:400;line-height:1.45;color:var(--ink-mute);
  font-variant-numeric:tabular-nums slashed-zero;font-variant-ligatures:none;
}
.code__note{
  font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);
  text-transform:uppercase;line-height:1.4;color:var(--ink-meta);
}

/* Поверхность листинга повторена здесь целиком, а не унаследована от
   .entry-content pre: блок обязан работать и вне текстовой колонки.
   Значения те же, что в блоке 35, расходиться им нельзя.
   Длинная строка: прокрутка живёт внутри pre и наружу не выходит.
   min-width:0 на фигуре обязателен — без него элемент сетки не сжимается
   ниже собственного содержимого и на 375 px растягивает всю страницу.
   overscroll-behavior-x не даёт доскроллить листинг до жеста «назад». */
/* margin-inline:0 обязателен: у figure браузер держит собственные поля
   по 40 px, а правило .entry-content figure их снимает только внутри
   статьи. На страницах инструментов листинг лежит вне .entry-content,
   и без этой строки он оказывался уже соседних блоков на 80 px. */
figure.code{margin-block:1.6em;margin-inline:0;min-width:0}
figure.code>pre{
  margin:0;min-width:0;max-width:100%;
  padding:var(--s-5) var(--s-6);border-radius:var(--r-s);
  background:var(--surface-card-2);border:1px solid var(--rule-hair);
  font-family:var(--f-mono);font-size:var(--fs-13);line-height:1.55;
  font-variant-numeric:tabular-nums slashed-zero;font-variant-ligatures:none;
  color:var(--ink-code-nam);
  white-space:pre;tab-size:2;-moz-tab-size:2;
  overflow-x:auto;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;
  scrollbar-width:thin;scrollbar-color:var(--rule) var(--surface-card-2);
}
figure.code>pre>code{
  display:block;min-width:0;
  background:none;border:0;padding:0;border-radius:0;
  font-family:inherit;font-size:inherit;color:inherit;
}

/* Восемь ролей. Больше классов не заводить: если лексеме не нашлось
   роли из этих восьми, она остаётся без span и берёт --ink-code-nam. */
.tok-key{color:var(--ink-code-key)}
.tok-str{color:var(--ink-code-str)}
.tok-num{color:var(--ink-code-num)}
.tok-rem{color:var(--ink-code-rem)}
.tok-nam{color:var(--ink-code-nam)}
.tok-op{color:var(--ink-code-op)}
.tok-path{color:var(--ink-code-path)}
.tok-fn{color:var(--ink-code-fn)}

/* В панели инструмента листинг живёт в правой колонке шириной около
   516 px, и туда не помещается ни один настоящий ответ API: строка
   уезжала под горизонтальную прокрутку, а читать по ней документ
   в полторы тысячи знаков нельзя. Поэтому здесь строки переносятся.
   В статье блога перенос не нужен и вреден: там колонка широкая,
   а листинг — команда или конфиг, где перенос меняет смысл строки. */
.calcpanel__out figure.code>pre{white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:visible}

/* На бумаге прокрутки нет: без переноса правая половина длинной строки
   просто не напечаталась бы. Цвета остаются — на белом листе светлая
   палитра даёт ещё больше, чем измеренные 5,5:1. */
@media print{
  figure.code>pre{white-space:pre-wrap;overflow-x:visible}
}

/* ---------- 41. Сцена и поле каталога ----------
   Поле — снос каталога. Частица здесь треугольник, и треугольник умеет то,
   чего не умеет квадрат: показывать, куда он идёт. Остриё развёрнуто по
   вектору сноса, поэтому поле читается как течение, а не как рассыпанное
   конфетти. Снос общий, разброс ±69° вокруг одной диагонали: пока курсор ни
   в одном поиске, каталог просто идёт мимо. Курсор в поиске — в шапке или в
   карточке героя — и все треугольники за 0,5 с разворачиваются остриями на
   это поле, а снос падает до 29 % скорости. Возврат вдвое медленнее
   разворота: та же асимметрия, что у ховера в блоке 40.
   Цвет: три тона вместо тринадцати. --ink-meta несёт 61 % частиц, --action
   24 %, cyan рубрики seo 15 %. Крупные контурные треугольники, каждый
   34-й, держат масштаб и вращаются сами по себе, а не по сносу.
   Глубина: три слоя, основание от 4,1 до 9,7 px, у крупных контурных —
   от 15,6 до 25,0 px. Мельче 4 px треугольник при повороте перестаёт быть
   треугольником и становится точкой переменного веса, поэтому у размера
   есть пол и ниже него не рисуется ничего.
   Плотность: делитель площади растёт вместе с шириной полосы —
   H/(0,47 + 2126/W). Постоянный делитель тут не годится: на телефоне полоса
   вытянута вдвое против десктопной и почти вся лежит ниже сгиба.
   Плотность падает над колонкой текста: там альфа частицы не выше 0,06,
   и --ink-meta держит запас к норме 4,50:1 (замеры — в отчёте макета).

   Чем это отличается от макета hero-a, где кроме героя на странице не было
   ничего. Там канвас лежал внутри .stage, а .stage оборачивал и шапку.
   На полной странице так нельзя: position:sticky держится внутри своего
   родителя, и шапка, завёрнутая в коробку высотой в один экран, уехала бы
   вверх вместе с героем и больше не вернулась. Родитель липкой шапки
   остался прежним — body, а поле разложено на два предмета:
   — .stage__field, сам канвас. Absolute от body, top:0, высота ставится
     скриптом по низу полосы .stage: ровно шапка плюс герой и ни пикселем
     ниже. z-index:-1 кладёт его под весь текст, но над фоном body — ради
     этого body и получает собственный контекст наложения;
   — .stage, полоса героя в потоке. Когда скрипт работает, она не рисует
     ничего; когда его нет — держит запасной фон в ::before.
   Границу поля задаёт низ .stage, а не высота экрана и не круглое число
   пикселей: секции ниже начинаются там, где поле уже кончилось. Второй
   рубеж — сами секции: и .card, и .site-foot непрозрачны, просвечивать
   сквозь них нечему даже при ошибке в первом.

   Альфа здесь легализована третьим местом после стекла и теней:
   градиент сцены и сама частица. Причина та же, что у --topbar-bg:
   слой сознательно просвечивает, и хекс этого не умеет. */
body{
  position:relative;   /* канвасу нужен контейнер, и это вся страница */
  isolation:isolate;   /* без своего контекста наложения z-index:-1 ушёл бы ПОД фон body */
  --stage-glow:rgba(255,255,255,.80);
  --stage-glow-0:rgba(255,255,255,0);
  --stage-dot:rgba(90,98,114,.09);
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) body{
    --stage-glow:rgba(26,38,60,.72);
    --stage-glow-0:rgba(26,38,60,0);
    --stage-dot:rgba(168,176,190,.13);
  }
}
:root[data-theme="dark"] body{
  --stage-glow:rgba(26,38,60,.72);
  --stage-glow-0:rgba(26,38,60,0);
  --stage-dot:rgba(168,176,190,.13);
}
/* Полоса героя. flow-root — не украшение: без него верхний отступ
   заголовка схлопнулся бы наружу, полоса началась бы на 32 px ниже
   шапки, и между шапкой и полем легла бы пустая ступенька. */
.stage{position:relative;display:flow-root}
.stage__body>.intro{margin:90px 0 60px}
/* Запасной фон: решётка 32×32 и тот же градиент, что несёт канвас.
   Живёт в ::before, а не на самой .stage, по двум причинам: маска гасит
   низ решётки (иначе она обрубалась бы поперёк страницы ровно по низу
   героя) и слой ложится под текст, не мешая ему. Гаснет он только тогда,
   когда его место занял канвас, — правило ниже. */
.stage::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-image:
    radial-gradient(122% 82% at 76% 4%, var(--stage-glow) 0%, var(--stage-glow-0) 62%),
    radial-gradient(circle at 1.4px 1.4px, var(--stage-dot) 1.4px, var(--stage-glow-0) 1.5px);
  background-size:auto, 32px 32px;
  background-repeat:no-repeat, repeat;
  -webkit-mask-image:linear-gradient(180deg,#000 0 84%,transparent 100%);
          mask-image:linear-gradient(180deg,#000 0 84%,transparent 100%);
}
/* JS занял место: статичную решётку гасим, её рисует канвас */
.stage.is-live::before{display:none}
/* Высота ставится скриптом. Без него канвас нулевой высоты и не рисует
   ничего — фон в это время держит .stage::before. Градиент сцены сидит
   на канвасе, а не на полосе: канвас накрывает и шапку, поэтому свет
   не обрывается по нижней кромке панели. */
.stage__field{
  position:absolute;top:0;left:0;width:100%;height:0;
  z-index:-1;pointer-events:none;display:block;
  background-image:radial-gradient(122% 82% at 76% 4%, var(--stage-glow) 0%, var(--stage-glow-0) 62%);
  background-repeat:no-repeat;
  -webkit-mask-image:linear-gradient(180deg,#000 0 84%,transparent 100%);
          mask-image:linear-gradient(180deg,#000 0 84%,transparent 100%);
}
@media print{.stage__field,.stage::before{display:none!important}}

/* ---------- 42. Цвет каталога ----------
   Два каталога обязаны различаться с первого взгляда и остаться одной
   системой. Ни рубрика, ни действие для этого не годятся: рубрик на
   странице шесть и они меняются от секции к секции, а синий действия
   один на весь сайт и означает «сюда можно нажать». Поэтому заведена
   третья роль — вид каталога. Новых красок она не приносит: инструменты
   берут синюю ступень, ту же, что несёт действие, калькуляторы —
   янтарную, ту же, которой в блоге помечено «вечнозелёное».
   Роль стоит ровно на трёх предметах, и ни один из них не кликается:
   насечка над H1, тонировка карточки поиска и плашка «что где лежит» —
   та самая, где словами написана граница между двумя каталогами.
   Зелёный сюда не входит: он занят состоянием «работает» и остаётся
   зелёным на обеих страницах. Цвет рубрики тоже не трогается: он
   по-прежнему живёт на плитках иконок, точках чипов и насечке секции. */
[data-kind]{
  --kind:var(--action);
  --kind-wash:var(--action-wash);
  --kind-line:var(--action-line);
}
[data-kind="calc"]{
  --kind:var(--signal-evergreen);
  --kind-wash:var(--signal-evergreen-wash);
  --kind-line:var(--signal-evergreen-line);
}
/* насечка над заголовком: на странице рубрики её красит --cat,
   здесь рубрики нет — красит вид каталога */
.cat-head h1.tick{--tick-c:var(--kind)}
/* карточка поиска: градиент из блока 6, но подложка и кромка берутся
   у вида каталога, а не у действия */
.findbox{background:linear-gradient(165deg, var(--kind-wash), var(--surface-card) 68%);border-color:var(--kind-line)}
/* плашка «что где лежит» — единственная цветная полоса до сетки карточек */
/* Предупреждение. Единственный на сайте случай, когда плашка должна
   останавливать, а не сообщать: в перенесённой записи это «после
   установки удаляем инсталлер» — не сделаешь, и на сервере останется
   открытая дверь. Берёт сигнальный уголь, как и тег «стало тяжелее». */
.notice--warn{background:var(--signal-ember-wash);border-color:var(--signal-ember-ink)}
.notice--warn .notice__ico{color:var(--signal-ember-ink)}
.notice--kind{margin-top:var(--s-6);background:var(--kind-wash);border-color:var(--kind-line)}
.notice--kind .notice__ico{color:var(--kind)}

/* ---------- 43. Обёртка каталога и пустой результат ----------
   А. Обёртка. Липкая полоса .catnav держится ровно столько, сколько
   живёт её родитель. Если родителем оставить <main>, полоса категорий
   доедет до подвала и будет висеть над блоком «Из блога», где ни одной
   категории уже нет. Обёртка .cats кончается там же, где кончается
   последняя категория, и полоса уходит вместе с ней. flow-root вместо
   пустого правила: он же не даёт верхнему отступу .catnav схлопнуться
   наружу обёртки.

   Б. Пустой результат. Фильтр прячет и карточки, и целые категории,
   и чипы этих категорий в липкой полосе. Когда не совпало ничего, от
   каталога остаётся одна полоса над пустотой — это читается не как
   «не нашлось», а как «страница сломалась». Поэтому на месте сетки
   встаёт карточка: она возвращает запрос словами, объясняет, что поиск
   смотрит и в синонимы, и даёт три выхода — сбросить фильтр, взять
   готовый короткий запрос, уйти в соседний каталог. Пунктир на
   утопленном фоне — тот же приём, что у .pn--empty: пустое место на
   сайте всегда обведено пунктиром.
   Заголовок здесь абзац, а не h2: сообщение живёт полсекунды и не
   должно попадать в структуру заголовков страницы. Озвучивает его
   строка счётчика — она aria-live. */
.cats{display:flow-root}
/* Полоса категорий, из которой фильтр вынес все чипы. До 980 px в ней
   нет ничего, кроме ленты, — тогда исчезает и сама полоса, иначе над
   пустым каталогом висела бы пустая коробка, и это первое, что читается
   как поломка. От 980 px в полосе остаётся поле фильтра, в котором
   человек сейчас печатает: полоса живёт, поле занимает её целиком. */
/* Кто кому уступает ширину в липкой полосе. Лента из шести категорий
   с точками и счётчиками просит 1016 px, поле фильтра — 236 px, а между
   ними на 1280 px есть 1183: кто-то должен ужаться. Ужимается поле, и
   в блоке 25 у него для этого нет разрешения — flex:0 0 236px не гнётся,
   поэтому лишнее уезжало под маску, и «Работа» на самой частой десктопной
   ширине оставалась наполовину за краем.
   Теперь основание поля равно 168 px — ровно столько, чтобы на 1280 px
   сумма основания и ленты совпала с шириной полосы и никто не ужимался.
   Свободную ширину выше 1280 px забирает поле и растёт до тех же 236 px,
   а ниже — первой отдаёт лента, для чего у неё и есть прокрутка с маской.
   Пол в 160 px не даёт полю схлопнуться в иконку.
   Подпись поля при этом короткая, «Фильтр»: полное «Фильтр по каталогу»
   осталось в label, на 160 px оно всё равно обрубалось многоточием. */
.catfind{flex:1 1 168px;min-width:160px;max-width:236px}
.catnav:not(:has(.catnav__list li:not([hidden]))) .catnav__list{display:none}
.catnav:not(:has(.catnav__list li:not([hidden]))) .catfind{flex:1 1 auto;margin-left:0}
@media (max-width:979px){
  .catnav:not(:has(.catnav__list li:not([hidden]))){display:none}
}
.empty{
  margin-top:var(--s-6);
  display:flex;flex-direction:column;align-items:flex-start;
  background:var(--surface-card-2);border-color:var(--rule);border-style:dashed;box-shadow:none;
}
.empty__ico{
  width:44px;height:44px;border-radius:12px;display:grid;place-items:center;
  background:var(--kind-wash);color:var(--kind);margin-bottom:var(--s-4);
}
.empty__ico svg{width:23px;height:23px}
.empty__h{font-size:var(--fs-20);font-weight:640;letter-spacing:var(--track-title);line-height:1.25;color:var(--ink)}
/* сам запрос набран тем же кеглем, но тише: читать надо «ничего
   не нашлось», а не разглядывать собственную опечатку */
.empty__h b{font-weight:inherit;color:var(--ink-meta)}
.empty__p{
  margin-top:var(--s-3);max-width:58ch;
  font-size:var(--fs-15);font-weight:500;line-height:1.55;color:var(--ink-mute);text-wrap:pretty;
}
.empty__acts{margin-top:var(--s-5);display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-3)}
.empty__acts .chip{cursor:pointer;background:var(--surface-card)}
.empty__note{
  margin-top:var(--s-5);padding-top:var(--s-4);align-self:stretch;
  border-top:1px solid var(--rule-hair);
  font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-meta);
}

/* ---------- 44. Запись блога ----------
   Лист на столе: карточка записи берёт --surface-card бумажной
   области (L*97,6, b*+3,1) и лежит на канве --surface-bg (L*94,9).
   Разница в 2,7 пункта светлоты — это и есть «лист», из-за неё
   страница чтения не выглядит той же коробкой, что каталог.
   Заголовок, лид и служебная строка сидят в одном поле сверху,
   насечка идёт по всей ширине колонки заголовка и служит
   верхней кромкой листа — как на главной и в шапке рубрики. */
.article{padding:0;overflow:hidden;margin-top:var(--s-7)}
.article__head{padding:clamp(20px,2.6vw,32px) clamp(18px,2.4vw,32px) 0}
.article__head h1{
  font-size:var(--fs-40);font-weight:620;letter-spacing:var(--track-display);
  line-height:1.1;max-width:22ch;
}
.article__head h1.tick{--tick-w:100%}
.article__lead{
  margin-top:var(--s-5);color:var(--ink-mute);
  font-size:var(--fs-17);font-weight:400;line-height:1.62;max-width:56ch;text-wrap:pretty;
}
/* Отметка о перепроверке. Стоит до текста, а не в подвале записи:
   человек имеет право узнать возраст материала раньше, чем начнёт
   его читать. Обе даты видны, и рядом сказано, что именно
   проверялось, — это не значок «свежее», а расписка. */
.recheck{margin:var(--s-6) clamp(18px,2.4vw,32px) 0}
.recheck>div{min-width:0}
.recheck__dates{
  display:flex;flex-wrap:wrap;gap:var(--s-2) var(--s-6);margin-bottom:var(--s-3);
  font-size:var(--fs-11);font-weight:600;letter-spacing:var(--track-caps);
  text-transform:uppercase;line-height:1.4;color:var(--ink-meta);
}
.recheck__dates span{display:inline-flex;align-items:baseline;gap:var(--s-3)}
.recheck__dates b{
  font-family:var(--f-mono);font-size:var(--fs-13);font-weight:400;letter-spacing:var(--track-text);
  text-transform:none;color:var(--ink);
  font-variant-numeric:tabular-nums slashed-zero;font-variant-ligatures:none;
}
.recheck__dates .is-ok{color:var(--signal-verified)}
/* Инлайновый код в отметке набран той же линией, что и в тексте записи.
   Рамка объявлена через :where(): её вес нулевой, поэтому холодный
   border-color из блока 1 остаётся за бумажным правилом. */
.recheck code{
  font-family:var(--f-mono);font-size:.9em;border-radius:5px;padding:.06em .34em;
  font-variant-numeric:tabular-nums slashed-zero;font-variant-ligatures:none;
  box-decoration-break:clone;-webkit-box-decoration-break:clone;
}
.recheck :where(code){border:1px solid var(--rule-hair)}
.article__figure{margin:clamp(20px,2.4vw,28px) 0 0;position:relative}
/* Лёгкая заливка поверх обложки.
   Рисунок вписан в кадр целиком, и по бокам от него остаётся белое поле —
   на широком экране это две пустые полосы в треть ширины. Гасим их мягкой
   заливкой от краёв внутрь: у самого края тон подложки страницы, к центру
   сходит на нет, так что самого рисунка заливка не касается.

   Наложение, а не фон фигуры: белое поле нарисовано ВНУТРИ картинки,
   и фон под ней не виден. Отсюда же ::after, а не свойство самой картинки —
   у <img> псевдоэлементов не бывает.

   Кадр задан пропорцией, а не inset: подпись под обложкой лежит в той же
   фигуре, и заливка во всю высоту накрыла бы и её.

   Цвет — свой токен, а не подложка страницы напрямую. В тёмной теме он пуст,
   и заливки нет вовсе: тёмный градиент поверх белого поля даёт не мягкий
   край, а грязный серый ореол вокруг светящегося пятна. Там обложка
   работает иначе — белой панелью на тёмной странице, только приглушённой,
   чтобы не слепила: см. .article__cover ниже. */
.article__figure::after{
  content:"";position:absolute;top:0;left:0;right:0;aspect-ratio:2/1;
  pointer-events:none;border-radius:inherit;
  background:
    linear-gradient(90deg,
      var(--cover-wash) 0%,
      color-mix(in srgb, var(--cover-wash) 55%, transparent) 12%,
      transparent 34%, transparent 66%,
      color-mix(in srgb, var(--cover-wash) 55%, transparent) 88%,
      var(--cover-wash) 100%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--cover-wash) 40%, transparent) 0%,
      transparent 18%, transparent 82%,
      color-mix(in srgb, var(--cover-wash) 40%, transparent) 100%);
}
/* В тёмной теме обложка — светлая панель на тёмной странице, и заливкой
   её не смягчить: серый градиент поверх белого даёт грязный ореол.
   Вместо этого чуть гасим саму картинку. Девяносто два процента — та
   граница, где панель перестаёт слепить, а рисунок ещё не выглядит
   присыпанным пылью. */
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .article__cover{filter:brightness(.92)}
}
:root[data-theme="dark"] .article__cover{filter:brightness(.92)}

/* Пропорция одна на всех ширинах. Раньше на большом экране шапка была
   2,5:1 — это годилось прежним обложкам, они рисовались узором и тянулись
   на любой кадр. Настоящему рисунку в такой полосе тесно: он вписывается
   по высоте и стоит островом среди белого. 2:1 даёт ему три четверти
   ширины вместо трёх пятых. */
.article__cover{aspect-ratio:2/1;border-bottom:0;border-top:1px solid var(--card-bd)}
.article__caption{
  padding:var(--s-4) clamp(18px,2.4vw,32px) 0;
  font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-meta);max-width:80ch;
}
/* Блок кода. В блоге таких записей много, и код — то место, где
   технический текст обычно разваливается. Линия та же, что у
   .formula и .exam на посадочной инструмента: моношрифт, холодная
   поверхность, волосяная рамка, перечёркнутый ноль, лигатуры
   выключены — код копируют, и «!=» не должно склеиваться.
   Сама подсветка, подпись над листингом и прокрутка длинной строки
   переехали в блок 38: код едет на страницы инструментов и в новые
   статьи, и таскать за ним половину блока записи не за чем. */
.article__foot{
  margin-top:clamp(24px,3vw,32px);
  padding:clamp(20px,2.4vw,24px) clamp(18px,2.4vw,32px) clamp(20px,2.4vw,24px);
  border-top:1px solid var(--rule-hair);
  display:flex;flex-direction:column;gap:var(--s-6);
}
/* Метки и ссылки автора — обычные чипы: отдельной пилюли с теми же
   размерами заводить не за чем, а ховер им уже описан в блоке 40. */
.tagrow{
  display:flex;flex-wrap:wrap;gap:var(--s-3);align-items:center;
  font-size:var(--fs-13);font-weight:500;color:var(--ink-meta);
}
.author{
  display:flex;gap:var(--s-5);align-items:flex-start;
  background:var(--surface-card-2);border:1px solid var(--rule-hair);
  border-radius:var(--r-s);padding:var(--s-5);
}
.author>div{min-width:0}
.author__name{font-size:var(--fs-17);font-weight:620;line-height:1.3}
.author__name a{color:var(--ink)}
.author__name a:hover{color:var(--action)}
.author__bio{margin-top:var(--s-3);font-size:var(--fs-15);font-weight:500;line-height:1.55;color:var(--ink-mute);max-width:62ch}
.author__links{margin-top:var(--s-4);display:flex;flex-wrap:wrap;gap:var(--s-3)}
/* Аватар не носит ни цвет действия, ни цвет рубрики: он ничего
   не сообщает и никуда не ведёт, поэтому остаётся служебным. */
.avatar{
  flex:0 0 auto;width:48px;height:48px;border-radius:50%;display:grid;place-items:center;
  background:var(--surface-card);border:1px solid var(--rule);
  color:var(--ink-mute);font-size:var(--fs-17);font-weight:620;
}
.avatar--sm{width:40px;height:40px;font-size:var(--fs-15)}

/* ---------- 45. Комментарии ---------- */
.comments__note{
  display:flex;gap:var(--s-4);align-items:flex-start;margin-bottom:var(--s-5);
  font-size:var(--fs-13);font-weight:500;line-height:1.5;color:var(--ink-meta);
}
.comments__note svg{width:16px;height:16px;flex:0 0 auto;margin-top:var(--s-1)}
.clist{list-style:none;display:flex;flex-direction:column}
.comment{display:flex;gap:var(--s-5);padding:var(--s-5) 0;border-top:1px solid var(--rule-hair)}
.clist>.comment:first-child{border-top:0;padding-top:var(--s-1)}
.comment>div{min-width:0}
.comment--reply{margin-left:clamp(16px,4vw,48px)}
.comment__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s-2) var(--s-4)}
.comment__name{font-size:var(--fs-15);font-weight:620;line-height:1.4;color:var(--ink)}
.comment__date{font-size:var(--fs-12);font-weight:550;color:var(--ink-meta);font-variant-numeric:tabular-nums}
.comment__text{
  margin-top:var(--s-3);font-size:var(--fs-15);font-weight:500;line-height:1.6;
  color:var(--ink-mute);max-width:70ch;font-variant-numeric:proportional-nums;
}
/* Ссылка остаётся строчной, растёт только область нажатия — приём блока 30 */
.comment__reply{margin-top:var(--s-3);font-size:var(--fs-13);font-weight:600}
.comment__reply a,
/* .c-reply — это <button>, а правило было написано только на <a>:
   кнопка доставалась браузеру неоформленной, серой рамкой из 90-х.
   Видно там, где форма открыта, то есть у записей свежее 27.01.2016. */
.comment__reply .c-reply{display:inline-flex;align-items:center;padding-block:var(--s-4);margin-block:calc(var(--s-4) * -1);
  background:none;border:0;color:var(--action);cursor:pointer;font:inherit;font-weight:600}
.cform{
  margin-top:var(--s-6);padding-top:var(--s-6);border-top:1px solid var(--rule-hair);
  display:grid;gap:var(--s-5);max-width:760px;
}
.cform h3{font-size:var(--fs-17);font-weight:620;line-height:1.3}
.cform__row{display:grid;gap:var(--s-5)}
@media (min-width:600px){.cform__row{grid-template-columns:1fr 1fr}}
.cform label{display:block;margin-bottom:var(--s-3);font-size:var(--fs-13);font-weight:600;color:var(--ink-mute)}
.cform input,.cform textarea,.cform select{
  width:100%;min-height:44px;padding:var(--s-4);
  font:inherit;font-size:var(--fs-15);font-weight:500;color:var(--ink);
  background:var(--surface-card-2);border:1px solid var(--rule);border-radius:var(--r-xs);
}
.cform textarea{min-height:120px;resize:vertical;line-height:1.6}
/* Подсказку в поле браузер красит своим серым — на тёмной теме это
   3,55:1, ниже нормы. У полей поиска цвет задан явно, здесь не был. */
.cform input::placeholder,.cform textarea::placeholder{color:var(--ink-meta);opacity:1}
/* Форма комментария отделена чертой от ленты над ней. На странице
   контактов над формой ничего нет, и черта читалась обрезком. */
.cform--plain{border-top:0;padding-top:0}
/* Свой значок вместо системного: системный на тёмной теме рисуется
   чёрным по тёмному и почти не виден.
   Рисуем его так же, как стрелку внешней ссылки в блоке 40: маска плюс
   currentColor. Цвет прямо в SVG зашивать нельзя — он тогда одинаков
   в обеих темах, а значку положено идти за цветом текста поля. */
.cform select{
  -webkit-appearance:none;appearance:none;padding-right:var(--s-8);
}
.cform__sel{position:relative;display:block}
.cform__sel::after{
  content:"";position:absolute;right:var(--s-5);top:50%;translate:0 -50%;
  width:18px;height:18px;pointer-events:none;
  background:var(--ink-mute);
  -webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>');
          mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>');
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain;
}
.cform input:focus,.cform textarea:focus,.cform select:focus{
  border-color:var(--action);background-color:var(--surface-card);outline:none;
  box-shadow:0 0 0 3px var(--action-wash-2);
}
.cform .btn{justify-self:start}

/* ---------- 46. Утилиты отступа ---------- */
.mt-3{margin-top:var(--s-3)}
.mt-4{margin-top:var(--s-4)}
.mt-5{margin-top:var(--s-5)}
.mt-6{margin-top:var(--s-6)}
.mt-7{margin-top:var(--s-7)}

/* ---------- 47. Хвост ленты рубрики ----------
   Пять записей — это вся рубрика, и лента обязана сказать это словами,
   а не обрываться на полуслове. Карточка стоит последней ячейкой той же
   сетки: пагинации здесь нет и не будет, потому что показывать нечего.
   Тонировка та же, что у .findbox и .tools, — «отсюда дорога в каталог». */
.endcard{
  display:flex;flex-direction:column;justify-content:center;
  background:linear-gradient(160deg, var(--surface-sunken), var(--surface-card) 70%);
  border-color:var(--action-line);
}
.endcard h3{font-size:var(--fs-17);font-weight:620;letter-spacing:var(--track-text);line-height:1.3}
.endcard p{margin-top:var(--s-3);font-size:var(--fs-15);font-weight:500;line-height:1.55;color:var(--ink-mute)}

/* ---------- 50. Годы в ленте блога ----------
   Пятьдесят семь карточек подряд читаются как свалка, поэтому лента
   разрезана по годам публикации. Группы намеренно неровные — такими они
   и были: в 2010 году двенадцать записей, в 2012 — две, в 2016 — одна.
   Год набран моноширинным: это число, а не слово, и стоит на той же
   линейке, что счётчики в карточке фактов. Насечки у года нет — на
   странице их и так две (заголовок и подвал), а одиннадцать линеек
   подряд превратили бы разметку в орнамент.
   Волосяная линия дотягивается до правого края колонки и служит
   верхней кромкой группы, а не украшением заголовка. */
.yearsec{margin-top:clamp(24px,3.4vw,48px)}
.year{
  display:flex;align-items:baseline;gap:var(--s-4);margin-bottom:var(--s-5);
  font-family:var(--f-mono);font-size:var(--fs-24);font-weight:400;
  letter-spacing:var(--track-title);line-height:1.2;color:var(--ink);
}
.year,.year__n{font-variant-numeric:tabular-nums}
.year::after{content:"";flex:1 1 auto;height:1px;background:var(--rule-hair);align-self:center}
.year__n{
  font-family:var(--f-sans);font-size:var(--fs-13);font-weight:500;
  color:var(--ink-meta);white-space:nowrap;
}

/* ---------- 51. Страница об авторе ----------
   Три правила на весь файл. Первое — единый вертикальный зазор между
   блоками внутри карточки: у .entry-content--flat margin-top обнулён
   намеренно, и задавать его должен тот, кто ставит колонку в карточку.
   Второе — лента подборок по следам блога, одна колонка на любой
   ширине: строка .drow с 820 px раскладывается в три колонки и делит
   ширину карточки, а не колонки сетки. Третье — заголовок подборки
   третьего уровня: в блоке 12 описан только h2. Ничего выше
   не переопределяется. */
.about-gap{margin-top:var(--s-6)}
.trail{display:grid;gap:var(--s-5);margin-top:var(--s-6)}
.trail .digest__head h3{
  font-size:var(--fs-20);font-weight:640;letter-spacing:var(--track-title);line-height:1.25;
}

/* ---------- 52. Плитка рубрики и строка одиночной рубрики ----------
   А. Плитка. У рубрики блога нет ни иконки, ни описания: в досье их нет,
   а рисовать по догадке нельзя. Плитка собрана из того, что есть: точка
   кольца, имя, счётчик записей и годы. Своего у неё ровно одно правило —
   сдвиг точки вниз, чтобы она встала на середину первой строки имени
   (кегль 15 px, интерлиньяж 1,4 — это 21 px), а не на её верх.
   Б. Строка. У восьми рубрик ровно по одной записи, и счётчик «1» на
   плитке не сообщает ничего. Такая рубрика показана строкой: слева имя
   рубрики, дальше сама запись, справа её дата. Первая колонка 152 px —
   по самому длинному имени («Социальные сети») в кегле 11 px прописными;
   ниже 820 px строка складывается в три этажа, как и обычная .drow.
   Движения здесь нет: и .tcard, и .drow уже описаны в блоке 40. */
.tcard--cat .chip__dot{margin-top:var(--s-3)}
.drow--one{grid-template-columns:minmax(0,1fr)}
.drow--one .drow__meta{grid-column:1}
@media (min-width:820px){
  .drow--one{grid-template-columns:152px minmax(0,1fr) auto}
  .drow--one .drow__meta{grid-column:auto}
}

/* ---------- 53. След расчёта на посадочной инструмента ----------
   Разбор примера — это цепочка вычислений, а не инструкция. Компонент
   .steps под неё не подходит: он даёт каждому шагу карточку с крупным
   кружком, и три строки арифметики занимают пол-экрана. Проза тоже мимо:
   в .entry-content кегль 17,5 и отбивка 20 px — размер для чтения абзацев,
   а не для строк счёта.

   Отсюда модификатор: тот же порядковый номер, но набранный моно и мелко,
   без карточки, строки вплотную. Числа табличные — иначе при сравнении
   строк разряды не встают друг под друга, а весь смысл следа в том,
   чтобы его можно было пересчитать глазами сверху вниз. */
.steps--calc{
  display:grid;gap:var(--s-2);margin-top:var(--s-5);
  counter-reset:calc-step;
  /* Одна колонка обязательна явно: с 820 px базовый .steps ставит две,
     и след расчёта распадался на два столбца, где шаг 3 оказывался
     слева от шага 2. Порядок вычислений важнее плотности. */
  grid-template-columns:1fr;
}
.steps--calc>li{
  display:grid;grid-template-columns:22px minmax(0,1fr);gap:var(--s-3);align-items:baseline;
  background:none;border:0;padding:0;margin:0;counter-increment:calc-step;
}
/* Кружок из базового .steps здесь снимается целиком: он 30 px в колонке
   на 22 и растягивался на две строки через grid-row. Остаётся один
   моноширинный номер. */
.steps--calc>li::before{
  content:counter(calc-step);
  grid-row:auto;width:auto;height:auto;min-width:0;
  background:none;border:0;border-radius:0;display:block;place-items:initial;
  font-family:var(--f-mono);font-size:var(--fs-12);font-weight:400;
  color:var(--ink-meta);text-align:right;
  font-variant-numeric:tabular-nums;
}
.steps--calc>li>p{
  margin:0;font-size:var(--fs-15);line-height:1.5;color:var(--ink-mute);
  font-variant-numeric:tabular-nums;
}
/* последний шаг — тот, что приводит к ответу: он выделен не цветом,
   а плотностью, чтобы не спорить с зелёной плашкой результата ниже */
.steps--calc>li:last-child>p{color:var(--ink);font-weight:500}

/* ---------- 54. Собственные виджеты инструментов ----------
   До этого блока каждый такой виджет носил стиль в атрибуте style —
   семьдесят пять штук по каталогу, с сырыми пикселями вместо токенов.
   Здесь они собраны по ролям: полоса образцов, сетка превью, список.
   Правило то же, что и везде: цвета и отступы только токенами. */

/* Полоса образцов: шкала оттенков, палитра из картинки. Ступени тянутся
   поровну, поэтому полоса одинаково выглядит и в узкой правой колонке
   панели, и во всю ширину секции. */
.swatchbar{display:flex;border-radius:var(--r-s);overflow:hidden;border:1px solid var(--card-bd)}
.swatchbar>*{flex:1 1 0;min-width:0}
/* overflow нужен самому образцу, а не только полосе: подпись у палитры
   печатается цветом, подобранным под свой образец, и, выехав на соседний
   цвет, оказалась бы на фоне с непроверенным контрастом. Скрипт прячет
   не влезающие подписи при отрисовке, но окно можно растянуть и после
   неё — тогда обрезка остаётся единственной защитой. */
.swatchbar__c{height:104px;display:flex;align-items:flex-end;justify-content:center;padding-bottom:var(--s-4);overflow:hidden}
.swatchbar__c>span{font-family:var(--f-mono);font-size:var(--fs-12)}
/* Одиннадцать ступеней в узкой колонке становятся уже цифры на них.
   Ниже 30 px подпись всё равно не читается, поэтому её убираем совсем:
   полоса цвета сама по себе информативнее обрезанного числа. */
@media (max-width:1100px){
  .calcpanel__out .swatchbar__c{height:80px}
  /* Убрать с экрана — не значит убрать вообще. display:none выносит узел
     из дерева доступности, и подпись пропадала не только у глаза, но и
     у скринридера, причём молча: специфичность этого правила (0-2-1)
     перебивает .visually-hidden (0-1-0), так что запасной путь через
     класс тут не работал. Прячем тем же приёмом, что и .visually-hidden:
     узел остаётся в потоке чтения, но не занимает места. */
  .calcpanel__out .swatchbar__c>span{
    position:absolute;width:1px;height:1px;margin:-2px;padding:0;border:0;
    overflow:hidden;clip-path:inset(50%);white-space:nowrap;
  }
}

/* Список фактов без боковых полей. Нужен там, где он лежит не внутри
   карточки, а прямо в секции: собственные поля секции уже отбили край,
   и вторые сдвигают строки относительно заголовка над ними. */
.facts--flush{padding-inline:0}

/* Системный выбор цвета рядом с текстовым полем. Сторона 48 px равна
   высоте .calcpanel__inp — иначе ряд разъезжается по вертикали. */
.colorpick{
  flex:0 0 auto;width:48px;height:48px;padding:2px;cursor:pointer;
  border:1px solid var(--rule);border-radius:var(--r-xs);background:var(--surface-card);
}
/* Обычный .calcpanel__wrap держит справа место под единицу измерения.
   Там, где вместо единицы стоит отдельная кнопка палитры, этот запас
   только отодвигает текст от правого края поля. */
.calcpanel__wrap:has(.colorpick){gap:var(--s-3)}
.calcpanel__wrap:has(.colorpick) .calcpanel__inp{padding-right:var(--s-5)}

/* Окно предпросмотра: рамка, скругление, обрезка содержимого. Отступа
   сверху здесь намеренно нет. Он у каждого свой, а собственный margin
   компонента оказывался в файле позже утилит .mt-* и молча их перебивал:
   у обеих записей одна специфичность, и решает порядок строк. Отбивку
   ставит вызывающая сторона утилитой — так её видно прямо в разметке. */
.tprev{border:1px solid var(--rule);border-radius:var(--r-s);overflow:hidden}
/* Шахматка под образцом цвета. Без неё полупрозрачный цвет на однотонной
   подложке выглядит просто другим цветом, и альфа не читается вовсе. */
.tprev--checker{
  background-image:
    linear-gradient(45deg,var(--rule-hair) 25%,transparent 25%,transparent 75%,var(--rule-hair) 75%),
    linear-gradient(45deg,var(--rule-hair) 25%,transparent 25%,transparent 75%,var(--rule-hair) 75%);
  background-size:16px 16px;background-position:0 0,8px 8px;
}
/* Сцена: предмет стоит по центру, вокруг воздух. Тень и фильтр видны
   только тогда, когда предмету есть куда их отбросить. */
.tprev--stage{
  display:grid;place-items:center;padding:var(--s-8) var(--s-5);
  background:var(--surface-card);border-color:var(--rule-hair);
}
.tprev__box{
  width:150px;height:100px;border-radius:var(--r-s);display:grid;place-items:center;
  background:var(--surface-card-2);border:1px solid var(--rule-hair);
  font-size:var(--fs-13);font-weight:600;color:var(--ink-meta);
}
.tprev__fill{height:132px}
.tprev__img{width:100%;height:auto;display:block}

/* Список без маркеров, строки в колонку. Маркер здесь мешает: строки
   не перечисление для чтения, а набор предметов с кнопками внутри. */
.plainlist{list-style:none;display:grid;gap:var(--s-3)}
/* Образец подобранного кегля. Размер ставит скрипт — он и есть ответ
   инструмента; здесь только то, что от размера не зависит. */
.clsample{line-height:1.25;font-weight:600;color:var(--ink)}

/* Поле с готовым кодом на выдачу. Высоту тянем от содержимого, но не
   ниже 280 px: пустая рамка в две строки не читается как «здесь код».
   resize оставлен по вертикали — длинную выдачу удобно растянуть. */
.codebox{
  width:100%;height:auto;min-height:280px;padding:var(--s-5);
  line-height:1.6;font-size:var(--fs-14);resize:vertical;
}
/* Подпись над таким полем: она подпись, а не строчный текст. */
.calcpanel__help--lab{display:block;margin-bottom:var(--s-3)}

/* Окно предпросмотра с полями внутри: образец текста нельзя прижимать
   к рамке, иначе не видно, как он выглядит на своём фоне. */
.tprev--pad{padding:var(--s-6)}
/* Утопленная подложка под предпросмотром картинки: пока файл не выбран,
   рамка пустая, и без неё в ней просвечивает панель — рамка читается
   как обводка вокруг ничего. С подложкой это место под картинку. */
.tprev--sunk{background:var(--surface-card-2)}
/* Три кегля образца в проверке контраста. Цвет здесь не задаём: он и
   есть проверяемая величина, его ставит скрипт из полей формы. */
.ccprev__lg{font-size:var(--fs-24);font-weight:700;line-height:1.25}
.ccprev__md{margin-top:var(--s-4);font-size:var(--fs-16);line-height:1.6}
.ccprev__sm{margin-top:var(--s-4);font-size:var(--fs-13);line-height:1.5}

/* ---- поля, которые браузер рисует сам ----
   input[type=file] и type=range приходят с оформлением операционной
   системы, поэтому у них нет общего вида с .calcpanel__inp. Здесь их
   приводят к тому же росту и той же рамке, что у обычного поля: три
   инструмента ставили это одинаковым куском в атрибуте style. */
.filefield{
  width:100%;min-height:48px;padding:var(--s-4) var(--s-5);
  border:1px solid var(--rule);border-radius:var(--r-xs);
  background:var(--surface-card);color:var(--ink);font:inherit;font-size:var(--fs-14);
}
.rangefield{width:100%;accent-color:var(--action)}
/* Зона перетаскивания. Пунктир здесь несёт смысл: сплошная рамка читается
   как поле ввода, пунктирная — как место, куда кладут. */
.dropzone{
  display:grid;place-items:center;min-height:96px;padding:var(--s-5);
  border:2px dashed var(--rule);border-radius:var(--r-s);
  background:var(--surface-card-2);color:var(--ink-meta);
  font-size:var(--fs-14);text-align:center;
}
/* Подпись, которой нужна своя строка: по умолчанию .calcpanel__help
   строчный, и рядом с флажком или сегментом он прилипает к ним сбоку. */
.calcpanel__help--blk{display:block}
/* Ряд с флажком: квадрат не должен растягиваться по высоте подписи. */
.checkrow{display:flex;align-items:center;gap:var(--s-3)}
.checkrow input{width:18px;height:18px;flex:0 0 auto}
/* Высокое окно предпросмотра — под градиент: на низком не видно, как
   ложатся остановки цвета. */
.tprev--tall{height:220px;border-color:var(--rule-hair)}
/* Загруженная фотография в предпросмотре: вписывается целиком, не
   обрезается и не растягивается — иначе палитра считается по одному
   куску картинки, а показывается другой. */
.tprev__photo{display:block;width:100%;height:auto;max-height:320px;object-fit:contain}
/* Короткий вариант поля с кодом: там, где выдача заведомо в несколько
   строк, 280 px пустой рамки выглядят как ошибка загрузки. */
.codebox--short{min-height:150px}

/* vasilenko.info — сетка готовых иконок в панели генератора favicon.

   Кадры переехали из секции ниже в правую колонку панели: человек двигает
   поле и скругление и должен видеть, что от этого делается с рисунком.
   Колонка узкая, поэтому сетка набирает столбцы сама, по ширине плитки:
   при любой ширине контейнера строка заполняется целиком и ничего
   не вылезает за край — минимум столбца меньше самой узкой колонки. */
.icogrid{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(92px,1fr));
  gap:var(--s-4);
}
/* Пустая сетка не должна отбивать пустой отступ над строкой «ничего нет». */
.icogrid:empty{display:none}

/* Плитка сама и есть ссылка на файл: кнопка «Скачать» в столбце шириной
   около сотни пикселей не помещается, а плитка целиком даёт цель крупнее
   пальца. Цвет наследуется вниз, поэтому на наведении подписи уходят
   в акцент вместе с рамкой — видно, что нажимается вся плитка. */
/* Подложка — --surface-card, а не --surface-card-2: вторым покрашена
   сама панель .calcpanel__out, внутри которой плитка и лежит, и на
   ней плитка держалась на одной волосяной рамке. Шахматку внутри это
   не путает: её узнают по узору, а не по цвету основы. */
.icogrid__t{
  display:flex;flex-direction:column;align-items:center;gap:var(--s-3);
  padding:var(--s-4);
  border:1px solid var(--rule-hair);border-radius:var(--r-s);
  background:var(--surface-card);
  color:var(--ink);text-decoration:none;
}
.icogrid__t:hover{border-color:var(--action-line);color:var(--action)}
/* Файл .ico — главный из восьми: его кладут в корень и без строчки в head.
   Поэтому он единственный помечен акцентной подложкой. */
.icogrid__t--key{border-color:var(--action-line);background:var(--action-wash-2)}

/* Шахматка под кадром: без неё прозрачный фон иконки неотличим от подложки
   плитки, и человек не понимает, что именно получил. Высота фиксирована,
   чтобы плитки стояли ровной строкой при разных размерах кадров. */
.icogrid__pit{
  display:grid;place-items:center;
  width:100%;height:72px;border-radius:var(--r-xs);
  background-color:var(--surface-card);
  background-image:
    linear-gradient(45deg,var(--rule-hair) 25%,transparent 25%,transparent 75%,var(--rule-hair) 75%),
    linear-gradient(45deg,var(--rule-hair) 25%,transparent 25%,transparent 75%,var(--rule-hair) 75%);
  background-size:12px 12px;background-position:0 0,6px 6px;
}
/* У .ico кадра нет — вместо картинки стоит подпись, шахматка ей мешает. */
.icogrid__pit--txt{
  background-image:none;
  font-family:var(--f-mono);font-size:var(--fs-16);color:var(--ink-meta);
}
/* Сторону кадра ставит скрипт: это натуральный размер иконки, то есть
   данные, а не оформление. Запасное значение — на случай, если разметку
   собрали без него. */
.icogrid__i{display:block;width:var(--icon-px,32px);height:var(--icon-px,32px);image-rendering:auto}

.icogrid__s{font-family:var(--f-mono);font-size:var(--fs-12);color:inherit}
/* Имя файла длиннее столбца (apple-touch-icon.png), поэтому рвём его
   в любом месте: иначе оно вылезает за край плитки. */
.icogrid__f{
  font-size:var(--fs-11);line-height:1.4;color:var(--ink-meta);
  text-align:center;overflow-wrap:anywhere;
}

/* Палитра из изображения — полоса цветов в правой колонке панели.
   Размеры и раскладку держит .swatchbar из блока 54; здесь только то,
   чем эта полоса отличается от шкалы оттенков. */

/* У шкалы ступени равны, а здесь ширина образца — его доля в картинке.
   Доля идёт через flex-grow, а .swatchbar>*{min-width:0} позволяет ей
   сойтись в ноль: цвет, занявший полпроцента, пропал бы из полосы
   вовсе. Предел ставим волосяной, в один шаг сетки. Больше нельзя:
   предел в 16 px отбирал место у крупных долей через flex-grow и на
   узком экране равнял все двенадцать образцов между собой — полоса
   переставала показывать пропорцию, которую страница обещает словами.
   Двенадцать волосков — это 24 px, они помещаются даже на 320 px. */
#pf-strip>*{min-width:var(--s-1)}

/* image-compressor: список результатов переехал в правую колонку панели.
   Раскладку ему задавал инлайновый стиль в разметке — строки одна под
   другой с зазором. Класса под такую сетку в проекте нет, поэтому
   правило здесь: без него карточки склеятся в сплошную стопку. */
#ic-list{display:grid;gap:var(--s-5)}

/* vasilenko.info — список задач.
   Список переехал из отдельной секции в правую колонку панели, и от смены
   окружения пострадала ровно одна вещь: отметка задачи.

   Отметка — это .calcpanel__seg label, залитая var(--surface-card-2).
   Тем же цветом залита и сама панель .calcpanel__out, куда список приехал:
   кружок совпал с фоном знак в знак и держался на одной волосяной рамке,
   то есть перестал читаться как кнопка. В форме слева этой беды нет —
   там под ним карточка. Возвращаем отметке карточную подложку. */
.calcpanel__out .calcpanel__seg label{background:var(--surface-card)}

/* ---- страница 404 ----
   Шапка без карточки справа: колонка одна, всё по центру. Правило стоит
   в этом блоке, а не рядом с .cat-head, намеренно — оно обязано идти
   ПОСЛЕ медиазапроса, который делает шапку двухколоночной с 900 px.
   Специфичность взята двойная, чтобы порядок блоков в файле можно было
   поменять, не сломав страницу. */
.cat-head.cat-head--solo{grid-template-columns:1fr;justify-items:center;text-align:center}
/* Насечка у шапок каталога тянется во всю ширину — она там линейка
   над длинным заголовком. По центру такая читается как разделитель,
   а не как метка, поэтому здесь возвращаем короткую, как у разделов. */
.cat-head.cat-head--solo h1.tick{--tick-w:72px}
.cat-head.cat-head--solo .tick::before{margin-inline:auto}
.cat-head.cat-head--solo .cat-head__lead{margin-inline:auto;max-width:54ch}
/* Рисунок — украшение, поэтому берёт тот же приглушённый цвет, что и
   насечка над заголовком. Акцентный тут был бы обещанием: синим на сайте
   покрашено то, что нажимается. */
.art404{
  display:block;width:min(100%,340px);height:auto;
  margin:0 auto var(--s-6);color:var(--ink-meta);
}
@media (max-width:640px){ .art404{width:min(100%,240px)} }

/* ---- место ошибки в листинге ----
   Форматтер и раньше называл строку и колонку словами, но показать её
   было негде: блок кода при ошибке очищался. Теперь в него печатается
   кусок исходника вокруг ошибки, и место видно глазом, а не по номеру.
   Роли те же, что у подсветки: номер строки — приглушённый, сама строка
   помечена подложкой сигнала, каретка под ней стоит на нужной колонке. */
.code__ln{
  display:inline-block;width:3.5ch;margin-right:var(--s-4);
  text-align:right;color:var(--ink-meta);
  font-variant-numeric:tabular-nums;user-select:none;
}
/* Строка с ошибкой. Подложка, а не цвет текста: красный текст на месте
   кода спорил бы с подсветкой, а подложка ни одну роль не перекрывает. */
.code__bad{display:block;background:var(--signal-ember-wash);border-radius:var(--r-xs)}
.code__bad .code__ln{color:var(--signal-ember-ink)}
/* Каретка. Ставится пробелами, поэтому у неё тот же моношрифт и та же
   ширина знака, что у строки над ней — иначе она укажет мимо. */
/* Отступ под номером строки задаём тем же выражением, что и ширину
   самого номера. Пробелами это не набирается: 3,5ch плюс 12 px не
   равны целому числу знаков моноширинного шрифта, и каретка уезжала
   на пол-символа. */
.code__caret{display:block;color:var(--signal-ember-ink);white-space:pre;padding-left:calc(3.5ch + var(--s-4))}

/* ---- проза внутри комментария ----
   Комментарии перенесены из вордпресса: в них абзацы, списки, куски кода
   и цитаты. Раньше .comment__text красил только сам блок, а его дети —
   ничего, и обсуждение выходило сплошной кашей: три абзаца слипались
   в один, а вставленный кем-то код шёл обычным текстом вперемешку.
   Кегль здесь на ступень меньше статейного: это разговор вокруг записи,
   а не сама запись, и он не должен спорить с ней по весу. */
.comment__text > * + *{margin-top:.85em}
.comment__text p{line-height:1.6;text-wrap:pretty}
.comment__text a{color:var(--action);text-decoration:underline;text-decoration-color:var(--action-line);text-underline-offset:2px}
.comment__text a:hover{text-decoration-color:currentColor}
.comment__text strong{font-weight:620;color:var(--ink)}
/* Списки — тем же приёмом, что в статье: точка вместо маркера. */
.comment__text ul,.comment__text ol{display:flex;flex-direction:column;gap:.4em;padding-left:1.4em}
.comment__text ul{list-style:none;padding-left:0}
.comment__text ul li{position:relative;padding-left:1.4em}
.comment__text ul li::before{
  content:"";position:absolute;left:.3em;top:.7em;width:5px;height:5px;
  border-radius:50%;background:var(--action);opacity:.8;
}
.comment__text ol li::marker{color:var(--action);font-weight:600}
/* Цитата из соседнего комментария: её узнают по левой линии, а не по
   кавычкам — кавычки в разговоре и так встречаются. */
.comment__text blockquote{
  margin-left:0;padding-left:var(--s-5);border-left:2px solid var(--rule);
  color:var(--ink-meta);
}
/* Код в комментарии. Строчный — приметой, блочный — со своей прокруткой:
   переносить чужой код нельзя, перенос меняет смысл строки. */
.comment__text code{
  background:var(--surface-card-2);border:1px solid var(--rule-hair);border-radius:var(--r-xs);
  padding:.1em .35em;font-family:var(--f-mono);font-size:.92em;color:var(--ink);
}
.comment__text pre{
  margin:0;padding:var(--s-5);border-radius:var(--r-s);
  background:var(--surface-card-2);border:1px solid var(--rule-hair);
  font-family:var(--f-mono);font-size:var(--fs-13);line-height:1.55;color:var(--ink-code-nam);
  white-space:pre;overflow-x:auto;overscroll-behavior-x:contain;
  scrollbar-width:thin;scrollbar-color:var(--rule) var(--surface-card-2);
}
.comment__text pre code{background:none;border:0;padding:0;font-size:inherit}

/* ---- картинки в статье ----
   Оригиналы из блога разного размера: от 258 px до полной ширины колонки.
   Мелкую нельзя растягивать — она рассыплется, — поэтому она остаётся
   своего размера, но встаёт по центру: прижатая влево, она читается как
   сбой вёрстки, а по центру — как иллюстрация. */
.entry-content figure{display:flex;flex-direction:column;align-items:center;text-align:center}
.entry-content figure img{margin-inline:auto}
.entry-content figcaption{text-align:center;max-width:70ch}
/* У листинга подпись и содержимое остаются слева: код по центру нечитаем. */
.entry-content figure.code{display:block;text-align:left}

/* ---- ловушка для роботов ----
   Поле должно быть недоступно человеку и доступно роботу: тот читает
   разметку, а не экран. Поэтому не display:none — часть роботов такие
   поля пропускает, — а вынос за пределы видимости. aria-hidden и
   tabindex=-1 стоят в разметке: до поля нельзя добраться ни голосом,
   ни клавишей. */
.cform__trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* Строка «отвечаете такому-то» над кнопкой отправки. */
.cform__reply{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-4);
  font-size:var(--fs-13);font-weight:500;color:var(--ink-mute)}
.cform__reply b{color:var(--ink);font-weight:620}
.btn--sm{min-height:36px;padding:var(--s-2) var(--s-4);font-size:var(--fs-13)}
/* Плашка ответа внутри формы прижата к левому краю, как и кнопка. */
.cform .notice{margin-bottom:var(--s-2)}

/* ---------- 40. Движение ----------
   Единственное место в файле, где живут transition и animation.
   Компоненты выше описывают только конечные состояния.
   Блок целиком лежит внутри no-preference: движения нет, пока браузер
   не сказал, что оно допустимо — это надёжнее, чем гасить его через
   transition-duration:.01ms. Побайтово одинаков во всех шести файлах.
   Правило распределения: появление ease-out, исчезновение ease-in,
   трансформация quint. Асимметрия — главный измеренный приём:
   подсветка появляется мгновенно и медленно гаснет. */
@media (prefers-reduced-motion: no-preference){

  /* Переход между страницами объявляется здесь же, внутри no-preference,
     а не гасится задним числом отдельным reduce-блоком: это была
     единственная строка блока, жившая по старому правилу. */
  @view-transition{ navigation:auto }

  html{scroll-behavior:smooth}
  /* линия под шапкой: появляется сразу, гаснет медленно */
  .topbar{transition:border-color var(--dur-2) var(--ease-in),box-shadow var(--dur-3) var(--ease-in)}
  .topbar.is-stuck{transition-duration:var(--dur-0)}

  /* Рельс разделов: пометка текущего появляется сразу, гаснет медленно —
     при быстрой прокрутке иначе получается мигание по всему списку. */
  .railnav__link{transition:color var(--dur-2) var(--ease-in),border-color var(--dur-2) var(--ease-in)}
  .railnav__link:hover,.railnav__link[aria-current="true"]{transition-duration:var(--dur-0)}

  /* Полоса перехода выезжает снизу и уходит туда же. visibility
     переключается нулевой длительностью, но с задержкой на весь уход —
     иначе полоса пропала бы мгновенно, не доехав до края. */
  .jumpbar{transition:transform var(--dur-3) var(--ease-out-quint),visibility 0s}
  .jumpbar[data-off]{transition:transform var(--dur-2) var(--ease-in),visibility 0s var(--dur-2)}
  /* Шапка уплотняется при прокрутке. Двигаются только поля панели
     и размер знака: заставлять переезжать сам ряд — значит дёргать
     меню и поиск под курсором. Уплотнение отдаёт странице десять
     пикселей высоты и одновременно говорит «вы уехали вниз» —
     то же, что раньше говорила одна линия под шапкой.
     Сжимается быстрее, чем разжимается: возврат к полной высоте
     не должен выглядеть скачком, когда человек долистал до верха. */
  .topbar__inner{transition:padding-top var(--dur-2) var(--ease-in),padding-bottom var(--dur-2) var(--ease-in)}
  .brand__mark{transition:transform var(--dur-2) var(--ease-in)}
  .topbar.is-stuck .topbar__inner{transition-duration:var(--dur-1)}
  .topbar.is-stuck .brand__mark{transition-duration:var(--dur-1)}
  ::view-transition-group(root){animation-duration:var(--dur-3);animation-timing-function:var(--ease-out-quart)}

  /* А. Асимметричный ховер: вход 0 мс, выход 140/200 мс */
  .chip,.tcard,.post,.hub,.tool,.pn,.drow,.mainmenu a,.iconbtn,.btn,.endrow a,
  .lang__btn,.lang__menu a,.more>summary,.subs__links a,.faq__i>summary,.toc a,
  .totop,.pager a,.foot li a,.crumbs a,.section__more,.calcpanel__seg label,
  .promo--card,
  .findbox__hint a,.exam tbody tr,.topsearch__btn,.findbox__btn,
  .icogrid__t{
    transition:background-color var(--dur-2) var(--ease-in),
               border-color     var(--dur-2) var(--ease-in),
               color            var(--dur-2) var(--ease-in),
               box-shadow       var(--dur-3) var(--ease-in),
               transform        var(--dur-3) var(--ease-in);
  }
  .chip:hover,.tcard:hover,.post:hover,.hub:hover,.tool:hover,.pn:hover,.drow:hover,
  .mainmenu a:hover,.iconbtn:hover,.btn:hover,.btn:active,.endrow a:hover,.lang__btn:hover,
  .lang__menu a:hover,.more>summary:hover,.subs__links a:hover,.faq__i>summary:hover,
  .toc a:hover,.totop:hover,.pager a:hover,.foot li a:hover,.crumbs a:hover,
  .section__more:hover,.calcpanel__seg label:hover,.findbox__hint a:hover,
  .exam tbody tr:hover,
  .topsearch__btn:hover,.findbox__btn:hover,.icogrid__t:hover{transition-duration:var(--dur-0)}

  /* та же асимметрия для текста внутри карточки */
  .tcard__name a,.tcard__open,.post__title a,.post__go,.hub h3 a,.hub__go,
  .tool h3 a,.tool__go,.pn__title,.drow__title,.brand__name,.chip__n{
    transition:color var(--dur-2) var(--ease-in),background-color var(--dur-2) var(--ease-in);
  }
  .tcard:hover .tcard__name a,.tcard:hover .tcard__open,.post:hover .post__title a,
  .post:hover .post__go,.hub:hover h3 a,.hub:hover .hub__go,.tool:hover h3 a,
  .tool:hover .tool__go,.pn:hover .pn__title,.drow:hover .drow__title,
  .brand:hover .brand__name,.chip:hover .chip__n{transition-duration:var(--dur-0)}

  /* В. Стрелка «Открыть» и повороты шевронов */
  .tcard__open svg,.tool__go svg,.hub__go svg,.post__go svg,
  .more>summary svg,.lang__chev,.faq__i>summary::after{
    transition:transform var(--dur-3) var(--ease-out-quart);
  }
  .cover>svg{transition:transform var(--dur-4) var(--ease-out-quart)}

  /* З. Подсветка строк, зависящих от поля под курсором */
  [data-dep]{transition:background-color var(--dur-2) var(--ease-out-quart)}

  /* И. Шкала безубыточности следует за формулой.
     Исключение к запрету анимации width/left: оба элемента
     абсолютно спозиционированы внутри дорожки фиксированной высоты,
     layout не пересчитывается. */
  .bescale__fill{transition:width var(--dur-3) var(--ease-out-quart)}
  .bescale__mark{transition:left var(--dur-3) var(--ease-out-quart)}

  /* Г. Вспышка изменившегося значения: появление мгновенное,
     угасание за 420 мс — та же асимметрия, растянутая, потому что
     здесь нужно успеть прочитать. Движение не украшает расчёт,
     оно его объясняет. */
  @keyframes vi-flash{from{background-color:var(--flash)}to{background-color:transparent}}
  .is-changed{animation:vi-flash var(--dur-flash) var(--ease-out-quart)}

  /* Е. Подтверждение копирования — единственный overshoot на сайте
     и единственное применение --ease-back. */
  @keyframes vi-ok{0%{opacity:0;transform:scale(.8)}50%{transform:scale(1.1)}100%{opacity:1;transform:scale(1)}}
  .btn.is-ok .ic{animation:vi-ok var(--dur-4) var(--ease-back)}

  /* Ж. Отказ при отправке. Два прохода = 640 мс, только по submit. */
  @keyframes vi-shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-3px)}75%{transform:translateX(3px)}}
  .calcpanel__inp.is-rejected{animation:vi-shake var(--dur-4) var(--ease-in-out-quint) 2}
}

/* Заголовок секции витрины — ссылка на страницу раздела. Цвет и вес
   берёт у заголовка, а не у ссылки: шесть подчёркнутых синих строк
   превратили бы витрину в оглавление. Подчёркивание приходит по наведению
   и по фокусу — этого хватает, чтобы понять, что сюда можно нажать. */
.catsec__head h2 a{color:inherit;text-decoration:none}
.catsec__head h2 a:hover,.catsec__head h2 a:focus-visible{text-decoration:underline;text-underline-offset:3px}

/* ── свои рекламные блоки ─────────────────────────────────────────────
   Блок не должен выглядеть чужой врезкой: он берёт ту же карточку,
   те же скругления и те же цвета, что всё остальное на странице.
   В исходном модуле оформление лежало инлайном и меняло имена классов
   каждый день — приём против фильтров блокировщиков. Здесь блоки свои,
   и куда важнее, чтобы они не выпадали из оформления ни в одной теме. */
/* Ширину блок берёт у соседей, а не у себя. Между полноширинными
   секциями — «предыдущая-следующая», «ещё из рубрики», лентой блога,
   каталогом — он такой же полноширинный: узкая врезка посреди широких
   карточек выглядит промахом вёрстки, а не задумкой.
   А в теле статьи он равняется на рисунки и код: те стоят колонкой
   в 940 пикселей, и врезка встаёт с ними в один ряд. */
.promo{margin:var(--s-8) auto;width:100%}
.entry-content>.promo,
.promo[data-slot="post_top"]{width:min(100%,940px)}
/* Внутри текста воздуха нужно меньше: там врезка стоит между абзацами,
   а не между крупными блоками страницы. */
.entry-content>.promo{margin:var(--s-7) auto}
/* Внутри текста записи блок занимает широкую колонку, как рисунки
   и код: в узкой колонке текста карточка со значком не помещается. */
.entry-content>.promo{grid-column:wide;justify-self:center;width:min(100%,940px)}

.promo--card{
  display:flex;gap:var(--s-4);align-items:center;
  text-decoration:none;color:inherit;
}
.promo--card:hover{border-color:var(--action-line);text-decoration:none;box-shadow:var(--shadow-h)}
.promo__img{flex:none;width:56px;height:56px;border-radius:var(--r-s);object-fit:cover}
/* min-width:0 обязателен: без него длинное слово в описании растягивает
   флекс-элемент и выталкивает кнопку за край карточки. */
.promo__b{flex:1;min-width:0;display:block}
.promo__t{display:block;font-size:var(--fs-16);font-weight:640;line-height:1.3;color:var(--ink)}
.promo__d{display:block;margin-top:2px;font-size:var(--fs-14);line-height:1.5;color:var(--ink-meta)}
.promo__cta{
  flex:none;display:inline-flex;align-items:center;gap:6px;
  font-size:var(--fs-14);font-weight:600;color:var(--action);white-space:nowrap;
}
.promo__cta .ic{width:16px;height:16px}
/* На узком экране карточка складывается в столбик, иначе три части
   делят между собой триста пятьдесят точек и не остаётся ни одной. */
@media (max-width:559px){
  .promo--card{flex-wrap:wrap;gap:var(--s-3)}
  .promo__b{flex-basis:100%;order:2}
  .promo__cta{order:3}
}
/* Подпись блока. Она обязана быть видна, но не спорить с содержимым:
   в тихом режиме это серое слово в углу, в заметном — плашка цветом
   ссылки. Без подписи блок неотличим от карточки сайта — на первой же
   пробе кнопка посреди статьи прочиталась как часть текста. */
.promo{position:relative}
.promo__tag{
  position:absolute;top:-9px;left:var(--s-5);
  padding:2px 8px;border-radius:999px;
  background:var(--surface-bg-2);border:1px solid var(--rule-hair);
  font-size:var(--fs-11);font-weight:600;letter-spacing:.04em;
  text-transform:uppercase;color:var(--ink-meta);
}
/* Заметный режим: рамка и подложка цветом ссылки. Включается одним
   переключателем в админке — блок начинает бросаться в глаза, оставаясь
   при этом частью оформления, а не чужой врезкой. */
.promo--loud{border-color:var(--action-line);background:var(--action-wash)}
.promo--loud .promo__tag{background:var(--action);border-color:var(--action);color:#fff}

.promo--raw{display:flex;justify-content:center;padding-top:var(--s-4)}
.promo--raw img{max-width:100%;height:auto}

/* ── поле с кнопкой в одну строку ─────────────────────────────────────
   Инструменты, которые теперь ходят на сервер, начинаются с адреса
   и кнопки рядом. На узком экране кнопка уходит под поле: рядом
   с полем в 320 точек ей остаётся сотня, а это уже не кнопка. */
.fieldgo{display:flex;gap:var(--s-3);align-items:stretch}
.fieldgo .calcpanel__inp{flex:1 1 auto;min-width:0}
.fieldgo .btn{flex:0 0 auto;white-space:nowrap}
@media (max-width:559px){.fieldgo{flex-wrap:wrap}.fieldgo .btn{width:100%}}
/* Пока идёт запрос — кнопка занята, поле не трогаем. */
.fieldgo .btn[aria-busy="true"]{opacity:.65;pointer-events:none}


/* ── инструмент во всю ширину ─────────────────────────────────────────
   Конструктору сетки и подобным тесно в колонке 1320 px: превью
   и код рядом не помещаются, и приходится резать одно из двух.
   Признак ставит сама страница инструмента, атрибутом на теле;
   остальным сорока трём ширина остаётся прежней. */
body[data-tool-wide] .wrap{max-width:min(100% - 2 * var(--s-5), 1720px)}

/* ---------- 55. Рельс разделов ──────────────────────────────────────
   Инструмент занимает несколько экранов: наверху панель, под ней ещё
   пять-восемь разделов, о существовании которых с первого экрана не
   догадаться. Ряд ссылок под панелью помогает, но уезжает вместе с ней.
   Слева от колонки на широком мониторе пустует поле шириной с саму
   колонку — туда и идёт список разделов. Он ничего не добавляет к
   странице, а повторяет её: те же заголовки, тот же порядок. Польза в
   двух вещах — виден весь состав страницы сразу и помечен раздел, в
   котором вы находитесь.
   Список строит скрипт из заголовков секций: своей разметки у рельса
   нет и в исходниках инструментов он не упоминается.
   Порог в 1760 точек не круглое число, а расчёт: колонка 1320 плюс по
   220 на рельс с отбивкой с каждой стороны. Ниже порога поле уже самого
   рельса, и он лёг бы на текст — поэтому там его просто нет. */
.railnav{display:none}
@media (min-width:1760px){
  .railnav{
    display:block;position:fixed;z-index:20;
    top:50%;transform:translateY(-50%);
    left:calc(50% - var(--wrap) / 2 - 206px);
    width:186px;max-height:72vh;overflow-y:auto;overscroll-behavior:contain
  }
}
/* Инструмент во всю ширину забирает поле себе: там колонка растёт до
   1720, и рельсу нужен монитор ещё шире. */
body[data-tool-wide] .railnav{display:none}
@media (min-width:2160px){
  body[data-tool-wide] .railnav{display:block;left:calc(50% - 860px - 206px)}
}
.railnav__title{
  font-size:var(--fs-12);font-weight:500;letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-meta);padding-inline:12px;margin-bottom:8px
}
.railnav__list{list-style:none;margin:0;padding:0;display:grid;gap:1px}
.railnav__link{
  display:block;padding:7px 12px;border-inline-start:2px solid var(--rule-hair);
  color:var(--ink-mute);text-decoration:none;
  font-size:var(--fs-13);line-height:1.35;text-wrap:pretty
}
.railnav__link:hover,.railnav__link:focus-visible{color:var(--ink);border-inline-start-color:var(--rule)}
/* Раздел, в котором вы сейчас. Помечен и цветом, и линией слева:
   одного цвета мало — в списке из восьми строк глаз ищет край. */
.railnav__link[aria-current="true"]{
  color:var(--action);border-inline-start-color:var(--action);font-weight:500
}

/* ---------- 56. Полоса перехода к результату ────────────────────────
   Пара к рельсу разделов, только для узкого экрана. От 860 точек панель
   стоит в две колонки, результат виден рядом с настройками и держится
   липким — переходить некуда. Ниже 860 колонки складываются в столбик,
   результат уезжает под форму, и на телефоне человек правит поля, не
   видя ни одной посчитанной цифры.
   Полоса внизу экрана зовёт туда, где вас сейчас нет: не видно
   результата — «К результату», видно — «К настройкам». Решать ничего
   не надо, выбор в ней один.
   Стекло здесь то же самое, что у шапки и у полосы категорий: тот же
   --topbar-bg, та же пара saturate+blur и та же подмена сплошным фоном
   там, где размытия нет. Третий приём был бы третьим языком в системе.
   Живёт только пока панель на экране. Ушли читать разборы ниже — полоса
   уехала вниз: там она закрывала бы текст и звала бы неизвестно куда.
   Разметку ставит скрипт; в исходниках инструментов её нет. */
/* Высота полосы названа один раз: её просят и запас у панели, и нижний
   отступ прокрутки, а это не потомки полосы — значит объявление идёт
   в корень, иначе переменная до них не дойдёт.
   Складывается из измеренного: кнопка 48 (46 минимума плюс губа), поля
   полосы по 12, линия сверху 1, вырез экрана снизу. Плюс --s-3 зазора,
   чтобы поле, к которому приехали табом, не касалось полосы краем. */
:root{
  --jump-h:calc(48px + var(--s-4) * 2 + 1px + var(--s-3) + env(safe-area-inset-bottom, 0px))
}
.jumpbar{display:none}
/* screen and — не формальность: лист бумаги узкий, под правило шириной
   до 859 он подходит, и на печать уходил бы и запас под полосу, и она
   сама. На бумаге ни того, ни другого быть не должно. */
@media screen and (max-width:859px){
  .jumpbar{
    display:block;position:fixed;z-index:30;left:0;right:0;bottom:0;
    padding:var(--s-4) clamp(16px, 2.6vw, 32px);
    padding-bottom:calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
    background:var(--topbar-bg);
    border-top:1px solid var(--rule-hair);
    -webkit-backdrop-filter:saturate(1.6) blur(14px);
            backdrop-filter:saturate(1.6) blur(14px);
  }
  @supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
    .jumpbar{background:var(--surface-card)}
  }
  /* Полосы не нужно — она уезжает за нижний край, а не гаснет на месте:
     на стекле исчезновение по прозрачности читается как поломка отрисовки.
     visibility убирает её из обхода табом и от чтения с экрана. */
  .jumpbar[data-off]{transform:translateY(120%);visibility:hidden}
  .jump{width:100%}
  /* Стрелка показывает сторону, в которую поедет страница. Разворот —
     статичное состояние кнопки, а не движение: полоса к этому моменту
     уже стоит на месте. */
  .jump__ic{transform:rotate(0)}
  .jump--up .jump__ic{transform:rotate(180deg)}
  /* Панель отдаёт полосе её высоту, иначе последнее поле формы навсегда
     осталось бы под ней. Метку ставит тот же скрипт, что и полосу, и ровно
     на ту панель, за которой полоса следит: без скрипта — как и на витрине
     калькуляторов, где такая же панель стоит образцом, — запаса нет, и на
     остальных панелях страницы его тоже нет. */
  .calcpanel[data-jump]{padding-bottom:var(--jump-h)}
  /* И столько же снизу отмеряет прокрутка к цели. Иначе поле, к которому
     приехали табом, встаёт ровно под полосу: кольца фокуса не видно, а
     тычок по видимой середине поля попадает в кнопку перехода.
     Верхний отступ прокрутки живёт в блоке 31а и здесь не трогается —
     это другая сторона и другое свойство, они не складываются. */
  html:has(.jumpbar){scroll-padding-bottom:var(--jump-h)}
}

/* ---------- 57. Плашка согласия на cookie ───────────────────────────
   Спрашивает один раз и уходит навсегда: решение живёт в браузере,
   в localStorage. Разметку строит assets/consent.js — в HTML её нет
   вовсе, иначе на кешированной странице она мелькала бы и тем, кто
   уже ответил.

   Стоит внизу и во всю ширину, как полоса перехода к результату
   (блок 56), и по той же причине: на телефоне это единственное место,
   где заметно и при этом не закрывает содержимое. Стекло — тот же
   приём, что у шапки и у полосы: фон под ней читается, но не мешает.
   ------------------------------------------------------------------ */
.consent{
  position:fixed;left:0;right:0;bottom:0;z-index:60;
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:var(--s-4) var(--s-6);
  padding:var(--s-5) var(--s-6);
  padding-bottom:calc(var(--s-5) + env(safe-area-inset-bottom, 0px));
  background:var(--topbar-bg);
  -webkit-backdrop-filter:saturate(1.6) blur(14px);
  backdrop-filter:saturate(1.6) blur(14px);
  border-top:1px solid var(--rule);
}
.consent__text{
  flex:1 1 30ch;margin:0;
  font-size:var(--fs-13);font-weight:500;color:var(--ink-mute);line-height:1.5;
}
.consent__text a{color:var(--action);text-decoration:underline;text-underline-offset:2px}
.consent__acts{display:flex;flex-wrap:wrap;gap:var(--s-3);margin:0}

/* Полоса перехода к результату живёт там же, внизу. Встретиться им
   негде — плашка показывается один раз и уходит, — но если встретятся,
   плашка обязана быть выше: она требует ответа, а полоса нет. */
@media screen and (max-width:859px){
  .consent{padding-inline:var(--s-4)}
}
