/* @import block — MUST precede all rule sets per CSS Spec §3.
   1. Fonts (Inter, Inter Tight — Bauhaus successor display + body).
   2. Design tokens compiled из Spec layers (Inv-CSS-tokens-from-Spec).
   Регенерация tokens: python3 scripts/css_compile.py --owner olgarozet */
@import url("https://fonts.bunny.net/css?family=inter:400,500,700,900|inter-tight:400,500,700|bad-script:400|marck-script:400&display=swap");
/* Relative path resolves correctly in BOTH production (site root) AND
   path-based preview (/<fqdn>/styles.css → /<fqdn>/_tokens.generated.css).
   Root-absolute "/_tokens.generated.css" works в production но 404'es в
   path-based preview (admin observation 2026-05-11 «не открывается»). */
@import url("_tokens.generated.css");

/* Inv-TYPO-font-metric-override (text/typography.md, 2026-05-12): «Inter Fallback»
   = local Arial с metric-override declarations matching Inter's box-metrics.
   Effect: while Inter loads (FOIT/FOUT window), text occupies the EXACT same
   pixel-box as final Inter → CLS = 0 при font-swap (web.dev «Reduce web font CLS»).
   Values: широко-цитируемые approximate metrics для Inter→Arial fallback (Brian
   Loubris adjust-tool / Malte Ubl / Capsize seek-oss). Calibrate per-font if
   precision matters. font-family stack chains: Inter → Inter Fallback → system. */
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  size-adjust: 107.4%;
  ascent-override: 90%;
  descent-override: 22.5%;
  line-gap-override: 0%;
}

/* No hand-rolled :root here — every custom property is Spec-derived and lives in
   _tokens.generated.css (Inv-CSS-tokens-from-Spec; «no hardcode»):
     · hub fluid scale (--font-size-*, --space-*, --max-width) ← text/site.md
     · fixed type/spacing ladders (--fs-*, --sp-*), --breakpoint, --touch-min ← text/site.md
     · typography rhythm (--baseline, --measure, --rail, --lh-*, --tracking-caps) ← text/typography.md
     · semantic colour palette (--surface*, --ink*, --muted, --rule*, --accent,
       --emphasis, --cta-*, --overlay*) across hub-day / hub-night / editorial,
       plus editorial --paris-* — ← knowledge/people/olgarozet/site/_design.yaml +
       text/site.md / text/event-*.md. Both surface/theme modes redeclare the SAME
       tokens — no parallel namespaces; WCAG-AA verified per pair.
   Edit the Spec layer, regenerate: python3 scripts/css_compile.py --owner olgarozet --event paris-2026-09 */

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
    overflow-x: hidden;
}

html {
    font: 400 var(--font-size-base)/1.8 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
    -webkit-text-size-adjust: 100%;
    -webkit-font-smoothing: antialiased;
    scroll-behavior: smooth;
    overscroll-behavior: none;
    /* Root canvas bg — catches UA default #fff leakage in mask-fade zones
       and any uncovered body area. */
    background: var(--surface);
}

/* .footer-content was transparent — mask-fade on .footer-portrait could
   expose html bg through it. Explicit bg closes the gap. */
.footer-content { background: var(--surface); }

/* Night-theme portrait swap: dedicated regenerated asset (`portrait_night`),
   not CSS inversion. Day img hidden in night mode and vice-versa. Both stack
   in the same grid cell so layout is identical regardless of theme. */
.footer-portrait.night { display: none; }
:root[data-theme="night"] .footer-portrait.day { display: none; }
:root[data-theme="night"] .footer-portrait.night { display: block; }

body {
    color: var(--ink);
    background: var(--surface);
    margin: 0;
    padding: 0;
}

.content-wrapper {
    max-width: var(--max-width);
    margin: 0 auto;
    padding: var(--space-top) var(--space-padding) 0;
    padding-left: calc(var(--space-padding) + 1em);
}

h1 {
    font-size: var(--font-size-h1);
    font-weight: 200;
    margin-bottom: 0.6em;
    letter-spacing: var(--tracking-display);
    color: var(--emphasis);
    line-height: var(--lh-flush);
}

h1::after {
    content: ' —';
    color: var(--ink);
    font-weight: 100;
    font-size: 0.6em;
}

.artist-highlight {
    color: var(--emphasis);
    letter-spacing: var(--tracking-caps-emphasis);
    font-weight: 600;
}

.artist-highlight a {
    color: inherit;
    text-decoration: underline;
    text-decoration-color: var(--emphasis);
    text-underline-offset: 0.15em;
}

section {
    margin: var(--space-section) 0;
    padding-top: calc(var(--space-section) * 0.7);
    border-top: 1px solid var(--rule);
}

section:first-of-type {
    border-top: none;
    padding-top: 0;
    margin-top: 0;
}

h2 {
    font-size: clamp(0.9rem, 0.85rem + 0.2vw, 1rem);
    font-weight: 500;
    margin-bottom: 0.8em;
    color: var(--muted);
}

p {
    margin: 0.8em 0;
    font-size: var(--font-size-p);
}

/* Inv-TYPO-text-wrap-pretty (text/typography.md, 2026-05-12): declarative
   orphan/widow + last-line-balance handling for body prose and headings.
   Chrome 117+ / Safari 26+ — older browsers fall back to default browser
   wrap (progressive enhancement; no fallback hazard).
   Subsumes prior authoring-discipline path of Inv-TYPO-orphan-widow.
   Rutter R. (2024) «End to widows» — text-wrap: pretty in production. */
p,
li,
h1, h2, h3, h4, h5, h6,
.lead,
.day-notes,
.day-theme,
.day-date,
.top-banner {
  text-wrap: pretty;
}

.inspire {
    position: relative;
}

.inspire::before {
    display: none;
}

a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px solid #ccc;
    transition: border-color 0.2s;
}

a:hover {
    border-color: var(--ink);
}

.price {
    /* Hub-site consultations pricing («15 000 ₽» в p_site). admin 2026-05-12
       ergonomic re-audit: clamp 0.85rem-1.15rem рендеры 13.6-18.4px — нижняя
       граница ниже body floor 14px на min viewport. Fix: max() с --fs-body-min
       (14px floor per text/site.md::design_tokens.fs-body-min). Через token,
       не literal — Spec-driven floor cascade. */
    font-size: max(clamp(0.85rem, 0.8rem + 0.2vw, 1.15rem), var(--fs-body-min));
    color: var(--muted);
}

