/* ═══════════════════════════════════════════════
       ARNO FONT
       ═══════════════════════════════════════════════ */
    @font-face { font-family: 'Arno'; font-weight: 400; font-style: normal; font-display: swap; src: url('/fonts/arno-400-normal.woff2') format('woff2'); }
    @font-face { font-family: 'Arno'; font-weight: 500; font-style: normal; font-display: swap; src: url('/fonts/arno-500-normal.woff2') format('woff2'); }
    @font-face { font-family: 'Arno'; font-weight: 700; font-style: normal; font-display: swap; src: url('/fonts/arno-700-normal.woff2') format('woff2'); }
    @font-face { font-family: 'Arno'; font-weight: 400; font-style: italic; font-display: swap; src: url('/fonts/arno-400-italic.woff2') format('woff2'); }

    /* ═══════════════════════════════════════════════
       INTER VARIABLE — self-hosted сабсеты (Google-subsetted woff2)
       unicode-range заставляет браузер качать только тот subset,
       символы которого есть на странице. На главной обычно latin + cyrillic,
       extended варианты подгружаются по запросу (имена, технические термины).
       ═══════════════════════════════════════════════ */
    @font-face {
      font-family: 'Inter'; font-weight: 100 900; font-style: normal; font-display: swap;
      src: url('/fonts/inter-cyrillic-ext.woff2') format('woff2');
      unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
    }
    @font-face {
      font-family: 'Inter'; font-weight: 100 900; font-style: normal; font-display: swap;
      src: url('/fonts/inter-cyrillic.woff2') format('woff2');
      unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
    }
    @font-face {
      font-family: 'Inter'; font-weight: 100 900; font-style: normal; font-display: swap;
      src: url('/fonts/inter-latin-ext.woff2') format('woff2');
      unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
    }
    @font-face {
      font-family: 'Inter'; font-weight: 100 900; font-style: normal; font-display: swap;
      src: url('/fonts/inter-latin.woff2') format('woff2');
      unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
    }

    /* ═══════════════════════════════════════════════
       RESET & BASE
       ═══════════════════════════════════════════════ */
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    /* No global scroll-behavior: smooth. Globally smooth scroll causes jank
       on iOS Safari programmatic scrolls (hash-jumps, infinite-scroll loaders)
       and conflicts with the header scroll observer. Use scrollTo({behavior:'smooth'})
       per call where smoothness is needed. */
    html { -webkit-text-size-adjust: 100%; height: 100%; -webkit-tap-highlight-color: transparent; background: rgb(20, 20, 20); scrollbar-width: none; -ms-overflow-style: none; overflow-x: hidden; }
    @media (prefers-reduced-motion: no-preference) {
      html { scroll-behavior: auto; } /* explicit auto — was 'smooth' globally; reverted for perf */
    }
    html::-webkit-scrollbar { width: 0; height: 0; display: none; }
    body { scrollbar-width: none; -ms-overflow-style: none; overflow-x: hidden; }
    body::-webkit-scrollbar { width: 0; height: 0; display: none; }
    /* GLOBAL: any overflow-x scroller (year strip, tag strip, sub-nav, table)
       hides its native scrollbar. Defence-in-depth — individual rules also
       set this but in case any future selector misses, the catch-all here
       guarantees no horizontal bar ever appears on mobile.
       Roman regressed on this three times — DO NOT remove this block. */
    [style*="overflow-x"], [style*="overflow:"]::-webkit-scrollbar,
    *::-webkit-scrollbar:horizontal { display: none; height: 0; }

    body {
      font-family: 'Arno', 'EB Garamond', Georgia, serif;
      font-size: 16px;
      line-height: normal;
      color: rgb(0, 0, 0);
      background: rgb(255, 255, 255);
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      display: flex;
      flex-direction: column;
      min-height: 100%;
    }

    /* Custom scrollbar */
    ::-webkit-scrollbar { width: 8px; height: 8px; }
    ::-webkit-scrollbar-track { background: var(--dark); }
    ::-webkit-scrollbar-thumb { background: var(--gray-dim); border-radius: 4px; }
    ::-webkit-scrollbar-thumb:hover { background: var(--gray-secondary); }
    @supports (scrollbar-color: auto) {
      * { scrollbar-color: var(--gray-dim) var(--dark); scrollbar-width: thin; }
    }

    a { color: inherit; text-decoration: none; }
    img { max-width: 100%; display: block; }
    ul, ol { list-style: none; }
    button { font-family: inherit; }

    /* ═══════════════════════════════════════════════
       CSS VARIABLES
       ═══════════════════════════════════════════════ */
    :root {
      /* ── Palette ── */
      --black: rgb(0, 0, 0);
      --dark: rgb(20, 20, 20);
      --white: rgb(255, 255, 255);
      --gray-secondary: rgb(191, 191, 191);
      --gray-dim: rgb(139, 139, 139);
      --link-blue: rgb(0, 0, 238);
      --gray-border: rgb(220, 220, 220);
      --rule: 1px solid var(--gray-border);
      --rule-strong: 1px solid var(--dark);
      --partner-tint: rgb(248, 244, 236);

      /* ── Fonts ── */
      --serif: 'Arno', 'EB Garamond', Georgia, serif;
      --sans: 'Inter', 'Helvetica Neue', 'HelveticaNeueCyr', Arial, sans-serif;

      /* ── Modular type scale — newspaper readability floor ──
         Fluid via clamp(min, preferred, max). Anchors: 375px → 1600px.
         Minimum floors keep every UI element comfortably readable on any screen. */
      --step--2: clamp(14px, 0.3vw + 12.5px, 16px);   /* smallest UI: chips, micro caps */
      --step--1: clamp(16px, 0.4vw + 14.5px, 18px);   /* meta, figcaption, hints */
      --step-0:  clamp(18px, 0.5vw + 16px, 21px);     /* UI default, lead */
      --step-1:  clamp(21px, 0.8vw + 18px, 26px);     /* card author italic */
      --step-2:  clamp(24px, 1.1vw + 20px, 32px);     /* card title, H3, abstract */
      --step-3:  clamp(29px, 1.4vw + 23.5px, 40px);   /* H2 */
      --step-4:  clamp(35px, 1.9vw + 27.5px, 52px);   /* article hero title */
      --step-5:  clamp(41px, 2.5vw + 31px, 62px);     /* issue hero display */
      --step-6:  clamp(50px, 3.2vw + 37px, 78px);     /* large editorial display */

      /* ── Long-read body: own fluid scale, ratio 1.5 line-height ──
         Отдельный токен (НЕ из --step-*): меняет ТОЛЬКО текст статьи + его
         пропорциональных детей (эпиграф 0.92em, маркеры сносок 0.65em, h4/h5).
         Хром сайта на --step-* — не затрагивается. Подняли 21-24 → 22-25 (+1px,
         все устройства), h2/h3 подняты следом чтобы лестница осталась
         h2 > h3 ≥ body > сноски (Roman 2026-06). */
      --body-size: clamp(22px, 0.45vw + 20.3px, 25px);
      --body-lh: 1.5;

      /* ── Container & adaptive gutters (% of viewport) ──
         Content collects more on wide monitors instead of stretching.
         Container width = min(88vw, --container-max).
         Side margin = (100vw - container) / 2, with a floor of 28px.

         Result, side-margin %:
           1024px → 6%   (61px)
           1280px → 6%   (77px)
           1440px → 6%   (86px)
           1600px → 9%   (140px)  ← container caps here
           1920px → 14%  (280px)
           2560px → 24%  (620px)  — sides take ~half the screen on 4K
      */
      --container-max: 1240px;
      --container-ratio: 84vw;
      --content-margin: max(28px, calc((100vw - min(var(--container-ratio), var(--container-max))) / 2));
      --gutter: var(--content-margin);
      --content-width: 760px;       /* long-read measure */
      --measure-wide: 1100px;       /* hero/meta band */

      /* ── Rhythm tokens — 8px baseline ── */
      --space-1: 8px;
      --space-2: 16px;
      --space-3: 24px;
      --space-4: 40px;
      --space-5: 64px;
      --space-6: 96px;
      --space-7: 144px;
      --space-8: 200px;

      /* ── Grid gaps — balanced (less air than previous iteration) ── */
      --card-gap-x: clamp(32px, 3.5vw, 64px);
      --card-gap-y: clamp(56px, 6vw, 96px);
      --card-gap: var(--card-gap-x); /* legacy alias for compat */
      --section-gap: clamp(64px, 8vw, 128px);
      --section-pad-y: clamp(48px, 6vw, 96px);
      --grid-cols: 3;

      /* ── Legacy widths (retained for compat) ── */
      --hero-width: clamp(300px, 51.7vw, 760px);
      --card-width: clamp(180px, 22vw, 360px);

      /* ── Header ── */
      --header-height: clamp(80px, 8.5vw, 130px);
      --header-height-shrunk: 64px;

      /* ── Motion ── */
      --spring: cubic-bezier(0.34, 1.56, 0.64, 1);
      --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
      /* Универсальный ховер для кликабельных картинок (cards, hero, related).
         Softer spring (1.4 overshoot вместо 1.56) — pop без jitter. 0.45s
         даёт ощущение пружинной микроанимации, не "плавно растягивается"
         (как было 0.7s) и не "резко стало больше" (как просто scale без
         микро-сдвига brightness). Записано в .claude/DESIGN.md как правило. */
      --hover-img-ease: cubic-bezier(0.34, 1.4, 0.64, 1);
      --hover-img-duration: 0.42s;
      --hover-img-scale: 1.035;

      /* ── Shadows ── */
      --shadow-rest: 0 1px 6px rgba(0,0,0,0.04);
      --shadow-hover: 0 12px 32px rgba(0,0,0,0.08);
    }

    /* ═══════════════════════════════════════════════
       EDGE-CASE SAFETY NET
       Универсальная защита: длинные слова, пустые блоки,
       overflow, картинки без размеров — все случаи покрыты.
       ═══════════════════════════════════════════════ */

    /* Картинки и видео — никогда не выходят за контейнер. */
    img, video, svg {
      max-width: 100%;
      height: auto;
      display: block;
    }

    /* Flex/grid-children могут схлопываться корректно (без min-width: auto баг). */
    .article-card, .related-card, .archive-issue-card,
    .author-article-row, .archive-article-item,
    .issue-toc-item, .header-toc-item,
    .hero-article, .nd-hero-text, .nd-hero-cover,
    .home-aside, .home-sidebar-issue,
    .home-issue-feature, .home-feed,
    .article-meta-block, .article-author-bio {
      min-width: 0;
    }

    /* Длинные слова никогда не вылезают и не ломают сетку. Hyphens для русского
       срабатывает корректно (html[lang=ru]). */
    .article-card-title, .related-card-title, .archive-issue-title,
    .hero-article-title, .issue-hero-title, .nd-hero-title,
    .issue-toc-item-title, .article-nav-title,
    .author-name, .archive-article-item-title,
    .author-article-title, .search-result-title,
    .home-issue-feature-title, .article-hero-title,
    .article-body h1, .article-body h2, .article-body h3,
    .article-body h4, .article-body h5 {
      overflow-wrap: break-word;
      word-break: normal;
      /* No auto-hyphenation in titles/headings — breaking the last word with a hyphen
         reads badly (фидбэк Романа: Алексеев/Галкин). Long words still wrap whole;
         only a word longer than the line is broken (overflow-wrap above). */
      hyphens: manual;
      -webkit-hyphens: manual;
    }

    /* URL/слаги в подписях/сносках — разрывать по любой точке. */
    figcaption, .footnotes, .legal-footnote {
      overflow-wrap: anywhere;
      word-break: break-word;
    }

    /* Скрыть пустые мета-блоки: если в CMS нет автора/рубрики — не оставлять
       призраков с margin'ами. */
    .article-card-rubric:empty, .article-card-author:empty,
    .article-card-issue:empty, .article-card-title:empty,
    .related-card-author:empty, .related-card-title:empty,
    .archive-issue-title:empty, .archive-issue-meta:empty,
    .hero-article-rubric:empty, .hero-article-author:empty,
    .nd-hero-rubric:empty, .nd-hero-authors:empty, .nd-hero-issue:empty,
    .issue-toc-item-rubric:empty, .issue-toc-item-author:empty,
    .article-meta-row:empty, .article-author-bio:empty,
    .home-feed-label:empty, .article-related-label:empty {
      display: none !important;
    }

    /* Figure без картинки внутри — пустой контейнер, скрыть. */
    figure:not(:has(img)):not(:has(picture)) { display: none; }

    /* Картинка без alt — добавлять прозрачный outline для отладки в dev.
       (применяется только когда есть глобальный класс body.dev-audit) */
    body.dev-audit img:not([alt]) { outline: 2px dashed rgb(255,0,0); }

    /* Длинные таблицы — горизонтальный скролл вместо разрушения layout.
       Скроллбар скрыт (см. правило feedback_no_horizontal_scrollbars). */
    /* Tables from DOCX («Классификация поколений», вып.133) — match the print look:
       solid gray header row + thin row rules. textutil emits no <thead>; the header cells
       carry <b>, so the FIRST <tr> is styled as the header. */
    /* Print-faithful: slim, content-width (not stretched), thin rules, dark header.
       Numeric/date columns stay on one line; the first column wraps. */
    .article-body table {
      /* Full-width table that always FITS the column — no horizontal scroll, no cut
         columns (feedback_no_horizontal_scrollbars). The label column (first) wraps;
         the narrow numeric/date columns stay on one line. */
      width: 100%;
      max-width: 100%;
      table-layout: auto;
      border-collapse: collapse;
      margin: 1.1em 0;
      font-family: var(--sans);
      font-size: 0.8em;
      line-height: 1.3;
      font-variant-numeric: tabular-nums;
    }
    .article-body table td, .article-body table th {
      padding: 6px 14px;
      text-align: left;
      vertical-align: top;
      border-bottom: 1px solid rgba(0,0,0,0.1);
    }
    /* Cells may still wrap their text in <p> on un-normalized (old) articles —
       kill the inherited red-line indent and paragraph margins so text sits flush
       and the header row stays a tight bar, not a tall gap. */
    .article-body table td p, .article-body table th p { margin: 0; text-indent: 0; display: inline; }
    /* First column (labels) wraps to fit; numeric/date columns stay on one line so a
       range like "1941–1955" never breaks. ("(Y)" is bound to its word with nbsp.) */
    .article-body table td:first-child, .article-body table th:first-child { white-space: normal; }
    .article-body table td:not(:first-child),
    .article-body table th:not(:first-child) { white-space: nowrap; padding-left: 10px; padding-right: 10px; }
    .article-body table tr:first-child td,
    .article-body table thead td, .article-body table th {
      background: #8d8d8d;
      color: #fff;
      font-weight: 500;
      border-bottom: none;
      padding-top: 7px; padding-bottom: 7px;
    }
    .article-body table tr:first-child td b, .article-body table th b { font-weight: 500; }
    .article-body table tr:last-child td { border-bottom: none; }

    /* Длинные `<a>` (URL в тексте) — переносить везде. */
    .article-body a, .footnotes a, figcaption a {
      overflow-wrap: anywhere;
      word-break: break-word;
    }

    /* Контейнер для предотвращения horizontal scroll от ошибочного overflow.
       КРИТИЧНО: overflow-x: clip (НЕ hidden!) — иначе сломается position:sticky
       у архивного подменю и других sticky-элементов (sticky требует overflow:visible
       или overflow:clip на всех предках). */
    html, body { overflow-x: clip; }

    /* ═══════════════════════════════════════════════
       SPRING PHYSICS ANIMATIONS
       ═══════════════════════════════════════════════ */
    @keyframes pageEnter {
      from { opacity: 0; transform: translateY(12px); }
      to { opacity: 1; transform: translateY(0); }
    }
    .page-enter { animation: pageEnter 0.4s var(--spring); }

    @keyframes pulse {
      0%, 100% { opacity: 0.3; }
      50% { opacity: 1; }
    }

    /* Lazy image fade-in */
    img[loading="lazy"] {
      opacity: 0;
      transition: opacity 0.5s ease;
    }
    img[loading="lazy"].loaded {
      opacity: 1;
    }

    /* Touch device hardening — covers display in their REST state by
       default; lift+level animation plays on tap (.is-tapping class
       added by JS), then page navigates while animation runs in parallel.

       IMPORTANT: do NOT write `:hover` rules inside this `(hover: none)`
       block. Even with `transform: none` they tell iOS Safari "there
       are hover styles here" → iOS emulates hover on FIRST tap, which
       absorbs the click and breaks navigation. Override styles
       targeting non-:hover selectors only. */
    @media (hover: none), (pointer: coarse) {

      /* Touch device: cover sits in its tilted REST state by default
         (same as desktop pre-hover), preview-style. On `.is-tapping`
         (set on touchstart of the card) we play the SAME "lift + level"
         animation that desktop shows on hover — gives instant visual
         feedback while the next page loads in parallel. */
      .archive-issue-cover { perspective: 600px; }
      .archive-issue-cover-blur {
        width: clamp(180px, 70%, 260px);
      }
      .archive-issue-card.is-tapping .archive-issue-cover-blur {
        transform: scale(1.05) rotateX(0deg) rotate(0deg) translateY(-4px) !important;
        transition: transform 0.45s var(--spring);
      }

      /* Home issue feature on touch: REST = tilted (matches desktop
         rest state) so user sees the editorial flourish immediately on
         page load. On tap = lifts and straightens (matches desktop
         hover) — the same "cover comes off the page" gesture the user
         already knows from /archive. Issue page navigates in parallel
         while the animation plays. */
      .home-issue-feature-cover-inner {
        transition: transform 0.5s var(--spring) !important;
      }
      .home-issue-feature.is-tapping .home-issue-feature-cover-inner {
        transform: scale(1.0) rotateX(0deg) rotate(0deg) translateY(-4px) !important;
      }
    }
    a, button, [onclick], .article-card, .archive-issue-card, .related-card,
    .home-issue-feature, .archive-author-name, .archive-author-article,
    .archive-filter-trigger, .archive-tab, .archive-year-btn {
      touch-action: manipulation;
      -webkit-tap-highlight-color: transparent;
    }

    /* View Transitions API — only kicks in on supporting browsers
       (Chrome/Edge/Opera, Safari 18+). Soft 200ms cross-fade replaces the
       instant DOM swap on hash navigation. Header, footer, FAB are kept
       in place via view-transition-name so they don't blink. */
    @media (prefers-reduced-motion: no-preference) {
      ::view-transition-old(root),
      ::view-transition-new(root) {
        animation-duration: 220ms;
        animation-timing-function: var(--ease-out, ease-out);
      }
      ::view-transition-group(site-header),
      ::view-transition-group(site-footer) {
        animation-duration: 0s; /* don't animate persistent chrome */
      }
    }
    .site-header { view-transition-name: site-header; }
    footer.site-footer, footer { view-transition-name: site-footer; }

    /* ── Hover style system ──
       Titles and headings: subtle color fade on hover (no underlines).
       Small nav links: animated underline via background-size. */
    a.animated-link {
      background-image: linear-gradient(currentColor, currentColor);
      background-size: 0% 1px;
      background-position: 0 100%;
      background-repeat: no-repeat;
      transition: background-size 0.3s var(--spring), color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    a.animated-link:hover {
      background-size: 100% 1px;
    }
    }
    /* Titles, author names, headings — color fade only */
    .archive-author-name,
    .author-name,
    .article-card-author,
    .article-card-title,
    .issue-grid-card-title,
    .article-nav-title,
    .archive-article-item-title,
    .search-result-title,
    .related-card-title,
    .hero-article-title,
    .author-article-title {
      transition: color 0.2s;
    }
    /* All title :hover color shifts wrapped in (hover: hover) to prevent
       iOS Safari's hover-emulation quirk on tap (first tap shows hover,
       second tap clicks → broke navigation on archive issue covers). */
    @media (hover: hover) and (pointer: fine) {
      .archive-author-name:hover,
      .author-name:hover,
      .article-card:hover .article-card-title,
      .issue-grid-card:hover .issue-grid-card-title,
      .article-nav-item:hover .article-nav-title,
      .archive-article-item:hover .archive-article-item-title,
      .search-result-item:hover .search-result-title,
      .related-card:hover .related-card-title,
      .hero-article:hover .hero-article-title,
      .author-article-row:hover .author-article-title {
        color: var(--gray-dim);
      }
    }
    /* Tags/chips: color shift + subtle scale */
    .tag-chip {
      transition: border-color 0.2s, color 0.2s, transform 0.25s var(--ease-out);
    }
    @media (hover: hover) and (pointer: fine) {
    .tag-chip:hover {
      border-color: var(--dark);
      color: var(--dark);
      transform: scale(1.04);
    }
    }
    .tag-cloud-item {
      transition: color 0.2s, transform 0.25s var(--ease-out);
    }
    @media (hover: hover) and (pointer: fine) {
    .tag-cloud-item:hover {
      color: var(--dark);
      transform: scale(1.06);
    }
    }

    /* Scroll reveal */
    .reveal {
      opacity: 0;
      transform: translateY(16px);
      transition: opacity 0.5s ease, transform 0.5s var(--ease-out);
    }
    .reveal.visible {
      opacity: 1;
      transform: translateY(0);
    }

    /* ═══════════════════════════════════════════════
       18+ AGE GATE MODAL
       ═══════════════════════════════════════════════ */
    .age-gate {
      position: fixed;
      top: 0; left: 0; right: 0; bottom: 0;
      background: rgba(0, 0, 0, 0.92);
      z-index: 10000;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .age-gate-box {
      background: var(--white);
      padding: 48px 40px;
      max-width: 440px;
      width: 90%;
      text-align: center;
    }
    .age-gate-title {
      font-family: var(--serif);
      font-size: 28px;
      font-weight: 700;
      margin-bottom: 16px;
      color: var(--dark);
    }
    .age-gate-text {
      font-family: var(--sans);
      font-size: var(--step-0);
      line-height: 22px;
      color: var(--gray-dim);
      margin-bottom: 32px;
    }
    .age-gate-buttons {
      display: flex;
      gap: 16px;
      justify-content: center;
    }
    .age-gate-btn {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1.2px;
      padding: 14px 36px;
      border: var(--rule-strong);
      cursor: pointer;
      transition: background 0.2s, color 0.2s;
    }
    .age-gate-btn--yes {
      background: var(--dark);
      color: var(--white);
    }
    @media (hover: hover) and (pointer: fine) {
    .age-gate-btn--yes:hover { background: var(--black); }
    }
    .age-gate-btn--no {
      background: transparent;
      color: var(--dark);
    }
    @media (hover: hover) and (pointer: fine) {
    .age-gate-btn--no:hover { background: rgb(245,245,245); }
    }

    /* ═══════════════════════════════════════════════
       HEADER — centered logo, absolute left/right
       ═══════════════════════════════════════════════ */
    .site-header {
      position: fixed;
      top: 0; left: 0; right: 0;
      z-index: 1002;
      background: rgb(0, 0, 0);
      height: var(--header-height);
      transition: height 0.4s var(--ease-out), background 0.3s ease;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .site-header.shrunk {
      height: var(--header-height-shrunk);
      background: rgba(0, 0, 0, 0.97);
    }
    .site-header.transparent {
      background: transparent;
    }
    .site-header.transparent.shrunk {
      background: rgba(0, 0, 0, 0.97);
    }
    /* Lang-specific name visibility: RU shows main only, EN shows sub only */
    body[data-lang="ru"] .header-logo-sub { display: none; }
    body[data-lang="en"] .header-logo-main { display: none; }
    body[data-lang="en"] .header-logo-sub {
      font-size: clamp(11px, 1.2vw, 16px);
      font-weight: 500;
      color: var(--white);
      letter-spacing: 0.5px;
    }
    body[data-lang="en"] .site-header.shrunk .header-logo-sub {
      font-size: clamp(9px, 0.9vw, 13px);
    }
    /* Reading progress bar — full-width, scales on the X-axis from 0 to 1
       as the user scrolls. Using transform (not width) keeps it on the GPU
       so there's no per-frame reflow → smooth, springy motion. */
    .reading-progress {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 4px;
      background: rgba(255,255,255,0.7);
      z-index: 10001;
      transform: scaleX(0);
      transform-origin: left center;
      transition: transform 0.18s cubic-bezier(0.22, 1, 0.36, 1), background 0.2s, opacity 0.2s;
      opacity: 0;
      pointer-events: none;
      will-change: transform;
    }
    .reading-progress.active { opacity: 1; }
    /* The bar sits ABOVE the always-dark header (z-index higher), so a
       white-ish background is visible on every page. No need to invert. */
    /* Article issue context bar */
    /* Article top bar — sits above hero, holds issue link + Contents trigger.
       Lives inside the dark hero block so its background matches. */
    .article-top-bar-wrap {
      background: var(--dark);
      padding-top: var(--header-height);
    }
    .article-top-bar {
      max-width: var(--measure-wide);
      margin: 0 auto;
      padding: clamp(32px, 4vw, 56px) var(--content-margin) 0;
      font-family: var(--sans);
      font-size: var(--step--1);
      letter-spacing: 0.04em;
      color: var(--gray-secondary);
      display: flex;
      align-items: center;
      gap: 16px;
    }
    .article-top-bar a {
      color: var(--gray-secondary);
      transition: color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .article-top-bar a:hover { color: var(--white); }
    }
    .article-top-bar-sep { color: rgba(255,255,255,0.25); }
    /* When top-bar present, hero doesn't need its own header padding */
    .article-top-bar-wrap + .nd-hero { padding-top: 0; }

    /* Header context bar — dynamic navigation shown when header is shrunk */
    /* Header context: single tight label, centered in the header between
       search-icon (left) and nav (right). One continuous text-run by default;
       issue-page mode (prev/next/contents) opts in via .ctx-issue class. */
    .header-issue-context {
      display: none;
      font-family: var(--sans);
      font-size: var(--step--1);
      letter-spacing: 0.04em;
      color: rgba(255,255,255,0.65);
      white-space: nowrap;
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      max-width: min(60vw, 720px);
      overflow: hidden;
      text-overflow: ellipsis;
      text-align: center;
      opacity: 0;
      transition: opacity 0.3s ease;
      pointer-events: none;
    }
    .header-issue-context:not(:empty) { display: block; }
    /* Issue-page variant: prev/next/contents distributed across header width */
    .header-issue-context.ctx-issue {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 24px;
      left: calc(var(--content-margin) + 60px);
      right: calc(var(--content-margin) + 240px);
      transform: translateY(-50%);
      max-width: none;
    }
    .header-issue-context.ctx-issue:empty { display: none; }
    /* Context appears ONLY after the user has scrolled (header gets .shrunk).
       At the top of an article/issue page, logo is visible — context hidden. */
    .site-header.shrunk .header-issue-context:not(:empty) {
      opacity: 1;
      pointer-events: auto;
    }
    /* Hide logo only when header is shrunk AND context is non-empty —
       so the context block can take logo's centerpiece position. */
    .site-header.shrunk:has(.header-issue-context:not(:empty)) .header-logo {
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.3s ease;
    }
    .header-issue-context strong {
      color: var(--white);
      font-weight: 500;
      font-size: var(--step--1);
      line-height: 1.2;
    }
    .header-issue-context a {
      color: rgba(255,255,255,0.6);
      text-decoration: none;
      font-family: var(--sans);
      font-size: var(--step--1);
      line-height: 1.2;
      letter-spacing: 0.3px;
      transition: color 0.2s, border-color 0.2s;
      border-bottom: 1px solid transparent;
      padding-bottom: 1px;
    }
    @media (hover: hover) and (pointer: fine) {
    .header-issue-context a:hover {
      color: var(--white);
      border-bottom-color: rgba(255,255,255,0.6);
    }
    }
    .header-issue-context .ctx-sep { color: rgba(255,255,255,0.25); }
    /* Article title in header — desktop only, truncated with ellipsis */
    .header-issue-context .ctx-article-title {
      flex: 0 1 auto;
      min-width: 0;
      max-width: 50ch;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      color: rgba(255,255,255,0.65);
      font-family: var(--serif);
      font-size: var(--step-0);
      line-height: 1.2;
      letter-spacing: 0;
      position: relative;
    }
    .header-issue-context .ctx-desktop { display: none; }
    @media (min-width: 768px) {
      .header-issue-context .ctx-desktop { display: inline; }
    }

    /* TOC dropdown from header — fullscreen overlay matching issue page TOC */
    .header-toc-dropdown {
      position: fixed;
      top: var(--header-height-shrunk);
      left: 0;
      right: 0;
      bottom: 0;
      display: flex;
      flex-direction: column;
      justify-content: center;     /* center the TOC block vertically in viewport */
      background: rgba(20, 20, 20, 0.98);
      overflow: hidden;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.2s ease;
      z-index: 1001;
    }
    /* TOC close — borderless square, distance from header bottom equals distance from right edge */
    .header-toc-close {
      position: fixed;
      --toc-close-edge: 16px;
      top: calc(var(--header-height-shrunk) + var(--toc-close-edge));
      right: var(--toc-close-edge);
      z-index: 1003;
      background: none;
      border: none;
      color: rgba(255,255,255,0.7);
      width: 36px;
      height: 36px;
      cursor: pointer;
      display: none;
      align-items: center;
      justify-content: center;
      padding: 0;
      transition: color 0.2s;
    }
    .header-toc-dropdown.open ~ .header-toc-close,
    .header-toc-close.open { display: inline-flex; }
    @media (hover: hover) and (pointer: fine) {
    .header-toc-close:hover { color: var(--white); }
    }
    .header-toc-close::before { content: '✕'; font-size: 20px; line-height: 1; }
    .header-toc-close { font-size: 0; }  /* hide any text content (e.g. "Esc"); only ::before shows */
    .header-toc-dropdown .header-toc-scroll {
      flex: 1;
      overflow-y: auto;
      scrollbar-width: none;
      -ms-overflow-style: none;
    }
    .header-toc-dropdown .header-toc-scroll::-webkit-scrollbar { display: none; width: 0; }
    .header-toc-dropdown .header-toc-scroll::-webkit-scrollbar { width: 4px; }
    .header-toc-dropdown .header-toc-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); }
    .header-toc-dropdown.open {
      opacity: 1;
      pointer-events: auto;
    }
    .header-toc-dropdown::-webkit-scrollbar { width: 4px; }
    .header-toc-dropdown::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); }
    .header-toc-items {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: clamp(24px, 2.5vw, 48px) clamp(72px, 7vw, 144px);
      max-width: var(--container-max);
      margin: 0 auto;
      padding: clamp(40px, 5vw, 80px) var(--content-margin);
    }
    @media (max-width: 1023px) { .header-toc-items { grid-template-columns: repeat(2, 1fr); gap: 24px clamp(40px, 5vw, 64px); padding: 32px var(--content-margin); } }
    @media (max-width: 640px)  { .header-toc-items { grid-template-columns: 1fr; gap: 24px 0; padding: 24px var(--content-margin); } }
    .header-toc-item {
      padding: 4px 0;
      cursor: pointer;
      transition: color 0.2s;
    }
    .header-toc-item-body {
      display: flex;
      flex-direction: column;
      gap: 8px;
      font-family: var(--serif);
      font-size: var(--step-1);
      line-height: 1.25;
      color: rgba(255,255,255,0.85);
      transition: color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .header-toc-item:hover .header-toc-item-body { color: var(--white); }
    }
    @media (hover: hover) and (pointer: fine) {
    .header-toc-item:hover .header-toc-item-title { color: var(--white); }
    }
    /* Active item — currently open article: short white marker bar to the left of title */
    .header-toc-item.active .header-toc-item-title {
      color: var(--white);
      font-weight: 500;
      position: relative;
    }
    .header-toc-item.active .header-toc-item-title::before {
      content: '';
      position: absolute;
      left: -16px;
      top: 4px;
      bottom: 4px;
      width: 2px;
      background: var(--white);
    }
    .header-toc-item-title { color: rgba(255,255,255,0.92); transition: color 0.2s; }
    .header-toc-item-rubric { transition: color 0.2s; }
    @media (hover: hover) and (pointer: fine) {
    .header-toc-item:hover .header-toc-item-rubric { color: rgba(255,255,255,0.7); }
    }
    .header-toc-item-num {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: rgba(255,255,255,0.3);
      flex-shrink: 0;
    }
    .header-toc-item-rubric {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: rgba(255,255,255,0.4);
    }
    /* Author name in the issue «Содержание» / header TOC. Was a tiny inline
       clamp(14px,1.1vw,17px) — VM/Egor flagged it as «крошечный» next to the
       title. Lift to step-0 serif so the author reads as a real line between
       the rubric (small caps) and the title. */
    .header-toc-item-author {
      font-family: var(--serif);
      font-size: var(--step-0);
      color: rgba(255,255,255,0.6);
      transition: color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .header-toc-item:hover .header-toc-item-author { color: rgba(255,255,255,0.85); }
    }
    .header-toc-nav {
      flex-shrink: 0;
      display: flex;
      justify-content: space-between;
      padding: 16px var(--content-margin);
      border-top: 1px solid rgba(255,255,255,0.1);
      background: rgba(20, 20, 20, 0.98);
    }
    .header-toc-nav a {
      padding: 4px 0;
      font-family: var(--sans);
      font-size: var(--step--1);
      color: rgba(255,255,255,0.5);
      transition: color 0.15s;
      text-decoration: none;
    }
    @media (hover: hover) and (pointer: fine) {
    .header-toc-nav a:hover { color: var(--white); }
    }

    .header-inner {
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      position: relative;
    }

    .header-left {
      position: absolute;
      left: var(--content-margin);
      display: flex;
      align-items: center;
    }

    .subscribe-btn {
      position: absolute;
      left: var(--content-margin);
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      letter-spacing: 1.2px;
      text-transform: uppercase;
      color: white;
      background: transparent;
      border: 1px solid rgba(255,255,255,0.5);
      padding: 0 18px;
      line-height: 36px;
      cursor: pointer;
      transition: border-color 0.3s ease, background 0.3s ease, opacity 0.2s ease;
      white-space: nowrap;
      display: none;
    }
    @media (hover: hover) and (pointer: fine) {
    .subscribe-btn:hover {
      border-color: white;
      background: rgba(255,255,255,0.1);
    }
    }
    /* Home before scroll (desktop): show BOTH — the magnifier at the left margin
       and the subscribe button just to its right. After scroll (header shrunk)
       the subscribe hides and only the search icon remains. Wrapped in the
       desktop media query so the mobile header (single lupa) is untouched. */
    @media (min-width: 769px) {
      body.is-home .site-header:not(.shrunk) .subscribe-btn {
        display: inline-flex;
        left: calc(var(--content-margin) + 48px);
      }
      body.is-home .site-header:not(.shrunk) .header-search-icon--desktop {
        display: inline-flex;
      }
    }

    .header-logo {
      cursor: pointer;
      text-align: center;
      display: flex;
      align-items: center;
      gap: clamp(8px, 1vw, 14px);
      /* Anchor element — reset link defaults */
      color: inherit;
      text-decoration: none;
    }
    @media (hover: hover) and (pointer: fine) {
    .header-logo:hover, .header-logo:focus, .header-logo:visited {
      color: inherit;
      text-decoration: none;
    }
    }
    .header-logo-icon {
      height: clamp(28px, 3vw, 44px);
      width: auto;
      color: var(--white);
      flex-shrink: 0;
      transition: height 0.4s var(--ease-out);
      display: block;
    }
    .site-header.shrunk .header-logo-icon {
      height: clamp(22px, 2vw, 28px);
    }
    .site-header.shrunk .header-logo-text {
      display: none;
    }
    .header-logo-main {
      font-family: var(--sans);
      font-size: clamp(13px, 1.2vw, 17px);
      font-weight: 500;
      letter-spacing: 0.08em;
      color: var(--white);
      text-transform: uppercase;
      white-space: nowrap;
    }
    .site-header.shrunk .header-logo-main {
      font-size: clamp(12px, 0.9vw, 14px);
    }
    .header-logo-sub {
      font-family: var(--sans);
      font-size: clamp(12px, 0.9vw, 14px);
      font-weight: 400;
      letter-spacing: 0.06em;
      color: var(--gray-secondary);
      text-transform: uppercase;
      white-space: nowrap;
    }
    .site-header.shrunk .header-logo-sub {
      font-size: clamp(11px, 0.7vw, 12px);
    }

    .header-right {
      position: absolute;
      right: var(--content-margin);
      display: flex;
      align-items: center;
      gap: clamp(12px, 1.5vw, 20px);
    }

    .header-nav-link {
      font-family: var(--sans);
      font-size: clamp(14px, 1.1vw, 17px);
      line-height: 22px;
      font-weight: 500;
      color: var(--white);
      transition: opacity 0.2s;
      white-space: nowrap;
      background-image: linear-gradient(currentColor, currentColor);
      background-size: 0% 1px;
      background-position: 0 100%;
      background-repeat: no-repeat;
      transition: background-size 0.3s var(--spring), opacity 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .header-nav-link:hover { background-size: 100% 1px; }
    }
    .header-nav-link.active { opacity: 1; background-size: 100% 1px; }

    /* Mobile hamburger */
    .header-mobile-toggle {
      display: none;
      background: none;
      border: none;
      cursor: pointer;
      padding: 8px;
      position: relative;
      z-index: 1;
    }
    .header-mobile-toggle span {
      display: block;
      width: 24px;
      height: 2px;
      background: var(--white);
      transition: transform 0.3s ease, opacity 0.2s ease;
    }
    .header-mobile-toggle span + span { margin-top: 6px; }
    /* Hamburger → X animation */
    .header-mobile-toggle.open span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
    .header-mobile-toggle.open span:nth-child(2) { opacity: 0; }
    .header-mobile-toggle.open span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

    /* Mobile nav — slide-down panel like old site */
    /* Mobile menu — fullscreen, site-style typography.
       Layout: logo at ~1/4 from top, items group centered in remaining space. */
    .mobile-nav {
      position: fixed;
      inset: 0;
      background: var(--dark);
      z-index: 1001;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: clamp(60px, 12vh, 100px) 24px 40px;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.25s ease;
    }
    .mobile-nav-items {
      flex: 1;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: clamp(10px, 2vh, 22px);
    }
    .mobile-nav.open { opacity: 1; pointer-events: auto; }
    .mobile-nav a {
      display: block;
      font-family: var(--serif);
      font-size: clamp(28px, 6vw, 40px);
      line-height: 1.3;
      color: var(--white);
      text-align: center;
      text-decoration: none;
      padding: 4px 0;
      transition: color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .mobile-nav a:hover { color: var(--gray-secondary); }
    }
    .mobile-nav-logo {
      padding: 0;
      flex-shrink: 0;
    }
    .mobile-nav-logo svg {
      width: 64px;
      height: auto;
      color: var(--white);
      display: block;
      margin: 0 auto;
    }
    /* When mobile menu is open, hide everything else in the header (logo, search, context) */
    .site-header.menu-open .header-logo,
    .site-header.menu-open .header-search-icon,
    .site-header.menu-open .header-issue-context,
    .site-header.menu-open .subscribe-btn,
    .site-header.menu-open .header-right > *:not(.header-mobile-toggle) {
      opacity: 0;
      pointer-events: none;
      visibility: hidden;
    }
    .mobile-nav-lang-btn {
      margin-top: clamp(16px, 3vh, 32px);
      background: none;
      border: 1px solid rgba(255,255,255,0.35);
      color: var(--white);
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      padding: 10px 24px;
      cursor: pointer;
      letter-spacing: 0.12em;
      transition: border-color 0.2s, background 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .mobile-nav-lang-btn:hover { border-color: var(--white); background: rgba(255,255,255,0.06); }
    }
    /* Search + burger icons on top of the open menu */
    .mobile-nav.open ~ .site-header .header-mobile-toggle,
    .mobile-nav.open ~ .site-header .header-search-icon--mobile { z-index: 1002; }
    /* Mobile overlay backdrop — not used with fullscreen menu but kept harmless */
    .mobile-nav-overlay { display: none; }

    /* Header spacer */
    .header-spacer {
      height: var(--header-height);
    }

    /* ═══════════════════════════════════════════════
       MAIN CONTENT
       ═══════════════════════════════════════════════ */
    #app {
      flex: 1 0 auto;
    }

    /* ═══════════════════════════════════════════════
       LOADING / ERROR STATES
       ═══════════════════════════════════════════════ */
    /* Loading indicator — three small dots, same style as the infinite-scroll
       sentinel. ThumbHash placeholders cover image-loading visuals; this
       fallback is shown for pages where we don't yet know the layout
       (search, etc.). Consistent dot rhythm across the site. */
    .loading-indicator {
      display: flex;
      gap: 8px;
      justify-content: center;
      align-items: center;
      padding: clamp(60px, 8vw, 96px) 24px;
      min-height: 120px;
    }
    .loading-indicator .loading-dot {
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: var(--gray-dim);
      animation: loaderPulse 1.4s infinite ease-in-out both;
    }
    .loading-indicator .loading-dot:nth-child(1) { animation-delay: -0.32s; }
    .loading-indicator .loading-dot:nth-child(2) { animation-delay: -0.16s; }
    .loading-indicator--compact {
      padding: 16px 0;
      min-height: 0;
    }
    .loading-indicator--inline {
      padding: 4px 0;
      min-height: 0;
      gap: 4px;
    }
    .loading-indicator--inline .loading-dot {
      width: 4px;
      height: 4px;
    }
    @keyframes loaderPulse {
      0%, 80%, 100% { transform: scale(0.5); opacity: 0.4; }
      40%           { transform: scale(1);   opacity: 1; }
    }

    /* Skeleton loading */
    @keyframes skeletonShimmer { 0% { background-position: -400px 0; } 100% { background-position: 400px 0; } }
    .skeleton-pulse {
      background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
      background-size: 800px 100%;
      animation: skeletonShimmer 1.6s linear infinite;
      border-radius: 0;
    }
    .skeleton-pulse-dark {
      background: linear-gradient(90deg, #1a1a1a 25%, #222 50%, #1a1a1a 75%);
      background-size: 800px 100%;
      animation: skeletonShimmer 1.6s linear infinite;
      border-radius: 0;
    }
    /* Skeleton helper classes were removed — all skeleton markup now uses
       inline width/height styles on .skeleton-pulse / .skeleton-pulse-dark
       primitives, layered inside real layout classes (.articles-grid,
       .archive-issues-grid, .issue-toc-grid, .nd-hero-grid, etc.) so the
       placeholder occupies the exact same slots as the loaded page. */

    .empty-state {
      text-align: center;
      padding: clamp(80px, 15vh, 200px) 24px;
      min-height: 50vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }
    .empty-state-title {
      font-family: var(--serif);
      font-size: 22px;
      font-weight: 400;
      margin-bottom: 10px;
      color: var(--gray-dim);
    }
    .empty-state-text {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-secondary);
      margin-bottom: 24px;
      max-width: 360px;
      line-height: 1.5;
    }
    .retry-btn {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1px;
      color: var(--gray-dim);
      background: none;
      border: var(--rule);
      padding: 10px 28px;
      cursor: pointer;
      transition: all 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .retry-btn:hover { border-color: var(--dark); color: var(--dark); }
    }

    /* ═══════════════════════════════════════════════
       HOME — ISSUE FEATURE (white block at top, centered, rotated cropped cover)
       Cover bottom 1/3 cropped via container aspect-ratio + object-fit, then a thin
       divider, compact meta (№N · year) and theme title. Vertical-efficient.
       ═══════════════════════════════════════════════ */
    /* Default: hidden on desktop (right-column aside takes its place); shown on mobile.
       Anchor element (was <section onclick> — section didn't navigate
       reliably on iOS Safari touch). Reset anchor styles. */
    .home-issue-feature {
      display: none;
      background: var(--white);
      padding: calc(var(--header-height) + clamp(20px, 2vw, 36px)) var(--content-margin) clamp(28px, 3vw, 48px);
      text-align: center;
      cursor: pointer;
      color: inherit;
      text-decoration: none;
    }
    @media (hover: hover) and (pointer: fine) {
    .home-issue-feature:hover, .home-issue-feature:focus, .home-issue-feature:visited {
      color: inherit;
      text-decoration: none;
    }
    }
    .home-issue-feature-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      margin-bottom: clamp(18px, 2vw, 28px);
    }
    /* Cover container is WIDER than the cover image so rotation corners don't clip
       on left/right, while container height is LESS than image height so bottom is cropped. */
    .home-issue-feature-cover {
      perspective: 900px;
      display: flex;
      align-items: flex-start;
      justify-content: center;
      margin: 0 auto;
      width: clamp(290px, 36vw, 420px);
      max-width: 100%;
      height: clamp(150px, 14vw, 200px);  /* ~half of natural cover — crops bottom half */
      overflow: hidden;
    }
    .home-issue-feature-cover-inner {
      /* position:relative wrap; thumbhash lives in ::before pseudo at
         inset:0, so blur backdrop matches img bounds exactly — no halo. */
      position: relative;
      width: clamp(230px, 28vw, 340px);
      max-width: 80%;
      aspect-ratio: 3/4;
      display: block;
      transform: scale(0.92) rotateX(8deg) rotate(-8deg);
      transform-origin: 50% 50%;
      /* 3D tilt-card pattern (книжка в перспективе) — отдельная семантика
         от плоских image-hovers. Используем тот же spring-ease для
         консистентности микро-движения. */
      transition: transform var(--hover-img-duration) var(--hover-img-ease);
      overflow: hidden;
      border: 1px solid rgba(0,0,0,0.1);
    }
    .home-issue-feature-cover-inner.has-thumbhash::before {
      content: '';
      position: absolute;
      inset: 0;
      background-image: var(--thumb-bg);
      background-size: cover;
      background-position: center;
      z-index: 0;
    }
    .home-issue-feature-cover-inner img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      object-position: top;
      display: block;
      position: relative;
      z-index: 1;
      /* No opacity-fade: cached image renders instantly (1y immutable
         cache header on /issues/N/cover.webp). The thumbhash bg behind
         shows briefly only on cold load; the image overpaints it. */
    }
    @media (hover: hover) and (pointer: fine) {
      .home-issue-feature:hover .home-issue-feature-cover-inner {
        /* Soft spring on hover — releases the tilt, slight settle. */
        transform: scale(0.96) rotateX(4deg) rotate(-4deg);
      }
    }
    .home-issue-feature-cover-placeholder {
      background: rgb(245,245,245);
      aspect-ratio: 3/4;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .home-issue-feature-cover-placeholder span {
      font-family: var(--serif);
      font-size: 64px;
      color: rgba(0,0,0,0.15);
    }
    /* Divider — thin gray rule like the one separating tabs / archive submenu */
    .home-issue-feature-divider {
      width: clamp(280px, 36vw, 440px);
      max-width: 100%;
      height: 0;
      border-top: var(--rule);
      margin: 0 auto clamp(12px, 1.2vw, 16px);
    }
    .home-issue-feature-meta {
      font-family: var(--sans);
      font-size: var(--step--1);
      letter-spacing: 0.04em;
      color: var(--gray-dim);
      margin-bottom: 8px;
    }
    .home-issue-feature-title {
      font-family: var(--serif);
      font-size: var(--step-2);
      line-height: 1.2;
      letter-spacing: -0.005em;
      font-weight: 500;
      color: var(--dark);
      max-width: 720px;
      margin: 0 auto;
      transition: color 0.2s ease;
    }
    @media (hover: hover) and (pointer: fine) {
      .home-issue-feature:hover .home-issue-feature-title { color: var(--gray-dim); }
    }

    /* ═══════════════════════════════════════════════
       HOME — HERO ARTICLE (dark block below the issue feature)
       ═══════════════════════════════════════════════ */
    .home-hero {
      /* Light gray band separates the editorial-pick "hero" article from
         the white issue-feature block above (mobile) and the white article
         feed below. Distinct but unobtrusive — keeps the site light. */
      background: rgb(245,245,245);
      color: var(--dark);
      padding: calc(var(--header-height) + clamp(32px, 4vw, 64px)) var(--content-margin) var(--section-pad-y);
    }
    /* Desktop layout: hero article left + aside right */
    .home-hero-inner {
      display: flex;
      gap: clamp(80px, 9vw, 160px);
      align-items: flex-start;
      max-width: var(--container-max);
      margin: 0 auto;
    }
    .hero-article {
      cursor: pointer;
      flex: 1 1 0;
      min-width: 0;
      max-width: clamp(560px, 60vw, 880px);
      text-align: left;
    }
    .hero-article-img {
      width: 100%;
      aspect-ratio: 16/9;
      object-fit: cover;
      margin-bottom: clamp(16px, 1.6vw, 24px);
      background: rgb(245,245,245);
      /* Унифицированный image-hover transition (см. --hover-img-* tokens). */
      transition: transform var(--hover-img-duration) var(--hover-img-ease), filter var(--hover-img-duration) ease, opacity 0.5s ease;
      transform-origin: center center;
      will-change: transform;
    }
    .hero-article-img-wrap {
      overflow: hidden;
      margin-bottom: clamp(16px, 1.6vw, 24px);
    }
    .hero-article-img-wrap .hero-article-img { margin-bottom: 0; }
    @media (hover: hover) and (pointer: fine) {
      .hero-article:hover .hero-article-img {
        transform: scale(var(--hover-img-scale));
        filter: brightness(1.04);
      }
    }
    .hero-article-rubric {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      margin-bottom: 16px;
    }
    .hero-article-title {
      font-family: var(--serif);
      font-size: var(--step-4);
      line-height: 1.02;
      letter-spacing: -0.02em;
      font-weight: 500;
      color: var(--dark);
      margin-bottom: 24px;
    }
    .hero-article-author {
      font-family: var(--serif);
      font-size: var(--step-1);
      line-height: 1.35;
      color: var(--gray-dim);
    }
    .hero-article-author a {
      color: var(--gray-dim);
      border-bottom: 1px solid rgba(0,0,0,0.15);
      transition: border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .hero-article-author a:hover { border-bottom-color: rgba(0,0,0,0.5); }
    }
    .hero-article-desc {
      font-family: var(--serif);
      font-size: var(--step-0);
      line-height: 1.5;
      color: var(--gray-dim);
      margin-top: 20px;
      max-width: 60ch;
      display: -webkit-box;
      -webkit-line-clamp: 3;
      -webkit-box-orient: vertical;
      overflow: hidden;
    }
    /* Hero issue/year — superseded by the .card-plaque overlay on the
       hero image (matches the rest of the site). Hidden everywhere. */
    .hero-article-issue { display: none; }

    /* Home aside — wraps partner slot (optional) + current issue card. Right column on desktop. */
    .home-aside {
      flex: 0 0 clamp(220px, 26%, 320px);
      min-width: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      text-align: center;
    }
    /* Home sidebar — current issue */
    .home-sidebar-issue {
      min-width: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      text-align: center;
      width: 100%;
    }
    .sidebar-issue-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      margin-bottom: 20px;
    }
    .sidebar-issue-cover-wrap {
      position: relative;
      width: 70%;
      margin-bottom: 24px;
      cursor: pointer;
      perspective: 330px;
      overflow: visible;
      transform: scale(0.82) rotateX(8deg) rotate(-10deg);
      transform-origin: 50% 50%;
      transition: transform 0.3s var(--ease-out);
    }
    .sidebar-issue-cover-wrap.has-thumbhash::before {
      content: '';
      position: absolute;
      inset: 0;
      background-image: var(--thumb-bg);
      background-size: cover;
      background-position: center;
      z-index: 0;
    }
    .sidebar-issue-cover-wrap img {
      position: relative;
      z-index: 1;
    }
    @media (hover: hover) and (pointer: fine) {
    .sidebar-issue-cover-wrap:hover {
      transform: scale(1) translateY(0);
    }
    }
    img.sidebar-issue-cover {
      width: 100%;
      height: auto;
      display: block;
    }
    .sidebar-issue-cover-placeholder {
      width: 100%;
      aspect-ratio: 3/4;
      background: rgb(245,245,245);
      display: flex;
      align-items: center;
      justify-content: center;
      margin-bottom: 16px;
      cursor: pointer;
    }
    .sidebar-issue-cover-placeholder span {
      font-family: var(--serif);
      font-size: 48px;
      color: rgba(0,0,0,0.12);
      font-weight: 400;
    }
    .sidebar-issue-num {
      font-family: var(--sans);
      font-size: var(--step-0);
      font-weight: 500;
      letter-spacing: 0.02em;
      color: var(--dark);
      margin-bottom: 8px;
      cursor: pointer;
    }
    .sidebar-issue-title {
      font-family: var(--serif);
      font-size: var(--step-2);
      font-weight: 400;
      line-height: 1.2;
      color: var(--dark);
      cursor: pointer;
    }
    .sidebar-issue-year {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      margin-top: 8px;
    }

    /* ── Partner rotator in home hero sidebar ───
       Shown when MOCK_PARTNERS array is non-empty. When empty, issue card
       occupies the right column alone (old-site fallback). */
    .partner-slot {
      position: relative;
      background: var(--partner-tint);
      color: var(--dark);
      padding: clamp(18px, 1.8vw, 24px) clamp(20px, 2vw, 28px) clamp(28px, 2.4vw, 36px);
      margin-bottom: clamp(20px, 2vw, 28px);
      min-height: 180px;
    }
    .partner-slide {
      display: none;
    }
    .partner-slide.active {
      display: block;
      animation: partnerFadeIn 0.4s ease;
    }
    @keyframes partnerFadeIn {
      from { opacity: 0; }
      to { opacity: 1; }
    }
    .partner-slot-label {
      font-family: var(--sans);
      /* Partner label is the "ПАРТНЁР ВЫПУСКА" caps line. 10px was below
         the readable floor; lift to 11-13px fluid. */
      font-size: clamp(11px, 0.25vw + 10px, 13px);
      font-weight: 700;
      letter-spacing: 0.2em;
      text-transform: uppercase;
      color: var(--gray-dim);
      margin-bottom: 12px;
    }
    .partner-slot-name {
      font-family: var(--sans);
      font-size: clamp(15px, 1.2vw, 18px);
      font-weight: 600;
      color: var(--dark);
      margin-bottom: 10px;
      letter-spacing: 0.3px;
    }
    .partner-slot-body {
      font-family: var(--serif);
      font-size: clamp(14px, 1.05vw, 16px);
      line-height: 1.45;
      color: var(--dark);
      margin-bottom: 12px;
    }
    .partner-slot-cta {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      letter-spacing: 0.5px;
      color: var(--dark);
      border-bottom: 1px solid var(--dark);
      padding-bottom: 2px;
    }
    .partner-dots {
      position: absolute;
      bottom: 10px;
      left: 50%;
      transform: translateX(-50%);
      display: flex;
      gap: 6px;
    }
    .partner-dot {
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: rgba(0,0,0,0.18);
      cursor: pointer;
      transition: background 0.2s;
    }
    .partner-dot.active { background: rgba(0,0,0,0.6); }
    /* When partner-slot present, the sidebar issue card sits below it */
    .home-sidebar-issue.has-partner-above {
      border-left: none;
      padding-left: 0;
    }

    /* ── Issue TOC modal (replaces inline collapsible) ─── */
    .issue-toc-trigger {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--white);
      background: none;
      border: 1px solid rgba(255,255,255,0.4);
      padding: 10px 22px;
      cursor: pointer;
      margin-top: 18px;
      transition: background 0.2s, border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-toc-trigger:hover {
      background: rgba(255,255,255,0.08);
      border-color: var(--white);
    }
    }

    /* ═══════════════════════════════════════════════
       HOME — ARTICLES FEED
       ═══════════════════════════════════════════════ */
    .home-feed {
      padding: var(--section-pad-y) var(--content-margin) var(--section-pad-y);
    }
    .home-feed-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.14em;
      color: var(--gray-dim);
    }
    /* Header row above current-issue feed — label LEFT, link to full
       issue contents RIGHT. Mirrors the dark archive band header, gives
       a clear visual break between hero (one editorial pick) and the
       feed (rest of the current issue). */
    .home-feed-header {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 24px;
      margin-bottom: clamp(28px, 3vw, 48px);
      padding-bottom: 18px;
      border-bottom: 1px solid rgb(230,230,230);
    }
    .home-feed-issue-link {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      text-decoration: none;
      transition: color 0.25s var(--ease-out);
      white-space: nowrap;
    }
    @media (hover: hover) and (pointer: fine) {
    .home-feed-issue-link:hover { color: var(--dark); }
    }
    @media (max-width: 640px) {
      .home-feed-header { flex-direction: column; align-items: flex-start; gap: 8px; }
    }
    /* Archive band — dark section after the current-issue feed. The change in
       background colour signals "this is from the archive, not this issue".
       Card text colours invert so they read on the dark surface. */
    .home-feed--archive {
      background: var(--dark);
      color: rgb(220,220,220);
      padding: clamp(48px, 6vw, 96px) var(--content-margin);
      margin-top: clamp(32px, 4vw, 64px);
    }
    .home-archive-header {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 24px;
      margin-bottom: clamp(28px, 3vw, 48px);
      padding-bottom: 18px;
      border-bottom: 1px solid rgba(255,255,255,0.18);
    }
    .home-archive-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.14em;
      color: rgba(255,255,255,0.7);
    }
    .home-archive-all {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: rgba(255,255,255,0.7);
      text-decoration: none;
      transition: color 0.25s var(--ease-out);
    }
    @media (hover: hover) and (pointer: fine) {
    .home-archive-all:hover { color: var(--white); }
    }
    /* Card content needs light text on dark band. */
    .home-feed--archive .article-card-title { color: var(--white); }
    @media (hover: hover) and (pointer: fine) {
    .home-feed--archive .article-card:hover .article-card-title { color: rgba(255,255,255,0.7); }
    }
    .home-feed--archive .article-card-meta,
    .home-feed--archive .article-card-author,
    .home-feed--archive .article-card-issue {
      color: rgba(255,255,255,0.55);
    }
    .home-feed--archive .article-card-rubric { color: rgba(255,255,255,0.6); }
    .articles-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: var(--card-gap-y) var(--card-gap-x);
    }
    @media (max-width: 1023px) { .articles-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 640px)  { .articles-grid { grid-template-columns: 1fr; } }
    .article-card { cursor: pointer; }
    @media (hover: hover) and (pointer: fine) {
    .article-card:hover .article-card-title { color: var(--gray-dim); }
    }
    img.article-card-img {
      width: 100%;
      aspect-ratio: 4/3;
      object-fit: cover;
      background: rgb(240,240,240);
      margin-bottom: 20px;
      overflow: hidden;
      /* Унифицированный image-hover (см. --hover-img-* tokens, DESIGN.md
         раздел "Hover system / clickable images"). Spring 1.4 overshoot
         даёт pop без jitter; filter brightness — micro-cue что элемент
         кликабелен, без резкого скачка только-scale. */
      transition: transform var(--hover-img-duration) var(--hover-img-ease), filter var(--hover-img-duration) ease, opacity 0.5s ease;
      transform-origin: center center;
      will-change: transform;
    }
    /* Unified hover — desktop only. No lift of the whole card; soft
       micro-spring zoom + brightness on the cover. Wrapped in (hover:hover)
       to NOT fire on touch (iOS hover-emulation otherwise eats the click,
       see feedback_ios_hover_quirk memory). */
    .article-card { transition: none; }
    @media (hover: hover) and (pointer: fine) {
      .article-card:hover .article-card-img {
        transform: scale(var(--hover-img-scale));
        filter: brightness(1.04);
      }
    }
    .article-card-img-wrap {
      overflow: hidden;
      margin-bottom: 20px;
      /* Reserve the slot height before the <img> loads so nothing jumps when
         it does. Same ratio as img.article-card-img + the .has-thumbhash variant
         so layout matches whether or not a thumbhash was generated. */
      aspect-ratio: 4/3;
    }
    /* ThumbHash placeholder — set as background-image on the wrap. The image
       above is small (32×N PNG decoded from ~25-byte hash); we stretch it
       cover-style. Real img fades in over this. */
    .article-card-img-wrap.has-thumbhash {
      background-size: cover;
      background-position: center;
      background-repeat: no-repeat;
      aspect-ratio: 4/3;
    }
    .article-card-img-wrap.has-thumbhash .article-card-img {
      background: transparent;
    }
    /* Universal blur reveal — every cover wrapper with .has-thumbhash gets
       its real <img> faded in over the decoded blur. The img starts at
       opacity 0; .loaded (set by setupLazyImages on load) crossfades it in. */
    .has-thumbhash {
      background-size: cover;
      background-position: center;
      background-repeat: no-repeat;
    }
    .has-thumbhash > img,
    .has-thumbhash img.sidebar-issue-cover {
      opacity: 0;
      /* Keep transform/filter transitions from the unified hover-tokens
         (см. --hover-img-* + DESIGN.md / Hover-система). Раньше тут было
         только opacity 0.45s — это перебивало transform spring на card hover,
         и hover-zoom не работал на карточках с thumbhash (т.е. на большинстве). */
      transition: opacity 0.45s ease,
                  transform var(--hover-img-duration) var(--hover-img-ease),
                  filter var(--hover-img-duration) ease;
    }
    .has-thumbhash > img.loaded,
    .has-thumbhash img.sidebar-issue-cover.loaded { opacity: 1; }
    /* Archive issue cover — direct <img>, no wrapper. img has its own
       size constraints (max-width 72% of parent, max-height 100%) AND
       its own 3D transform. Hover/touch transforms applied on .archive-
       issue-cover img directly (see further below + touch override
       block at the top of stylesheet). */
    /* Placeholder card-image for articles without coverImage —
       same aspect as the real image, soft gray bg, typographic №N · YEAR. */
    .article-card-img-wrap--placeholder {
      aspect-ratio: 4/3;
      background: rgb(244,244,244);
      border: 1px solid rgb(228,228,228);
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 4px;
      font-family: var(--serif);
      color: rgb(170,170,170);
      transition: background 0.3s ease, color 0.3s ease;
    }
    @media (hover: hover) and (pointer: fine) {
    .article-card:hover .article-card-img-wrap--placeholder {
      background: rgb(20,20,20);
      color: rgb(220,220,220);
      border-color: var(--dark);
    }
    }
    .article-card-placeholder-num {
      font-size: var(--step-3);
      font-weight: 500;
      letter-spacing: -0.01em;
      line-height: 1;
    }
    .article-card-placeholder-year {
      font-family: var(--sans);
      font-size: var(--step--1);
      letter-spacing: 0.08em;
      color: rgb(180,180,180);
    }
    .article-card-rubric {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      margin-bottom: 8px;
    }
    .article-card-title {
      font-family: var(--serif);
      font-size: var(--step-2);
      line-height: 1.2;
      font-weight: 500;
      color: var(--dark);
      letter-spacing: -0.005em;
      margin-bottom: 12px;
      transition: color 0.2s;
    }
    .article-card-author {
      font-family: var(--serif);
      font-size: var(--step-1);
      line-height: 1.35;
      color: var(--dark);
    }
    /* Old below-title issue line — superseded by the plaque-on-image
       pattern below. Hidden everywhere; the .card-plaque element shows
       the same info ON the image instead, saving vertical space and
       making №+year discoverable on hover (desktop) / always (touch). */
    .article-card-issue { display: none; }
    .article-card-rubric { display: none; }

    /* ═══════════════════════════════════════════════
       UNIVERSAL CARD PLAQUE — №N · YYYY overlay on image
       Sits absolute bottom-right of the cover img wrapper. Inherited
       from the old site: tiny sans caps, semi-opaque dark backdrop,
       hairline aesthetic — no rounded corners, no shadow.
       Desktop: fades in on card hover. Touch: always visible.
       Works inside .article-card-img-wrap, .related-card-img-wrap,
       .hero-article-img-wrap (one selector pattern for everything).
       ═══════════════════════════════════════════════ */
    .article-card-img-wrap,
    .related-card-img-wrap,
    .hero-article-img-wrap {
      position: relative; /* anchor for the absolute plaque */
    }
    .card-plaque {
      position: absolute;
      left: 0;
      bottom: 0;
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: var(--dark);
      /* Light backdrop, dark text — matches old-site pattern. Slightly
         more opaque to keep contrast without backdrop-filter blur, which
         was a known fps killer on mobile (29 elements with blur active
         during scroll, см. perf audit). */
      background: rgba(255, 255, 255, 0.94);
      padding: 6px 10px;
      line-height: 1;
      pointer-events: none;
      opacity: 0;
      transform: translateY(2px);
      transition: opacity 0.28s var(--ease-out), transform 0.28s var(--ease-out);
      z-index: 3;
      white-space: nowrap;
    }
    @media (hover: hover) and (pointer: fine) {
    .article-card:hover .card-plaque,
    .related-card:hover .card-plaque,
    .hero-article:hover .card-plaque {
      opacity: 1;
      transform: translateY(0);
    }
    }
    /* Touch devices: no hover state to reveal info → show it permanently. */
    @media (hover: none), (pointer: coarse) {
      .card-plaque {
        opacity: 1;
        transform: none;
      }
    }

    .load-more-wrap {
      text-align: center;
      padding: 48px 0 20px;
    }

    /* ── Infinite scroll sentinel ── */
    .infinite-sentinel {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 10px;
      min-height: 80px;
      padding: 64px 0 32px;
      pointer-events: none;
    }
    .infinite-sentinel[data-status="done"] { display: none; }
    .infinite-sentinel-dot {
      width: 6px;
      height: 6px;
      background: var(--gray-secondary);
      border-radius: 50%;
      opacity: 0.4;
      animation: sentinelPulse 1.4s infinite ease-in-out both;
    }
    .infinite-sentinel-dot:nth-child(1) { animation-delay: -0.32s; }
    .infinite-sentinel-dot:nth-child(2) { animation-delay: -0.16s; }
    .infinite-sentinel[data-status="idle"] .infinite-sentinel-dot {
      animation-play-state: paused;
      opacity: 0.15;
    }
    @keyframes sentinelPulse {
      0%, 80%, 100% { opacity: 0.2; transform: scale(0.8); }
      40% { opacity: 0.9; transform: scale(1); }
    }
    .load-more-btn {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1.2px;
      color: var(--dark);
      background: none;
      border: var(--rule-strong);
      padding: 0 32px;
      line-height: 43px;
      cursor: pointer;
      transition: background 0.2s ease, color 0.2s ease;
    }
    @media (hover: hover) and (pointer: fine) {
    .load-more-btn:hover { background: var(--dark); color: var(--white); }
    }
    .load-more-btn:disabled { opacity: 0.3; cursor: default; }
    @media (hover: hover) and (pointer: fine) {
    .load-more-btn:disabled:hover { background: none; color: var(--dark); }
    }

    /* ═══════════════════════════════════════════════
       NEWSLETTER SECTION
       ═══════════════════════════════════════════════ */
    .newsletter-section {
      background: rgb(245,245,245);
      padding: clamp(36px, 3.8vw, 56px) var(--content-margin);
      text-align: center;
    }
    .newsletter-title {
      font-family: var(--serif);
      font-size: clamp(26px, 2.2vw, 32px);
      font-weight: 400;
      color: var(--dark);
      margin-bottom: 12px;
    }
    .newsletter-subtitle {
      font-family: var(--sans);
      font-size: 16px;
      color: var(--gray-dim);
      margin-bottom: 28px;
    }
    .newsletter-form {
      display: flex;
      max-width: 460px;
      margin: 0 auto;
      gap: 0;
    }
    .newsletter-input {
      flex: 1;
      font-family: var(--sans);
      font-size: var(--step-0);
      padding: 0 18px;
      line-height: 46px;
      height: 46px;
      border: 1px solid rgb(200,200,200);
      border-right: none;
      background: var(--white);
      outline: none;
      transition: border-color 0.3s ease;
      /* iOS Safari/Chrome ставят border-radius + inset shadow по умолчанию
         через native -webkit-appearance. Сайт везде sharp corners. */
      -webkit-appearance: none;
      appearance: none;
      border-radius: 0;
      color: var(--dark);
    }
    .newsletter-input:focus { border-color: var(--dark); }
    .newsletter-submit {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1.2px;
      background: var(--dark);
      color: var(--white);
      border: var(--rule-strong);
      padding: 0 24px;
      line-height: 46px;
      height: 46px;
      cursor: pointer;
      transition: background 0.2s;
      white-space: nowrap;
    }
    @media (hover: hover) and (pointer: fine) {
    .newsletter-submit:hover { background: var(--black); }
    }
    .newsletter-message {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      margin-top: 12px;
    }

    /* ═══════════════════════════════════════════════
       ARTICLE PAGE
       ═══════════════════════════════════════════════ */
    /* nd-hero: grid layout — text LEFT, cover RIGHT */
    .nd-hero {
      background: var(--dark);
      color: var(--white);
      padding-top: var(--header-height);
      padding-bottom: clamp(28px, 3vw, 48px);
    }
    .nd-hero-grid {
      display: grid;
      /* Bigger cover slot: tilt the ratio in favour of the image.
         Was 1.25/0.75 — cover felt small relative to title. 1fr/1fr
         gives the cover ~50% of the hero width, balancing visual mass
         with the title block. Vertical-cover override shifts back to
         text-heavy because portrait covers occupy less optical area. */
      grid-template-columns: 1fr 1fr;
      align-items: center;
      min-height: min(800px, calc(100vh - var(--header-height) - 20px));
      max-width: var(--measure-wide);
      margin: 0 auto;
      gap: clamp(40px, 5vw, 80px);
    }
    .nd-hero-grid.no-cover {
      grid-template-columns: 1fr;
      min-height: 420px; /* no cover → keep compact */
    }
    .nd-hero-grid.vertical-cover { grid-template-columns: 1.15fr 0.85fr; }
    .nd-hero-text {
      padding: clamp(48px,5vw,80px) clamp(24px,3vw,48px);
      display: flex;
      flex-direction: column;
      justify-content: center;
      text-align: center;
    }
    .nd-hero-cover {
      display: flex;
      flex-direction: column;
      justify-content: center;
    }
    .nd-hero-rubric {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      margin-bottom: 20px;
    }
    .nd-hero-title {
      font-family: var(--serif);
      font-size: var(--step-4);
      font-weight: 500;
      line-height: 1.06;
      letter-spacing: -0.02em;
      margin-bottom: 24px;
      hyphens: auto;
      overflow-wrap: break-word;
    }
    .nd-hero-authors {
      font-family: var(--serif);
      font-size: var(--step-1);
      line-height: 1.35;
      color: var(--gray-secondary);
    }
    .nd-hero-authors a {
      color: var(--gray-secondary);
      border-bottom: 1px solid rgba(255,255,255,0.2);
      cursor: pointer;
      transition: border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .nd-hero-authors a:hover { border-bottom-color: rgba(255,255,255,0.6); }
    }
    .nd-hero-issue {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      margin-top: 24px;
      text-transform: uppercase;
      letter-spacing: 0.12em;
    }
    .nd-hero-issue a {
      color: var(--gray-dim);
      border-bottom: 1px solid rgba(255,255,255,0.15);
      transition: border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .nd-hero-issue a:hover { border-bottom-color: rgba(255,255,255,0.5); }
    }
    .nd-hero-cover {
      position: relative;
      overflow: visible;
      padding: 3%;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }
    .nd-hero-cover figure {
      margin: 0 auto;
      max-width: 100%;
    }
    /* Inner blur-wrapper. РАНЬШЕ был `display: inline-block` чтобы
       обхватить точные границы img. Но inline-block shrinks к INTRINSIC
       размеру img (HTML width=""), а thumbhash placeholder ставит маленькие
       intrinsic димы (typically 100-200px). Img внутри `width:100%` тогда
       = 100% от 200px = маленькая. ВСЕГДА используем block + width:100%
       чтобы контейнер заполнял свой slot, img стретчится с ним. См.
       feedback_hero_cover_size_3rd_regression memory.
       Halo concern — img снаружи box не вылазит, потому что img.width=100%
       контейнера → bounds совпадают. */
    .nd-hero-cover-blur {
      position: relative;
      display: block;
      width: 100%;
      max-width: 100%;
      line-height: 0;
    }
    .nd-hero-cover-blur img { width: 100%; display: block; }
    /* Thumbhash blur placeholder ВЫКЛЮЧЕН глобально (был только на мобиле).
       Причина: контейнер .nd-hero-cover-blur — block+width:100% (fix для
       hero-size regression, см. feedback_hero_cover_size_3rd_regression).
       img внутри использует object-fit:contain → при portrait-обложке в
       wide slot (или vice versa) blur ::before с inset:0 покрывает ВЕСЬ
       container, включая letterbox-зоны вокруг img → видимое halo за
       границами картинки (Roman 2026-05-27 скрин со статьёй №620
       Ханс-Ульрих Обрист). Картинка появляется без placeholder при
       загрузке — мгновенный flash приемлемее, чем постоянный halo. */
    .nd-hero-cover-blur.has-thumbhash::before {
      content: '';
      position: absolute;
      inset: 0;
      background-image: var(--thumb-bg);
      background-size: cover;
      background-position: center;
      z-index: 0;
      display: none;
    }
    .nd-hero-cover img {
      /* Заполняет slot целиком. Раньше `width: auto` показывал картинку
         в natural pixel-size — мелкие оригиналы (595×420 и подобные)
         выглядели крошечно на десктопе в 550px slot. Теперь stretch до
         ширины контейнера, max-height ограничивает по высоте чтобы
         portrait-обложки не вытягивали hero за viewport. */
      width: 100%;
      max-width: 100%;
      height: auto;
      max-height: min(800px, calc(100vh - var(--header-height) - 40px));
      object-fit: contain;
      display: block;
      margin: 0 auto;
      filter: grayscale(100%);
      position: relative;
      z-index: 1;
      opacity: 0;
      transition: opacity 0.45s ease;
    }
    .nd-hero-cover img.loaded { opacity: 1; }
    /* Fallback: if JS doesn't set .loaded (no thumbhash path), still show. */
    .nd-hero-cover:not(:has(.has-thumbhash)) img { opacity: 1; }
    .nd-hero-grid.vertical-cover .nd-hero-cover img {
      max-height: min(680px, calc(100vh - var(--header-height) - 60px));
    }
    .nd-hero-cover figcaption {
      font-family: var(--sans);
      /* Уменьшен на ступень типографической шкалы (Roman 2026-05-26):
         подпись подчинённая, не должна конкурировать с самой картинкой. */
      font-size: var(--step--2);
      font-style: italic;
      color: rgba(255,255,255,0.55);
      padding: 8px 0 0;
      line-height: 1.4;
      margin: 0 auto;
      max-width: 480px; /* предотвращает растягивание длинной подписи на всю ширину */
    }
    @media (max-width: 768px) {
      .nd-hero-grid, .nd-hero-grid.vertical-cover {
        grid-template-columns: 1fr;
      }
      /* .nd-hero-cover-blur уже block+width:100% глобально (см. main rule).
         На мобиле дополнительно ограничиваем высоту img — 60vh чтобы
         portrait-обложки не вытягивали hero за пол-экрана. */
      .nd-hero-cover img { max-height: 60vh; object-fit: contain; }
      /* Убираем 3% padding на мобиле — тёмные полосы родительского
         hero-фона по бокам картинки (Roman 2026-05-26 скрин с красными X
         слева и справа от обложки). На десктопе padding остаётся для
         editorial breathing room в split-layout 1fr/1fr. */
      .nd-hero-cover { padding: 0; }
      /* (Thumbhash blur ::before выключен глобально, см. main rule.) */
    }
    /* Article metadata block */
    .article-meta-block {
      max-width: var(--content-width);
      margin: 0 auto;
      padding: 32px 24px;
      border-top: var(--rule);
      font-family: var(--sans);
      font-size: var(--step--1);
      line-height: 1.65;
      color: var(--gray-dim);
      display: flex;
      flex-wrap: wrap;
      gap: 12px 32px;
    }
    .article-meta-row {
      display: inline;
    }
    .article-meta-label {
      color: var(--dark);
      font-weight: 500;
      margin-right: 4px;
    }
    .article-meta-row a {
      color: var(--gray-dim);
      border-bottom: var(--rule);
      transition: border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .article-meta-row a:hover {
      border-bottom-color: var(--dark);
    }
    }

    /* Abstract block */
    .article-abstract {
      max-width: var(--content-width);
      margin: 0 auto;
      padding: 32px 24px 0;
      font-family: var(--serif);
      font-size: var(--step-0);
      line-height: 1.5;
      color: var(--gray-dim);
      font-style: italic;
    }

    /* Author bio block — sidebar on desktop, inline on mobile */
    .article-author-bio {
      font-family: var(--sans);
      font-size: var(--step--1);
      line-height: 1.55;
      color: var(--gray-dim);
      font-style: italic;
    }
    /* Имя автора — полужирным курсивом, на отдельной строке над текстом
       био (Roman 2026-06-09). Цвет и курсив как у тела, отличается весом. */
    .article-author-bio-name {
      display: block;
      font-weight: 600;
      font-style: italic;
      color: var(--gray-dim);
      margin-bottom: 4px;
    }
    /* Desktop: float right as margin note alongside article body. Breakpoint 1320px,
       NOT 1100: the float pulls itself 260px into the right gutter (margin-right
       -(220+40)). Gutter = (vw - content-width)/2, so it fits without clipping the
       viewport only when vw ≥ 760 + 2·260 ≈ 1280 (1320 adds breathing room). Below
       that the bio would run off the right edge — «биосправка обрезана» (фидбэк
       редакции 2026-07, Гранильщиков). Under 1320 → inline block above body. */
    @media (min-width: 1320px) {
      .article-author-bio {
        float: right;
        clear: right;
        width: 220px;
        margin: 0 calc(-1 * (220px + 40px)) 24px 24px;
        padding: 0;
        position: relative;
        border-left: 2px solid var(--gray-border);
        padding-left: 16px;
      }
    }
    /* Tablet / narrow desktop: inline block above body */
    @media (max-width: 1319px) {
      .article-author-bio {
        max-width: var(--content-width);
        margin: 0.5em auto 1.5em;
        padding: 0 24px;
      }
    }

    /* Desktop: article body needs overflow visible for bio sidebar float */
    @media (min-width: 1320px) {
      .article-body {
        overflow: visible;
      }
    }

    .article-feature-img {
      max-width: 1100px;
      margin: 0 auto;
    }
    .article-feature-img img {
      width: 100%;
      display: block;
    }
    .article-feature-img figcaption {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      padding: 16px 24px;
      line-height: 1.5;
    }

    .article-body {
      max-width: var(--content-width);
      margin: 0 auto;
      padding: var(--section-pad-y) 24px var(--section-pad-y);
      font-family: var(--serif);
      font-size: var(--body-size);
      line-height: var(--body-lh);
      color: var(--dark);
    }
    .article-body p {
      margin-bottom: 0;
      text-indent: 1.5em;
    }
    /* No indent only for genuine section-openers: article start, after headings,
       after the epigraph block, footnotes. НЕ после figure/blockquote — в этом
       журнале иллюстрации и цитаты стоят ВНУТРИ потока, и следующий абзац это
       продолжение, поэтому отступ первой строки должен сохраняться (иначе
       непонятно: новый абзац или иллюстрация «съела» отступ — фидбэк редакции
       2026-06). */
    .article-body > .article-author-bio + p,
    .article-body > p:first-child,
    .article-body h1 + p,
    .article-body h2 + p,
    .article-body h3 + p,
    .article-body h4 + p,
    .article-body h5 + p,
    .article-body p.section-break + p,
    .article-body .epigraph-author + p,
    .article-body .footnotes-heading + ol p,
    .article-body ol.footnotes p {
      text-indent: 0;
    }
    /* Авторское выравнивание вправо (исходный <p align="right">). Уважаем
       выравнивание, но БЕЗ эпиграфных padding-left:40% / больших margin / forced
       italic: иначе многоабзацный правый блок (цитата + атрибуция) разносит на
       огромные расстояния, а роман-подписи становятся курсивом. Курсив берётся
       только из <em> в исходнике. Нормализованные .epigraph (JS) — отдельно ниже.
       (art_2617 «огромные расстояния перед подписями», 2026-06) */
    .article-body p[align="right"] {
      text-align: right; text-indent: 0;
    }
    /* Правый эпиграф-блок (normalizeRightAlignedBlocks): строки одной цитаты/
       атрибуции — ПЛОТНО, без 40%-отступа; курсив только из <em>. Зазоры —
       только сверху/снизу блока (.rq-first/.rq-last) и между эпиграфами (.rq-sep,
       на месте авторских пустых абзацев). См. art_2617. */
    .article-body p.rq {
      text-align: right; text-indent: 0; font-style: normal;
      margin: 0.28em 0; line-height: 1.4;
    }
    .article-body p.rq em, .article-body p.rq i { font-style: italic; }
    .article-body p.rq-first { margin-top: 1.5em; }
    .article-body p.rq-last { margin-bottom: 1.6em; }
    .article-body p.rq-sep { margin-top: 1.35em; }
    /* Автор/источник эпиграфа — обычным начертанием (400), НЕ жирным: атрибуция
       не должна перевешивать саму цитату. Arno подгружается только в 400
       (arno-400-normal.woff2), поэтому любой вес >400 браузер синтезирует в
       faux-bold — 500 визуально = жирный. Держим 400, отличие от цитаты даёт
       курсив самой цитаты (<em>). См. art_2616. */
    .article-body p.rq-author { font-weight: 400; }
    .article-body p.epigraph,
    .article-body .epigraph-block {
      font-style: italic; font-size: 0.92em; text-align: right;
      padding-left: 40%; margin: 1.5em 0 0.2em; line-height: 1.4; text-indent: 0;
    }
    .article-body .epigraph-author {
      text-align: right; font-size: 0.85em; font-weight: 500;
      margin: 0 0 1.5em; text-indent: 0;
    }
    /* Author signature removed — name already in hero */
    /* Exhibition info block — centered metadata before article text */
    .article-body .exhibition-info {
      text-align: center;
      margin: 0 0 2em;
      padding: 1.5em 0;
      border-bottom: 1px solid var(--gray-border, #ddd);
    }
    .article-body .exhibition-info p {
      margin-bottom: 0.15em;   /* «без интервала» — плотный блок выходных данных выставки */
      text-indent: 0;
      font-size: 0.92em;
      line-height: 1.4;
    }
    .article-body h2, .article-body h3 {
      font-family: var(--serif);
      font-weight: 400;
      margin: 2.5em 0 1em;
    }
    .article-body h2 { font-size: clamp(24px, 2.2vw, 30px); line-height: 1.25; }
    /* Подзаголовок должен читаться как подзаголовок: серифом того же размера, что и
       текст, он сливался с абзацем («выглядит как ошибка» — фидбэк редакции). Arno
       грузится в реальных 700, поэтому делаем ПОЛУЖИРНЫМ — единый вид для всех
       подзаголовков, как во вступлении главреда. */
    .article-body h3 { font-size: clamp(22px, 0.6vw + 19.5px, 26px); line-height: 1.3; font-weight: 700; }
    .article-body h4, .article-body h5 { font-weight: 600; margin: 2em 0 0.8em; }
    /* ── Article images: unified rules ──
       All article images are inside <figure> elements.
       Two types: regular (centered) and .figure__side (float-left, extends beyond text block).
       Matches old site layout: figure__side has float:left with negative margin. */

    /* width: fit-content вместо display:table — фигура ужимается до картинки и
       центрируется (margin auto). table-caption у подписи давал кросс-браузерный
       баг (Safari): подпись бралась по ширине КОЛОНКИ, а не картинки, и вылезала
       за края узкого изображения. Теперь подпись (ниже) держится строго по
       картинке через width:0/min-width:100%. (фидбэк редакции 2026-06) */
    /* Скелетон для ленивых картинок в теле: пока картинка не загрузилась — на её
       месте пульсирующая заглушка (а не пустота), высота зарезервирована, меньше
       «прыжков» при прокрутке. Снимается классом .loaded (вешает setupLazyImages).
       Обложка в шапке использует отдельный thumbhash-blur. */
    .article-body figure:has(img:not(.loaded)) {
      min-height: 180px;
      background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
      background-size: 800px 100%;
      animation: skeletonShimmer 1.6s linear infinite;
    }
    .article-body figure {
      /* Centered figures need real breathing room above — paragraphs use red-line
         indent with no inter-paragraph space, so a small top margin reads as the
         text touching the image (фидбэк Романа 2026-06-30). */
      margin: 30px auto 26px;
      text-align: center;
      position: relative;
      /* clear LEFT only (side images float left). NOT `both` — a long author bio
         floats in the right margin, and `clear: both` dropped every figure below
         it, leaving a dead gap. clear:left lets figures sit alongside the bio
         while still never overlapping a left side-image (фидбэк Романа 2026-06-30). */
      clear: left;
      width: fit-content;
      max-width: 100%;
      /* Floor so a fit-content figure doesn't collapse to the caption's longest word
         while its lazy image is still loading (image has no width yet → «совсем узко»,
         потом расширяется). Loaded images wider than this keep their natural width. */
      min-width: min(340px, 100%);
    }
    /* Section break (dinkus «* * *») — centered, no red-line indent, with air above
       and below. Typographic norm for an asterism scene-break. */
    .article-body p.section-break {
      text-align: center;
      text-indent: 0;
      margin: 2em 0;
      letter-spacing: 0.5em;
      color: var(--gray-dim);
      font-weight: 400;
    }
    /* Dialogue/interview turn — a new speaker starts a new block, so отбиваем сверху
       (no red-line indent on the labelled line). */
    .article-body p.dialogue-turn {
      margin-top: 1.6em;
      text-indent: 0;
    }
    /* Consecutive frames of one captioned series (caption shown once on the first) — pull
       the twins up so the sequence reads as a group. ONLY when the frames are truly
       adjacent (no text between): the placement engine normally puts a paragraph between
       figures, and there a series frame must keep the full top margin, or the text reads
       as touching the image (фидбэк Романа 2026-06-30). */
    .article-body figure + figure.figure--series { margin-top: -6px; }
    .article-body figure img {
      width: auto !important;
      height: auto !important;
      max-width: 100%;
      display: block;
      margin: 0 auto;
      object-fit: contain;
      filter: grayscale(100%);
    }
    .article-body figcaption {
      font-family: var(--sans);
      /* Fluid figcaption: 15px small phones → 17px tablet+. Italic + gray. */
      font-size: clamp(15px, 0.4vw + 13.5px, 17px);
      color: var(--gray-dim);
      padding-top: 12px; line-height: 1.45; font-style: italic;
      text-align: left;
      /* width:0 → подпись не расширяет fit-content фигуру (та держится по
         картинке); min-width:100% → подпись ровно по ширине картинки и переносится
         по ней. Не table-caption — кросс-браузерно надёжнее (Safari). */
      display: block; width: 0; min-width: 100%; box-sizing: border-box;
    }
    .article-body figcaption br,
    .article-feature-img figcaption br {
      display: none;
    }

    /* Artist page / visual spreads — full-width images */
    .article-body.artist-page {
      max-width: 860px;
    }
    .article-body.artist-page figure {
      margin: 0 0 4px;
      padding: 0;
      line-height: 0;
    }
    .article-body.artist-page figure img {
      width: 100% !important;
      max-width: 100%;
    }
    .article-body.artist-page p {
      margin: 16px 0;
    }

    /* Side-float figure: extends left beyond text block.
       Center of image aligns with left edge of text column.
       Uses % of parent (article-body width) for true responsiveness.
       width = 40%, margin-left = -20% (half of width) → center at left edge. */
    .article-body figure.figure__side {
      float: left;
      clear: left;
      width: 40%;
      margin: 0.5em 5% 0.5em -20%;
      text-align: left;
      min-width: 0;
    }
    .article-body figure.figure__side img {
      width: 100% !important;
      max-width: 100%;
    }
    .article-body figure.figure__side figcaption {
      padding-top: 10px;
      max-width: 100%;
    }

    /* Телефон в ЛАНДШАФТЕ. Мобильный full-width reset фигур живёт в
       @media (max-width: 768px), но landscape-телефоны шире (~812-932px) → туда
       НЕ попадают и получают десктопный side-float с margin-left:-20%, который
       уезжает за левый край вьюпорта и обрезает картинку вместе с подписью
       (фидбэк редакции 2026-06). Ловим короткий touch-вьюпорт и повторяем
       full-width reset. max-height:600 отсекает планшеты (их landscape-высота
       744px+). */
    @media (orientation: landscape) and (max-height: 600px) and (pointer: coarse) {
      .article-body figure,
      .article-body figure.figure__side {
        float: none !important;
        clear: both;
        width: auto !important;
        max-width: 100%;
        margin: 2em auto !important;
        display: block;
        text-align: center;
      }
      .article-body figure img,
      .article-body figure.figure__side img {
        width: 100% !important;
        max-width: 100%;
        height: auto !important;
        margin: 0 auto;
        display: block;
      }
      .article-body figcaption,
      .article-body figure.figure__side figcaption {
        display: block !important;
        text-align: left;
        width: 100%;
        max-width: 100%;
        margin: 0;
        padding: 14px 0 0;
      }
    }

    /* ── Article body: unified typography rules ── */
    .article-body::after { content: ''; display: table; clear: both; }

    /* ── Translation credit (Перевод с... Имени Фамилии) ── */
    .translation-credit {
      margin-top: 2.5em;
      padding-top: 1em;
      border-top: 1px solid var(--gray-border, #e2e2e2);
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 400;
      letter-spacing: 0.03em;
      color: var(--gray-dim, #8b8b8b);
      text-transform: none;
    }
    .translation-credit strong { font-weight: 400; }
    /* ── Editorial credit (Материал подготовил..., Беседу вёл...) ── */
    .editorial-credit {
      margin-top: 2.5em;
      padding-top: 1em;
      border-top: 1px solid var(--gray-border, #e2e2e2);
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 400;
      letter-spacing: 0.03em;
      color: var(--gray-dim, #8b8b8b);
      text-transform: none;
    }
    .editorial-credit strong { font-weight: 500; letter-spacing: 0.02em; }

    /* Section number dividers (author's structural numbering: "1.", "2.", etc.) */
    .section-number {
      text-align: center;
      font-family: var(--sans);
      font-size: var(--step-0);
      font-weight: 600;
      letter-spacing: 0.05em;
      color: var(--gray-dim, #8b8b8b);
      margin: 2.5em 0 1.5em;
      padding: 0;
    }

    /* Comics viewer */
    .comics-viewer {
      max-width: 860px;
      margin: 0 auto 60px;
      padding: 0 20px;
    }
    .comics-panel {
      margin: 0 0 4px;
      padding: 0;
      line-height: 0;
    }
    .comics-panel img {
      display: block;
      width: 100%;
      height: auto;
      cursor: zoom-in;
      filter: grayscale(100%);
    }
    .comics-panel figcaption {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      line-height: 1.4;
      padding: 6px 0;
      text-align: center;
    }
    .comics-page-count {
      text-align: center;
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      margin-top: 20px;
      letter-spacing: 0.05em;
    }

    /* Links */
    .article-body a {
      color: inherit;
      text-decoration: underline;
      text-underline-offset: 2px;
    }
    @media (hover: hover) and (pointer: fine) {
    .article-body a:hover { color: var(--gray-dim); }
    }

    /* Inline styles */
    .article-body em { font-style: italic; }
    .article-body strong, .article-body b { font-weight: 700; }

    /* Superscript footnote references in text */
    .article-body sup {
      font-family: var(--sans);
      font-size: 0.65em;
      line-height: 0;
      vertical-align: super;
    }
    /* Footnote marker = серенький прямоугольничек с цифрой. Бокс ЕДИНСТВЕННЫЙ и
       живёт на самом <a> с цифрой (детерминированно рисуется во всех браузерах).
       `sup.fn-ref` бокс УБРАН (ниже) — иначе бокс на sup + бокс на <a> = двойной
       (VM 2026-06). Пустые wrapper-<a> вложенных CMS-маркеров удаляются в JS
       (postRender), без `:has()`. Те же значения, что у номера в списке (0.07 /
       radius 3px / hover 0.14) — вид одинаковый в тексте и в списке. */
    .article-body sup a {
      text-decoration: none;
      display: inline-block;
      background: rgba(0,0,0,0.07);
      color: #555;
      font-weight: 500;
      border-radius: 3px;
      padding: 0.05em 0.35em;
      line-height: 1.2;
      cursor: pointer;
      /* inline-block наследует first-line text-indent абзаца и применяет к
         своему контенту → бокс растягивался до ~57px. Сброс обязателен. */
      text-indent: 0;
    }
    @media (hover: hover) and (pointer: fine) {
    .article-body sup a:hover { background: rgba(0,0,0,0.14); color: var(--dark); }
    }

    /* Lists (general) */
    .article-body ul {
      margin: 18px 0;
      padding-left: 28px;
      list-style: disc;
    }
    .article-body li { margin-bottom: 8px; }

    /* ── Нумерованные списки в тексте (author-numbered «N.» абзацы) ──
       Правило вёрстки: блочное выравнивание по левому полю, БЕЗ висячего отступа.
       Номер уже отбивает пункт; переносы выключены в один уровень с номером (по
       левому краю) — читается ровнее, чем когда перенос уходит вправо под текст.
       Красная строка отменена (text-indent:0), пункты разделяет вертикальный
       воздух. Ставится JS-проходом styleNumberedListItems() на прогоны ≥3 подряд
       идущих «N.»-абзацев; тот же класс кладёт пайплайн (article-normalize) при
       импорте. Держим строку ниже правил text-indent:0 для section-openers
       (2403–2415), чтобы отступ первой строки не появлялся у первого пункта. */
    .article-body p.num-item {
      text-indent: 0;
      margin: 0.34em 0;         /* дыхание между пунктами вместо красной строки */
    }
    .article-body p.num-item--first { margin-top: 1.15em; }
    .article-body p.num-item--last { margin-bottom: 1.15em; }
    /* Абзац сразу после списка — обычная красная строка (не продолжение пункта). */
    .article-body p.num-item--last + p:not(.num-item) { text-indent: 1.5em; }

    /* ── Footnotes / endnotes section (unified) ──
       CMS uses: <ol><li data-footnote-id><a>^</a> <cite>text</cite></li></ol>
       Some old articles use <p> with "1. text" or "1) text" format.
       All get same visual treatment: smaller font, gray, no list markers. */
    .article-body ol {
      margin: 18px 0;
      padding-left: 0;
      list-style: none;
      counter-reset: footnote-counter;
    }
    .article-body ol li {
      counter-increment: footnote-counter;
      font-family: var(--sans);
      /* step--1 (16–18px), not step-0 — примечания должны быть ЗАМЕТНО мельче
         основного текста (--body-size 21–24px), как в печатном журнале. step-0
         давал лишь ~12% разницы и совпадал с body на части ширин (VM 2026-06). */
      font-size: var(--step--1);
      line-height: 1.55;
      color: #555;
      margin-bottom: 10px;
      padding-left: 36px;
      position: relative;
    }
    /* Hide CSS counter when fn-back link provides the number, or CMS already numbers.
       Also hide for .fn-numbered class (set by JS for broad browser compat) */
    .article-body ol li:has(.fn-back)::before { content: none; }
    .article-body ol li[data-footnote-id]::before { content: none; }
    .article-body ol li.fn-numbered::before { content: none; }
    .article-body ol li::before {
      content: counter(footnote-counter);
      position: absolute;
      left: 0;
      top: 0.15em;
      display: inline-block;
      background: rgba(0,0,0,0.07);
      color: #555;
      font-family: var(--sans);
      font-weight: 500;
      font-size: 0.82em;
      border-radius: 3px;
      padding: 0.05em 0.4em;
      min-width: 1.5em;
      text-align: center;
    }
    /* Links in footnotes: don't break mid-word, wrap as whole units */
    .article-body ol li a[href] {
      word-break: break-all;
      overflow-wrap: anywhere;
    }
    .article-body .fn-back {
      /* Same серый прямоугольничек as the in-text marker and the ::before number */
      background: rgba(0,0,0,0.07);
      color: #555;
      text-decoration: none;
      font-family: var(--sans);
      font-weight: 500;
      font-size: 0.82em;
      border-radius: 3px;
      padding: 0.05em 0.4em;
      min-width: 1.5em;
      text-align: center;
      position: absolute;
      left: 0;
      top: 0.15em;
    }
    .article-body .fn-back::after { content: none; }
    @media (hover: hover) and (pointer: fine) {
    .article-body .fn-back:hover { background: rgba(0,0,0,0.14); color: var(--dark); }
    }
    /* Hide the CMS "^" back-link in footnotes */
    .article-body ol li > a[href*="footnote-marker"],
    .article-body ol li > a[href*="_ftnref"] {
      display: none !important;
    }
    /* Citation text inside footnotes */
    .article-body ol li cite {
      font-style: normal;
      font-family: var(--sans);
    }
    /* Footnotes section heading */
    .article-body .footnotes-heading,
    .footnotes-heading {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1px;
      color: var(--gray-dim);
      margin: 2em 0 0.5em;
    }
    /* Clickable footnote list items — номер-прямоугольничек темнеет на hover,
       pointer на всю строку. Тот же вид и поведение, что у маркера в тексте. */
    .article-body ol li[data-fn-clickable] { cursor: pointer; }
    .article-body ol li[data-fn-clickable]::before,
    .article-body ol li[data-fn-clickable] .fn-back { cursor: pointer; }
    @media (hover: hover) and (pointer: fine) {
    .article-body ol li[data-fn-clickable]:hover::before,
    .article-body ol li[data-fn-clickable]:hover .fn-back {
      background: rgba(0,0,0,0.14);
      color: var(--dark);
    }
    }

    /* Blockquotes */
    .article-body blockquote {
      border-left: 2px solid var(--gray-border, rgb(200,200,200));
      padding-left: 1.5em;
      margin: 2em 0;
      font-style: italic;
      color: var(--gray-dim);
    }

    /* Horizontal rules */
    .article-body hr {
      border: none;
      border-top: 1px solid var(--gray-border, #ddd);
      margin: 2.5em 0;
    }

    /* Paragraphs — duplicate removed, see main rule above */

    /* Article nav (prev/next in issue) */
    .article-nav {
      max-width: var(--content-width);
      margin: 0 auto;
      padding: 48px 24px 64px;
      display: flex;
      justify-content: space-between;
      gap: 40px;
      border-top: 1px solid rgb(230,230,230);
    }
    .article-nav-item {
      cursor: pointer;
      max-width: 48%;
    }
    @media (hover: hover) and (pointer: fine) {
    .article-nav-item:hover .article-nav-title { color: var(--gray-dim); }
    }
    .article-nav-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      margin-bottom: 10px;
    }
    .article-nav-title {
      font-family: var(--serif);
      font-size: var(--step-1);
      line-height: 1.3;
      color: var(--dark);
      transition: color 0.2s;
    }
    .article-nav-author {
      font-family: var(--serif);
      font-size: var(--step-0);
      color: var(--gray-dim);
      margin-top: 4px;
    }
    .article-nav-item--next { text-align: right; margin-left: auto; }

    /* Read-also block — card style with covers (matches home feed) for clear separation from article body */
    .article-related {
      max-width: var(--container-max);
      margin: var(--section-gap) auto 0;
      padding: var(--section-pad-y) var(--content-margin) var(--section-pad-y);
      border-top: var(--rule);
    }
    .article-related-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.14em;
      color: var(--gray-dim);
      margin-bottom: clamp(40px, 5vw, 72px);
    }
    .related-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: var(--card-gap-y) var(--card-gap-x);
    }
    @media (max-width: 1023px) { .related-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 640px)  { .related-grid { grid-template-columns: 1fr; } }
    .related-card { cursor: pointer; }
    @media (hover: hover) and (pointer: fine) {
      .related-card:hover .related-card-title { color: var(--gray-dim); }
    }
    .related-card-img-wrap {
      overflow: hidden;
      margin-bottom: 18px;
    }
    .related-card { transition: none; }
    .related-card-img {
      width: 100%;
      aspect-ratio: 4/3;
      object-fit: cover;
      background: rgb(240,240,240);
      display: block;
      /* Унифицированный image-hover (см. --hover-img-* tokens, DESIGN.md). */
      transition: transform var(--hover-img-duration) var(--hover-img-ease), filter var(--hover-img-duration) ease, opacity 0.5s ease;
      transform-origin: center center;
      will-change: transform;
    }
    @media (hover: hover) and (pointer: fine) {
      .related-card:hover .related-card-img {
        transform: scale(var(--hover-img-scale));
        filter: brightness(1.04);
      }
    }
    .related-card-body {
      display: flex;
      flex-direction: column;
      gap: 6px;
    }
    .related-card-title {
      font-family: var(--serif);
      font-size: var(--step-2);
      line-height: 1.2;
      letter-spacing: -0.005em;
      color: var(--dark);
      transition: color 0.2s;
    }
    .related-card-author {
      font-family: var(--serif);
      font-size: var(--step-1);
      color: var(--gray-dim);
      margin-top: 2px;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
    }
    .related-card-meta {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      letter-spacing: 0.02em;
      margin-top: 4px;
    }

    /* ═══════════════════════════════════════════════
       ISSUE PAGE
       ═══════════════════════════════════════════════ */
    .issue-hero {
      background: var(--dark);
      color: var(--white);
      padding-top: var(--header-height);
      position: relative;
      overflow: hidden;
      min-height: clamp(480px, 65vh, 750px);
      display: flex;
      align-items: center;
      justify-content: center;
    }
    /* Tilted cover as isometric background object — must NOT obscure text */
    .issue-hero-cover {
      position: absolute;
      width: clamp(260px, 30vw, 420px);
      aspect-ratio: 3/4;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%) rotate(-12deg);
      opacity: 0.3;
      filter: drop-shadow(0 12px 40px rgba(0,0,0,0.5));
      transition: transform 0.3s ease-out;
      pointer-events: none;
      z-index: 0;
    }
    .issue-hero-cover img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    .issue-hero-cover span {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 100%;
      font-family: var(--serif);
      font-size: 72px;
      color: rgba(255,255,255,0.06);
      background: rgba(255,255,255,0.03);
    }
    .issue-hero-inner {
      position: relative;
      z-index: 1;
      display: flex;
      flex-direction: column;
      align-items: center;
      text-align: center;
      padding: var(--section-pad-y) var(--content-margin) var(--section-pad-y);
      max-width: var(--measure-wide);
      margin: 0 auto;
    }
    .issue-hero-label {
      font-family: var(--serif);
      font-size: var(--step-2);
      font-weight: 400;
      color: var(--white);
      margin-bottom: 12px;
    }
    .issue-hero-title {
      font-family: var(--serif);
      font-size: var(--step-5);
      font-weight: 400;
      line-height: 1.0;
      letter-spacing: -0.02em;
      margin-bottom: clamp(32px, 4vw, 56px);
    }
    .issue-hero-authors {
      font-family: var(--serif);
      font-size: var(--step-0);
      color: rgba(255,255,255,0.6);
      line-height: 1.55;
      max-width: 820px;
      margin-bottom: 32px;
    }
    .issue-hero-authors a {
      color: rgba(255,255,255,0.7);
      text-decoration: none;
      border-bottom: 1px solid transparent;
      transition: color 0.2s, border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-hero-authors a:hover {
      color: var(--white);
      border-bottom-color: rgba(255,255,255,0.5);
    }
    }
    .issue-pdf-btn {
      display: inline-block;
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 400;
      color: rgba(255,255,255,0.5);
      text-decoration: none;
      border-bottom: 1px solid rgba(255,255,255,0.25);
      transition: color 0.2s, border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-pdf-btn:hover {
      color: var(--white);
      border-bottom-color: var(--white);
    }
    }

    /* Issue Intro (Вступление) */
    .issue-intro {
      max-width: var(--content-width);
      margin: 0 auto;
      padding: var(--section-pad-y) 24px var(--section-pad-y);
    }
    .issue-intro-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: var(--gray-dim);
      margin-bottom: 40px;
    }
    .issue-intro-body {
      font-family: var(--serif);
      font-size: var(--body-size);
      line-height: var(--body-lh);
      color: var(--dark);
    }
    .issue-intro-body p { margin-bottom: 0; text-indent: 1.5em; }
    .issue-intro-body > p:first-child { text-indent: 0; }

    /* Issue Cartoon (Комикс) */
    .issue-cartoon {
      max-width: 760px;
      margin: 0 auto 40px;
      padding: 0 24px;
    }
    .issue-cartoon-card {
      display: flex;
      border: var(--rule);
      cursor: pointer;
      transition: box-shadow 0.3s ease, border-color 0.3s ease, transform 0.45s var(--ease-out);
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-cartoon-card:hover {
      border-color: var(--dark);
      box-shadow: var(--shadow-hover);
      transform: translateY(-3px);
    }
    }
    .issue-cartoon-preview {
      flex: 0 0 45%;
      line-height: 0;
    }
    .issue-cartoon-preview img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      filter: grayscale(100%);
    }
    .issue-cartoon-info {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      padding: 36px 24px;
      text-align: center;
    }
    .issue-cartoon-rubric {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      margin-bottom: 12px;
    }
    .issue-cartoon-title {
      font-family: var(--serif);
      font-size: clamp(24px, 2.2vw, 30px);
      font-weight: 500;
      line-height: 1.25;
      margin-bottom: 20px;
    }
    .issue-cartoon-read {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1px;
      border-bottom: var(--rule-strong);
      padding-bottom: 3px;
    }

    /* Cartoon overlay (fullscreen view) */
    .issue-cartoon-overlay {
      display: none;
      position: fixed;
      inset: 0;
      z-index: 9999;
      background: rgba(0,0,0,0.95);
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      justify-content: center;
      padding: 40px 20px;
      scrollbar-width: none;
      -ms-overflow-style: none;
    }
    .issue-cartoon-overlay::-webkit-scrollbar { display: none; width: 0; }
    .issue-cartoon-overlay.active {
      display: flex;
    }
    .issue-cartoon-overlay img {
      max-width: 900px;
      width: 100%;
      height: auto;
      margin: auto;
      filter: grayscale(100%);
    }
    .issue-cartoon-close {
      position: fixed;
      top: 16px;
      right: 24px;
      font-size: 36px;
      color: #fff;
      background: none;
      border: none;
      cursor: pointer;
      z-index: 10000;
      line-height: 1;
    }

    /* Issue ad / distribution page */
    .issue-ad {
      padding: 32px var(--content-margin) 24px;
      max-width: calc(var(--content-width) + 2 * var(--content-margin));
    }
    .issue-ad-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      letter-spacing: 1.5px;
      text-transform: uppercase;
      color: var(--gray-dim);
      margin-bottom: 16px;
    }
    .issue-ad-card {
      cursor: pointer;
      transition: transform 0.3s var(--ease-out), box-shadow 0.3s;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-ad-card:hover {
      transform: scale(1.01);
      box-shadow: 0 8px 32px rgba(0,0,0,0.12);
    }
    }
    .issue-ad-card img {
      width: 100%;
      height: auto;
      display: block;
    }
    .issue-ad-overlay {
      display: none;
      position: fixed;
      inset: 0;
      z-index: 9999;
      background: rgba(0,0,0,0.95);
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      justify-content: center;
      scrollbar-width: none;
      -ms-overflow-style: none;
      padding: 40px 20px;
    }
    .issue-ad-overlay.active { display: flex; }
    .issue-ad-overlay::-webkit-scrollbar { display: none; width: 0; }
    .issue-ad-overlay img {
      max-width: 900px;
      width: 100%;
      height: auto;
      margin: auto;
    }
    .issue-ad-close {
      position: fixed;
      top: 16px;
      right: 24px;
      font-size: 36px;
      color: #fff;
      background: none;
      border: none;
      cursor: pointer;
      z-index: 10000;
      line-height: 1;
    }

    /* Issue navigation (prev/next) */
    .issue-nav {
      display: flex;
      justify-content: space-between;
      padding: 16px var(--content-margin);
      border-top: var(--rule);
    }
    .issue-nav a {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--dark);
      border-bottom: 1px solid transparent;
      transition: border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-nav a:hover { border-bottom-color: var(--dark); }
    }

    /* First article card — after cartoon, before issue nav */
    .issue-start-reading {
      max-width: var(--content-width);
      margin: 0 auto;
      padding: 40px 24px;
    }
    .issue-start-reading-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1.2px;
      color: var(--gray-dim);
      margin-bottom: 16px;
    }
    .issue-start-reading-card {
      cursor: pointer;
      padding: 28px 32px;
      border: 1px solid var(--gray-border, #ddd);
      transition: border-color 0.3s ease, background 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-start-reading-card:hover {
      border-color: var(--dark);
      background: rgb(252, 252, 252);
    }
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-start-reading-card:hover .issue-start-reading-title {
      color: var(--gray-dim);
    }
    }
    .issue-start-reading-num {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: rgba(0, 0, 0, 0.25);
      margin-bottom: 8px;
    }
    .issue-start-reading-rubric {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.8px;
      color: var(--gray-dim);
      margin-bottom: 10px;
    }
    .issue-start-reading-title {
      font-family: var(--serif);
      font-size: clamp(22px, 2vw, 28px);
      font-weight: 500;
      line-height: 1.25;
      color: var(--dark);
      margin-bottom: 10px;
      transition: color 0.2s;
    }
    .issue-start-reading-author {
      font-family: var(--serif);
      font-size: clamp(16px, 1.3vw, 19px);
      color: var(--gray-dim);
    }

    /* Issue TOC — dark background, always visible */
    .issue-toc {
      background: var(--dark);
      color: var(--white);
      padding: 0 var(--content-margin) clamp(32px, 3vw, 48px);
    }
    .issue-toc.collapsed {
      padding-bottom: 0;
    }
    .issue-toc.collapsed .issue-toc-grid {
      display: none;
    }
    .issue-toc.collapsed .issue-toc-arrow {
      transform: rotate(-90deg);
    }
    .issue-toc-label {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1.2px;
      color: rgba(255,255,255,0.35);
      padding: 16px 0;
      margin-bottom: 0;
      border-bottom: 1px solid rgba(255,255,255,0.1);
      text-align: center;
      cursor: pointer;
      user-select: none;
      transition: color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-toc-label:hover { color: rgba(255,255,255,0.6); }
    }
    .issue-toc-arrow {
      display: inline-block;
      margin-left: 6px;
      transition: transform 0.3s var(--ease-out);
      font-size: var(--step--1);
    }
    .issue-toc-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 0 clamp(56px, 5.5vw, 112px);
      max-width: var(--container-max);
      margin: 0 auto;
    }
    .issue-toc-item {
      cursor: pointer;
      display: flex;
      flex-direction: column;
      padding: 28px 0;
      border-bottom: 1px solid rgba(255,255,255,0.08);
      transition: background 0.2s ease;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-toc-item:hover {
      background: rgba(255,255,255,0.04);
    }
    }
    .issue-toc-item-head {
      display: flex;
      align-items: baseline;
      gap: 8px;
      margin-bottom: 6px;
    }
    .issue-toc-item-num {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: rgba(255,255,255,0.3);
      flex-shrink: 0;
      min-width: 26px;
    }
    .issue-toc-item-rubric {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: rgba(255,255,255,0.45);
    }
    .issue-toc-item-title {
      font-family: var(--serif);
      font-size: var(--step-2);
      line-height: 1.2;
      font-weight: 400;
      color: rgba(255,255,255,0.92);
      padding-left: 0;
      transition: color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-toc-item:hover .issue-toc-item-title {
      color: var(--white);
    }
    }
    .issue-toc-item-author {
      font-family: var(--serif);
      font-size: var(--step-1);
      line-height: 1.35;
      color: rgba(255,255,255,0.55);
      padding-left: 0;
      margin-top: 12px;
    }
    .issue-toc-item-author a {
      color: rgba(255,255,255,0.45);
      text-decoration: none;
      border-bottom: 1px solid transparent;
      transition: color 0.2s, border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .issue-toc-item-author a:hover {
      color: rgba(255,255,255,0.8);
      border-bottom-color: rgba(255,255,255,0.4);
    }
    }
    @media (max-width: 1023px) {
      .issue-toc-grid { grid-template-columns: repeat(2, 1fr); }
      .issue-hero-cover { width: clamp(200px, 25vw, 300px); }
    }
    @media (max-width: 640px) {
      .issue-toc-grid { grid-template-columns: 1fr; }
    }

    /* ═══════════════════════════════════════════════
       ARCHIVE PAGE
       ═══════════════════════════════════════════════ */
    .archive-page {
      padding-top: var(--header-height);
    }
    /* Archive tabs — same row dimensions as year/letter nav (sans 14px, same padding) */
    .archive-header {
      padding: 20px var(--content-margin);
      display: flex;
      align-items: center;
      justify-content: center;
      flex-wrap: wrap;
      gap: 20px;
      border-bottom: var(--rule);
      position: sticky;
      top: var(--header-height-shrunk);
      background: var(--white);
      z-index: 10;
      /* Force own compositing layer — kills sticky-flicker on fast scroll. */
      will-change: transform;
      transform: translateZ(0);
      backface-visibility: hidden;
      max-height: 240px;
      min-height: 64px;
      box-sizing: border-box;
      overflow: hidden;
      transition: max-height 0.25s ease, padding 0.25s ease, min-height 0.25s ease, border-color 0.2s ease, opacity 0.2s ease;
    }
    body.archive-compact .archive-header {
      padding-top: 8px;
      padding-bottom: 8px;
      min-height: 40px;
    }
    body.archive-past-top .archive-header {
      max-height: 0;
      padding-top: 0;
      padding-bottom: 0;
      border-bottom-color: transparent;
      opacity: 0;
      pointer-events: none;
    }
    .archive-title { display: none; }
    .archive-tabs {
      display: flex;
      gap: clamp(28px, 3vw, 56px);
      justify-content: center;
    }
    .archive-tab {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 400;
      color: var(--gray-dim);
      background: none;
      border: none;
      padding: 4px 0;
      cursor: pointer;
      transition: color 0.25s var(--ease-out), font-weight 0.25s var(--ease-out);
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-tab:hover { color: var(--dark); }
    }
    .archive-tab.active { color: var(--dark); font-weight: 600; }

    .archive-content {
      /* Tighter top spacing — sticky sub-bar (year/filter) sits close to tabs */
      padding: clamp(8px, 1vw, 16px) var(--content-margin) var(--section-pad-y);
      max-width: var(--container-max);
      margin: 0 auto;
    }

    /* Archive — Issues tab */
    .archive-layout {
      display: block;
    }
    /* Horizontal year/letter filter — always sticky, full size at top, compact on scroll.
       Layout: ["Все"] · [.archive-year-scroll]  "Все" sibling of scroll → no overlap. */
    .archive-year-bar {
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 14px var(--content-margin);
      margin: 0 calc(-1 * var(--content-margin)) clamp(20px, 2vw, 32px);
      border-bottom: var(--rule);
      font-family: var(--sans);
      position: sticky;
      top: calc(var(--header-height-shrunk) + 56px);
      background: var(--white);
      z-index: 9;
      will-change: transform;
      transform: translateZ(0);
      backface-visibility: hidden;
      min-height: 56px;
      box-sizing: border-box;
      transition: padding 0.45s var(--ease-out), min-height 0.45s var(--ease-out), top 0.4s var(--ease-out);
      contain: paint;
    }
    body.archive-compact .archive-year-bar {
      top: calc(var(--header-height-shrunk) + 40px);
      padding: 8px var(--content-margin);
      min-height: 40px;
    }
    /* Past-top — tabs hidden, year-bar moves up to top */
    body.archive-past-top .archive-year-bar {
      top: var(--header-height-shrunk);
    }
    .archive-year-bar-all {
      flex-shrink: 0;
      padding: 4px 0;
    }
    /* Horizontal scroll, hidden scrollbar, items centered.
       Triple-layered defence on iOS where the native scrollbar sometimes
       leaks through CSS hiding rules:
         1) scrollbar-width: none + ::-webkit-scrollbar { display: none } — CSS spec way
         2) padding-bottom: 24px + margin-bottom: -24px — moves scrollbar
            off-screen, parent's `contain: paint` clips it
         3) Parent .archive-year-bar already has `contain: paint` to clip */
    .archive-year-scroll {
      flex: 1;
      min-width: 0;
      display: flex;
      justify-content: safe center;
      gap: clamp(14px, 1.5vw, 24px);
      overflow-x: auto;
      overflow-y: hidden;
      scrollbar-width: none;
      -ms-overflow-style: none;
      white-space: nowrap;
      scroll-behavior: smooth;
      padding-bottom: 24px;
      margin-bottom: -24px;
    }
    .archive-year-scroll::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; -webkit-appearance: none; appearance: none; }
    .archive-year-scroll::-webkit-scrollbar-track,
    .archive-year-scroll::-webkit-scrollbar-thumb { display: none !important; background: transparent !important; }
    /* When content overflows, center is lost — fall back to flex-start so scroll works. */
    @supports (scroll-snap-type: x mandatory) {
      .archive-year-scroll { scroll-snap-type: x proximity; }
      .archive-year-btn { scroll-snap-align: start; }
    }
    .archive-year-btn { flex-shrink: 0; white-space: nowrap; padding: 4px 0; }
    /* Spy state — color contrast only; the bar already shows year-of-scroll
       via natural scroll position. No underline (was too heavy). */
    .archive-year-btn.spy-active,
    .archive-letter-nav a.spy-active {
      color: var(--dark);
      font-weight: 600;
    }
    .archive-year-btn {
      display: inline-block;
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      background: none;
      border: none;
      padding: 4px 2px;
      cursor: pointer;
      transition: color 0.25s var(--ease-out), font-weight 0.25s var(--ease-out);
      text-align: left;
      /* Belt-and-suspenders: NO underline-like indicators in our style.
         Strip every source — UA <button> defaults, focus outlines,
         text-decoration inherited from anywhere. */
      text-decoration: none !important;
      outline: none;
      border-bottom: none;
    }
    .archive-year-btn:focus,
    .archive-year-btn:focus-visible { outline: none; box-shadow: none; }
    @media (hover: hover) and (pointer: fine) {
    .archive-year-btn:hover { color: var(--dark); }
    }
    .archive-year-btn.active { color: var(--dark); font-weight: 600; }
    /* Same rule for archive tabs — color + weight only, no underline ever. */
    .archive-tab,
    .archive-tab:focus,
    .archive-tab:focus-visible,
    .archive-letter-nav a,
    .archive-letter-nav a:focus,
    .archive-letter-nav a:focus-visible {
      text-decoration: none !important;
      outline: none;
      border-bottom: none;
    }

    .archive-issues-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      /* Tighter vertical gap — rows used to drift too far apart. */
      gap: clamp(32px, 4vw, 64px) clamp(32px, 4.5vw, 72px);
      max-width: var(--container-max);
      margin: 0 auto;
      /* align-items: start so cards keep their NATURAL height and don't
         stretch to match the tallest sibling. Without this, the 1fr row
         inside the card absorbs the extra height → cover container grows
         → image floats down → captions (caption block sits BELOW the
         stretched cover) start at different Y per card. start = top-
         alignment of the № and title rows across the grid row. */
      align-items: start;
    }
    @media (max-width: 1023px) {
      .archive-issues-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: clamp(28px, 4.5vw, 44px) clamp(28px, 4vw, 40px);
      }
    }
    @media (max-width: 640px) {
      .archive-issues-grid {
        grid-template-columns: 1fr;
        gap: clamp(28px, 7vw, 44px) 0;
      }
    }
    .archive-issue-card {
      cursor: pointer;
      text-align: center;
      display: grid;
      /* All rows AUTO (was `1fr auto auto`). Combined with parent's
         align-items: start, each card takes its natural height; cover
         keeps its fixed clamp height; captions sit immediately below
         the cover at the SAME Y across the entire grid row. Fixes the
         "№132 caps drop because its title wraps to 2 lines" issue. */
      grid-template-rows: auto auto auto;
      min-width: 0;
      color: inherit;
      text-decoration: none;
      -webkit-tap-highlight-color: transparent;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-issue-card:hover, .archive-issue-card:focus, .archive-issue-card:visited {
      color: inherit;
      text-decoration: none;
    }
    }
    @media (hover: hover) and (pointer: fine) {
      .archive-issue-card:hover .archive-issue-title { color: var(--gray-dim); }
    }
    /* Cover slot: fixed height = the SAME row baseline for the caption
       block below every cover. Inside the slot, the image is CENTER-
       aligned vertically (covers have varying intrinsic heights —
       centering looks balanced; flex-end stacked them on a hard floor
       which felt heavy). Caption block under the slot inherits the
       slot's bottom edge as a common Y, so titles top-align across the row. */
    .archive-issue-cover {
      perspective: 600px;
      position: relative;
      height: clamp(260px, 22vw, 320px);
      margin-bottom: clamp(20px, 2vw, 32px);
      overflow: visible;
      display: flex;
      align-items: center;          /* covers float to vertical centre */
      justify-content: center;
    }
    /* Archive cover: thumbhash blur shown immediately, real image
       overpaints when loaded. Wrapper has fixed aspect-ratio (3:4 —
       MAM cover convention) so bg and img coincide exactly with no
       bleed possible. The 3D transform lives on the wrapper. */
    .archive-issue-cover-blur {
      display: block;
      position: relative;
      width: clamp(160px, 60%, 240px);
      max-height: 100%;
      aspect-ratio: 3 / 4;
      margin: 0 auto;
      border: 1px solid rgb(227, 227, 227);
      box-shadow: 0 6px 18px rgba(0,0,0,0.04);
      overflow: hidden;
      transform: scale(0.95) rotateX(8deg) rotate(-8deg);
      transform-origin: 50% 70%;
      transition: transform 0.55s var(--spring), box-shadow 0.4s ease;
      background-image: var(--thumb-bg, none);
      background-size: cover;
      background-position: center;
    }
    .archive-issue-cover-blur img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
      transition: opacity 0.35s ease;
      opacity: 0;
    }
    .archive-issue-cover-blur img.loaded { opacity: 1; }
    /* If no thumbhash, also fade in img — wrapper has no bg to lean on. */
    .archive-issue-cover-blur:not(.has-thumbhash) {
      background: rgb(245, 245, 245);
    }
    @media (hover: hover) and (pointer: fine) {
      .archive-issue-card:hover .archive-issue-cover-blur {
        transform: scale(1.05) rotateX(0deg) rotate(0deg) translateY(-4px);
        box-shadow: 0 14px 28px rgba(0,0,0,0.10);
      }
    }
    .archive-issue-cover span {
      font-family: var(--serif);
      font-size: 32px;
      color: rgba(255,255,255,0.12);
      font-weight: 400;
    }
    /* New hierarchy: №N + year FIRST (prominent), theme SECOND (smaller serif) */
    .archive-issue-num {
      font-family: var(--serif);
      font-size: var(--step-2);
      font-weight: 500;
      line-height: 1.15;
      letter-spacing: -0.005em;
      color: var(--dark);
      margin-bottom: 6px;
      transition: color 0.2s;
    }
    /* :hover MUST be wrapped — bare :hover with visual change triggers
       iOS Safari's "first tap shows hover, second tap clicks" quirk,
       which combined with the .is-tapping transform animation makes
       the tap take 4 attempts before navigation fires. NEVER write a
       visual-changing :hover rule outside @media (hover: hover). */
    @media (hover: hover) and (pointer: fine) {
      .archive-issue-card:hover .archive-issue-num { color: var(--gray-dim); }
    }
    .archive-issue-title {
      font-family: var(--serif);
      font-size: var(--step-1);
      line-height: 1.3;
      color: var(--gray-dim);
      transition: color 0.2s;
    }
    .archive-issue-meta {
      font-family: var(--sans);
      font-size: var(--step--1);
      letter-spacing: 0.04em;
      color: var(--gray-dim);
    }

    /* Archive — Articles tab: 2nd-submenu bar with dropdown triggers (like year-bar) */
    .archive-articles-bar {
      display: flex;
      flex-wrap: wrap;
      /* Center children vertically so the icon, dropdowns and chips share
         the same optical centerline. Baseline-align made the icon sit lower
         than the text. */
      align-items: center;
      gap: clamp(20px, 2.5vw, 40px);
      padding: 14px var(--content-margin);
      margin: 0 calc(-1 * var(--content-margin)) clamp(20px, 2vw, 32px);
      border-bottom: var(--rule);
      font-family: var(--sans);
      font-size: var(--step--1);
      position: sticky;
      top: calc(var(--header-height-shrunk) + 56px);
      background: var(--white);
      z-index: 9;
      will-change: transform;
      transform: translateZ(0);
      backface-visibility: hidden;
      min-height: 56px;
      box-sizing: border-box;
      transition: padding 0.45s var(--ease-out), min-height 0.45s var(--ease-out), top 0.4s var(--ease-out);
      /* No `contain: paint` — popups need to overflow below the bar. */
    }
    /* When panel is open right below, collapse bar's bottom margin so the panel
       sits flush against the bar (no double gap). */
    .archive-articles-bar:has(+ .archive-filter-panel) {
      margin-bottom: 0;
    }
    body.archive-compact .archive-articles-bar {
      top: calc(var(--header-height-shrunk) + 40px);
      padding: 8px var(--content-margin);
      min-height: 40px;
    }
    body.archive-past-top .archive-articles-bar { top: var(--header-height-shrunk); }
    /* Dropdown trigger — text + chevron, journal-style (no boxes) */
    .archive-filter-trigger {
      cursor: pointer;
      color: var(--gray-dim);
      /* Inter variable → font-weight animates smoothly. */
      transition: color 0.25s var(--ease-out), font-weight 0.25s var(--ease-out);
      padding: 4px 0;
      white-space: nowrap;
      position: relative;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-filter-trigger:hover { color: var(--dark); }
    }
    .archive-filter-trigger::after {
      content: '';
      display: inline-block;
      width: 6px;
      height: 6px;
      margin-left: 8px;
      border-right: 1px solid currentColor;
      border-bottom: 1px solid currentColor;
      transform: translateY(-2px) rotate(45deg);
      transition: transform 0.3s var(--ease-out);
    }
    .archive-filter-trigger.open {
      color: var(--dark);
    }
    .archive-filter-trigger.open::after {
      transform: translateY(1px) rotate(-135deg);
    }
    .archive-filter-trigger.has-active {
      color: var(--dark);
      font-weight: 500;
    }
    /* Wrapper used to position the popup beneath each dropdown trigger. */
    .archive-filter-dropdown {
      position: relative;
      display: inline-block;
    }
    /* Popup — floats below the trigger, small panel with options.
       Each dropdown is independent; only one popup is open at a time. */
    /* Filter popup — absolute-positioned panel below the trigger.
       Content can be tall (32 years, 80 tags) — the popup caps at viewport
       height with a hidden-scrollbar overflow so options remain reachable
       without showing a scrollbar UI. Тема uses up to 6 columns on wide
       screens to keep things compact. */
    .archive-filter-popup {
      position: absolute;
      top: calc(100% + 10px);
      left: 0;
      background: var(--white);
      border: 1px solid rgb(220,220,220);
      box-shadow: 0 12px 40px rgba(0,0,0,0.1);
      z-index: 40;
      font-family: var(--sans);
      font-size: var(--step--1);
      padding: 6px 0;
      max-height: min(70vh, 560px);
      overflow-y: auto;
      overflow-x: hidden;
      scrollbar-width: none;
      -ms-overflow-style: none;
      animation: popupIn 0.18s var(--ease-out);
    }
    .archive-filter-popup::-webkit-scrollbar {
      display: none;
    }
    /* Soft fade at the bottom signals "there's more — scroll".
       Only present when JS adds .has-more-below (set on scroll position update). */
    .archive-filter-popup.has-more-below::after {
      content: '';
      position: sticky;
      bottom: 0;
      display: block;
      height: 28px;
      margin-top: -28px;
      pointer-events: none;
      background: linear-gradient(to bottom, rgba(255,255,255,0), var(--white) 85%);
    }
    @keyframes popupIn {
      from { opacity: 0; transform: translateY(-4px); }
      to   { opacity: 1; transform: translateY(0); }
    }
    /* Year: 3 columns — 32 years (10-11 per column) fit on one screen without scroll. */
    .archive-filter-popup[data-dd="year"] {
      min-width: 360px;
      max-width: 420px;
    }
    .archive-filter-popup[data-dd="year"] .archive-filter-popup-options {
      columns: 3;
      column-gap: 4px;
    }
    /* Rubric & Тема: fluid multi-column.
       Popup width is clamp(min, preferred, max) — preferred is a % of viewport
       so it scales smoothly from phone to widescreen. Inside, column-width:
       200px lets the browser pack as many ~200px columns as fit. */
    .archive-filter-popup[data-dd="rubric"],
    .archive-filter-popup[data-dd="tag"] {
      width: clamp(320px, 90vw, 1280px);
      max-width: calc(100vw - 32px);
    }
    .archive-filter-popup[data-dd="rubric"] .archive-filter-popup-options,
    .archive-filter-popup[data-dd="tag"] .archive-filter-popup-options {
      column-width: 200px;
      column-gap: 4px;
    }
    .archive-filter-popup-search {
      width: 100%;
      border: none;
      border-bottom: 1px solid rgb(232,232,232);
      padding: 10px 16px;
      font: inherit;
      font-size: var(--step--1);
      color: var(--dark);
      outline: none;
      background: transparent;
      box-sizing: border-box;
      margin-bottom: 6px;
    }
    .archive-filter-popup-search::placeholder { color: var(--gray-secondary); }
    .archive-filter-popup-options {
      /* No scroll, no max-height — popup expands to content. */
    }
    .archive-filter-popup-option {
      padding: 7px 16px;
      color: var(--gray-dim);
      cursor: pointer;
      display: flex;
      justify-content: space-between;
      align-items: baseline;
      gap: 12px;
      transition: background 0.12s, color 0.12s;
      break-inside: avoid;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-filter-popup-option:hover { background: rgb(248,248,248); color: var(--dark); }
    }
    .archive-filter-popup-option.active {
      color: var(--dark);
      font-weight: 500;
    }
    .archive-filter-popup-option-label {
      flex: 1;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    /* Tooltip — shown only on options whose label was truncated to ellipsis.
       Truncation detection happens in JS (markTruncatedFilterOptions) after
       render: the .is-truncated class is added only to overflowing labels. */
    .archive-filter-popup-option { position: relative; }
    @media (hover: hover) and (pointer: fine) {
    .archive-filter-popup-option.is-truncated:hover::after {
      content: attr(data-full);
      position: absolute;
      left: 50%;
      bottom: calc(100% + 4px);
      transform: translateX(-50%);
      background: var(--dark);
      color: var(--white);
      font-family: var(--sans);
      font-size: 12px;
      font-weight: 400;
      letter-spacing: 0;
      padding: 6px 10px;
      white-space: nowrap;
      pointer-events: none;
      z-index: 50;
      box-shadow: 0 4px 12px rgba(0,0,0,0.18);
      animation: tooltipIn 0.12s ease;
      max-width: 320px;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    }
    @keyframes tooltipIn { from { opacity: 0; transform: translateX(-50%) translateY(2px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
    /* Active marker: × (multiplication sign) signals "click to REMOVE". */
    .archive-filter-popup-option.active .archive-filter-popup-option-label::before {
      content: '×';
      margin-right: 6px;
      color: var(--dark);
      font-weight: 500;
      font-size: 1.05em;
    }
    .archive-filter-popup-count {
      color: var(--gray-secondary);
      font-size: var(--step--2);
      white-space: nowrap;
    }
    .archive-filter-popup-empty {
      padding: 20px 16px;
      color: var(--gray-secondary);
      text-align: center;
      font-size: var(--step--2);
    }
    /* On narrow screens, anchor the Тема popup to the bar's left edge so it
       doesn't overflow off-screen when its trigger is in the middle. */
    @media (max-width: 640px) {
      /* Filter popups on mobile: full-width with EQUAL 16px margins on left,
         right, and BOTTOM. Popup has fixed max-height so it always fits the
         screen — INTERNAL scroll handles overflow, with a soft fade at the
         bottom signalling there's more content. JS (_repositionArchivePopup)
         computes the exact bottom-of-viewport and sets max-height accordingly. */
      .archive-filter-popup,
      .archive-filter-popup[data-dd="year"],
      .archive-filter-popup[data-dd="rubric"],
      .archive-filter-popup[data-dd="tag"] {
        min-width: 0;
        max-width: calc(100vw - 32px);
        width: calc(100vw - 32px);
        overflow-y: auto;
        scrollbar-width: none;
        -ms-overflow-style: none;
        /* max-height computed by JS to (viewport bottom - popup top - 16px).
           CSS fallback for first paint before JS runs. */
        max-height: calc(100vh - 200px);
      }
      .archive-filter-popup[data-dd="year"] .archive-filter-popup-options {
        /* 2 columns на ≤768px: на телефоне 4-char «2025» + count рвутся в
           3 колонки до «2…». Roman 2026-05-26. На tablet 2 колонки тоже ок. */
        columns: 2;
      }
      .archive-filter-popup[data-dd="rubric"] .archive-filter-popup-options,
      .archive-filter-popup[data-dd="tag"] .archive-filter-popup-options {
        column-width: auto;
        /* 1 column on mobile — длинные рубрики/темы (e.g. «Современное
           искусство», «Постколониальные исследования») перерастают узкие
           колонки и truncated до «...». На десктопе ellipsis + hover-tooltip
           решают, на мобиле hover'а нет → нужно показывать ПОЛНЫЙ label
           через перенос по словам. Roman 2026-05-26. */
        columns: 1;
      }
      /* На мобиле разрешаем перенос label'ов фильтров: hover-tooltip
         не работает на touch, ellipsis скрывает важный текст. */
      .archive-filter-popup-option {
        align-items: flex-start; /* count выровнен к первой строке label */
      }
      .archive-filter-popup-option-label {
        white-space: normal;     /* перенос по словам */
        overflow: visible;
        text-overflow: clip;
        word-break: break-word;
      }
      /* Fade hint at the bottom — appears via .has-more-below class set by
         updatePopupScrollHint(). */
      .archive-filter-popup.has-more-below::after { display: block; }
    }
    /* Backdrop unused now — popup is inline. Hide it. */
    .archive-filter-backdrop {
      display: none;
    }
    /* View toggle — 6 same-thickness sticks. In LIST mode, 3 horizontal sticks
       are visible at top/middle/bottom. In GRID mode, 3 additional vertical
       sticks appear (fade in + morph from the middle horizontal) and form a
       2×2 cell grid alongside the originals. All 6 sticks share the same
       1.5px thickness, so the grid icon reads as "lines making cells", not
       filled blocks. */
    .archive-view-toggle {
      width: 20px;
      height: 20px;
      background: none;
      border: none;
      padding: 0;
      cursor: pointer;
      position: relative;
      margin-left: auto;
      color: var(--dark);
      flex-shrink: 0;
      align-self: center;
      opacity: 0.55;
      transition: opacity 0.2s, transform 0.4s var(--ease-out);
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-view-toggle:hover { opacity: 1; }
    }
    .archive-view-toggle:active { transform: scale(0.92); }
    /* Desktop hover label — appears below the icon */
    .archive-view-toggle::after {
      content: attr(data-label);
      position: absolute;
      top: calc(100% + 6px);
      right: 0;
      font-family: var(--sans);
      font-size: var(--step--2);
      letter-spacing: 0.04em;
      color: var(--dark);
      background: var(--white);
      padding: 4px 8px;
      border: 1px solid rgb(230,230,230);
      white-space: nowrap;
      pointer-events: none;
      opacity: 0;
      transform: translateY(-4px);
      transition: opacity 0.18s ease, transform 0.18s ease;
      z-index: 30;
    }
    @media (hover: hover) {
      .archive-view-toggle:hover::after {
        opacity: 1;
        transform: translateY(0);
      }
    }
    .archive-view-toggle svg {
      width: 100%;
      height: 100%;
      display: block;
      overflow: visible;
    }
    .archive-view-toggle rect {
      fill: currentColor;
      stroke: none;
    }
    /* 3 horizontals — ALWAYS visible at top/middle/bottom. Same 1.5px thickness. */
    .archive-view-toggle .vt-h1 { x: 2px; y: 3px;    width: 16px; height: 1.5px; }
    .archive-view-toggle .vt-h2 { x: 2px; y: 9.25px; width: 16px; height: 1.5px; }
    .archive-view-toggle .vt-h3 { x: 2px; y: 15.5px; width: 16px; height: 1.5px; }

    /* 3 vertical "duplicates" — each starts identical to the middle horizontal
       (x=2, y=9.25, w=16, h=1.5), then via CSS transform rotates 90° around its
       center AND shrinks to 87.5% length (14px) AND translates to its column
       position. This gives a true rotation animation rather than a width/height
       morph. With transform-box: fill-box, the transform origin is the rect's
       own center, not the SVG's origin. */
    .archive-view-toggle .vt-v {
      transform-box: fill-box;
      transform-origin: center;
      transition:
        transform 0.55s cubic-bezier(0.45, 0, 0.25, 1.25),
        opacity 0.35s ease;
    }

    /* DESTINATION-icon convention:
       — When in GRID/cards mode, show LINES icon (click goes to list)  → verticals hidden
       — When in LIST mode,        show GRID icon  (click goes to cards) → verticals visible */
    .archive-view-toggle.is-grid .vt-v {
      opacity: 0;
      transform: rotate(0deg) scaleX(1);
    }
    /* Verticals visible: rotate 90° + shrink to 87.5% length + translate to L/M/R column.
       After transform: rect is 1.5×14 vertical bar, top at y=3, bottom at y=17.
       No tails — verticals fit exactly between top and bottom horizontals. */
    .archive-view-toggle.is-list .vt-v {
      opacity: 1;
    }
    .archive-view-toggle.is-list .vt-v1 {
      transform: translate(-7.25px, 0) rotate(90deg) scaleX(0.875);
    }
    .archive-view-toggle.is-list .vt-v2 {
      transform: rotate(90deg) scaleX(0.875);
    }
    .archive-view-toggle.is-list .vt-v3 {
      transform: translate(7.25px, 0) rotate(90deg) scaleX(0.875);
    }
    /* Inline chips zone (right next to the trigger) */
    .archive-filter-chips-inline {
      display: inline-flex;
      flex-wrap: wrap;
      align-items: baseline;
      gap: 8px;
      flex: 1;
      min-width: 0;
    }
    .archive-filter-clear-link {
      color: var(--gray-dim);
      cursor: pointer;
      text-decoration: underline;
      text-underline-offset: 3px;
      transition: color 0.2s;
      margin-left: 6px;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-filter-clear-link:hover { color: var(--dark); }
    }
    /* Filter panel — STICKY right under the bar so it remains visible at any
       scroll position. No internal scroll; sections (Рубрики / Темы) inside. */
    .archive-filter-panel {
      padding: clamp(16px, 1.6vw, 24px) var(--content-margin) clamp(20px, 2vw, 32px);
      margin: 0 calc(-1 * var(--content-margin)) clamp(20px, 2vw, 32px);
      border-bottom: var(--rule);
      font-family: var(--sans);
      background: var(--white);
      display: flex;
      flex-direction: column;
      gap: clamp(16px, 2vw, 28px);
      position: sticky;
      top: calc(var(--header-height-shrunk) + 56px + 56px);
      z-index: 8;
      will-change: transform;
      transform: translateZ(0);
      backface-visibility: hidden;
      contain: paint;
    }
    body.archive-compact .archive-filter-panel { top: calc(var(--header-height-shrunk) + 40px + 40px); }
    body.archive-past-top .archive-filter-panel { top: calc(var(--header-height-shrunk) + 40px); }
    .archive-filter-group { display: flex; flex-direction: column; gap: 12px; }
    .archive-filter-group-label {
      font-size: var(--step--2);
      text-transform: uppercase;
      letter-spacing: 0.14em;
      color: var(--gray-dim);
    }
    .archive-filter-group-pills {
      display: flex;
      flex-wrap: wrap;
      gap: 6px 18px;
    }
    /* Wrap pill — single text item inside the group */
    .archive-filter-pill {
      cursor: pointer;
      font-size: var(--step--1);
      color: var(--gray-dim);
      padding: 4px 0;
      transition: color 0.2s;
      white-space: nowrap;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-filter-pill:hover { color: var(--dark); }
    }
    .archive-filter-pill.active {
      color: var(--dark);
      font-weight: 500;
    }
    .archive-filter-pill-count {
      font-size: var(--step--2);
      color: var(--gray-secondary);
      margin-left: 4px;
    }
    /* Chip — selected filter shown inline in the bar */
    .archive-filter-chip {
      display: inline-flex;
      align-items: baseline;
      gap: 6px;
      padding: 4px 10px 4px 12px;
      background: var(--dark);
      color: var(--white);
      font-size: var(--step--2);
      cursor: pointer;
      transition: background 0.2s;
      white-space: nowrap;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-filter-chip:hover { background: var(--black); }
    }
    .archive-filter-chip-x {
      opacity: 0.55;
      font-size: 1.1em;
      line-height: 1;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-filter-chip:hover .archive-filter-chip-x { opacity: 1; }
    }
    @media (max-width: 1023px) { .archive-filter-panel { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 640px)  { .archive-filter-panel { grid-template-columns: 1fr; } }
    /* Filter option — single row: name + count. Marker not needed (selected items
       are pulled out to the chips zone above). */
    .archive-filter-option {
      cursor: pointer;
      font-size: var(--step--1);
      color: var(--gray-dim);
      padding: 4px 0;
      transition: color 0.2s;
      display: flex;
      justify-content: space-between;
      align-items: baseline;
      gap: 12px;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-filter-option:hover { color: var(--dark); }
    }
    .archive-filter-option-name { flex: 1; min-width: 0; }
    .archive-filter-option-count {
      font-size: var(--step--2);
      color: var(--gray-secondary);
      flex-shrink: 0;
    }
    .archive-rubric-clear {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      background: none;
      border: none;
      cursor: pointer;
      padding: 6px 4px;
      text-decoration: underline;
      text-underline-offset: 3px;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-rubric-clear:hover { color: var(--dark); }
    }
    .archive-active-rubrics {
      display: flex;
      flex-wrap: wrap;
      gap: 6px;
    }
    .archive-active-rubric-chip {
      font-family: var(--sans);
      font-size: var(--step--1);
      padding: 4px 10px;
      background: var(--dark);
      color: var(--white);
      cursor: pointer;
      letter-spacing: 0.02em;
    }
    .archive-active-rubric-chip::after { content: ' ×'; opacity: 0.6; }
    @media (hover: hover) and (pointer: fine) {
    .archive-active-rubric-chip:hover::after { opacity: 1; }
    }
    /* Articles list — 3-col grid (same proportions as TOC/Авторы). */
    .archive-article-list {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: clamp(20px, 2.2vw, 36px) clamp(40px, 4.5vw, 80px);
      max-width: var(--container-max);
      margin: 0 auto;
    }
    @media (max-width: 1023px) { .archive-article-list { grid-template-columns: repeat(2, 1fr); gap: 18px clamp(32px, 4vw, 56px); } }
    @media (max-width: 640px)  { .archive-article-list { grid-template-columns: 1fr; gap: 16px 0; } }
    /* Grid view — cards with covers. Uses the same articles-grid as home. */
    .archive-articles-grid {
      max-width: var(--container-max);
      margin: 0 auto;
    }
    .archive-article-item {
      cursor: pointer;
      padding: 4px 0;
      transition: color 0.2s;
      min-width: 0;
    }
    .archive-article-item-body {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    @media (max-width: 480px) {
      .archive-article-item { padding: 12px 0; border-bottom: var(--rule); }
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-article-item:hover .archive-article-item-title { color: var(--gray-dim); }
    }
    .archive-article-item-title {
      font-family: var(--serif);
      font-size: var(--step-2);
      line-height: 1.22;
      letter-spacing: -0.005em;
      color: var(--dark);
      transition: color 0.2s;
    }
    /* Author in archive list — sans, dimmer than title (catalog style). */
    .archive-article-item-author {
      font-family: var(--sans);
      font-size: var(--step--1);
      line-height: 1.4;
      color: var(--gray-dim);
    }
    /* Meta — always visible. issue · year · rubric */
    .archive-article-item-meta {
      font-family: var(--sans);
      font-size: var(--step--2);
      color: var(--gray-secondary);
      letter-spacing: 0.04em;
      text-transform: uppercase;
    }

    /* Archive — Authors tab */
    .archive-authors-list {
      max-width: 100%;
      margin: 0 auto;
    }
    .archive-letter-header {
      font-family: var(--serif);
      font-size: clamp(32px, 3.5vw, 48px);
      font-weight: 400;
      color: var(--dark);
      padding: 32px 0 8px;
      margin-bottom: 6px;
    }
    .archive-letter-header:first-child { padding-top: 0; }
    /* CSS multi-column: items flow continuously top→bottom, then next column.
       No row-alignment → rows are as tall as the item itself (not the tallest). */
    .archive-letter-group {
      columns: 3;
      column-gap: clamp(56px, 5.5vw, 112px);
      max-width: var(--container-max);
      margin: 0 auto clamp(32px, 4vw, 56px);
    }
    @media (max-width: 1023px) { .archive-letter-group { columns: 2; column-gap: clamp(32px, 4vw, 56px); } }
    @media (max-width: 640px)  { .archive-letter-group { columns: 1; } }
    .archive-author-item {
      padding: 6px 0;
      min-width: 0;
      break-inside: avoid;
    }
    .archive-author-name-wrap {
      display: flex;
      align-items: baseline;
      gap: 10px;
      min-width: 0;
    }
    .archive-author-name {
      font-family: var(--serif);
      font-size: var(--step-1);
      line-height: 1.25;
      color: var(--dark);
      cursor: pointer;
      flex: 1;
      min-width: 0;
      /* font-weight isn't animatable smoothly for Arno (discrete files).
         Default-dark + +/- marker carry the state — no weight bump on expand. */
      transition: color 0.25s var(--ease-out);
      display: inline-flex;
      align-items: baseline;
      gap: 10px;
    }
    /* «+» marker — visible affordance that the row expands.
       Becomes «−» when item is expanded. Typographic, sans, dim. */
    .archive-author-name::before {
      content: '+';
      font-family: var(--sans);
      font-size: var(--step-0);
      font-weight: 400;
      color: var(--gray-secondary);
      flex-shrink: 0;
      line-height: 1;
      transition: color 0.25s var(--ease-out), transform 0.3s var(--ease-out);
      width: 0.8em;
      display: inline-block;
    }
    /* Default is already --dark; hover/expanded use a subtle hint.
       :hover wrapped to prevent iOS hover-emulation tap delay
       (see feedback_ios_hover_quirk memory). */
    @media (hover: hover) and (pointer: fine) {
      .archive-author-name:hover::before { color: var(--dark); }
    }
    .archive-author-item.expanded .archive-author-name::before {
      content: '−';
      color: var(--dark);
    }
    .archive-author-count {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      flex-shrink: 0;
      white-space: nowrap;
      margin-left: auto;
      padding-left: 8px;
    }
    /* Collapsed → expanded: animate max-height + opacity for a soft drop-in.
       max-height isn't ideal (browser doesn't know intrinsic height), but a
       generous cap (1200px is more than any author's 8-article list needs)
       keeps the animation smooth without measuring. break-inside on the
       parent .archive-author-item keeps the multi-column flow stable. */
    /* Snap expansion — was max-height transition, but inside a multi-
       column .archive-letter-group (3-col desktop / 2-col tablet) the
       column rebalance ran every animation frame for 350 ms → felt
       super slow on mid-range mobile. Now: display none → block + a
       brief fade-in via animation. Net visual is barely different,
       perceived performance is night-and-day. */
    .archive-author-articles { display: none; }
    .archive-author-item.expanded .archive-author-articles {
      display: block;
      animation: authorArticlesFadeIn 0.22s var(--ease-out);
    }
    @keyframes authorArticlesFadeIn {
      from { opacity: 0; transform: translateY(-2px); }
      to   { opacity: 1; transform: translateY(0); }
    }
    /* Reserved — expanded state styling handled above via color only. */
    /* Articles inside author accordion — aligned to the AUTHOR NAME's text
       (past the +/− marker). Hairline DIVIDERS between rows do the grouping
       so a separate vertical line would be redundant. */
    .archive-author-articles-grid {
      display: flex;
      flex-direction: column;
      gap: 2px;
      margin: 10px 0 6px calc(0.8em + 10px);
      padding-left: 0;
    }
    .archive-author-article {
      cursor: pointer;
      padding: 6px 0;
      transition: color 0.25s var(--ease-out);
    }
    /* Hairline divider between articles — keeps the list scannable without
       inflating vertical rhythm. First article has no top border. */
    .archive-author-article + .archive-author-article {
      border-top: 1px solid rgb(238,238,238);
      padding-top: 8px;
      margin-top: 2px;
    }
    .archive-author-article-body {
      display: flex;
      flex-direction: column;
      gap: 1px;
    }
    @media (hover: hover) and (pointer: fine) {
      .archive-author-article:hover .archive-author-article-title { color: var(--dark); }
    }
    /* Meta (issue + year) on top — small sans caps, journalistic byline feel.
       Title below — serif, dim until hover. */
    .archive-author-article-meta {
      font-family: var(--sans);
      font-size: var(--step--2);
      color: var(--gray-secondary);
      letter-spacing: 0.06em;
      text-transform: uppercase;
      line-height: 1.4;
    }
    .archive-author-article-title {
      font-family: var(--serif);
      font-size: var(--step-0);
      line-height: 1.3;
      color: var(--gray-dim);
      transition: color 0.25s var(--ease-out);
    }
    .archive-author-more {
      margin-left: calc(0.8em + 10px);
    }
    .archive-author-more {
      display: inline-block;
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      padding: 4px 0;
      margin-left: 18px;
      cursor: pointer;
      transition: color 0.15s;
      border-bottom: 1px solid transparent;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-author-more:hover { color: var(--dark); border-bottom-color: var(--dark); }
    }
    /* Letter nav — sticky 2nd-submenu, single line, horizontal scroll if needed. */
    .archive-letter-nav {
      display: flex;
      flex-wrap: nowrap;
      align-items: center;
      justify-content: center;
      gap: clamp(14px, 1.5vw, 24px);
      padding: 14px var(--content-margin);
      margin: 0 calc(-1 * var(--content-margin)) clamp(20px, 2vw, 32px);
      border-bottom: var(--rule);
      font-family: var(--sans);
      position: sticky;
      top: calc(var(--header-height-shrunk) + 56px);
      background: var(--white);
      z-index: 9;
      will-change: transform;
      transform: translateZ(0);
      backface-visibility: hidden;
      min-height: 56px;
      box-sizing: border-box;
      transition: padding 0.45s var(--ease-out), min-height 0.45s var(--ease-out), top 0.4s var(--ease-out);
      contain: paint;
    }
    body.archive-compact .archive-letter-nav {
      top: calc(var(--header-height-shrunk) + 40px);
      padding: 8px var(--content-margin);
      min-height: 40px;
    }
    body.archive-past-top .archive-letter-nav {
      top: var(--header-height-shrunk);
    }
    .archive-letter-scroll {
      flex: 1;
      min-width: 0;
      display: flex;
      justify-content: safe center;
      gap: clamp(12px, 1.4vw, 22px);
      overflow-x: auto;
      overflow-y: hidden;
      scrollbar-width: none;
      -ms-overflow-style: none;
      white-space: nowrap;
    }
    .archive-letter-scroll::-webkit-scrollbar { display: none !important; height: 0 !important; }
    .archive-letter-scroll { padding-bottom: 24px; margin-bottom: -24px; }
    .archive-letter-nav a {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 400;
      color: var(--gray-dim);
      text-decoration: none;
      padding: 4px 2px;
      transition: color 0.25s var(--ease-out), font-weight 0.25s var(--ease-out);
      flex-shrink: 0;
    }
    @media (hover: hover) and (pointer: fine) {
    .archive-letter-nav a:hover { color: var(--dark); }
    }
    .archive-letter-nav a.active { color: var(--dark); font-weight: 600; }

    /* ═══════════════════════════════════════════════
       AUTHOR PAGE
       ═══════════════════════════════════════════════ */
    .author-page {
      padding-top: var(--header-height);
    }
    .author-page-inner {
      padding: var(--section-pad-y) var(--content-margin) var(--section-pad-y);
      max-width: var(--container-max);
      margin: 0 auto;
    }
    .author-header {
      display: flex;
      gap: 40px;
      align-items: flex-start;
      margin-bottom: clamp(48px, 6vw, 88px);
    }
    .author-photo {
      width: 112px;
      height: 112px;
      object-fit: cover;
      filter: grayscale(100%);
      flex-shrink: 0;
    }
    .author-header-text { flex: 1; }
    .author-name {
      font-family: var(--serif);
      font-size: var(--step-3);
      line-height: 1.1;
      letter-spacing: -0.015em;
      font-weight: 500;
      color: var(--dark);
      margin-bottom: 16px;
    }
    .author-type {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      text-transform: lowercase;
      margin-bottom: 6px;
    }
    .author-count {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
    }
    .author-bio {
      font-family: var(--serif);
      /* Раньше фиксированный 17px — меньше даже --step--1 (16-18px). Bio
         на странице автора — основной читаемый блок профиля, должен иметь
         lead-typography размер. --step-0 (18-21px fluid). Roman 2026-05-26. */
      font-size: var(--step-0);
      line-height: 1.5;
      color: var(--dark);
      margin-bottom: 24px;
      padding-bottom: 24px;
      border-bottom: var(--rule);
      max-width: var(--content-width);
    }
    /* Author page article grid — same card layout as the home feed */
    .author-articles-grid {
      margin-top: clamp(32px, 4vw, 64px);
    }
    /* Legacy class kept for fallback — same 3-col grid */
    .author-articles-list {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: clamp(20px, 2vw, 36px) clamp(32px, 3.5vw, 64px);
    }
    @media (max-width: 1023px) { .author-articles-list { grid-template-columns: repeat(2, 1fr); } }
    @media (max-width: 640px)  { .author-articles-list { grid-template-columns: 1fr; } }
    .author-article-row {
      cursor: pointer;
      padding: 10px 24px 10px 0;
      transition: color 0.2s;
    }
    .author-article-row-body {
      display: flex;
      flex-direction: column;
      gap: 4px;
    }
    @media (max-width: 480px) {
      .author-article-row { padding: 12px 0; border-bottom: var(--rule); }
    }
    @media (hover: hover) and (pointer: fine) {
    .author-article-row:hover .author-article-title { color: var(--gray-dim); }
    }
    .author-article-title {
      font-family: var(--serif);
      font-size: clamp(17px, 1.4vw, 21px);
      line-height: 1.35;
      font-weight: 400;
      color: var(--dark);
      transition: color 0.2s;
    }
    .author-article-rubric {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: var(--gray-dim);
    }
    .author-article-issue {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      letter-spacing: 0.02em;
    }

    /* ═══════════════════════════════════════════════
       ABOUT PAGE
       ═══════════════════════════════════════════════ */
    .about-page {
      padding-top: var(--header-height);
    }
    .about-page-inner {
      max-width: var(--content-width);
      margin: 0 auto;
      padding: 0 24px 80px;
    }
    /* Sticky sub-nav — uses the same visual contract as Archive tabs:
       sans step--1, gap clamp(28-56px), min-height 64px, centered,
       border-bottom rule, sticky under the shrunk header.
       Active link is dark + 600 weight; default gray-dim. */
    /* About sub-navigation strip — sticky horizontal scroller.
       CANONICAL HIDDEN-SCROLLBAR PATTERN: WebKit `display:none` alone
       isn't enough on iOS Safari when the element scrolls and has
       `position: sticky` — Safari sometimes still renders the bar. The
       fix is a TWO-LAYER structure: an outer container with `contain:
       paint` + `overflow: hidden` clips at its border-box; the inner
       scroller has `padding-bottom + margin-bottom: -PADDING` which
       pushes the native scrollbar BELOW the parent's visible area,
       where contain:paint clips it. (Same pattern as .archive-year-bar
       / .archive-year-scroll — see feedback_no_horizontal_scrollbars
       memory.) */
    .about-subnav {
      position: sticky;
      top: var(--header-height-shrunk);
      background: var(--white);
      z-index: 10;
      border-bottom: var(--rule);
      margin: 0 calc(-1 * var(--content-margin)) clamp(28px, 3vw, 40px);
      min-height: 64px;
      overflow: hidden;       /* clips scrollbar pushed below by inner */
      contain: paint;          /* iOS belt-and-braces */
      display: flex;
      align-items: center;
      box-sizing: border-box;
    }
    .about-subnav-scroll {
      display: flex;
      justify-content: safe center;
      align-items: center;
      flex-wrap: nowrap;
      gap: clamp(28px, 3vw, 56px);
      padding: 20px var(--content-margin) 44px;  /* extra 24px below */
      margin-bottom: -24px;                       /* pull inner up 24px */
      font-family: var(--sans);
      font-size: var(--step--1);
      width: 100%;
      box-sizing: border-box;
      overflow-x: auto;
      overflow-y: hidden;
      white-space: nowrap;
      scrollbar-width: none;
      -ms-overflow-style: none;
      will-change: transform;
      transform: translateZ(0);
      backface-visibility: hidden;
    }
    .about-subnav-scroll::-webkit-scrollbar { display: none !important; height: 0 !important; width: 0 !important; -webkit-appearance: none; }
    .about-subnav a {
      color: var(--gray-dim);
      text-decoration: none;
      /* Inter loads as a variable font (wght@100..900 axis), so font-weight
         IS smoothly animatable here. Combine weight + color for a richer
         active-state cue without a visible step. */
      transition: color 0.25s var(--ease-out), font-weight 0.25s var(--ease-out);
      white-space: nowrap;
      padding: 4px 0;
      font-weight: 400;
    }
    @media (hover: hover) and (pointer: fine) {
    .about-subnav a:hover { color: var(--dark); }
    }
    .about-subnav a.active { color: var(--dark); font-weight: 600; }
    .about-page section {
      scroll-margin-top: calc(var(--header-height-shrunk) + 70px);
    }
    .about-page section + section {
      margin-top: clamp(48px, 6vw, 80px);
    }
    .about-page h2 {
      font-family: var(--serif);
      font-size: clamp(22px, 2vw, 28px);
      font-weight: 500;
      color: var(--dark);
      margin: 0 0 20px;
    }
    .about-page p {
      font-family: var(--serif);
      font-size: var(--body-size);
      line-height: var(--body-lh);
      margin-bottom: 16px;
      color: var(--dark);
    }
    .about-page .about-lead {
      font-size: clamp(28px, 3.2vw, 50px);
      line-height: 1.22;
      font-weight: 400;
      text-align: center;
      color: var(--dark);
      margin: clamp(20px, 3vw, 40px) auto 0;
      padding-bottom: 24px;
      /* Break out of 760px container on desktop */
      width: min(960px, calc(100vw - 48px));
      position: relative;
      left: 50%;
      transform: translateX(-50%);
    }
    .about-page .about-lead-divider {
      width: 48px;
      height: 1px;
      background: var(--dark);
      margin: 0 auto 28px;
    }
    /* Numbers banner — 4 key stats. Each cell: serif number + sans label. */
    .about-numbers {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: clamp(16px, 3vw, 48px);
      margin: clamp(36px, 4vw, 56px) 0 clamp(28px, 3vw, 40px);
      padding: clamp(24px, 3vw, 32px) 0;
      border-top: 1px solid rgb(232,232,232);
      border-bottom: 1px solid rgb(232,232,232);
    }
    .about-number-item { text-align: center; }
    .about-number-value {
      font-family: var(--serif);
      font-size: clamp(28px, 4vw, 44px);
      line-height: 1.05;
      color: var(--dark);
      margin-bottom: 6px;
      letter-spacing: -0.01em;
    }
    .about-number-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      line-height: 1.3;
    }
    @media (max-width: 640px) {
      .about-numbers { grid-template-columns: repeat(2, 1fr); gap: 28px 16px; }
    }
    /* Geography line — small sans below numbers, lists distribution cities. */
    .about-geo {
      font-family: var(--sans);
      font-size: var(--step--1);
      line-height: 1.55;
      color: var(--gray-dim);
      text-align: center;
      margin: 0 auto;
      max-width: 720px;
    }
    /* Editorial list — label + names per row, hairline dividers. */
    .about-editorial-list {
      margin-top: 0;
    }
    .about-editorial-row {
      display: grid;
      grid-template-columns: minmax(200px, 240px) 1fr;
      gap: 24px;
      padding: 16px 0;
      border-bottom: 1px solid rgb(238,238,238);
    }
    .about-editorial-row:first-child { border-top: 1px solid rgb(238,238,238); }
    .about-editorial-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      line-height: 1.5;
    }
    .about-editorial-names {
      font-family: var(--serif);
      font-size: var(--step-0);
      line-height: 1.4;
      color: var(--dark);
    }
    @media (max-width: 640px) {
      .about-editorial-row {
        grid-template-columns: 1fr;
        gap: 4px;
        padding: 14px 0;
      }
    }
    /* Where-to-buy — intro then cities grouped, shops as compact rows. */
    .about-buy-intro {
      color: var(--gray-dim);
      font-size: var(--step-0);
      line-height: 1.55;
      margin-bottom: clamp(20px, 2vw, 28px);
    }
    .about-cities-list {
      display: flex;
      flex-direction: column;
      gap: clamp(24px, 3vw, 36px);
    }
    .about-city-group h3 {
      font-family: var(--serif);
      font-size: clamp(20px, 1.6vw, 22px);
      font-weight: 500;
      color: var(--dark);
      margin: 0 0 12px;
    }
    .about-city-shops {
      display: flex;
      flex-direction: column;
      gap: 6px;
    }
    /* Shop row — small subtle dot bullet, regular weight, so it doesn't
       compete with the city header (which is the only bold serif here). */
    .about-shop-row {
      font-family: var(--sans);
      font-size: var(--step--1);
      line-height: 1.5;
      padding-left: 14px;
      position: relative;
    }
    .about-shop-row::before {
      content: '·';
      position: absolute;
      left: 4px;
      top: 0;
      color: var(--gray-secondary);
      font-size: 1.2em;
      line-height: 1.4;
    }
    .about-shop-name {
      color: var(--dark);
      font-weight: 400;
    }
    .about-shop-name[href] {
      border-bottom: 1px solid transparent;
      transition: border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .about-shop-name[href]:hover { border-bottom-color: var(--dark); }
    }
    .about-shop-addr {
      color: var(--gray-dim);
    }
    /* Partners — grayscale logo grid. Editorial intro + 4-col grid on desktop,
       2-col on mobile. Grayscale + dimmed opacity → restored on hover. No
       borders, no captions; the logos themselves are recognizable. */
    .about-partners-intro {
      font-family: var(--serif);
      font-size: var(--body-size);
      line-height: var(--body-lh);
      color: var(--dark);
      max-width: 720px;
      margin-bottom: clamp(28px, 3vw, 40px);
    }
    .about-partners-grid {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: clamp(20px, 3vw, 40px);
      align-items: center;
      margin-bottom: clamp(28px, 3vw, 36px);
    }
    @media (max-width: 640px) {
      .about-partners-grid { grid-template-columns: repeat(2, 1fr); }
    }
    .about-partner {
      display: flex;
      align-items: center;
      justify-content: center;
      height: clamp(56px, 7vw, 80px);
      filter: grayscale(1);
      opacity: 0.72;
      transition: filter 0.25s ease, opacity 0.25s ease, transform 0.25s var(--ease-out);
    }
    @media (hover: hover) and (pointer: fine) {
    .about-partner:hover {
      filter: grayscale(0);
      opacity: 1;
    }
    }
    @media (hover: hover) and (pointer: fine) {
    a.about-partner:hover { transform: translateY(-1px); }
    }
    .about-partner img {
      max-width: 100%;
      max-height: 100%;
      width: auto;
      height: auto;
      object-fit: contain;
    }
    /* Some SVGs are drawn in white/near-white for dark backgrounds. On the
       white About page they need to be inverted to become visible black. */
    .about-partner.is-invert img { filter: invert(1); }
    /* Институция без логотипа (pop/off/art) — название текстом в плитке грида. */
    .about-partner.is-text { filter: none; }
    .about-partner.is-text span {
      font-family: var(--sans);
      font-size: clamp(15px, 1.4vw, 18px);
      letter-spacing: 0.01em;
      color: var(--dark);
      white-space: nowrap;
    }
    /* Институции, поддерживавшие журнал ранее — текстовый список под гридом. */
    .about-partners-past {
      margin-top: clamp(26px, 4vw, 40px);
    }
    .about-partners-past-title {
      font-family: var(--sans);
      font-size: var(--step--1);
      text-transform: uppercase;
      letter-spacing: 0.06em;
      color: var(--gray-dim);
      margin-bottom: 8px;
    }
    /* .about-page p (serif/dark) перебивает по специфичности — поднимаем scope. */
    .about-page .about-partners-past-list {
      font-family: var(--sans);
      font-size: clamp(14px, 1.1vw, 16px);
      line-height: 1.7;
      color: var(--gray-dim);
      margin-bottom: 0;
    }
    .about-partners-note {
      font-family: var(--sans);
      font-size: var(--step--2);
      color: var(--gray-dim);
      text-align: center;
      padding-top: clamp(16px, 2vw, 24px);
      border-top: 1px solid rgb(238,238,238);
    }
    .about-partners-note a {
      color: var(--gray-dim);
      border-bottom: 1px solid transparent;
      transition: color 0.2s, border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .about-partners-note a:hover {
      color: var(--dark);
      border-bottom-color: var(--dark);
    }
    }

    /* Contacts — final focal block, centered, journalistic restraint. */
    .about-contacts {
      text-align: center;
      font-family: var(--serif);
      font-size: clamp(17px, 1.5vw, 20px);
      line-height: 1.6;
      color: var(--dark);
      padding: clamp(32px, 4vw, 48px) 0 0;
    }
    .about-contacts > div { margin-bottom: 4px; }
    .about-contacts a {
      color: var(--dark);
      text-decoration: none;
      border-bottom: 1px solid rgba(20,20,20,0.15);
      transition: border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .about-contacts a:hover { border-bottom-color: var(--dark); }
    }

    /* ═══════════════════════════════════════════════
       SEARCH PAGE
       ═══════════════════════════════════════════════ */
    .search-page {
      padding-top: var(--header-height);
    }
    .search-page-inner {
      padding: clamp(20px, 2vw, 32px) var(--content-margin) clamp(40px, 4.1vw, 60px);
      margin: 0 auto;
    }
    /* Page titles hidden — context already in nav */
    .search-page-title { display: none; }
    .subscribe-page .search-page-title {
      display: block;
      font-family: var(--serif);
      font-size: clamp(28px, 2.4vw, 36px);
      font-weight: 500;
      color: var(--dark);
      margin-bottom: 16px;
      text-align: center;
    }

    /* Subscribe page — block centered vertically + horizontally.
       Mobile: pad more generously and keep input/button comfortable. */
    .subscribe-page {
      min-height: calc(100vh - var(--header-height));
      padding: calc(var(--header-height) + clamp(24px, 4vw, 64px)) var(--content-margin) clamp(40px, 4vw, 60px);
      display: flex;
      align-items: center;
      justify-content: center;
      background: var(--white); /* explicit — иначе html dark просвечивает на iOS если subscribe-page не растягивается до viewport */
    }
    /* Mobile: content наверх под header, не центрировать вертикально.
       На viewport 800px формаhe ~140px — `align-items: center` оставлял
       ~300px пустого пространства сверху (Roman 2026-05-26 скрин). */
    @media (max-width: 768px) {
      .subscribe-page {
        align-items: flex-start;
        padding-top: calc(var(--header-height) + 32px);
      }
    }
    .subscribe-page-inner {
      max-width: 560px;
      width: 100%;
      text-align: center;
    }
    .subscribe-page-title {
      font-family: var(--serif);
      font-size: clamp(28px, 4vw, 44px);
      font-weight: 500;
      letter-spacing: -0.01em;
      color: var(--dark);
      margin: 0 0 16px;
      line-height: 1.1;
    }
    .subscribe-page-subtitle {
      font-family: var(--sans);
      font-size: var(--step-0);
      color: var(--gray-dim);
      margin: 0 0 28px;
      line-height: 1.5;
    }
    .search-input-wrap {
      position: relative;     /* anchor для .search-autocomplete dropdown */
      margin-bottom: 24px;
      padding-bottom: 18px;
      border-bottom: var(--rule);
    }
    /* Autocomplete dropdown — abs-pos под input. Скрыт по умолчанию,
       показывается JS при наборе с >=2 chars. */
    .search-autocomplete {
      display: none;
      position: absolute;
      top: calc(100% - 18px);   /* compensate для padding-bottom родителя */
      left: 0;
      right: 0;
      background: var(--white);
      border: 1px solid var(--gray-border);
      border-top: none;
      z-index: 100;
      max-height: 400px;
      overflow-y: auto;
      scrollbar-width: none;
    }
    .search-autocomplete::-webkit-scrollbar { display: none; }
    .search-ac-item {
      display: flex;
      align-items: center;
      gap: 12px;
      width: 100%;
      padding: 10px 14px;
      border: none;
      background: var(--white);
      cursor: pointer;
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--dark);
      text-align: left;
      border-bottom: 1px solid var(--gray-border);
      transition: background 0.12s;
      -webkit-appearance: none;
      appearance: none;
      border-radius: 0;
    }
    .search-ac-item:last-child { border-bottom: none; }
    @media (hover: hover) and (pointer: fine) {
      .search-ac-item:hover { background: rgb(248,248,248); }
    }
    .search-ac-icon {
      font-family: var(--sans);
      font-size: var(--step--2);
      color: var(--gray-dim);
      font-weight: 600;
      width: 14px;
      text-align: center;
      flex-shrink: 0;
    }
    /* Тип подсказки словом (тема/автор/рубрика) вместо криптического #/@/·. */
    .search-ac-kind {
      font-family: var(--sans);
      font-size: 10px;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      color: var(--gray-dim);
      background: rgba(0,0,0,0.05);
      border-radius: 3px;
      padding: 2px 6px;
      min-width: 52px;
      text-align: center;
      flex-shrink: 0;
    }
    .search-ac-item--author .search-ac-kind { color: var(--dark); background: rgba(0,0,0,0.09); }
    .search-ac-label {
      flex: 1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .search-input {
      font-family: var(--serif);
      font-size: clamp(22px, 2.2vw, 32px);
      width: 100%;
      padding: 6px 0 10px;
      border: none;
      background: transparent;
      color: var(--dark);
      outline: none;
    }
    .search-input::placeholder {
      color: var(--gray-secondary);
      font-style: italic;
    }
    .search-context-badge {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      margin-top: 10px;
      letter-spacing: 0.03em;
      display: inline-block;
    }
    .search-context-badge strong { color: var(--dark); font-weight: 500; }
    .search-results-count {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      color: var(--gray-dim);
      margin-bottom: 20px;
      text-transform: uppercase;
      letter-spacing: 0.12em;
    }
    .search-section-heading {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      margin: 28px 0 14px;
      padding-bottom: 0;
      border-bottom: none;
    }
    .semantic-loader {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 12px 0;
      font-family: var(--sans);
      font-size: var(--step--2);
      letter-spacing: 0.12em;
      text-transform: uppercase;
      color: var(--gray-dim);
    }
    .semantic-loader-bar {
      width: 24px;
      height: 1px;
      background: var(--gray-secondary);
      animation: pulse 1.4s ease-in-out infinite;
    }
    /* Search results — TOC-style с разделителями как в архиве. Title в serif,
       meta в sans uppercase, excerpt в serif italic (precis). */
    /* Карточка автора над результатами — ведёт на страницу автора. */
    .search-entities {
      display: flex;
      flex-direction: column;
      gap: 8px;
      margin-bottom: 18px;
    }
    .search-entity-card {
      display: flex;
      align-items: baseline;
      gap: 12px;
      padding: 14px 16px;
      border: 1px solid var(--gray-border);
      border-left: 3px solid var(--dark);
      cursor: pointer;
      transition: background 0.15s, border-color 0.15s;
    }
    @media (hover: hover) and (pointer: fine) {
      .search-entity-card:hover { background: rgb(248,248,248); }
    }
    .search-entity-kind {
      font-family: var(--sans);
      font-size: 10px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: var(--gray-dim);
      flex-shrink: 0;
    }
    .search-entity-name {
      font-family: var(--serif);
      font-size: clamp(19px, 0.6vw + 17px, 24px);
      line-height: 1.2;
      color: var(--dark);
    }
    .search-entity-meta {
      font-family: var(--sans);
      font-size: var(--step--2);
      color: var(--gray-secondary);
      letter-spacing: 0.03em;
      margin-left: auto;
      white-space: nowrap;
      flex-shrink: 0;
    }
    @media (max-width: 560px) {
      .search-entity-card { flex-wrap: wrap; gap: 6px 12px; }
      .search-entity-meta { margin-left: 0; width: 100%; }
    }

    .search-result-item {
      padding: 14px 0;
      cursor: pointer;
      transition: color 0.2s;
      border-bottom: 1px solid var(--gray-border);
    }
    .search-result-item:last-child { border-bottom: none; }
    .search-result-item-body {
      display: flex;
      flex-direction: column;
      gap: 6px;
    }
    @media (hover: hover) and (pointer: fine) {
    .search-result-item:hover .search-result-title { color: var(--gray-dim); }
    }
    .search-result-title {
      font-family: var(--serif);
      font-size: clamp(18px, 0.5vw + 16.5px, 22px);
      line-height: 1.3;
      color: var(--dark);
      transition: color 0.2s;
    }
    .search-result-meta {
      font-family: var(--sans);
      font-size: var(--step--2);
      color: var(--gray-secondary);
      letter-spacing: 0.04em;
      text-transform: uppercase;
    }
    .search-result-excerpt {
      font-family: var(--serif);
      font-size: var(--step--1);
      line-height: 1.5;
      color: var(--gray-dim);
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
      margin-top: 2px;
    }
    /* Search match highlight — тонкое выделение в духе сайта. Не жёлтое
       цветное (детское), а подчёркивание dark цветом + лёгкая dark тонировка.
       Чёткое но не кричащее. Roman 2026-05-26. */
    .search-result-title mark,
    .search-result-excerpt mark,
    .ai-answer-text mark {
      background: transparent;
      color: var(--dark);
      font-weight: 600;
      padding: 0;
      border-radius: 0;
      box-shadow: inset 0 -2px 0 var(--dark);
    }

    /* ═══════════════════════════════════════════════
       SEARCH RESULTS — single column layout (2026-05-26 redesign).
       Раньше был grid 2col: text слева, AI+semantic справа. Юзер часто
       не смотрел в правую колонку → AI ответ и semantic results были
       невидимы. Теперь: AI ответ ВВЕРХУ (full width), потом count +
       inline фильтры (Год/Рубрика), потом combined feed (text first,
       semantic после divider).
       ═══════════════════════════════════════════════ */
    /* AI справка — collapsible <details>. Стиль: типографика сайта, без
       панелей/фоновой подложки. Заголовок-кнопка как archive section heading. */
    .search-ai-details {
      margin: 0 0 clamp(28px, 3vw, 44px);
    }
    .search-ai-summary {
      list-style: none;
      cursor: pointer;
      display: flex;
      align-items: baseline;
      gap: 14px;
      padding: 14px 0;
      border-top: 1px solid var(--gray-border);
      border-bottom: 1px solid var(--gray-border);
      user-select: none;
      transition: border-color 0.2s ease;
    }
    .search-ai-summary::-webkit-details-marker { display: none; }
    .search-ai-summary::marker { content: ''; }
    .search-ai-summary-label {
      flex-shrink: 0;
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--dark);
    }
    .search-ai-summary-status {
      flex: 1 1 auto;
      min-width: 0;
      font-family: var(--serif);
      font-size: var(--step--1);
      font-style: italic;
      color: var(--gray-dim);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .search-ai-summary-chevron {
      flex-shrink: 0;
      width: 10px;
      height: 10px;
      border-right: 1px solid var(--gray-dim);
      border-bottom: 1px solid var(--gray-dim);
      transform: rotate(45deg);
      transition: transform 0.25s ease;
      margin-bottom: 4px;   /* visually align with text baseline */
    }
    @media (hover: hover) and (pointer: fine) {
      .search-ai-summary:hover { border-color: var(--dark); }
      .search-ai-summary:hover .search-ai-summary-chevron { border-color: var(--dark); }
      .search-ai-summary:hover .search-ai-summary-status { color: var(--dark); }
    }
    .search-ai-details[open] .search-ai-summary-chevron {
      transform: rotate(-135deg);
      margin-bottom: 0;
      margin-top: 4px;
    }
    /* Когда раскрыто — preview-текст не нужен, body начинается с того же
       первого предложения. Иначе задвоение (Roman 2026-05-27 скрин). */
    .search-ai-details[open] .search-ai-summary-status { display: none; }
    .search-ai-block {
      padding: 20px 0 4px;
    }
    /* Loader в теле справки — виден когда details открыт + ещё нет первого
       token'а от стриминга. После arrival of token заменяется threadHtml'ом. */
    .ai-answer-loader {
      display: flex;
      align-items: center;
      gap: 10px;
      font-family: var(--sans);
      font-size: var(--step--2);
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      padding: 4px 0 16px;
    }
    .search-results-header {
      display: flex;
      flex-wrap: wrap;
      align-items: baseline;
      gap: 14px 18px;
      margin-bottom: 20px;
      padding-bottom: 14px;
      border-bottom: var(--rule);
    }
    .search-results-filters {
      display: flex;
      flex-wrap: wrap;
      gap: 6px 8px;
      align-items: center;
    }
    /* Минималистичные filter chips в духе archive-year-btn — просто текст
       без рамок. Активный = dark color + bold. Hover = dark color. */
    .search-filter-chip {
      font-family: var(--sans);
      font-size: var(--step--1);
      padding: 2px 0;
      margin: 0 8px 0 0;
      border: none;
      background: none;
      color: var(--gray-dim);
      cursor: pointer;
      letter-spacing: 0.02em;
      transition: color 0.2s, font-weight 0.2s;
      -webkit-appearance: none;
      appearance: none;
    }
    @media (hover: hover) and (pointer: fine) {
      .search-filter-chip:hover { color: var(--dark); }
    }
    .search-filter-chip.active {
      color: var(--dark);
      font-weight: 600;
    }
    .search-filter-sep {
      display: inline-block;
      width: 1px;
      height: 12px;
      background: var(--gray-border);
      margin: 0 8px 0 4px;
      vertical-align: middle;
    }
    .search-results-list {
      display: block;
    }
    .search-more-divider {
      font-family: var(--sans);
      font-size: var(--step--1);
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      padding: 28px 0 16px;
      border-top: var(--rule);
      margin-top: 24px;
    }
    /* ═══════════════════════════════════════════════
       AI CHAT THREAD — multi-turn conversation.
       Каждая turn: question (italic, gray dim) + answer (regular).
       Первый вопрос совпадает с основным search query и не показывается.
       ═══════════════════════════════════════════════ */
    .ai-chat-question {
      font-family: var(--serif);
      font-size: var(--step-0);
      font-style: italic;
      color: var(--gray-dim);
      padding: 18px 0 8px;
      border-top: 1px solid var(--gray-border);
      margin-top: 12px;
    }
    .ai-chat-question::before {
      content: '→ ';
      color: var(--dark);
      font-style: normal;
    }
    .ai-chat-answer {
      margin: 0 0 4px;
    }
    .ai-chat-pending {
      margin-top: 12px;
    }
    .ai-chat-pending .ai-chat-question {
      margin-top: 0;
    }
    /* ═══════════════════════════════════════════════
       AI FOLLOW-UP — uточняющий вопрос под AI ответом.
       Lightweight: каждый submit = новый search query с follow-up text.
       ═══════════════════════════════════════════════ */
    .ai-followup {
      display: flex;
      align-items: stretch;
      margin-top: 18px;
      padding-top: 14px;
      border-top: 1px solid var(--gray-border);
      gap: 0;
    }
    .ai-followup-input {
      flex: 1;
      font-family: var(--sans);
      font-size: var(--step--1);
      padding: 8px 12px;
      border: 1px solid var(--gray-border);
      border-right: none;
      background: var(--white);
      color: var(--dark);
      outline: none;
      transition: border-color 0.2s ease;
      -webkit-appearance: none;
      appearance: none;
      border-radius: 0;
    }
    .ai-followup-input:focus { border-color: var(--dark); }
    .ai-followup-submit {
      font-family: var(--sans);
      font-size: var(--step-0);
      padding: 0 18px;
      background: var(--dark);
      color: var(--white);
      border: 1px solid var(--dark);
      cursor: pointer;
      transition: background 0.2s;
      -webkit-appearance: none;
      appearance: none;
      border-radius: 0;
    }
    @media (hover: hover) and (pointer: fine) {
    .ai-followup-submit:hover { background: var(--black); }
    }
    /* Legacy 2-col classes — оставлены для совместимости если где-то
       ещё всплывут. На /search больше не используются. */
    .search-results-grid {
      display: block;
    }
    .search-results-grid--single { display: block; }
    .search-results-primary { }

    /* ═══════════════════════════════════════════════
       AI ANSWER (inline in search)
       ═══════════════════════════════════════════════ */
    .ai-answer-text {
      font-family: var(--serif);
      /* Fluid AI-answer body: 18-20px. Slightly above UI default for a
         comfortable read on touch screens. */
      font-size: clamp(18px, 0.4vw + 16.5px, 20px);
      line-height: 1.65;
      color: var(--dark);
      padding: 8px 0 20px;
    }
    .ai-answer-text p { margin: 0 0 1.2em; }
    .ai-answer-text p:last-child { margin-bottom: 0; }
    .ai-answer-text a {
      color: var(--dark);
      border-bottom: 1px solid rgb(180,180,180);
      text-decoration: none;
    }
    @media (hover: hover) and (pointer: fine) {
    .ai-answer-text a:hover { border-bottom-color: var(--dark); }
    }
    .ai-answer-text strong { font-weight: 600; }
    .ai-answer-text .ai-h1 { font-size: clamp(20px, 0.6vw + 17.5px, 24px); font-weight: 700; margin: 14px 0 6px; font-family: var(--serif); }
    .ai-answer-text .ai-h2 { font-size: clamp(18px, 0.4vw + 16.5px, 20px); font-weight: 600; margin: 12px 0 4px; font-family: var(--serif); }
    .ai-answer-text .ai-list { margin: 6px 0 10px 18px; padding: 0; }
    .ai-answer-text .ai-list li { margin-bottom: 4px; }
    /* Numbered citation [N] inside AI answer body — superscript, sans, gray,
       hover dark. Same pattern as .article-body sup для визуального единства. */
    .ai-answer-text .ai-cite {
      font-family: var(--sans);
      font-size: 0.65em;
      line-height: 0;
      vertical-align: super;
      color: var(--gray-dim);
      letter-spacing: 0;
      margin: 0 1px;
    }
    .ai-answer-text .ai-cite a {
      color: var(--gray-dim);
      text-decoration: none;
      border-bottom: none;
      font-weight: 500;
      padding: 0 1px;
    }
    @media (hover: hover) and (pointer: fine) {
      .ai-answer-text .ai-cite a:hover { color: var(--dark); }
    }
    /* Sources block — нумерованный список со ссылками на статьи в стиле сайта.
       Раньше chips (.tag-chip) — выбивались из общей типографики и не давали
       достаточно контекста (только title + №). Теперь: title в serif, мета в
       sans под ней. Номера слева ([1], [2]) — связывают с inline [N] сносками. */
    .ai-answer-sources {
      margin: 20px 0 4px;
      padding-top: 16px;
      border-top: 1px solid var(--gray-border);
    }
    .ai-answer-sources-label {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      color: var(--gray-dim);
      margin-bottom: 10px;
    }
    .ai-source-list {
      list-style: none;
      margin: 0;
      padding: 0;
      counter-reset: ai-src;
    }
    .ai-source-item {
      counter-increment: ai-src;
      border-bottom: 1px solid var(--gray-border);
    }
    .ai-source-item:last-child { border-bottom: none; }
    .ai-source-link {
      display: grid;
      grid-template-columns: 32px 1fr;
      gap: 4px 14px;
      align-items: baseline;
      padding: 12px 0;
      text-decoration: none;
      color: inherit;
      transition: color 0.2s ease;
    }
    .ai-source-link::before {
      content: '[' counter(ai-src) ']';
      grid-row: 1 / span 2;
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      letter-spacing: 0;
      font-weight: 500;
    }
    .ai-source-title {
      font-family: var(--serif);
      font-size: clamp(16px, 0.3vw + 14.5px, 18px);
      line-height: 1.35;
      color: var(--dark);
      transition: color 0.2s ease;
    }
    .ai-source-meta {
      font-family: var(--sans);
      font-size: var(--step--2);
      color: var(--gray-dim);
      letter-spacing: 0.02em;
    }
    @media (hover: hover) and (pointer: fine) {
      .ai-source-link:hover .ai-source-title { color: var(--gray-dim); }
      .ai-source-link:hover::before { color: var(--dark); }
    }
    .search-examples {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin: 16px 0 32px;
    }
    .search-examples-label {
      font-family: var(--sans);
      font-size: var(--step--2);
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: var(--gray-dim);
      width: 100%;
      margin-bottom: 2px;
    }
    .search-example {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      background: none;
      border: var(--rule);
      padding: 4px 10px;
      cursor: pointer;
      transition: all 0.15s ease;
    }
    @media (hover: hover) and (pointer: fine) {
    .search-example:hover {
      border-color: var(--dark);
      color: var(--dark);
    }
    }
    .search-result-tags { margin-top: 6px; }
    .search-result-tags .tag-chip { font-size: var(--step--2); padding: 4px 10px; margin: 3px 3px 3px 0; }

    /* ═══════════════════════════════════════════════
       FOOTER
       ═══════════════════════════════════════════════
       Single visual mass on mobile — brand (logo + name) is the only
       "louder" element; all contact / credit / copyright lines share ONE
       size and ONE color (gray-dim), with no opacity variations. This
       keeps the footer balanced and readable. Desktop reuses the same
       tokens but rearranges into a 2-column grid. */
    .site-footer {
      background: var(--dark);
      color: var(--gray-dim);
      flex-shrink: 0;
      font-family: var(--sans);
      /* Fluid: 13px on small phones → 14px on tablet+. Min 13 keeps the
         footer legible on touch without overpowering the body content. */
      font-size: clamp(13px, 0.3vw + 11.5px, 14px);
      line-height: 1.5;
      padding: 28px var(--content-margin) 32px;
      display: flex;
      flex-direction: column;
      align-items: center;
      text-align: center;
      gap: 8px;
    }
    .site-footer a {
      color: var(--gray-dim);
      text-decoration: none;
      transition: color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .site-footer a:hover { color: var(--white); }
    }
    /* Brand row — logo + title inline, always one line. Logo height tracks
       brand text so the row reads as a single tight band. */
    .footer-brand {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      cursor: pointer;
      margin-bottom: 6px;
    }
    .footer-logo-icon {
      width: 22px;
      height: auto;
      color: var(--white);
      flex-shrink: 0;
    }
    .footer-brand-name {
      font-family: var(--sans);
      /* Fluid: 14px small phones → 15px desktop, paired with logo icon. */
      font-size: clamp(14px, 0.3vw + 12.5px, 15px);
      font-weight: 500;
      color: var(--white);
      letter-spacing: 0.5px;
      text-transform: uppercase;
      white-space: nowrap;
    }
    /* Contacts. Address forced to one line via nowrap; JS sets a short
       variant ("Москва, Б. Палашевский …") on mobile so it always fits. */
    .footer-contacts {
      display: contents; /* lines flow as direct children of footer */
    }
    .footer-address { white-space: nowrap; }
    .footer-credits a {
      color: var(--gray-dim);
      border-bottom: 1px solid transparent;
      transition: color 0.2s, border-color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .footer-credits a:hover {
      color: var(--white);
      border-bottom-color: rgba(255,255,255,0.4);
    }
    }
    /* On very narrow screens (<340px), allow the address to wrap. */
    @media (max-width: 340px) {
      .footer-address { white-space: normal; }
    }
    /* Desktop layout (>768px): 2-column grid. Brand+contacts top, credits
       and copyright share the bottom row. Same font tokens — desktop just
       repositions, not restyles. */
    @media (min-width: 769px) {
      .site-footer {
        display: grid;
        grid-template-columns: 1fr 1fr;
        /* LEFT: brand (row 1) → address (row 2) → email (row 3).
           RIGHT: blank row 1 → credits (row 2) → copyright (row 3).
           Email and copyright share row 3, so they sit on the same baseline. */
        grid-template-areas:
          "brand     ."
          "address   credits"
          "email     copyright";
        align-items: baseline;
        text-align: left;
        column-gap: 32px;
        row-gap: 6px;
        padding: 36px var(--content-margin);
      }
      .footer-contacts { display: contents; }
      .footer-brand {
        grid-area: brand;
        justify-self: start;
        align-self: center;
        margin-bottom: 14px; /* extra breathing room after brand row */
      }
      .footer-address  { grid-area: address;  justify-self: start; text-align: left; }
      .footer-email    { grid-area: email;    justify-self: start; text-align: left; }
      .footer-credits  { grid-area: credits;  justify-self: end;   text-align: right; }
      .footer-copyright{ grid-area: copyright; justify-self: end;  text-align: right; }
      .footer-logo-icon { width: 28px; }
      /* Desktop sets its own font-size to 15px to match the larger logo. */
      .footer-brand-name { font-size: 15px; }
    }
    /* Hide old structures if anything lingers */
    .footer-col-title, .footer-col-nav, .site-footer-inner, .footer-bottom, .footer-block-mobile-nav,
    .footer-block, .footer-mobile-brand, .footer-mobile-contacts { display: none; }
    /* Legacy .footer-bottom / .footer-copyright rules removed — new footer
       sets all sizes via the .site-footer cascade. */

    /* ═══════════════════════════════════════════════
       RESPONSIVE — fluid/rubber
       ═══════════════════════════════════════════════ */

    /* Tablet: 768px - 1279px */
    @media (max-width: 1279px) {
      :root {
        --content-margin: max(40px, 5vw);
      }
      .issue-grid { grid-template-columns: repeat(3, 1fr); }
    }

    /* Mobile: < 768px */
    @media (max-width: 768px) {
      :root {
        --content-margin: 24px;
        --header-height: 72px;
      }

      /* Safe area for notch phones */
      body { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }
      .site-header { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }

      /* Mobile header: logo + name on the same row, always when there's space */
      .header-logo-icon { height: 24px !important; }
      .site-header.shrunk .header-logo-icon { height: 20px !important; }
      .header-logo-text { display: none; }
      body.is-home .site-header:not(.shrunk) .header-logo-text { display: block; }
      body.is-home .site-header:not(.shrunk) .header-logo-main { font-size: 13px; letter-spacing: 0.06em; }
      body.is-home .site-header:not(.shrunk) .header-logo-sub { display: none; }
      body[data-lang="en"].is-home .site-header:not(.shrunk) .header-logo-main { display: none; }
      body[data-lang="en"].is-home .site-header:not(.shrunk) .header-logo-sub { display: block; font-size: 13px; }

      .header-inner { padding: 0; justify-content: space-between; position: relative; }
      .header-right .header-nav-link { display: none; }
      /* MOBILE EN-button override — see "LANGUAGE TOGGLE" block above for
         policy. body.is-home wins via specificity AND we restyle the
         EN button into a tight bordered chip that sits in the LEFT slot
         (same position as the search-icon on other routes). On every
         non-home page, the mobile EN button stays hidden. */
      .header-right .lang-toggle-btn { display: none !important; }
      body.is-home .header-right .lang-toggle-btn {
        display: inline-flex !important;
        position: absolute;
        left: 8px;
        top: 50%;
        transform: translateY(-50%);
        margin-left: 0;
        padding: 3px 6px;
        border: 1px solid rgba(255,255,255,0.4);
        border-radius: 0;
        font-size: 10px;
        line-height: 1;
        letter-spacing: 0.05em;
        min-height: 22px;
        align-items: center;
      }
      @media (hover: hover) and (pointer: fine) {
      body.is-home .header-right .lang-toggle-btn:hover {
        border-color: var(--white);
        background: rgba(255,255,255,0.06);
        opacity: 1;
      }
      }
      .header-right { position: static; gap: 0; }
      /* Subscribe button — desktop only on home; hide on mobile */
      .subscribe-btn { display: none !important; }
      /* Show mobile-search-icon LEFT, burger RIGHT; logo centered in between */
      .header-search-icon--desktop { display: none; }
      .header-search-icon--mobile {
        display: inline-flex;
        left: 12px;
        width: 44px;
        height: 44px;
      }
      /* HOME mobile: search-icon hidden — EN-button takes the left slot.
         Search remains in the burger menu. Mutual exclusion of lupa and
         EN on mobile is intentional: the two elements never co-exist. */
      body.is-home .header-search-icon--mobile { display: none; }
      .header-search-icon--mobile svg { width: 18px; height: 18px; }
      .header-logo {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        flex-shrink: 0;
        transition: opacity 0.2s;
        flex-direction: row;
        align-items: center;
        gap: 8px;
        white-space: nowrap;
      }
      /* Mobile: context positioning. left/right SAFE from search-icon (left 12-56)
         and burger (right 12-56). Reset translateX from the base rule that would
         otherwise drag the block to the LEFT, overlapping the search icon. */
      .header-issue-context:not(:empty) {
        left: 60px;
        right: 60px;
        top: 50%;
        position: absolute;
        font-size: var(--step--1);
        gap: 10px;
        justify-content: center;
        text-align: center;
        transform: translateY(-50%);
        max-width: none;
      }
      .header-issue-context .ctx-article-title { display: none; }
      .site-header.shrunk:has(.header-issue-context:not(:empty)) .header-logo {
        opacity: 0;
        pointer-events: none;
      }
      .header-mobile-toggle { display: flex; flex-direction: column; min-width: 44px; min-height: 44px; align-items: center; justify-content: center; position: absolute; right: 12px; z-index: 2; }
      /* Mobile home: logo is positioned via `left: 50%; translate(-50%)`,
         centered in viewport. To avoid colliding with the burger (right) we
         (1) shrink title font fluidly so it always fits the available area
         and (2) nudge the centered block left by half the burger area,
         placing it visually centered between the burger and the screen edge. */
      .header-logo {
        max-width: calc(100vw - 130px); /* 56 search + 56 burger + 18 buffer */
        overflow: visible;
        /* Logo's center MUST sit at the midpoint between the screen's LEFT
           edge and the burger's LEFT edge. Burger has right:12px and width
           44px → burger.left = vpW - 56. Midpoint = (vpW - 56)/2 = vpW/2 - 28.
           Logo is positioned via left:50%, so translate -50% (own width) plus
           -28px puts the visual center at vpW/2 - 28. Math-perfect midpoint. */
        transform: translate(calc(-50% - 28px), -50%) !important;
      }
      /* Mobile title: fluid font and tighter letter-spacing so "ХУДОЖЕСТВЕННЫЙ
         ЖУРНАЛ" never overruns the burger on any phone width. */
      body.is-home .site-header:not(.shrunk) .header-logo-main {
        font-size: clamp(10px, 2.7vw, 13px);
        letter-spacing: 0.03em;
      }
      body[data-lang="en"].is-home .site-header:not(.shrunk) .header-logo-sub {
        font-size: clamp(10px, 2.7vw, 13px);
      }
      /* Very narrow phones (≤340px) — reduce shift to avoid collision with
         the search icon on the left. */
      @media (max-width: 340px) {
        .header-logo { transform: translate(calc(-50% - 22px), -50%) !important; }
      }

      /* Mobile home: show white feature block on top, hide desktop right aside */
      .home-issue-feature {
        display: block;
        padding-top: calc(var(--header-height) + 24px);
      }
      .home-issue-feature-cover {
        max-width: min(60vw, 240px);
        height: clamp(100px, 28vw, 150px);
      }
      .home-hero {
        padding-top: clamp(28px, 4vw, 48px);  /* no header offset — feature block above */
        padding-left: 0;
        padding-right: 0;
      }
      .home-hero-inner {
        display: block;  /* single column on mobile */
      }
      .home-aside {
        display: none;  /* hidden — feature block replaces it */
      }
      .hero-article { max-width: 100%; }
      .hero-article {
        flex: 0 0 auto;
        max-width: 100%;
      }
      /* Full-bleed hero image: edge-to-edge on mobile, dramatic break
         between issue cover above and article feed below. */
      .hero-article-img-wrap {
        width: 100vw;
        max-width: 100vw;
        margin-left: 0;
        margin-right: 0;
      }
      .hero-article-img {
        width: 100%;
        max-width: 100%;
        aspect-ratio: 16/10;
      }
      /* Text below the full-bleed image gets its own gutter padding. */
      .hero-article-rubric,
      .hero-article-title,
      .hero-article-author,
      .hero-article-desc {
        padding-left: var(--content-margin);
        padding-right: var(--content-margin);
      }
      .hero-article-title { font-size: clamp(38px, 9vw, 52px); line-height: 1.05; }
      .home-sidebar-issue {
        border-left: none;
        padding-left: 0;
        border-top: none;
        padding-top: 0;
      }

      .articles-grid { grid-template-columns: 1fr; }
      .article-card-img-wrap { aspect-ratio: 16/9; }
      .search-results-grid { grid-template-columns: 1fr; }
      .search-results-primary { position: static; }
      /* AI-answer text inherits fluid sizing from base rule (18-20px). */

      .issue-hero { min-height: auto; }
      .issue-hero-inner { padding: 32px var(--content-margin) 36px; }
      .issue-hero-cover { width: 55vw; opacity: 0.25; }
      /* Issue hero authors: long author lists wrap a lot on mobile, so
         drop one step below desktop --step-0 to --step--1 (16-18px). */
      .issue-hero-authors { font-size: var(--step--1); line-height: 1.55; }

      .issue-toc-grid { grid-template-columns: repeat(2, 1fr); }
      .issue-toc-item { padding: 14px 12px; }
      /* TOC titles: use fluid scale instead of hard 15px (which was below
         the touch-comfort floor). step-1 = 21-26px → drop to step-0 only
         on mobile so the list reads as headlines, not labels. */
      .issue-toc-item-title { font-size: var(--step-0); padding-left: 24px; }
      .issue-toc-item-author { font-size: var(--step--1); padding-left: 28px; }
      .issue-toc-item-num { font-size: var(--step--2); min-width: 22px; }

      .archive-header { flex-direction: column; align-items: stretch; gap: 8px; }
      .archive-tabs { margin-left: 0; flex-wrap: wrap; }
      .archive-year-bar { gap: 10px; }
      .archive-letter-nav { gap: 10px; }
      .archive-issues-grid { grid-template-columns: 1fr; gap: clamp(40px, 10vw, 64px) 0; }
      /* Letter-nav: keep on scale token (--step--1 = 16-18px). 14px was
         too small for touch targets and broke parity with desktop. */
      .archive-letter-nav a { font-size: var(--step--1); flex-shrink: 0; }
      .archive-letter-group { columns: 2; }

      .author-article-row { grid-template-columns: 1fr; }
      .author-article-thumb { max-width: 200px; }

      .related-grid { grid-template-columns: 1fr; }
      .article-hero-title { font-size: clamp(34px, 8vw, 44px); line-height: 1.1; }
      .article-hero-inner { padding: 40px var(--content-margin) 48px; }
      /* Article body: --body-size already fluid 21-24px. Mobile inherits. */
      .article-body, .issue-intro-body { font-size: var(--body-size); line-height: var(--body-lh); }
      /* On mobile ALL figures become full-width, centered, caption below.
         Reset every float/width/display rule that desktop set.
         Crucially: kill the desktop negative margin so the figure stays
         inside .article-body padding. img and figcaption then share the
         same horizontal bounds. */
      .article-body figure,
      .article-body figure.figure__side {
        float: none !important;
        clear: both;
        width: auto !important;
        max-width: 100%;
        margin: 2em 0 !important;
        display: block;
        text-align: center;
      }
      .article-body figure img,
      .article-body figure.figure__side img {
        width: 100% !important;
        max-width: 100%;
        height: auto !important;
        margin: 0;
        display: block;
      }
      .article-body figcaption,
      .article-body figure.figure__side figcaption {
        display: block !important;
        text-align: left;
        width: 100%;
        max-width: 100%;
        margin: 0;
        padding-top: 14px;
        padding-left: 0;
        padding-right: 0;
      }
      .article-body blockquote { margin-left: 0; padding-left: 16px; }
      /* Adaptive on mobile: shrink to fit (smaller type, tight padding, all
         cells wrap) instead of horizontal-scrolling. Verified: a 3-column table
         fits 375px with no internal scroll. A genuinely wide table still
         scrolls as a fallback (hidden scrollbar). */
      .article-body table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; -ms-overflow-style: none; font-size: 0.72em; }
      .article-body table::-webkit-scrollbar { display: none; height: 0; }
      .article-body table td, .article-body table th { padding: 5px 8px; }
      .article-body table td:not(:first-child), .article-body table th:not(:first-child) { white-space: normal; }

      .article-nav { gap: 16px; }
      .article-nav-item { flex: 1; min-width: 0; }
      /* Prev/next titles: keep readable on touch — use --step--1 (16-18px)
         and --step--2 (14-16px) for the author below, instead of hard 16/15. */
      .article-nav-title { font-size: var(--step--1); line-height: 1.25; }
      .article-nav-author { font-size: var(--step--2); }
      /* Article + issue nav labels («ПРЕДЫДУЩАЯ», «СЛЕДУЮЩАЯ →»,
         «ПРЕДЫДУЩИЙ ВЫПУСК», «ЧИТАТЬ ВЫПУСК →»): keep side-by-side and on a
         single line on every viewport. Shrink font + tighten letter-spacing
         on narrow screens so they never wrap. */
      .article-nav .article-nav-label {
        white-space: nowrap;
        font-size: clamp(10px, 2.6vw, 13px);
        letter-spacing: 0.06em;
      }

      /* Footer mobile layout handled at component level; no overrides here. */

      /* Skeleton mobile behaviour is inherited from the real layout
         classes (.articles-grid → 1-col @ ≤640px, .nd-hero-grid → 1-col,
         .archive-issues-grid → 1-col @ ≤640px, .issue-toc-grid → 1-col
         @ ≤480px). No extra rules needed here. */
    }

    /* Small mobile: < 480px */
    @media (max-width: 480px) {
      :root {
        --content-margin: 20px;
      }
      /* Hero/TOC titles at this width: tighten via vw clamp so they scale
         smoothly from 320 to 480. step scale stays the floor. */
      .hero-article-title { font-size: clamp(32px, 9vw, 42px); line-height: 1.05; }
      .issue-toc-grid { grid-template-columns: 1fr; }
      .issue-toc-item { padding: 18px 4px; }
      /* 1-column TOC needs a bigger title since each row is full-width. */
      .issue-toc-item-title { font-size: var(--step-1); line-height: 1.3; }
      .issue-toc-item-author { font-size: var(--step-0); }
      .archive-issues-grid { grid-template-columns: 1fr; gap: clamp(36px, 10vw, 60px) 0; }
      .archive-letter-group { columns: 1; }
      .newsletter-form { flex-direction: column; gap: 12px; max-width: 100%; }
      .newsletter-input { border-right: 1px solid rgb(200,200,200); height: 52px; line-height: 52px; }
      .newsletter-submit { height: 52px; line-height: 52px; width: 100%; }
      .subscribe-page-title { margin-bottom: 12px; }
      .subscribe-page-subtitle { font-size: var(--step--1); margin-bottom: 24px; }
      .archive-tabs { flex-wrap: wrap; }
      .about-advisory-grid { grid-template-columns: 1fr; }

      /* Body inherits --body-size from base. Padding overrides retained
         for figure outset, but figcaption stays inside content padding
         (matches img bounds — see desktop figcaption width rule). */
      .article-body { padding: 0 var(--content-margin); }
    }

    /* (FAB removed — replaced by dynamic header context) */

    /* ═══════════════════════════════════════════════
       LANGUAGE TOGGLE — visibility rules
       ─────────────────────────────────────────────────
       DESKTOP: button sits in .header-right alongside nav-links. Shown
       on routes where switching language is meaningful (home/archive/
       about — three places where RU and EN content actually differ).
       Hidden on article/issue/author/search/tag/subscribe.
       MOBILE: header has TWO slots — search-icon LEFT, burger RIGHT.
       Showing EN next to the search-icon looks like cramped clutter.
       Rule: search-icon and EN are mutually exclusive on mobile.
         • Home mobile  — EN replaces search-icon (visitor's first
                           moment of language choice; search rarely
                           needed before any content is in front of you)
         • Other mobile — search-icon, no EN. Lang toggle still
                           available via burger menu (mnav-lang-toggle).
       The mobile-nav-lang-btn inside the burger panel follows the
       desktop rule: visible on home/archive/about. ═══════════════════ */
    .lang-toggle-btn,
    .mobile-nav-lang-btn { display: none; }
    body.is-home .lang-toggle-btn,
    body.is-archive .lang-toggle-btn,
    body.is-about .lang-toggle-btn {
      display: inline-flex;
    }
    body.is-home .mobile-nav-lang-btn,
    body.is-archive .mobile-nav-lang-btn,
    body.is-about .mobile-nav-lang-btn {
      display: inline-flex;
    }
    .lang-toggle-btn {
      font-family: var(--sans);
      font-size: clamp(11px, 1.2vw, 16px);
      font-weight: 500;
      color: var(--white);
      background: none;
      border: none;
      cursor: pointer;
      padding: 2px 4px;
      transition: opacity 0.2s, background 0.2s, color 0.2s;
      line-height: 20px;
      letter-spacing: 0.5px;
      margin-left: clamp(8px, 1vw, 14px);
    }
    @media (hover: hover) and (pointer: fine) {
    .lang-toggle-btn:hover { opacity: 0.7; }
    }
    /* Search icon — replaces "Поиск" text everywhere.
       display: НЕ ставится глобально — иначе перебивает mobile-media
       правила про show/hide отдельных вариантов (--desktop / --mobile).
       Сами варианты управляют display через media queries. */
    .header-search-icon {
      position: absolute;
      left: var(--content-margin);
      align-items: center;
      justify-content: center;
      color: var(--white);
      width: 36px;
      height: 36px;
      transition: opacity 0.2s;
      cursor: pointer;
    }
    @media (min-width: 769px) {
      .header-search-icon--desktop { display: inline-flex; }
    }
    .header-search-icon svg { width: 26px; height: 26px; display: block; }
    @media (hover: hover) and (pointer: fine) {
    .header-search-icon:hover { opacity: 0.7; }
    }
    /* Two stacked icons: magnifier (default), close X (when on /search).
       body.is-search shows close, hides magnifier. The icon morph is
       paired with toggleSearchRoute() — clicking the icon while on
       /search returns to the previous route. */
    .header-search-icon .hsi-icon { transition: opacity 0.2s ease; }
    .header-search-icon .hsi-close { position: absolute; inset: 0; margin: auto; opacity: 0; }
    body.is-search .header-search-icon .hsi-magnifier { opacity: 0; }
    body.is-search .header-search-icon .hsi-close { opacity: 1; }
    /* Desktop-only hide of the mobile-variant icon. Раньше это правило было
       ГЛОБАЛЬНЫМ (`.header-search-icon--mobile { display: none }` без media)
       и стояло ПОСЛЕ @media (max-width: 768px) → перебивало мобильную
       inline-flex видимость → mobile lupa не показывалась. Fix 2026-05-25.
       Заодно скрываем mobile lang-btn на desktop — та же проблема. */
    @media (min-width: 769px) {
      .header-search-icon--mobile { display: none; }
    }

    /* Legacy About styles removed — new About uses .about-editorial-row,
       .about-numbers, .about-cities-list, etc. defined above. */

    /* ═══════════════════════════════════════════════
       ARCHIVE — DIGESTS TAB
       ═══════════════════════════════════════════════ */
    .archive-digest-section {
      margin-top: 8px;
    }
    .archive-digest-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: var(--card-gap-y) var(--card-gap-x);
    }
    @media (max-width: 1023px) {
      .archive-digest-grid { grid-template-columns: repeat(2, 1fr); }
    }
    @media (max-width: 640px) {
      .archive-digest-grid { grid-template-columns: 1fr 1fr; gap: 24px 16px; }
    }

    /* ═══════════════════════════════════════════════
       ARTICLE TAGS
       ═══════════════════════════════════════════════ */
    .article-tags {
      max-width: var(--content-width);
      margin: 0 auto;
      padding: 0 24px 32px;
    }
    .article-tags-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.8px;
      color: var(--gray-dim);
      margin-bottom: 10px;
    }
    /* Без рамок — как метки на главной (редакция: чище). Раньше border + padding
       выбивались из типографики и «занимали больше места». Теперь просто серые
       ссылки через запятую, различимы цветом и ховером. */
    .tag-chip {
      display: inline-block;
      font-family: var(--sans);
      font-style: normal;
      font-size: var(--step--1);
      color: var(--gray-dim);
      text-decoration: none;
      margin: 3px 14px 3px 0;
    }

    /* ═══════════════════════════════════════════════
       TAG CLOUD (home page)
       ═══════════════════════════════════════════════ */
    .tags-cloud {
      padding: clamp(24px, 2.4vw, 36px) var(--content-margin) clamp(32px, 3.3vw, 48px);
      text-align: center;
    }
    .tags-cloud-title {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1px;
      color: var(--gray-dim);
      margin-bottom: clamp(14px, 1.5vw, 22px);
      padding-bottom: 14px;
      border-bottom: 1px solid rgb(230,230,230);
    }
    .tag-cloud-item {
      display: inline-block;
      font-family: var(--sans);
      color: var(--gray-dim);
      text-decoration: none;
      margin: 4px 6px;
    }

    /* ═══════════════════════════════════════════════
       TAG PAGE
       ═══════════════════════════════════════════════ */
    .tag-page {
      padding-top: var(--header-height);
    }
    .tag-page-inner {
      padding: clamp(32px, 3.3vw, 48px) var(--content-margin) clamp(40px, 4.1vw, 60px);
    }
    .tag-page-title {
      font-family: var(--serif);
      font-size: clamp(28px, 2.4vw, 35px);
      font-weight: 500;
      color: var(--dark);
      margin-bottom: 8px;
    }
    .tag-page-back {
      font-family: var(--sans);
      font-size: var(--step--1);
      margin-bottom: 16px;
    }
    .tag-page-back a {
      color: var(--gray-dim);
      text-decoration: none;
      transition: color 0.2s;
    }
    @media (hover: hover) and (pointer: fine) {
    .tag-page-back a:hover { color: var(--dark); }
    }
    .tag-page-count {
      font-family: var(--sans);
      font-size: var(--step--1);
      color: var(--gray-dim);
      margin-bottom: 32px;
    }
    .tag-page-related {
      margin-top: 48px;
      padding-top: 24px;
      border-top: var(--rule);
    }
    .tag-page-related-label {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.8px;
      color: var(--gray-dim);
      margin-bottom: 12px;
    }
    .tag-page-related-tags {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
    }
    .tag-chip-count {
      font-size: var(--step--2);
      color: var(--gray-secondary);
      margin-left: 4px;
    }

    /* ═══════════════════════════════════════════════
       VIEW TRANSITIONS
       ═══════════════════════════════════════════════
       Old DOM остаётся видимым; new fades in поверх него. Раньше
       параллельный fadeOut + fadeIn давал момент когда обе DOM ~50%
       opacity → проступал тёмный html background (rgb(20,20,20)) →
       чёрный flash между страницами. Fix 2026-05-25.
       ::view-transition-old(root) — no animation, stays opaque.
       ::view-transition-new(root) — fades in over old. */
    ::view-transition-old(root) { animation: none; }
    ::view-transition-new(root) { animation: fadeIn 0.2s ease; }
    @keyframes fadeIn { from { opacity: 0; } }

    /* ═══════════════════════════════════════════════
       REDUCED MOTION
       ═══════════════════════════════════════════════ */
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
      }
    }

    /* ═══════════════════════════════════════════════
       LEGAL MARKING — foreign agents (255-ФЗ compliant)
       Per law: 2x body font size, before article text, contrasting.
       ═══════════════════════════════════════════════ */

    /* Inline foreign agent mark — displayed as "Имя*" with no gap */
    .legal-mark {
      font-family: var(--sans);
      font-size: 0.6em;
      color: var(--gray-dim);
      vertical-align: super;
      line-height: 0;
      cursor: help;
    }

    /* JS-driven tooltip for foreign agent hover */
    .legal-tooltip {
      position: fixed;
      background: #222; color: #fff;
      font-family: var(--sans); font-size: 13px; line-height: 1.5;
      padding: 10px 14px; border-radius: 0;
      max-width: 360px; z-index: 9999;
      pointer-events: none;
      opacity: 0; transition: opacity 0.15s;
      box-shadow: 0 4px 16px rgba(0,0,0,0.25);
    }
    .legal-tooltip.visible { opacity: 1; }

    /* ═══ Legal disclaimer banner — 255-ФЗ & extremist org marking ═══
       Per law: same font/size/color as body text, before content,
       no footnotes/asterisks/hidden text. Must be prominent. */
    /* Per Постановление Правительства №2108:
       - Font size: 2x body text (body=23px → 46px)
       - Contrast: dark text on light background
       - Before article text, visible without scrolling */
    .legal-disclaimer {
      font-family: var(--sans);
      font-size: clamp(32px, 6vw, calc(var(--body-size) * 2));
      font-weight: 300;
      line-height: 1.15;
      color: rgb(110,110,110);
      padding: 20px 24px 8px;
      margin: 0 auto 0;
      max-width: 1100px;
    }
    .legal-disclaimer-item {
      margin-bottom: 8px;
    }
    .legal-disclaimer-item:last-child { margin-bottom: 0; }

    /* Extremist org marking */
    .legal-disclaimer-item.extremist {
      color: rgb(140,40,40);
    }

    /* Old footnote at bottom — keep as supplementary */
    .legal-footnote {
      font-family: var(--sans);
      font-size: var(--step--1);
      line-height: 18px;
      color: var(--gray-dim);
      border-top: 1px solid rgb(230,230,230);
      padding-top: 16px;
      margin-top: 32px;
    }
    .legal-footnote-item {
      margin-bottom: 6px;
    }
    .legal-footnote-item::before {
      content: '*';
      font-weight: 500;
      margin-right: 4px;
    }

    /* Footnote references — ТОЛЬКО позиционирование (надстрочно) и шрифт. Бокс
       НЕ здесь: он на `.article-body sup a` (см. выше). Раньше бокс был и тут, и
       на <a> → двойной прямоугольник (VM 2026-06). На реальных обработанных
       маркерах фон этого sup к тому же гасился рантаймом — ещё одна причина не
       держать бокс на sup. */
    sup.fn-ref {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 600;
      color: #555;
      cursor: pointer;
      margin: 0 1px;
      vertical-align: super;
      line-height: 0;
    }
    sup.fn-ref:empty { display: none; }

    /* Auto-linked URLs in article body */
    .article-body a[target="_blank"] {
      color: var(--dark);
      text-decoration: none;
      border-bottom: 1px solid var(--gray-secondary);
      transition: border-color 0.2s;
      word-break: break-all;
    }
    @media (hover: hover) and (pointer: fine) {
    .article-body a[target="_blank"]:hover {
      border-bottom-color: var(--dark);
    }
    }

    /* ═══════════════════════════════════════════════
       AGE GATE — redesigned
       ═══════════════════════════════════════════════ */
    .age-gate {
      position: fixed;
      top: 0; left: 0; right: 0; bottom: 0;
      background: rgba(0, 0, 0, 0.95);
      z-index: 10000;
      display: flex;
      align-items: center;
      justify-content: center;
      opacity: 1;
      transition: opacity 0.4s ease;
    }
    .age-gate.hiding {
      opacity: 0;
      pointer-events: none;
    }
    .age-gate-box {
      background: var(--white);
      padding: clamp(36px, 5vw, 56px) clamp(28px, 4vw, 48px);
      max-width: 420px;
      width: 90%;
      text-align: center;
    }
    .age-gate-badge {
      font-family: var(--sans);
      font-size: var(--step--2);
      font-weight: 500;
      letter-spacing: 1.5px;
      text-transform: uppercase;
      color: var(--gray-dim);
      margin-bottom: 20px;
    }
    .age-gate-title {
      font-family: var(--serif);
      font-size: clamp(32px, 4vw, 42px);
      font-weight: 500;
      margin-bottom: 16px;
      color: var(--dark);
      line-height: 1;
    }
    .age-gate-text {
      font-family: var(--sans);
      font-size: var(--step--1);
      line-height: 22px;
      color: var(--gray-dim);
      margin-bottom: 32px;
    }
    .age-gate-buttons {
      display: flex;
      gap: 12px;
      justify-content: center;
    }
    .age-gate-btn {
      font-family: var(--sans);
      font-size: var(--step--1);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 1.2px;
      padding: 14px 36px;
      border: var(--rule-strong);
      cursor: pointer;
      transition: background 0.2s, color 0.2s;
    }
    .age-gate-btn--yes {
      background: var(--dark);
      color: var(--white);
    }
    @media (hover: hover) and (pointer: fine) {
    .age-gate-btn--yes:hover { background: var(--black); }
    }
    .age-gate-btn--no {
      background: transparent;
      color: var(--dark);
    }
    @media (hover: hover) and (pointer: fine) {
    .age-gate-btn--no:hover { background: rgb(245,245,245); }
    }
    @media (max-width: 480px) {
      .age-gate-buttons { flex-direction: column; gap: 8px; }
      .age-gate-btn { width: 100%; }
    }