.cta {
    /* Inv-LDG-design-touch44 — interactive ≥ 44px (WCAG 2.5.5 AAA + Apple HIG).
       admin 2026-05-12 ergonomic audit: padding alone yielded ~30px height; explicit
       min-height ensures touch-target regardless of padding clamp at min viewport. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--touch-min);
    margin-top: 0.5em;
    padding: clamp(0.5em, 0.4em + 0.3vw, 1.2rem) clamp(1em, 0.8em + 0.5vw, 2.5rem);
    background: var(--cta-bg);
    color: var(--cta-text);
    border: none;
    border-radius: 4px;
    font-size: clamp(0.9rem, 0.85rem + 0.2vw, 1.1rem);
    font-weight: 500;
    transition: opacity 0.2s;
}

.cta:hover {
    opacity: 0.8;
    border: none;
}

.event {
    margin: clamp(1em, 0.8em + 0.5vw, 1.5rem) 0;
    padding: clamp(0.5em, 0.3em + 0.3vw, 1.5rem) 0;
}

.event-date {
    font-weight: 500;
    font-size: clamp(1rem, 0.95rem + 0.2vw, 1.2rem);
}

.event-details {
    font-size: clamp(0.9rem, 0.85rem + 0.15vw, 1.15rem);
    color: var(--muted);
    margin-top: 0.3em;
}

/* Footer - Full Width (outside content-wrapper) */
footer {
    width: 100%;
    margin-top: calc(var(--space-section) * 2.5);
    padding: 0;
    border: none;
    background: var(--surface);
}

.footer-content {
    position: relative;
    display: block;
    margin: 0;
    padding: 0;
    /* Fade in animation */
    opacity: 0;
    transform: translateY(30px);
    transition: opacity 1s ease-out,
        transform 1s ease-out;
}

.footer-content.visible {
    opacity: 1;
    transform: translateY(0);
}

.footer-portrait {
    display: block;
    width: 100%;
    height: auto;
    /* Soft edges - fade top and bottom */
    -webkit-mask-image: linear-gradient(to bottom,
            transparent 0%,
            black 8%,
            black 92%,
            transparent 100%);
    mask-image: linear-gradient(to bottom,
            transparent 0%,
            black 8%,
            black 92%,
            transparent 100%);
}

.social-icon {
    position: absolute;
    top: 18%;
    width: clamp(80px, 70px + 5vw, 140px);
    height: clamp(80px, 70px + 5vw, 140px);
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255, 255, 255, 0.9);
    border: none;
    transition: color 0.4s ease, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
    filter: drop-shadow(0 4px 24px rgba(0, 0, 0, 0.8));
    z-index: 10;
}

.social-icon:first-of-type {
    left: clamp(18%, 12% + 5vw, 28%);
}

.social-icon:last-of-type {
    right: clamp(14%, 10% + 4vw, 24%);
}

.social-icon:hover {
    color: #fff;
    transform: scale(1.15);
    border: none;
}

.social-icon svg {
    width: clamp(60px, 52px + 4vw, 110px);
    height: clamp(60px, 52px + 4vw, 110px);
}

.scroll-top {
    /* Inv-LDG-design-touch44 — scroll-to-top tappable ≥ 44px guarantee.
       Padding 2em ≈ 32px at root; min-height enforces ergonomic floor regardless. */
    display: block;
    min-height: var(--touch-min);
    text-align: center;
    padding: 2em;
    color: var(--rule);
    border: none;
    background: var(--surface);
    transition: color 0.3s, background 0.3s;
}

.scroll-top:hover {
    color: var(--accent);
    border: none;
}

/* Icons positioning on mobile */
@media (max-width: 767px) {
    .content-wrapper {
        padding-left: calc(var(--space-padding) + 0.5em);
    }

    .social-icon {
        width: clamp(70px, 18vw, 95px);
        height: clamp(70px, 18vw, 95px);
    }

    .social-icon svg {
        width: clamp(52px, 14vw, 75px);
        height: clamp(52px, 14vw, 75px);
    }

    .inspire::before {
        left: -1.15em;
    }

    .footer-portrait {
        width: 140%;
        margin-left: -20%;
        object-fit: cover;
    }

    .social-icon:first-of-type {
        left: 12%;
    }

    .social-icon:last-of-type {
        right: 5%;
    }
}

/* Accessibility */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        transition-duration: 0.01ms !important;
    }
}
/* ── Day/night theme: solar-driven via data-theme attribute (set by inline JS) ──
   The night-mode colour palette (pure black canvas; whiteness hierarchy for
   text emphasis; red reserved for buttons only — admin spec 2026-04-25) is
   compiled из _design.yaml (theme: night) into :root[data-theme="night"] в
   _tokens.generated.css. Only the *transition* rules and the portrait swap
   selectors live here. */
html, body { transition: background-color 600ms ease; }
body, a, p, h1, h2, h3, footer, .scroll-top { transition: color 600ms ease, background-color 600ms ease; }

/* ── Публикации section: semantic Sайт↔TG↔IG linkage ── */
#publications { padding: var(--space-section) 0; }
.publications-list { list-style: none; padding: 0; }
.publications-list li { margin: 0.5em 0; }
.pub {
    /* Inv-LDG-design-touch44 — publications-list interactive link ≥ 44px. */
    display: inline-flex;
    gap: 0.6em;
    align-items: center;
    min-height: var(--touch-min);
    text-decoration: none;
    color: var(--ink);
    border-bottom: 1px solid var(--rule);
    padding-bottom: 0.15em;
    transition: color 200ms ease, border-color 200ms ease;
}
.pub:hover { color: var(--accent); border-color: var(--accent); }
.pub-channel {
    color: var(--muted);
    font-size: 0.85em;
}
.pub-title { font-weight: 500; }

/* ── Gallery (/art/) & fade-nav for subpages ── */
.gallery { display: flex; flex-direction: column; }
.artwork { width: 100%; display: flex; justify-content: center; align-items: center; }
.artwork img { width: 100%; max-width: 100vw; height: auto; display: block; object-fit: contain; }
.artwork + .artwork { border-top: 1px solid rgba(0,0,0,0.03); }

.progress-bar { position: fixed; top: 0; left: 0; height: 2px; width: 100%; background: rgba(0,0,0,0.1); transform-origin: left; transform: scaleX(0); z-index: 101; }

.nav-fade { position: fixed; top: 0; left: 0; right: 0; padding: 16px 20px; display: flex; justify-content: space-between; align-items: center; opacity: 0; transition: opacity 0.3s ease; background: linear-gradient(to bottom, var(--overlay), transparent); pointer-events: none; z-index: 100; }
body:hover .nav-fade, .nav-fade:focus-within { opacity: 1; pointer-events: auto; }
.nav-fade a { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; font-size: var(--fs-body-min); color: var(--muted); text-decoration: none; transition: color 0.2s; display: inline-flex; align-items: center; min-height: var(--touch-min); padding: 0 0.5em; }
.nav-fade a:hover { color: var(--ink); }
@media (max-width: 768px) {
  .nav-fade { opacity: 1; pointer-events: auto; background: var(--overlay-solid); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }
}
@media (prefers-reduced-motion: reduce) { .nav-fade { transition: none; } }

/* ── Day/night toggle (Inv-IFACE-day-night-mode) — chrome on every page ──────
   Fixed top-right (the .nav-fade back-arrow is top-left when present — no
   overlap; on FQDN landings .nav-fade is absent so top-right is clear). The
   glyph (◐ auto / ☀ day / ☾ night) is swapped by the inline handler in
   _layout. Low-emphasis until hover/focus — like .nav-fade a. Uses only the
   semantic tokens (--ink/--ink-soft/--muted/--surface/--accent/--focus-outline-min),
   which redeclare per-mode, so it works in BOTH day and night. */
.theme-toggle {
  position: fixed;
  top: 12px;
  right: 14px;
  z-index: 101;
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font: 400 1.2rem/1 -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: 50%;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.theme-toggle:hover,
.theme-toggle:focus-visible { opacity: 1; color: var(--ink); border-color: var(--rule); }
.theme-toggle:focus { outline: none; }
.theme-toggle:focus-visible { outline: var(--focus-outline-min) solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .theme-toggle { transition: none; } }

/* ── Skip-link (WCAG 2.4.1 «Bypass Blocks») — visually hidden until focus ── */
.skip-link {
  position: absolute;
  top: -100px;
  left: 0;
  background: var(--ink);
  color: var(--surface);
  padding: 0.6em 1em;
  font: 500 0.95rem/1 'Inter', system-ui, sans-serif;
  text-decoration: none;
  z-index: 1000;
  border-radius: 0 0 4px 0;
  transition: top 150ms ease;
}
.skip-link:focus,
.skip-link:focus-visible { top: 0; outline: var(--focus-outline-min) solid var(--accent); outline-offset: 2px; }

/* Handwriting accent — render-time emitted via {hand:…} shortcode (admin 2026-05-13).
   Caveat: warm cursive с reasonable cyrillic coverage; pair с Inter body works editorially. */
/* Per Spec text/typography.md::Inv-TYPO-handwriting-high-calligraphy —
   handwriting accent = HIGH calligraphy (formal pen-script). Admin 2026-05-13:
   Marck Script «пока слабо» → escalated к Bad Script (Roman Shchyukin, Cyrillic
   + Latin formal pen-handwriting, более pronounced stroke contrast). Size
   bumped 1.55→1.85em + slight italic transform via skew (Bad Script ships
   upright; visual slant 8° matches copperplate convention). */
.handwriting {
  font-family: 'Bad Script', 'Marck Script', cursive;
  font-weight: 400;
  font-size: 1.4em;            /* admin 2026-05-13 «слишком крупна, -25% точно» (1.85→1.4) */
  line-height: 1;
  letter-spacing: 0.01em;
  vertical-align: -0.08em;
  color: #C53030;
  display: inline-block;
  transform: skewX(-8deg);
  transform-origin: 50% 60%;
}

/* Landing footer image — admin 2026-05-13 «в самый низ Посадочной» + «футер
   ночной темы» (theme-companion). Alpha-PNG composites через transparency
   on the active surface; dual-image swap для contextual congruence. */
.landing-footer-image { margin: var(--sp-7) auto var(--sp-5); max-width: min(var(--measure), 100%); text-align: center; }
.landing-footer-image img { width: 100%; height: auto; display: block; }
/* Theme-conditional swap (no JS, no re-render — declarative). When both day/night
   variants declared (img.theme-day + img.theme-night), [data-theme] attr on
   <html> root toggles визуальное отображение. */
[data-theme="day"] .landing-footer-image img.theme-night,
[data-theme="night"] .landing-footer-image img.theme-day { display: none; }

/* ══════════════════════════════════════════════════════════════════════
   EDITORIAL SURFACE · SWISS-MODERNIST · ARCHITECTURAL
   ──────────────────────────────────────────────────────────────────────
   Activated by `<html data-surface="editorial">` (emitted by _layout for
   event-landing pages and static-pages — kind-detected, no per-page CSS
   parallelism). Same semantic token vocabulary as the hub (--surface,
   --ink, --ink-soft, --muted, --rule, --accent…) — only the values change.
   No parallel `--paper`/`--ink` namespace, no `:has()` cascade-promote.

   Neo-grotesque single family (Inter Tight + Inter). Major-third type
   scale (μ = 1.25). Hard rectilinear grid. Hairlines as ornament.
   Single accent — Corbusier Outremer. Concrete-paper substrate.
   Reference: Akzidenz-Grotesk catalogues, Müller-Brockmann grids,
   Le Corbusier «Polychromie architecturale» (1931), Maison Domino.

   Mathematical scaffold (single source of truth — change here, propagate):
     fs base   17.5px · μ = 1.250 · scale: 0.64, 0.80, 1.00, 1.25, 1.56,
                                          1.95, 2.44, 3.05, 3.81 rem
     space     0.5rem × 1.5 geom : 0.50, 0.75, 1.13, 1.69, 2.53, 3.80,
                                   5.70, 8.54 rem
     measure   34em × 1.55 leading (≈ 66 char Inter RU)
     contrast  ink/surface 16.1:1 AAA · muted/surface 4.78:1 AA body
                                       accent/surface 9.12:1 AAA

   Owner-agnostic: no event-specific selectors, no paris-* hardcode.
   ══════════════════════════════════════════════════════════════════════ */

/* @import блок выше moved-к-top — CSS Spec §3 требует @import precede rules. */

/* ── 1. Editorial palette override — same semantic tokens, different values.
   Concrete-paper substrate, graphite ink (NOT black — softens to read as
   drawn-on-paper, not printed-by-laser). Single Outremer accent (Corbu
   Polychromie 31). Accent discipline: only on price + signup-btn + day-
   numeral. Anywhere else = ink/ink-soft/muted gradation.

   These editorial colour tokens are OWNER-specific → compiled из _design.yaml
   (surface: editorial) into :root[data-surface="editorial"] в
   _tokens.generated.css; --accent (Corbu Outremer) и --paris-* come из
   text/site.md / text/event-paris-2026-09.md. Verified contrast (on #F4F1EB):
       ink/surface       11.15:1  AAA
       ink-soft/surface   7.32:1  AAA
       muted/surface      4.52:1  AA  body
       accent/surface     ~9:1    AAA  (focal use only)
       rule/surface       1.62:1  hairline (decorative, non-text — carve-out) */

/* ── 2. Article-wrapper — type/space scale + grid scaffolding.
   Color comes from the surface tokens (cascade from :root / the data-surface
   block в _tokens.generated.css). These tokens are SCALE (μ=1.25 fs, 1.5ⁿ
   space) — orthogonal to palette, so they remain scoped to .article-wrapper. */
.article-wrapper {
  /* Type scale — major-third (μ = 1.250). Single SoT; reuse below. */

  /* Spacing — 0.5rem × 1.5ⁿ geometric. Eight steps cover entire page. */

  /* Measure — 34em ≈ 66 char Inter RU. 1.55 leading on Inter at 17.5px. */
  --gutter:  2em;
  --page:    52em;

  /* Type stacks — Swiss neo-grotesque discipline. Inter Tight for
     display (tighter optical sizing), Inter for body. Single family
     across the page — owner-agnostic, RU/EN coverage.
     «Inter Fallback» — local Arial с metric overrides (Inv-TYPO-font-metric-
     override, text/typography.md, 2026-05-12); matches Inter's box metrics
     so the swap from system fallback → loaded Inter triggers CLS = 0.
     Source: Brian Loubris / Malte Ubl adjust-tool / Capsize seek-oss
     widely-published values для Inter; calibrate if precision needed. */
  --sans-display: 'Inter Tight', 'Inter', 'Inter Fallback', -apple-system,
                  BlinkMacSystemFont, 'Helvetica Neue', system-ui, sans-serif;
  --sans:         'Inter', 'Inter Fallback', -apple-system, BlinkMacSystemFont,
                  'Helvetica Neue', system-ui, sans-serif;
  --mono:         'IBM Plex Mono', 'JetBrains Mono', ui-monospace,
                  SFMono-Regular, Menlo, Consolas, monospace;

  /* --tracking-caps now compiled из Spec text/typography.md::design_tokens
     (Inv-CSS-tokens-from-Spec). See _tokens.generated.css. */
}

/* ─────────────────────────────────────────────────────────────────
   Inv-TYPO-caps-tracked — SINGLE typeclass instance.
   Spec: knowledge/system/specifications/text/typography.md.
   ALL caps containers join via:
     (a) selector listed in :is() below — structural members
     (b) `.is-caps` utility class — content-driven опт-in
   Per-element CSS rules hold ONLY font/weight/size/color/margin specifics.
   Tracking + uppercase universal через ONE assertion — no drift.
   ───────────────────────────────────────────────────────────────── */
:is(
  /* Hero zone — landing three-level header */
  .article-wrapper h1,
  .article-wrapper .locus-h2,
  /* Display register — pricing rhymes h1 */
  .article-wrapper .pricing-display .pricing-amount,
  .article-wrapper .pricing-display .pricing-label,
  /* Section H2 — catalogue register (admin 2026-05-12 feedback.txt:
     «См. свой ответ в клод-диалогах про конгруэнтное использование прописных
     на Посадочной» — section h2 («Тема», «Программа», «Бронирование», …)
     joins the Caps typeclass; restores cohesion with locus-h2 + name-stamps +
     metadata-strips. Per Inv-TYPO-headings-weight-bound, weight remains ≤ 500;
     only `text-transform: uppercase` + `letter-spacing: var(--tracking-caps)`
     gained — no bold-in-body drift). */
  .article-wrapper > section > h2,
  /* Catalogue eyebrows + credit-lines */
  .article-wrapper .organizers,
  .article-wrapper .with,
  .article-wrapper .top-banner.is-caps,
  /* (day-numerals dropped from the Caps typeclass — Roman numerals are already
     uppercase letterforms; their leading/tracking is set per-element below, so
     keeping them here would force a per-element letter-spacing override — the
     exact drift the typeclass forbids. admin-typo audit 2026-05-11.) */
  /* Form CTA register */
  .article-wrapper section.signup .signup-h3,
  .article-wrapper .signup-btn,
  /* About-Organizer section header + name-stamp */
  .article-wrapper .about-organizer h2,
  .article-wrapper .about-organizer .org-name,
  .article-wrapper .about-organizer p strong,
  /* Publications channel meta */
  .article-wrapper .pub-channel,
  /* Default global heading (pre-article context) */
  body > h2,
  /* CTA button (legacy non-article) */
  .cta,
  /* Universal opt-in utility: any element gains caps register through class */
  .article-wrapper .is-caps,
  .is-caps
) {
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
}

/* spacer → end of typeclass; resume .article-wrapper scope (body defaults +
   page grid). Earlier edit incorrectly placed body/layout props в :root —
   admin diagnostic 2026-05-11 «узкая колонка слева» revealed it (display: grid
   applied к html instead of article-wrapper). */
.article-wrapper {
  /* sentinel — typeclass instance assertion above must match owner CSS contract */
  --_inv-typo-caps-tracked: enforced;

  /* ── Body defaults ─────────────────────────────────────────── */
  font-family: var(--sans);
  font-size: clamp(1rem, 0.93rem + 0.28vw, 1.0625rem);   /* 17→17.5px fluid */
  line-height: var(--lh-body);   /* Inv-TYPO-vertical-rhythm-baseline */
  color: var(--ink);
  background: var(--surface);
  /* font-feature-settings explicitly includes "onum" + "pnum" — без них
     браузер игнорирует font-variant-numeric: oldstyle-nums (low-level features
     overrride high-level). admin 2026-05-13 «висячие цифры» — «20-е» в body
     должна рендериться текстовыми (oldstyle) цифрами, не titling/lining. */
  font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "ss01" 1, "onum" 1, "pnum" 1;
  /* Body — oldstyle (text figures), warmer rhythm в editorial register.
     Pricing-amount + day-numeral re-declare lining/tabular per Bringhurst
     §3.2.3 (titling figures only when there is no alternative — i.e.
     when oldstyle figures break alignment). */
  font-variant-numeric: oldstyle-nums proportional-nums;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  hyphens: auto;
  -webkit-hyphens: auto;

  /* Page-level asymmetric grid (Müller-Brockmann): rail | body.
     Rail holds margin-numerals/dates; body holds prose. The grid
     applies to ALL top-level children — header, sections, footer.
     Mobile (<880px) collapses to single column; numerals inline. */
  max-width: var(--page);
  margin: 0 auto;
  padding: clamp(var(--sp-6), 10vh, var(--sp-8))
           clamp(var(--sp-3), 5vw, var(--sp-6))
           var(--sp-8);
  display: grid;
  grid-template-columns: var(--rail) minmax(0, var(--measure));
  column-gap: var(--gutter);
  align-content: start;
}

.article-wrapper > * {
  grid-column: 2;             /* default: body column */
}
@media (max-width: 880px) {
  .article-wrapper {
    grid-template-columns: 1fr;
    column-gap: 0;
  }
  .article-wrapper > * { grid-column: 1; }
}

.article-wrapper p,
.article-wrapper li,
.article-wrapper dd {
  text-wrap: pretty;                /* Inv-TYPO-orphan-widow — modern browsers */
  hanging-punctuation: first;       /* Inv-TYPO-hanging-punctuation — Bringhurst §2.3.3 */
}

.article-wrapper h1,
.article-wrapper h2,
.article-wrapper h3,
.article-wrapper .lead { text-wrap: balance; }

/* Time element ↔ visually-hidden microdata (a11y / SEO).
   Inline-block here so screen readers parse it; CSS .visually-hidden
   class on the element actually hides it from sighted users. */
.article-wrapper time { font-variant-numeric: oldstyle-nums; }

/* ── 3. Header — masthead, thesis-first ─────────────────────────────── */

.article-wrapper > header {
  /* admin 2026-05-13: header border-bottom удалён — дублировал .pricing-display
     top-rule, две параллельные линии в пустоте читались как избыточность.
     Header заканчивается визуальной массой самого hero-блока; ticket-rule
     ниже остаётся единственным горизонтальным маркером (single ticket frame). */
  margin: 0 0 var(--sp-6);
  padding: 0 0 var(--sp-4);
  display: grid;
  gap: var(--sp-2);
  max-width: var(--measure);
}

.article-wrapper h1 {
  /* Display monumental — Inter 900 (Black). 0.95 leading; two-line stack reads
     as architectural signage. Tracking: var(--tracking-caps) per Inv-TYPO-
     consistent-tracking-caps (admin 2026-05-11: «прописные — разряженно»);
     uppercase content gets positive tracking уvе-везде, including hero display. */
  font: 900 clamp(var(--fs-2xl), 1.6rem + 3.6vw, var(--fs-3xl))/var(--lh-display) var(--sans-display);
  color: var(--ink);
  margin: 0;
  font-feature-settings: "kern" 1, "liga" 1, "ss01" 1;
}
.article-wrapper h1::after { content: none; }

.article-wrapper .lead {
  /* Thesis line — Inter 500, slight optical scale up.
     No italic (italic is serif convention; in Swiss register lead
     declares its weight by structure, not style). */
  font: 500 var(--fs-m)/var(--lh-tight) var(--sans);
  color: var(--ink-soft);
  margin: 0;
  max-width: 32em;
  letter-spacing: var(--tracking-micro);
}

.article-wrapper .organizers,
.article-wrapper .with {
  /* ALL-CAPS letter-spaced authorship — Swiss credit-line register.
     Reads as masthead, not paragraph. */
  font: 500 var(--fs-2xs)/var(--lh-tight) var(--sans);
  color: var(--muted);
  margin: var(--sp-2) 0 0;
}

/* Status banner — quiet aside in tabular figures, no panel chrome. */
.article-wrapper .status-banner {
  font: 400 var(--fs-xs)/var(--lh-body) var(--sans);
  color: var(--muted);
  letter-spacing: var(--tracking-spread-mild);
  margin: 0;
  padding: 0;
  background: transparent;
  border: none;
}
.article-wrapper .status-banner::before { content: none; }

/* ── 4. Pricing colophon — masthead-bottom, NOT a hero panel.
   Single-line catalogue colophon. The thesis is the hero, not the
   price. Renders as «1 550 € · с участника». */

.article-wrapper .pricing-display {
  /* Re-think 2026-05-13 (admin): «вокруг ценника чуть побольше поля он не
     напоминает билет. При этом очень аккуратно-конгруэтно». Dashed bottom
     perforation removed (ticket cue retired); generous vertical breathing
     replaces the tear-edge as the «отдельный блок» signal. Top-rule kept —
     it's the universal section-rhythm separator, congruent с rest. Result:
     ценник = quietly-set display block, не bordered ticket.
     History: 2026-05-12 first iteration had 4-side ink-border (too tight);
     reconsider дal top-rule + bottom dashed (still ticket); now: top-rule
     only + larger field. Swiss-modernist: hairline + space, no enclosure. */
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: var(--sp-2) var(--sp-3);
  margin: var(--sp-6) 0;
  padding: var(--sp-6) 0 var(--sp-6);
  border-top: 1px solid var(--rule);          /* section-rhythm separator above (congruent) */
  text-align: left;
}
.article-wrapper .pricing-display .pricing-label {
  font: 500 var(--fs-2xs)/var(--lh-tight) var(--sans);
  color: var(--muted);
  order: 2;
}
.article-wrapper .pricing-display .pricing-amount {
  /* admin 2026-05-10: «Цену крупно, и как-либо ещё в рифму основному заголовку».
     Match h1 register: same weight (900), same display family, comparable scale,
     same tracking-caps token (positive — per Inv-TYPO-caps-tracked).
     Outremer accent сохранён — single-accent rule. */
  font: 900 clamp(var(--fs-2xl), 1.6rem + 3.6vw, var(--fs-3xl))/var(--lh-display) var(--sans-display);
  color: var(--accent);                      /* Outremer — single accent */
  font-variant-numeric: lining-nums tabular-nums;
  order: 1;
}
.article-wrapper .pricing-display .pricing-amount .currency {
  font-weight: 700;
  color: var(--accent);
  margin-left: 0.12em;
  font-size: 0.72em;
  letter-spacing: var(--tracking-zero);
}

/* Inv-TYPO-math-rel-aligned (text/typography.md, 2026-05-12) — relation glyphs
   (↔ ⇔ ↑ ↓ → ← ≤ ≥ < > = ≠ ≈ ≡ ⊆ ⊇ ∈ ∉ ∀ ∃ ⇒ ⇐ …) wrapped at render-time в
   <span class="math-rel">. Vertical-align tweak corrects per-font baseline drift
   (Inter ↔ sits below text-center; Cyrillic body baseline is higher).
   Token --math-rel-shift declared в text/site.md design_tokens (single SoT;
   per-font calibration без code change). display: inline-block + line-height: 1
   ensures the vertical-align reference is the box itself, not the inline baseline. */
.math-rel,
.article-wrapper .math-rel {
  display: inline-block;
  vertical-align: var(--math-rel-shift, 0.06em);
  line-height: 1;
}

/* H2/H3 in three-level header (admin 2026-05-10):
   landing_h1 (концепт) → ALL-CAPS .locus-h2 (locus + дата, tracking caps,
   smaller scale than h1) → .organizers-h3 (sentence-case credit-line, sans). */
.article-wrapper .locus-h2 {
  font: 700 clamp(var(--fs-l), 1.1rem + 1.6vw, var(--fs-xl))/1.1 var(--sans-display);
  color: var(--ink);
  margin: var(--sp-1) 0 0;
}
.article-wrapper .organizers-h3 {
  font: 500 var(--fs-m)/var(--lh-tight) var(--sans);
  color: var(--ink-soft);
  letter-spacing: var(--tracking-zero);
  text-transform: none;
  margin: var(--sp-2) 0 var(--sp-3);
  font-weight: 500;
}
.article-wrapper .pricing-display .pricing-note {
  font: 400 var(--fs-xs)/var(--lh-body) var(--sans);
  color: var(--muted);
  margin: 0;
  flex-basis: 100%;
  max-width: 32em;
  order: 3;
}

/* ── 5. Sections — Swiss grid rhythm, hairline boundaries ─────────── */

.article-wrapper { counter-reset: secctr dayctr; }

.article-wrapper > section {
  /* counter-increment retained for back-compat (некоторые legacy templates ссылаются);
     visual rendering numeral отвергнут — admin 2026-05-13 «убери нумерацию 1 ТЕМА,
     2 ПРОГРАММА». Section-rhythm теперь только через padding + margin air;
     horizontal hairlines удалены как избыточные — единственный «билет» (ticket
     metaphor) принадлежит .pricing-display (top-rule + dashed bottom = perforation).
     Остальные section-breaks — silent. */
  counter-increment: secctr;
  margin: var(--sp-5) 0 0;
  padding-top: 0;
  border: none;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-2);
  max-width: var(--measure);
}

.article-wrapper > section > h2 {
  font: 500 var(--fs-l)/var(--lh-tight) var(--sans-display);   /* μ² Inter Medium —
     Inv-TYPO-headings-weight-bound: body-context headings ≤ 500 (admin 2026-05-11
     «полужирный в body»). Hero display (.concept-h1 / .locus-h2 / .pricing-amount)
     carry heavier weight; section subheadings — medium register, no bold-in-body. */
  margin: 0 0 var(--sp-3);
  color: var(--ink);
  /* letter-spacing inherited из Caps typeclass (var(--tracking-caps), positive).
     Earlier --tracking-display-soft (negative) was for sentence-case rendering;
     caps register reverses sign per Bringhurst §3.2.5. text-transform: none
     removed — section h2 joins the Caps typeclass via :is(...) selector list
     above (admin 2026-05-12 «прописные где уместно» congruence). */
}
.article-wrapper > section > h2::before {
  /* Section numerals (1, 2, 3…) удалены — admin 2026-05-13 «убери нумерацию
     1 ТЕМА, 2 ПРОГРАММА». Catalogue-figure register не нужен; section identity
     несут сами h2-заголовки в tracking-caps. */
  content: none;
}

.article-wrapper section h3 {
  font: 500 var(--fs-m)/var(--lh-tight) var(--sans-display);
  margin: var(--sp-3) 0 var(--sp-1);
  color: var(--ink);
  letter-spacing: var(--tracking-tight);
}

.article-wrapper section p {
  margin: 0 0 var(--sp-3);
  line-height: var(--lh-body);
  color: var(--ink);
}
.article-wrapper section p:last-child { margin-bottom: 0; }

/* Opening-section first paragraph — small optical lift, no drop cap.
   In Swiss register, the GRID is the ornament; letterforms stay calm. */
.article-wrapper > section:first-of-type > p:first-of-type {
  font-size: var(--fs-m);
  line-height: var(--lh-body);
  color: var(--ink);
}

/* ── 7. Programme — chart of days. Hairline-divided entries, lining
   figure in rail, ALL-CAPS date label, neo-grotesque theme, calm body.
   Each day reads as a floor in a section drawing. */

.article-wrapper > section.programme {
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  max-width: var(--page);   /* programme breaks the measure-narrow */
}

.article-wrapper .days {
  list-style: none;
  padding: 0;
  margin: var(--sp-3) 0 0;
  counter-reset: dayctr;
  display: grid;
  gap: 0;
}

.article-wrapper .days > li {
  counter-increment: dayctr;
  display: grid;
  grid-template-columns: 3.4em 1fr;
  column-gap: var(--gutter);
  padding: var(--sp-4) 0;
  border-top: 1px solid var(--rule);
  align-items: baseline;
}
.article-wrapper .days > li:last-child  { border-bottom: 1px solid var(--rule); }

/* Day numeral — Inter Tight 700, lining/tabular. Catalogue figure
   in rail; weight signals primary; per-day accent reflects program arc
   (Inv-PARIS-design-arc-per-day): Day 1 warm-gray / Day 2 Outremer / Day 3
   Aalto-wood / Day 4 gallery-white. Tokens compiled из text/event-paris-2026-09.md
   design_tokens (Inv-CSS-tokens-from-Spec). */
.article-wrapper .days > li::before {
  content: counter(dayctr, upper-roman);   /* «I II III IV» — admin 2026-05-11 «нумерация дней — латинскими цифрами»; classical register, congruent with the «100 лет» framing */
  font: 700 var(--fs-m)/1 var(--sans-display);
  color: var(--accent);
  letter-spacing: var(--tracking-zero);
  align-self: start;
  padding-top: 0.18em;
}
/* Inv-PARIS-design-arc-per-day:
   Per-day accent (color) — четыре равноценных голоса контрапункта.
   Single uniform rule-weight (1px). Прежняя 1/3/2/1 progression отвергнута:
   она конструирует иерархию (Day 2 peak), что противоречит самой концепции
   программы — «counterpoint of modernism variants», где Le Corbusier /
   Aalto / Zadkine / Парижский ритейл = равные голоса диалектики, не
   pyramid с центром. Арку выражает horizontal arc-band у footer
   (4 equal-width полосы), не volume hierarchy в днях. */
.article-wrapper .days > li[data-day="1"]::before { color: var(--paris-day-1-accent, var(--accent)); }
.article-wrapper .days > li[data-day="1"]        { border-top: 1px solid var(--paris-day-1-accent, var(--rule)); }
.article-wrapper .days > li[data-day="2"]::before { color: var(--paris-day-2-accent, var(--accent)); }
.article-wrapper .days > li[data-day="2"]        { border-top: 1px solid var(--paris-day-2-accent, var(--accent)); }
.article-wrapper .days > li[data-day="3"]::before { color: var(--paris-day-3-accent, var(--accent)); }
.article-wrapper .days > li[data-day="3"]        { border-top: 1px solid var(--paris-day-3-accent, var(--rule)); }
.article-wrapper .days > li[data-day="4"]::before { color: var(--paris-day-4-accent, var(--accent)); }
.article-wrapper .days > li[data-day="4"]        { border-top: 1px solid var(--paris-day-4-accent, var(--rule)); }

/* arc-band footer-legend retired 2026-05-13. Filled colour-bars recreated
   Day-2 peak через Outremer's natural saturation, contradicting
   Inv-LDG-PARIS-days-equipotent. Polychromie-31 reference остаётся в
   event-paris-2026-09.md::references (Spec), не на rendered page. */

.article-wrapper .days .day-date {
  /* admin 2026-05-13: «даты мелко — нарушает спецификацию эргономики».
     fs-2xs (0.719rem) — это --fs-caps-min, для tracked-caps registers
     (organizers credit-line, metadata strips). Lowercase «8 сентября» —
     body register, должна быть ≥ --fs-body-min (fs-xs = 0.875rem). */
  font: 500 var(--fs-xs)/var(--lh-tight) var(--sans);
  color: var(--muted);
  margin: 0 0 var(--sp-1);
  grid-column: 2;
}
.article-wrapper .days .day-theme {
  font: 500 var(--fs-m)/var(--lh-tight) var(--sans-display);   /* medium — Inv-TYPO-headings-weight-bound */
  color: var(--ink);
  margin: 0 0 var(--sp-2);
  letter-spacing: var(--tracking-display-soft);
  grid-column: 2;
}
.article-wrapper .days .day-notes {
  margin: 0 0 var(--sp-2);
  color: var(--ink-soft);
  font-size: var(--fs-s);
  line-height: var(--lh-body);
  grid-column: 2;
  max-width: var(--measure);
}
.article-wrapper .days .day-notes:last-child { margin-bottom: 0; }

/* Evening-recur tile — повторяющийся «Вечер — вернисажи Paris Design Week»
   (admin 2026-05-13). Структурно отделён от day-notes (data-yaml::evenings_recurring
   registry + per-day evening: <key> reference); визуально — «вечерняя плашка»,
   конгруэнтная содержанию: Outremer-tint background (Polychromie 31 ~5%) +
   accent left-bar = late-day register, без shouting. Same body-size,
   no italic (Swiss editorial register бескомпромиссно). */
.article-wrapper .days .evening-recur {
  margin: var(--sp-3) 0 0;
  padding: var(--sp-2) var(--sp-3);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  border-left: 2px solid var(--accent);
  color: var(--ink-soft);
  font: 400 var(--fs-s)/var(--lh-body) var(--sans);
  grid-column: 2;
}
.article-wrapper .days .evening-recur .evening-label {
  font-weight: 500;
  letter-spacing: var(--tracking-micro);
  color: var(--accent);
}

@media (max-width: 700px) {
  .article-wrapper .days > li {
    grid-template-columns: 1fr;
    row-gap: var(--sp-1);
  }
  .article-wrapper .days > li::before {
    grid-column: 1;
    padding-top: 0;
    margin-bottom: var(--sp-1);
  }
  .article-wrapper .days .day-date,
  .article-wrapper .days .day-theme,
  .article-wrapper .days .day-notes,
  .article-wrapper .days .evening-recur { grid-column: 1; }
}

/* ── 8. Definition list (concept pairs) — Swiss term/definition ── */

.article-wrapper dl.pairs {
  margin: var(--sp-3) 0;
  display: grid;
  gap: var(--sp-4);
}
.article-wrapper dl.pairs dt {
  font: 500 var(--fs-m)/var(--lh-tight) var(--sans-display);   /* medium — Inv-TYPO-headings-weight-bound */
  color: var(--ink);
  margin: 0 0 var(--sp-1);
  letter-spacing: var(--tracking-tight);
}
.article-wrapper dl.pairs dt::before {
  content: "→";
  color: var(--accent);
  margin-right: 0.45em;
  font-weight: 500;
  letter-spacing: var(--tracking-zero);
}
.article-wrapper dl.pairs dd {
  margin: 0;
  line-height: var(--lh-body);
  color: var(--ink-soft);
  max-width: var(--measure);
}

/* ── 9. Lists — chart bullets, parallel rhythm ─────────────────────── */

.article-wrapper section ul {
  list-style: none;
  padding-left: 0;
  margin: var(--sp-2) 0 var(--sp-3);
}
.article-wrapper section ul li {
  margin: var(--sp-2) 0;
  padding-left: 1.4em;
  position: relative;
  line-height: var(--lh-body);
  color: var(--ink-soft);
}
.article-wrapper section ul li::before {
  /* Interpunct «·» aligned к first line's optical baseline (admin 2026-05-13:
     «буллиты не выровнены относительно текста и не пропорциональны»).
     font-size 1.4em was too prominent + top:-0.05em shifted the glyph above
     cap-height. New: 1.05em (subtle, proportional к body) + line-height
     matching body's lh-body so the dot sits на the same baseline rhythm as
     the «о/н/а» x-height in text. */
  content: "·";
  position: absolute;
  left: 0.45em;
  top: 0;
  color: var(--accent);
  font-weight: 700;
  font-size: 1.05em;
  line-height: var(--lh-body);
}

/* ── 10. Links — under-rule, focus-detected, accent on hover ────────── */

.article-wrapper a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--ink-soft);
  transition: border-color 180ms ease, color 180ms ease,
              background-color 180ms ease;
  padding: 0 0.04em;
}
.article-wrapper a:hover {
  color: var(--surface);
  background-color: var(--accent);
  border-bottom-color: var(--accent);
}
.article-wrapper a:focus-visible {
  outline: var(--focus-outline-min) solid var(--accent);
  outline-offset: 3px;
  border-bottom-color: transparent;
  border-radius: 1px;
}

/* ── 11. Signup — editorial offer, Swiss form rigor ─────────────────── */

.article-wrapper section.signup-wrap {
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--ink);
}
.article-wrapper section.signup-wrap > p {
  font: 400 var(--fs-s)/var(--lh-body) var(--sans);
  color: var(--muted);
  margin: 0 0 var(--sp-3);
}
/* admin 2026-05-10 paris-landing.md: form h3 дублирует section h2 «Забронировать»;
   visually-hidden преобразование сохраняет a11y (aria-labelledby точка-цели)
   но удаляет визуальное дублирование. */
.article-wrapper section.signup .signup-h3 {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0,0,0,0);
  white-space: nowrap; border: 0;
}
.article-wrapper .signup-form {
  display: grid;
  gap: var(--sp-2);
  max-width: 26em;
  margin-top: var(--sp-3);
}
.article-wrapper .signup-label {
  font: 400 var(--fs-xs)/var(--lh-tight) var(--sans);
  color: var(--muted);
}
.article-wrapper .signup-hint { color: var(--ink-soft); }
/* Was opacity:0.75 — failed Inv-SITE-a11y-six: 0.75×#6E6E6E on #F4F1EB
   computes to 2.86 contrast ratio (<4.5). Solid --ink-soft yields 7+. */
.article-wrapper .signup-input {
  width: 100%;
  padding: 0.55em 0;
  font: 400 var(--fs-s)/var(--lh-tight) var(--sans);
  color: var(--ink);
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--ink-soft);
  border-radius: 0;
  transition: border-color 180ms ease;
}
.article-wrapper .signup-input:focus { outline: none; }
.article-wrapper .signup-input:focus-visible {
  border-bottom-color: var(--accent);
  border-bottom-width: 2px;
  outline: var(--focus-outline-min) solid var(--accent);
  outline-offset: 4px;
  border-radius: 1px;
}
.article-wrapper .signup-consent {
  display: flex;
  align-items: flex-start;
  gap: 0.6em;
  font: 400 var(--fs-xs)/var(--lh-body) var(--sans);
  color: var(--muted);
}
.article-wrapper .signup-consent input { margin-top: 0.25em; accent-color: var(--accent); }
.article-wrapper .signup-btn {
  margin-top: var(--sp-1);
  padding: 0.85em 1.6em;
  background: var(--accent);
  color: var(--surface);
  border: 1px solid var(--accent);
  border-radius: 0;
  font: 700 var(--fs-xs)/1 var(--sans-display);
  cursor: pointer;
  transition: background 180ms ease, color 180ms ease, border-color 180ms ease;
  align-self: start;
  min-height: var(--touch-min);
}
.article-wrapper .signup-btn:hover,
.article-wrapper .signup-btn:focus-visible {
  background: var(--ink);
  color: var(--surface);
  border-color: var(--ink);
  outline: none;
}
.article-wrapper .signup-btn:focus-visible {
  outline: var(--focus-outline-min) solid var(--accent);
  outline-offset: 3px;
}
.article-wrapper .signup-msg,
.article-wrapper .signup-note {
  font: 400 var(--fs-xs)/var(--lh-body) var(--sans);
  color: var(--muted);
}

/* ── 12. Contact section — quiet post-signup ────────────────────────── */

.article-wrapper section.contact { margin-top: var(--sp-5); padding-top: 0; border-top: none; }
.article-wrapper section.contact h2 {
  font: 500 var(--fs-m)/var(--lh-tight) var(--sans-display);   /* medium — Inv-TYPO-headings-weight-bound */
  text-transform: none;
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
  margin: 0 0 var(--sp-1);
}
.article-wrapper section.contact h2::before { content: none; }
.article-wrapper section.contact p {
  font: 400 var(--fs-s)/var(--lh-body) var(--sans);
  color: var(--ink-soft);
  margin: 0;
}
.article-wrapper section.contact a {
  font-feature-settings: "tnum";
}

/* ── 13. About-organizers — colophon, end of article ────────────────── */

.article-wrapper .about-organizer {
  border-top: 1px solid var(--ink);
  padding-top: var(--sp-4);
  margin-top: var(--sp-7);
  font-size: var(--fs-s);
  color: var(--ink-soft);
  max-width: var(--measure);
  /* Editorial surface paints `footer { background: var(--surface) }` (≈line
     199) which would push hub-canvas through nested footer.about-organizer.
     `transparent` lets the editorial substrate flow through continuously. */
  background: transparent;
}
.article-wrapper .about-organizer h2 {
  font: 500 var(--fs-2xs)/var(--lh-tight) var(--sans);
  color: var(--muted);
  margin: 0 0 var(--sp-3);
}
.article-wrapper .about-organizer h2::before { content: none; }
.article-wrapper .about-organizer p {
  margin: 0 0 var(--sp-2);
  line-height: var(--lh-body);
}
/* Inv-TYPO-no-bold-in-body: <strong> в body normalised — weight 500 (sans body
   register), not bold. Inline emphasis preserved через caps + tracking, не weight
   (admin 2026-05-10: «полужирный внутри body — AI-writing»). */
.article-wrapper .about-organizer p strong {
  font-weight: 500;
  font-style: normal;
  color: var(--ink);
  font-size: 0.94em;
}
.article-wrapper .about-organizer .org-link {
  margin-top: var(--sp-3);
  /* admin 2026-05-12 ergonomic audit — org-link = sentence-case body register,
     not Caps typeclass member. Migrated --fs-2xs (caps-register, ≥11.5px)
     → --fs-xs (body-register, ≥14px floor via --fs-body-min). */
  font: 500 var(--fs-xs)/var(--lh-tight) var(--sans);
}

/* ── 14. Open-questions — internal block, hidden on public landing ──── */
.article-wrapper .open-questions { display: none; }

/* ── 15. Visually-hidden utility ────────────────────────────────────── */
.visually-hidden,
.article-wrapper .visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

/* ── 16. Mobile reading rhythm ──────────────────────────────────────── */

@media (max-width: 600px) {
  .article-wrapper {
    padding: var(--sp-5) var(--sp-3) var(--sp-6);
    font-size: 1rem;
  }
  .article-wrapper h1 {
    font-size: clamp(1.6rem, 5vw + 0.6rem, var(--fs-2xl));
    letter-spacing: var(--tracking-tight-soft);
  }
  .article-wrapper > section > h2::before {
    display: inline-block;
    margin-right: 0.4em;
  }
}

/* ── 17. Print — clean essay handoff ────────────────────────────────── */

@media print {
  body { background: #fff; color: #000; }
  .nav-fade, .theme-toggle, .progress-bar, .scroll-top, .signup-wrap, .signup-form,
  footer { display: none !important; }
  .article-wrapper {
    max-width: 100%;
    font-size: 11pt;
    line-height: var(--lh-loose);
    color: #000;
    background: #fff;
  }
  .article-wrapper a { color: #000; border-bottom: none; }
  .article-wrapper h1 { font-size: 22pt; }
  .article-wrapper section h2 { font-size: 13pt; color: #444; }
  .status-banner { display: none; }
}

/* ── 18. Reduced-motion / reduced-data ──────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .article-wrapper .signup-btn,
  .article-wrapper .signup-input,
  .article-wrapper a { transition: none; }
}
@media (prefers-reduced-data: reduce) {
  .footer-portrait { display: none; }
  .article-wrapper *,
  .article-wrapper *::before,
  .article-wrapper *::after { transition: none !important; animation: none !important; }
}

/* ── 19. CLS-prevention — explicit aspect-ratio for inline images ───── */
.article-wrapper img {
  max-width: 100%;
  height: auto;
  aspect-ratio: attr(width) / attr(height);
}
