/* ==========================================================================
   mo:sens — skeleton
   усе, що варто чіпати першим, лежить у :root
   everything worth touching first lives in :root
   ========================================================================== */

:root {
  /* колір / colour */
  --paper:      #f4f2ed;
  --ink:        #121212;   /* той самий чорний, що всередині лого / same black as the logo */
  --ink-soft:   #5c5e63;   /* дрібні підписи, лейбли / small labels and captions */
  --paper-soft: #9a9893;   /* те саме, але на чорному / the same, on the dark ground */
  --line-ink:   #333336;   /* лінія на чорному / the hairline on the dark ground */
  --ink-read:   #35373c;   /* абзаци для читання / reading paragraphs — див. нижче */
  --line:       #121212;   /* роздільники — той самий чорний, що текст */

  /* ТОВЩИНА ВОЛОСЯНОЇ ЛІНІЇ / THE HAIRLINE WIDTH
     роздільники чорні, тому товщина стала помітною: 1px суцільного
     чорного важчий за 1px бежевого. на екранах із подвійною щільністю
     0.5px — це рівно один фізичний піксель, тобто найтонше, що взагалі
     можна намалювати. на звичайних лишається 1px: тонше нікуди

     the rules are black now, so their weight became visible — 1px of
     solid black reads heavier than 1px of beige. on 2x displays 0.5px
     is exactly one device pixel, the thinnest line that can exist;
     on 1x screens it stays 1px, which is already the floor        */
  --hair:       1px;
  --accent:     #121212;   /* поки той самий, що ink — зміниться з палітрою */

  /* типографіка / type */
  --font-body:  "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-head:  "Helvetica Neue", Helvetica, Arial, sans-serif;

  /* НАЧЕРК АБЗАЦІВ / THE PARAGRAPH WEIGHT
     300 = helvetica neue light. світліший начерк на середньому сірому
     читається як вицвілий, тому колір абзаців одночасно потемнішав:
     --ink-soft #5c5e63 → --ink-read #35373c. дві правки працюють у пару,
     міняти одну без другої не варто.
     ⚠️ light-накреслення є в helvetica neue (mac/ios). на windows і android
     підставляється arial, у якого light-начерку немає — там абзаци
     лишаться regular. це не ламає сторінку, але вигляд буде рівно той,
     що зараз. постійний результат дасть тільки веб-шрифт зі своїм light.

     300 = helvetica neue light. a lighter weight over mid-grey reads as
     washed out, so the paragraph colour got darker at the same time.
     the two changes work as a pair — don't move one without the other.
     ⚠️ the light cut exists in helvetica neue (mac/ios). windows and
     android fall back to arial, which has no light face, so paragraphs
     stay regular there. nothing breaks; it just looks like it does today.
     only a webfont with its own light cut makes this consistent.      */
  --w-read:     300;

  --step--1:  clamp(0.82rem, 0.8rem + 0.1vw, 0.9rem);
  --step-0:   clamp(1rem, 0.96rem + 0.2vw, 1.12rem);
  --step-1:   clamp(1.2rem, 1.1rem + 0.5vw, 1.5rem);
  --step-2:   clamp(1.6rem, 1.3rem + 1.4vw, 2.4rem);
  --step-3:   clamp(2.2rem, 1.5rem + 3.2vw, 4.4rem);

  /* ритм / rhythm */
  --gutter:   clamp(1.25rem, 4vw, 3rem);
  --measure:  38rem;
  --section:  clamp(4.5rem, 10vw, 9rem);
  --shell:    76rem;   /* = max-width у .wrap / matches .wrap's max-width */

  /* відстань від краю екрана до внутрішнього краю .wrap.
     цим числом зображення тез виходять під обріз
     the distance from the screen edge to .wrap's inner edge.
     this is what lets the thesis images bleed off the page          */
  --edge:     max(var(--gutter), calc((100vw - var(--shell)) / 2 + var(--gutter)));
}

@media (min-resolution: 2dppx) {
  :root { --hair: 0.5px; }
}

/* --------------------------------------------------------------- reset --- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  overflow-x: hidden;   /* страхує від'ємні margin під обріз / guards the bleed margins */
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.55;
  letter-spacing: 0.005em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { display: block; max-width: 100%; height: auto; }

h1, h2, h3 {
  font-family: var(--font-head);
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -0.02em;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0; text-wrap: pretty; }

a { color: inherit; text-decoration: none; }

/* --------------------------------------------------------------- layout -- */

.wrap {
  width: 100%;
  max-width: 76rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section); }

/* секція, під якою одразу починається інша поверхня (чорна смуга).
   нижнє поле секції знімається, бо відступ уже несе сам блок
   усередині — інакше два відступи складаються в порожнечу
   a section that hands straight over to another surface (the dark band).
   its bottom padding is dropped: the block inside already carries the
   gap, and two paddings in a row just stack into a void            */
.section--flush-bottom { padding-bottom: 0; }

.rule { border: 0; border-top: var(--hair) solid var(--line); margin: 0; }

/* видимо тільки для скрінрідерів / screen-reader only */
.sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------------------------------------------------------- wordmark ----- */

/* ЛОГОТИП / THE LOGO
   файл: assets/img/logo/mosens-logo.svg — 989 × 134, тобто ~7.4:1
   через таку пропорцію розмір задається ШИРИНОЮ, не висотою
   file: assets/img/logo/mosens-logo.svg — 989 × 134, i.e. ~7.4:1
   because of that ratio, size is set by WIDTH, not height            */
.wordmark {
  display: inline-block;
  font-family: var(--font-head);
  font-size: var(--step-1);
  letter-spacing: -0.03em;
  line-height: 1;
}

.wordmark img {
  width: clamp(6.5rem, 8vw, 8.75rem);
  height: auto;
}

.wordmark--hero { font-size: var(--step-3); letter-spacing: -0.045em; }

.wordmark--hero img {
  width: min(100%, clamp(16rem, 52vw, 38rem));
}

/* КОРОТКИЙ ЗНАК / THE SHORT MARK
   файл: mosens-short-logo.svg — 65 × 233, тобто вертикальний, ~1 : 3.6.
   на відміну від текстового лого, цей задається ВИСОТОЮ.
   селектор із двох класів — щоб гарантовано перебити width із .wordmark img
   file: mosens-short-logo.svg — 65 × 233, vertical, ~1 : 3.6.
   unlike the wordmark, this one is sized by HEIGHT.
   two-class selector so it reliably beats the width in .wordmark img  */
.wordmark.wordmark--short img {
  width: auto;
  height: clamp(6.5rem, 17vh, 11rem);
}


/* ------------------------------------------------------------- header --- */

.site-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: var(--hair) solid transparent;
  transition: border-color 0.25s ease;
}
.site-header[data-scrolled="true"] { border-bottom-color: var(--line); }

.site-header .wrap {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem; padding-block: 1.1rem;
}

/* пунктів меню пʼять, тому на вузькому екрані нав переноситься окремим
   рядком під лого замість бургера. менше коду, нічого не ламається без js
   five nav items, so on narrow screens the nav wraps onto its own line
   under the logo instead of collapsing into a burger. less code, and
   nothing breaks without js                                          */
.nav {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(0.9rem, 3vw, 2.25rem);
}

@media (max-width: 34rem) {
  .site-header .wrap { justify-content: flex-start; }
  .nav { width: 100%; }
}

.nav a {
  font-size: var(--step--1);
  letter-spacing: 0.02em;
  padding-bottom: 2px;
  border-bottom: var(--hair) solid transparent;
  transition: border-color 0.2s ease, opacity 0.2s ease;
}
.nav a:hover { border-bottom-color: var(--ink); }
.nav a[aria-current="page"] { border-bottom-color: var(--ink); }

/* --------------------------------------------------------------- hero --- */

/* хіро по центру / the hero, centred
   min-height + align-content робить центрування навмисним, а не випадковим
   min-height + align-content make the centring deliberate, not accidental */
.hero { padding-block: clamp(3rem, 9vh, 6rem) var(--section); }

/* центрування — тільки для головної. на portfolio.html і history.html
   заголовки лишаються ліворуч, тому клас окремий
   centring applies to the home page only — portfolio.html and history.html
   keep their titles left-aligned, hence the separate class            */
.hero--home { text-align: center; }

.hero--home .wrap {
  min-height: min(76svh, 46rem);
  display: grid;
  align-content: center;
  justify-items: center;
  gap: clamp(1.5rem, 3.5vw, 2.25rem);
}

/* знак стоїть окремо, тайтл із сабтайтлом тримаються разом
   the mark stands apart, title and subtitle hold together           */
.hero--home .hero__mark { margin-bottom: clamp(0.75rem, 2vw, 1.5rem); }
.hero--home .cta { margin-top: clamp(0.75rem, 2vw, 1.5rem); }

.hero__mark { margin-bottom: 0; }

/* заголовок і підзаголовок хіро / the hero title and subtitle
   шрифт той самий, що всюди (--font-head), змінюється тільки кегель
   same typeface as everywhere (--font-head), only the size differs   */
.hero__title {
  font-family: var(--font-head);
  font-size: clamp(1.55rem, 1.05rem + 1.85vw, 2.6rem);
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: -0.024em;
  max-width: 36rem;
  margin: 0 auto;
  text-wrap: balance;
}

/* хіро головної — одне речення одним кеглем. міра ширша за звичайний
   .hero__title, бо рядок довший: на 36rem він розсипався б на п'ять
   рядків, а тут має стати у три
   the home hero is one sentence at one size. a wider measure than the
   default title because the line is longer — at 36rem it would break
   into five lines; here it should sit in three                       */
.hero--home .hero__title { max-width: 46rem; }

/* підзаголовок / the subtitle
   max-width підібрано так, щоб цей текст ставав рівно у два рядки
   the max-width is tuned so this copy sets in exactly two lines      */
.hero__sub {
  font-size: var(--step-0);
  font-weight: var(--w-read);
  line-height: 1.5;
  color: var(--ink-read);
  max-width: 30rem;
  margin-inline: auto;
  text-wrap: pretty;
}

.hero__lede {
  font-size: var(--step-1);
  font-weight: var(--w-read);
  line-height: 1.4;
  max-width: 40rem;
  color: var(--ink);
  margin-inline: auto;
  margin-bottom: 0;
  text-wrap: balance;
}

/* --------------------------------------------------------------- cta ---- */

.cta {
  display: inline-flex; align-items: center; gap: 0.6em;
  font-size: var(--step-0);
  padding: 0.75em 1.6em;
  border: 1px solid var(--ink);
  border-radius: 999px;
  transition: background 0.2s ease, color 0.2s ease;
}
.cta:hover { background: var(--ink); color: var(--paper); }

.cta__arrow { transition: transform 0.2s ease; }
.cta:hover .cta__arrow { transform: translateX(3px); }

.cta--plain {
  border: 0; padding: 0; border-radius: 0;
  border-bottom: 1px solid var(--ink);
}
.cta--plain:hover { background: none; color: inherit; opacity: 0.6; }

/* кнопка на темному тлі: та сама форма, кольори поміняні місцями
   the cta on a dark ground: same shape, the two colours swapped */
.cta--invert { border-color: var(--paper); color: var(--paper); }
.cta--invert:hover { background: var(--paper); color: var(--ink); }

/* ---------------------------------------------------- manifesto (pinned) */
/* ТЕЗИ / THE THESES
   тези по центру, без зображень. блок закріплюється при скролі: одна теза
   гасне, наступна проявляється, і лише пройшовши всі — сторінка рушає далі.

   ДВА РЕЖИМИ / TWO MODES
   1. базовий (нижче) — тези просто йдуть одна за одною. це те, що бачить
      користувач без js і той, у кого ввімкнено prefers-reduced-motion
   2. закріплений — увімкнюється, коли js ставить data-pinned="true"
   robust by design: without js, or with reduced motion, the theses simply
   stack. the pinned mode is opt-in from js.                            */

.thesis {
  text-align: center;
  max-width: 46rem;
  margin-inline: auto;
}

.thesis__lead {
  font-size: var(--step-2);
  line-height: 1.14;
  margin-bottom: 0.7em;
  text-wrap: balance;
}

.thesis__text {
  font-weight: var(--w-read);
  color: var(--ink-read);
  max-width: 34rem;
  margin-inline: auto;
  text-wrap: pretty;
}

.thesis--payoff .thesis__lead { font-size: clamp(1.9rem, 1.4rem + 2vw, 3.2rem); }
.thesis--payoff .thesis__text { color: var(--ink); font-size: var(--step-1); }

/* --- режим 1: базовий стек / mode 1: the plain stack ----------------- */
.thesis-stack { display: grid; gap: clamp(3rem, 9vw, 7rem); }

.thesis-dots { display: none; }

/* --- режим 2: закріплення / mode 2: pinned --------------------------- */
/* --per — скільки скролу припадає на одну тезу. це головне число тут:
   більше = довше читається кожна, менше = швидше проскакує
   --per is how much scroll each thesis gets. the one number that matters:
   larger = each lingers, smaller = they fly by                        */
[data-pinned="true"] { --per: 75vh; }

[data-pinned="true"] .thesis-track {
  position: relative;
  height: calc(var(--count, 6) * var(--per));
}

[data-pinned="true"] .thesis-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  align-items: center;
  padding-block: clamp(2rem, 6vh, 4rem);
  overflow: hidden;
}

[data-pinned="true"] .thesis-shell { height: 100%; }

[data-pinned="true"] .thesis-stack {
  display: block;
  position: relative;
  height: 100%;
  gap: 0;
}

/* БЕЗ css-transition НАВМИСНО / NO css transition, deliberately
   прозорість і зсув тез рахує js у кожному кадрі від фактичного положення
   скролу. якби тут стояв transition, він боровся б із цим розрахунком і
   повертав ту саму затримку, яку ми й прибирали.
   js computes opacity and offset every frame from the real scroll position.
   a transition here would fight that calculation and bring back exactly the
   delay we were removing.                                              */
[data-pinned="true"] .thesis {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  opacity: 0;
  pointer-events: none;
  will-change: opacity, transform, filter;
  backface-visibility: hidden;
}

/* точки-орієнтир / the position dots
   не нумерація, а підказка «скільки ще» — у закріпленому блоці без неї
   легко загубитися. не потрібні — прибери .thesis-dots з розмітки
   not numbering — an indicator of how much is left. easy to feel lost in
   a pinned block without it. don't want them? delete .thesis-dots       */
[data-pinned="true"] .thesis-dots {
  display: grid;
  place-items: center;
  height: 2.4rem;
}

/* дві шари в одній клітинці сітки — коми й лого стоять один на одному,
   js перекидає прозорість між ними на останній тезі
   two layers in one grid cell — the commas and the logo sit on top of each
   other, js crossfades between them on the final thesis              */
.thesis-dots__commas,
.thesis-dots__logo {
  grid-area: 1 / 1;
}

.thesis-dots__commas {
  display: flex;
  flex-direction: row;   /* явно, щоб ряд не міг стати колонкою / explicit */
  flex-wrap: nowrap;
  gap: 1.6rem;
  align-items: center;
  will-change: opacity, transform;
}

.thesis-dots__logo {
  width: clamp(7rem, 13vw, 11rem);
  height: auto;
  opacity: 0;
  will-change: opacity, transform;
}

/* самі коми / the commas themselves
   не точки, а коми — той самий знак, що в назві й у cta. позначка
   «скільки ще», яка водночас проговорює принцип
   not dots but commas — the same mark as in the name and the cta. an
   indicator of how much is left that also states the principle       */
/* ⚠️ селектор саме `.thesis-dots__commas > span`, а не `.thesis-dots span`.
   другий має вагу (0,1,1) і матчив ще й саму обгортку `.thesis-dots__commas`
   (вона теж <span>), забиваючи їй display:flex своїм display:block — через
   це коми вишиковувалися вертикально одна під одною.
   ⚠️ the selector is `.thesis-dots__commas > span`, not `.thesis-dots span`.
   the latter has weight (0,1,1) and also matched the wrapper
   `.thesis-dots__commas` (itself a <span>), overriding its display:flex with
   display:block — which is why the commas stacked vertically.          */
.thesis-dots__commas > span {
  display: block;
  line-height: 0;
  color: var(--line);
  transition: color 0.35s ease, transform 0.35s ease;
}

.thesis-dots__commas > span.is-active {
  color: var(--ink);
  transform: scale(1.4);
}

/* сама кома / the comma itself
   svg із viewBox, тому width: auto тримає пропорцію 0.6 : 1 сам.
   висота — єдине число, яким керується розмір
   an svg with a viewBox, so width: auto keeps its 0.6 : 1 ratio on its own.
   height is the only number that controls the size                     */
/* ⚠️ aspect-ratio тут обов'язковий. inline-svg без нього не має власної
   пропорції, width:auto резолвиться в дефолтні 300px, і шість ком
   розповзаються по всій ширині рядка — саме це й було видно
   ⚠️ the aspect-ratio is required. an inline svg without it has no
   intrinsic ratio, width:auto resolves to the default 300px, and the six
   commas spread across the entire row — which is exactly what happened  */
.comma {
  display: block;
  height: 0.78rem;
  width: auto;
  aspect-ratio: 64.59 / 107.04;
  fill: currentColor;
  transition: transform 0.3s ease;
}

/* --------------------------------------------------------------- work --- */

.section__label {
  font-size: var(--step--1);
  color: var(--ink-soft);
  letter-spacing: 0.06em;
  text-transform: lowercase;
  margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(22rem, 100%), 1fr));
  gap: clamp(1.25rem, 3vw, 2.5rem);
}

.grid--wide {
  grid-template-columns: repeat(auto-fill, minmax(min(28rem, 100%), 1fr));
}

/* ПРЕВʼЮ РОБІТ / THE WORK PREVIEW
   квадрати від краю до краю екрана, без проміжків між ними.
   сітка виходить із .wrap тим самим від'ємним margin, що й обкладинки
   кейсів — --edge якраз і дорівнює відстані від краю екрана до
   внутрішнього краю .wrap

   ⚠️ рівно ТРИ колонки, і ТРИ плитки в розмітці — це один ряд.
   проміжного розкладу на дві колонки навмисно немає: він розбив би
   ряд на 2 + 1 і залишив порожню клітинку. три квадрати тримаються
   аж до 34rem, нижче — стовпчик, бо три квадрати на 400px екрані
   дали б по 130px, і підпис у них не поміститься

   squares edge to edge, no gaps. the grid escapes .wrap with the same
   negative margin as the case covers — --edge is exactly the distance
   from the screen edge to .wrap's inner edge

   ⚠️ exactly THREE columns and THREE tiles — that is the one row.
   there is deliberately no intermediate two-column layout: it would
   break the row into 2 + 1 and leave an empty cell. the three squares
   hold down to 34rem; below that they stack, because three squares on
   a 400px screen would be 130px each and the caption wouldn't fit  */
.grid--work {
  margin-inline: calc(-1 * var(--edge));
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
}

@media (max-width: 34rem) {
  .grid--work { grid-template-columns: 1fr; }
}

/* --- плитка / the tile ------------------------------------------------ */

.card--tile a { display: block; }

/* квадрат тримає aspect-ratio на обгортці, а не на зображенні:
   зображення в проєкті різних пропорцій, object-fit кадрує їх по центру
   the wrapper holds the square, not the image: project images come in
   different ratios, and object-fit crops them from the centre        */
.card--tile .figure {
  margin: 0;
  aspect-ratio: 1 / 1;
  overflow: hidden;
}

.card--tile .figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* підпис відбитий від краю квадрата, а не від краю екрана — інакше
   в крайній лівій плитці текст лип би до самого краю
   the caption is inset from the tile's edge, not the screen's — otherwise
   the leftmost tile's text would sit flush against the screen edge   */
.card--tile .card__meta {
  padding: 0.9rem clamp(0.9rem, 2vw, 1.4rem) clamp(2.25rem, 5vw, 3.25rem);
}

.card img { transition: opacity 0.25s ease; }
.card a:hover img { opacity: 0.86; }

/* ЧОРНА ПАНЕЛЬ / THE DARK PANEL
   перехід від робіт до підходу: один екран, повністю чорний.
   ніякої лінії зверху — сама зміна тла вже є розділювачем, а волосяна
   лінія перед чорним полем не розділяє нічого, тільки бруднить край

   панель НЕ загорнута у .wrap, тому тло розтягується на всю ширину
   саме собою, без від'ємних margin. .wrap лежить усередині і тримає
   поля для тексту

   the handover from the work to the approach: one screen, solid black.
   no rule on top — the change of ground already separates them, and a
   hairline in front of a black field divides nothing, it just dirties
   the edge. the panel is NOT wrapped in .wrap, so the background fills
   the width by itself; .wrap sits inside and holds the text margins  */
/* ВИСОТА / THE HEIGHT
   смуга, а не екран. висота задається тільки полями — жодного
   min-height: 100svh. поле на цілий екран читалося б як окрема
   станція маршруту, а це перехід між двома блоками, який має
   пройти повз, а не зупинити
   a band, not a screen. the height comes from padding alone — no
   min-height: 100svh. a full-screen field would read as its own stop
   on the route, and this is a handover meant to be passed, not paused at */
/* кнопка «всі роботи» під плитками. плитки йдуть під обріз і закінчуються
   рівно на краю екрана, тож кнопка стає праворуч по краю .wrap — там, де
   око вже стоїть після останньої плитки
   the see-all-work button under the tiles: the tiles run to the screen
   edge, so the button aligns right, where the eye already is       */
.work-more {
  display: flex;
  justify-content: flex-end;
  padding-block: clamp(2rem, 5vw, 3.5rem) 0;
}

.panel-dark {
  background: var(--ink);
  color: var(--paper);
  padding-block: clamp(3.5rem, 9vw, 6.5rem);
}

.panel-dark .wrap {
  display: grid;
  justify-items: start;
  gap: clamp(2rem, 5vw, 3.25rem);
}

/* ⚠️ КЕГЕЛЬ НЕ ЗБІЛЬШУВАТИ. акцент тут робить тло, і тільки тло.
   спокуса очевидна — цілий чорний екран ніби просить великого рядка, —
   але це не головний cta сторінки, а перехід між двома блоками.
   великий текст на чорному тлі перекричав би і хіро, і форму, а вони
   тут головні. малий рядок на великому полі читається як пауза,
   а не як заклик — саме це й потрібно

   ⚠️ DO NOT SCALE THIS UP. the ground carries the emphasis, nothing else.
   a screen of black seems to ask for a big line, but this is a handover
   between two blocks, not the page's main cta — at display size it would
   shout down both the hero and the form, which are the real ones.
   a small line on a large field reads as a pause, not a call         */
.panel-dark__note {
  font-size: var(--step-1);
  font-weight: var(--w-read);
  line-height: 1.4;
  max-width: 28rem;
  margin: 0;
}

/* примітка під палітрою або під зображенням — коротка репліка, не абзац.
   вужча за --measure навмисно: вона мусить читатися як зауваження на полях,
   а не як ще один шматок тексту
   a short note under a palette or a figure — deliberately narrower than
   --measure so it reads as a marginal remark, not another block of copy */
.case-note {
  font-size: var(--step--1);
  font-weight: var(--w-read);
  line-height: 1.5;
  color: var(--ink-read);
  max-width: 30rem;
  margin-top: clamp(1.25rem, 3vw, 1.75rem);
}

/* --- відео в кейсі / the case video -----------------------------------
   відео вертикальне (9:16). на широкому екрані воно стоїть вузькою
   колонкою поруч із текстом — розтягнути його на всю ширину означало б
   віддати йому два екрани висоти заради того самого вмісту.
   нижче 52rem текст іде під відео

   the video is vertical (9:16). on wide screens it sits in a narrow
   column beside the text; stretching it to full width would spend two
   screens of height on the same content. below 52rem the text stacks  */
.video-row {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
}

@media (min-width: 52rem) {
  .video-row { grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); }
}

.video {
  display: block;
  width: 100%;
  height: auto;
  max-width: 20rem;
  background: var(--ink);   /* поки не завантажилось / while it loads */
}

.video-row__note {
  display: grid;
  gap: 1.1em;
  max-width: 30rem;
}

.video-row__note p {
  font-size: var(--step-0);
  font-weight: var(--w-read);
  line-height: 1.55;
  color: var(--ink-read);
}

.video-row__note p:first-child { color: var(--ink); }

/* --- відео + фото в один ряд / video and photo as one row --------------
   вертикальне відео (9:16) ліворуч і горизонтальне фото (4:3) праворуч,
   однієї висоти, без проміжку, під обріз. пропорції колонок — це просто
   відношення ширин при рівній висоті: 9/16 : 4/3 = 9 : 21.333.
   нижче 56rem — стос: відео вузькою колонкою, фото на всю ширину.
   ⚠️ якщо зміниться формат фото або відео — перерахувати колонки, інакше
   висоти розійдуться і ряд розвалиться

   a vertical 9:16 video left, a 4:3 photo right, equal height, no gap,
   full bleed. the column ratio is just the width ratio at equal height:
   9/16 : 4/3 = 9 : 21.333. stacks below 56rem.
   ⚠️ change either aspect ratio and the columns must be recomputed      */
.media-row { display: grid; gap: clamp(1.5rem, 4vw, 3rem); }
.media-row .figure { margin: 0; }

@media (min-width: 56rem) {
  .media-row {
    grid-template-columns: 9fr 21.3333fr;
    gap: 0;
    align-items: start;
    margin-inline: calc(-1 * var(--edge));
  }
  .media-row .video { max-width: none; aspect-ratio: 9 / 16; object-fit: cover; }
  .media-row img    { aspect-ratio: 4 / 3;  object-fit: cover; }
  .media-row .figure:first-child figcaption { padding-left: var(--edge); }
  .media-row .figure:last-child  figcaption { padding-inline: clamp(1rem, 2vw, 1.5rem) var(--edge); }
}

/* ------------------------------------------------------------ approach --- */
/* ПІДХІД ДО РОБОТИ / HOW WE WORK
   чотири розділи, кожен — рядок, розділені волосяною лінією.
   ніяких карток, рамок і плашок: рядок і лінія — це вже структура,
   а коробка навколо неї нічого не додає, крім ваги

   на широкому екрані рядок ділиться на три колонки:
   назва розділу | абзац | перелік. так кожен рядок лишається низьким,
   а око бачить усі чотири розділи одразу, без гортання

   four chapters, one row each, separated by a hairline. no cards, no
   boxes: a row and a rule already are the structure, and a box around
   them adds nothing but weight.
   on wide screens each row splits into three columns — name | text |
   list — which keeps every row short, so all four chapters are visible
   at once instead of scrolling past one another                      */
/* ⚠️ ЛІНІЇ ЙДУТЬ НА ВСЮ ШИРИНУ ЕКРАНА, ТЕКСТ — НІ.
   список розсунутий під обріз від'ємними полями, а внутрішні відступи
   повертають текст рівно на місце. так лінія торкається краю екрана,
   а колонки лишаються в тій самій сітці, що й решта сторінки.
   робити це через окремі <hr> не можна: лінія тут — межа рядка списку,
   і вона мусить рухатися разом із ним

   ⚠️ the rules run the full width of the viewport, the text does not:
   negative margins push the list out to the edge and matching padding
   pulls the content back into the page grid. it has to be the row's own
   border, not separate <hr>s — the line belongs to the row          */
.chapters {
  list-style: none;
  margin: 0;
  padding: 0;
  margin-inline: calc(-1 * var(--edge));
  border-top: var(--hair) solid var(--line);
}

.chapter {
  display: grid;
  gap: clamp(0.75rem, 2vw, 1.5rem);
  padding-block: clamp(1.75rem, 4vw, 2.75rem);
  padding-inline: var(--edge);
  border-bottom: var(--hair) solid var(--line);
}

@media (min-width: 60rem) {
  .chapter {
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) minmax(0, 1.35fr);
    gap: clamp(1.5rem, 4vw, 4rem);
    align-items: start;
  }
}

/* номер розділу. трекінг вужчий, ніж у решти дрібних лейблів (.06em):
   те розрідження робилося під слова, а цифри від нього розсипаються
   the chapter numeral. tighter tracking than the other small labels,
   whose .06em was set for words — digits fall apart at that width  */
.chapter__num {
  font-size: var(--step--1);
  color: var(--ink-soft);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  margin-bottom: 0.5rem;
}

.chapter__name {
  font-family: var(--font-head);
  font-size: var(--step-2);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.025em;
  margin: 0;
}

.chapter__text {
  font-size: var(--step-0);
  font-weight: var(--w-read);
  line-height: 1.55;
  color: var(--ink-read);
  max-width: 34rem;
}

/* перелік без маркерів і без крапок у кінці — це не речення, а ярлики.
   колір темніший за абзац навмисно: абзац пояснює, перелік називає
   no markers, no full stops — these are labels, not sentences. they sit
   darker than the paragraph on purpose: the paragraph explains,
   the list names                                                      */
.chapter__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.55rem;
  font-size: var(--step--1);
  line-height: 1.35;
  color: var(--ink);
}

/* МОКАПИ / THE MOCKUPS
   чорний квадрат — це фолбек. поклади файл у assets/img/projects/
   і впиши його в style="background-image:url(...)" у html
   the black square is the fallback. drop a file into assets/img/projects/
   and reference it in the inline style in the html                        */
.thumb {
  position: relative;
  aspect-ratio: 4 / 5;
  background-color: var(--ink);
  background-size: cover;
  background-position: center;
  overflow: hidden;
}

.thumb--wide { aspect-ratio: 3 / 2; }

.card__meta { padding-top: 0.9rem; }

.card__title { font-size: var(--step-1); margin-bottom: 0.15em; }

.card__note {
  font-size: var(--step--1);
  color: var(--ink-soft);
  max-width: 30rem;
}

.card a:hover .thumb { opacity: 0.88; transition: opacity 0.2s ease; }

/* ---------------------------------------------------------- case rows --- */

/* ⚠️ БЕЗ ЛІНІЙ І БЕЗ ВІДСТУПІВ МІЖ РЯДАМИ (на широкому екрані).
   зображення йдуть впритул одне під одним і складаються в суцільну
   вертикальну смугу від краю екрана до середини. саме ця смуга й
   розділяє кейси — роздільник тут був би третім засобом поверх двох,
   які вже працюють: обріз і зміна кадру.

   на вузькому екрані відступ ЛИШАЄТЬСЯ: там ряд стає стовпчиком, між
   зображеннями опиняється текст, і без поля кейси злипаються в одну
   стрічку, де незрозуміло, де закінчився один і почався наступний

   no rules and no gaps between rows on wide screens: the images stack
   flush into one continuous column, and that column is what separates
   the cases — a divider would be a third device on top of two that
   already work. on narrow screens the padding stays: the row becomes a
   stack, the text lands between the images, and without it the cases
   run together                                                      */
.case {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  padding-block: clamp(2.5rem, 6vw, 4rem);
}

/* ЗОБРАЖЕННЯ ВІД КРАЮ ЕКРАНА ДО СЕРЕДИНИ / EDGE-TO-CENTRE MEDIA
   на широкому екрані ряд ділиться рівно навпіл, а медіа-колонка
   виходить лівим краєм за .wrap — до самого краю екрана. від'ємний
   margin рівно --edge, тобто тій відстані, на якій .wrap стоїть
   від краю; тому зображення завжди впирається в край, на будь-якій
   ширині, без жодного числа в пікселях

   ⚠️ рівні колонки (1fr 1fr), а не 1.1fr — інакше «до середини»
   було б неправдою, а середина тут і є тим, за що чіпляється око
   при перегляді списку

   on wide screens the row splits exactly in half and the media column
   runs past .wrap on its left, out to the screen edge. the negative
   margin is exactly --edge — the distance .wrap sits from the edge —
   so the image meets the edge at every width, with no pixel number
   anywhere. equal columns on purpose: "to the middle" has to be true,
   and that middle is what the eye tracks down the list           */
@media (min-width: 56rem) {
  .case {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: center;
    padding-block: 0;
  }

  .case > a:first-child,
  .case > .thumb:first-child { margin-left: calc(-1 * var(--edge)); }
}

.case__title { font-size: var(--step-2); margin-bottom: 0.5em; }

.case__desc {
  font-weight: var(--w-read);
  color: var(--ink-read);
  max-width: var(--measure);
  margin-bottom: 1.75rem;
}

.tags { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; margin: 0; padding: 0; }

.tags li {
  font-size: var(--step--1);
  padding: 0.35em 0.85em;
  border: var(--hair) solid var(--line);
  border-radius: 999px;
  color: var(--ink-soft);
}

/* --------------------------------------------------------------- form --- */

.contact { display: grid; gap: clamp(2rem, 5vw, 3.5rem); }

@media (min-width: 56rem) {
  .contact { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); }
}

.contact__lead { font-size: var(--step-2); line-height: 1.16; max-width: 22rem; }

.form { display: grid; gap: 1.5rem; max-width: 34rem; }

.field { display: grid; gap: 0.45rem; }

.field label {
  font-size: var(--step--1);
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}

.field input,
.field textarea {
  font: inherit;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: var(--hair) solid var(--line);
  padding: 0.6rem 0;
  border-radius: 0;
  transition: border-color 0.2s ease;
}

.field input:focus,
.field textarea:focus {
  outline: none;
  border-bottom-color: var(--ink);
}

.field textarea { resize: vertical; min-height: 6rem; }

.form button {
  font: inherit;
  cursor: pointer;
  justify-self: start;
  background: var(--ink);
  color: var(--paper);
  border: 1px solid var(--ink);
  border-radius: 999px;
  padding: 0.75em 1.8em;
  transition: background 0.2s ease, color 0.2s ease;
}
.form button:hover { background: transparent; color: var(--ink); }

.form__note { font-size: var(--step--1); color: var(--ink-soft); }


/* ======================================================= on dark ground = */
/* ЧОРНА ПОВЕРХНЯ / THE DARK SURFACE
   один клас .on-ink перевертає блок на чорне. вішається на секцію
   контактів і на футер — вони йдуть підряд і читаються як одне
   чорне підвалля сторінки.

   ЧОМУ ОКРЕМИЙ КЛАС, А НЕ ДРУГА ТЕМА: тем тут дві не потрібно.
   потрібні дві поверхні в межах однієї теми, і кожен елемент, який
   на них живе, мусить знати обидва свої стани. усе, що нижче, —
   це перелік таких елементів, а не «нічний режим»

   one class flips a block to the dark ground. it goes on the contact
   section and on the footer — they run back to back and read as one
   dark foot to the page.
   this is not a second theme: it is a second surface inside one theme,
   and every element that lives on it needs to know both of its states */
.on-ink {
  background: var(--ink);
  color: var(--paper);
}

.on-ink .section__label,
.on-ink .form__note,
.on-ink .site-footer__meta { color: var(--paper-soft); }

/* лейбли полів — білі, не приглушені. це не підпис до чогось, а єдине,
   що пояснює, що писати в порожню лінію: поки поле не заповнене, воно
   несе всю інформацію про себе саме в лейблі
   the field labels are white, not muted. they are not captions but the
   only thing telling you what goes on an empty line — until the field
   is filled, the label carries everything the field knows about itself */
.on-ink .field label { color: var(--paper); }

/* ⚠️ ЛОГО. у svg зашитий #121212, тож на чорному воно просто зникає.
   invert(1) перевертає його в #ededed — це майже --paper (#f4f2ed),
   різниця непомітна на око, а знак однокольоровий, тому нічого
   іншого фільтр зіпсувати не може. правильніше було б вбудувати svg
   інлайном і поставити fill="currentColor", але це п'ять сторінок
   багатослівної розмітки заради тієї самої картинки

   ⚠️ THE LOGO. the svg carries a hard-coded #121212, so on black it
   simply vanishes. invert(1) turns it into #ededed — near enough to
   --paper that the eye can't tell, and the mark is single-colour so
   the filter has nothing else to damage. the cleaner fix is inlining
   the svg with fill="currentColor", but that is five pages of verbose
   markup for the same picture                                       */
.on-ink .wordmark img { filter: invert(1); }

.on-ink .field input,
.on-ink .field textarea {
  color: var(--paper);
  border-bottom-color: var(--paper);
}

/* ⚠️ ФОКУС НЕ МОЖЕ БУТИ «ЩЕ БІЛІШИМ». лінія вже біла, тож підсвітити
   її кольором нічим. тому фокус додає другу лінію тінню — візуально
   вона подвоює товщину, але тінь не займає місця в потоці, тобто
   поле не смикається й сусідні не з'їжджають. border-width тут дав би
   саме такий стрибок

   ⚠️ FOCUS CANNOT BE "WHITER". the line is already white, so there is
   no colour left to raise it with. focus lays a second line underneath
   as a shadow: it reads as double thickness, but a shadow takes no
   space in the flow, so nothing jumps. changing border-width would  */
.on-ink .field input:focus,
.on-ink .field textarea:focus {
  border-bottom-color: var(--paper);
  box-shadow: 0 1px 0 0 var(--paper);
}

/* автозаповнення в chrome малює власне світле тло поверх поля — на
   чорному це біла пляма. перебивається тільки внутрішньою тінню
   chrome paints its own light background on autofilled fields, which
   is a white patch here. an inset shadow is the only way to cover it */
.on-ink .field input:-webkit-autofill,
.on-ink .field input:-webkit-autofill:hover,
.on-ink .field input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--paper);
  -webkit-box-shadow: 0 0 0 100px var(--ink) inset;
  caret-color: var(--paper);
}

.on-ink .form button {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--paper);
}
.on-ink .form button:hover { background: transparent; color: var(--paper); }

.on-ink.site-footer { border-top-color: var(--line-ink); }


/* ======================================================== case page === */
/* сторінка одного кейсу / a single case study page                      */

.case-hero { padding-block: clamp(3rem, 9vh, 6rem) clamp(2rem, 6vw, 4rem); }

.case-hero__eyebrow {
  font-size: var(--step--1);
  color: var(--ink-soft);
  letter-spacing: 0.06em;
  margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}

.case-hero__title {
  font-family: var(--font-head);
  font-size: clamp(2.4rem, 1.4rem + 4vw, 5rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.035em;
  margin: 0 0 0.35em;
}

.case-hero__tagline {
  font-size: var(--step-1);
  font-weight: var(--w-read);
  color: var(--ink-read);
  max-width: 30rem;
  margin: 0;
}

/* мета-рядок / the meta row */
.case-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: clamp(1.25rem, 3vw, 2rem);
  margin-top: clamp(2.5rem, 6vw, 4rem);
  padding-top: clamp(1.5rem, 4vw, 2.25rem);
  border-top: var(--hair) solid var(--line);
}

.case-meta dt {
  font-size: var(--step--1);
  color: var(--ink-soft);
  letter-spacing: 0.05em;
  margin-bottom: 0.4rem;
}

.case-meta dd { margin: 0; font-size: var(--step-0); }

/* --- текстові блоки / prose blocks ---------------------------------- */
.case-section { padding-block: clamp(3rem, 8vw, 6rem); }

.case-section__head {
  font-size: var(--step--1);
  color: var(--ink-soft);
  letter-spacing: 0.06em;
  margin-bottom: clamp(1.25rem, 3vw, 2rem);
}

.prose {
  max-width: var(--measure);
  display: grid;
  gap: 1.1em;
}

.prose p { font-size: var(--step-1); font-weight: var(--w-read); line-height: 1.5; }

/* перший абзац лишається майже чорним, наступні — сірі. ієрархія тримається
   кольором, а не начерком: начерк тепер однаковий у всіх
   the first paragraph stays near-black, the rest go grey. the hierarchy is
   carried by colour now, not weight — the weight is the same throughout  */
.prose p + p { color: var(--ink-read); }

/* списки в тексті кейсу: без маркерів браузера, тире замість крапки —
   так само тонко, як усе інше на сторінці. кольором як другі абзаци
   lists inside case prose: no browser bullets, a dash instead of a dot,
   coloured like the follow-on paragraphs                                */
.prose ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75em; }
.prose li {
  position: relative;
  padding-left: 1.4em;
  font-size: var(--step-1);
  font-weight: var(--w-read);
  line-height: 1.5;
  color: var(--ink-read);
}
.prose li::before { content: "—"; position: absolute; left: 0; color: var(--ink-soft); }

/* велика заява / the big statement */
.statement {
  font-family: var(--font-head);
  font-size: clamp(1.6rem, 1.1rem + 2.2vw, 3rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.022em;
  max-width: 44rem;
  margin-inline: auto;
  text-align: center;
  text-wrap: balance;
}

/* --- зображення / figures -------------------------------------------- */
.figure { margin: 0; }

.figure img { display: block; width: 100%; height: auto; background: var(--ink); }

.figure figcaption {
  font-size: var(--step--1);
  color: var(--ink-soft);
  padding-top: 0.75rem;
}

/* ритм між зображеннями на сторінці кейсу. коли кілька ілюстрацій ідуть
   поспіль без тексту між ними, вони не мають злипатися підпис-до-краю:
   між сусідніми обгортками з фігурами — проміжок, перед лінією-розділювачем
   після фігури — повний відступ секції
   rhythm between figures on a case page. when several illustrations run
   back to back with no copy between them they must not stack caption-to-
   edge: a gap between neighbouring figure wraps, and a full section
   offset before a rule that follows a figure                            */
main > .wrap + .wrap { margin-top: clamp(1.5rem, 4vw, 3rem); }
main > .wrap + .rule { margin-top: clamp(3rem, 8vw, 6rem); }

/* під обріз, від краю до краю екрана / full-bleed, edge to edge */
.figure--bleed { margin-inline: calc(-1 * var(--edge)); }
.figure--bleed figcaption { padding-inline: var(--edge); }

.figure-pair {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(1rem, 2.5vw, 2rem);
}

@media (min-width: 56rem) {
  .figure-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
}

/* --- палітра / the palette ------------------------------------------- */
/* смуга кольорів іде під обріз і стоїть без проміжків, тому читається як
   одна система, а не як десять окремих плиток. підписи — під смугою на
   папері, а не всередині кольору: так вони читаються на будь-якому тоні
   і не треба вгадувати контраст для кожного зразка
   the colour band bleeds off-page and sits gapless, so it reads as one
   system rather than ten separate tiles. the labels go underneath on the
   paper, never inside the colour — that keeps them legible on every tone
   and avoids guessing contrast per swatch                              */
.palette {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(4.5rem, 1fr));
  gap: 0;
  margin-inline: calc(-1 * var(--edge));
}

.swatch { display: grid; }

.swatch__block { height: clamp(6rem, 14vw, 11rem); }

.swatch__meta {
  padding: 0.7rem 0.55rem 0;
  display: grid;
  gap: 0.1rem;
}

.swatch__hex {
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

.swatch__name { font-size: var(--step--1); color: var(--ink-soft); }

/* --- навігація кейсу / case navigation ------------------------------- */
.case-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  align-items: center;
  justify-content: space-between;
}


/* ==================================================== manifesto page === */
/* сторінка маніфесту / the manifesto page
   один стовпчик тексту й нічого більше. міра трохи ширша за звичайну
   (--measure), бо тут немає ані зображень, ані сайдбару, які тримали б око
   one column of text and nothing else. the measure is a touch wider than
   the usual --measure, because there is no image or sidebar to hold the
   eye here                                                             */

.manifesto-page { padding-block: clamp(3rem, 10vh, 7rem) clamp(4rem, 10vw, 8rem); }

.manifesto-page__eyebrow {
  font-size: var(--step--1);
  color: var(--ink-soft);
  letter-spacing: 0.06em;
  margin-bottom: clamp(2.5rem, 7vw, 4.5rem);
}

.manifesto-page__body {
  max-width: 42rem;
  display: grid;
  gap: clamp(1.5rem, 3.5vw, 2.25rem);
}

/* перше речення — це заголовок сторінки і водночас початок тексту
   the first sentence is both the page heading and the opening line */
.manifesto-page__lead {
  font-family: var(--font-head);
  font-size: clamp(1.5rem, 1.05rem + 1.7vw, 2.4rem);
  font-weight: 400;
  line-height: 1.16;
  letter-spacing: -0.022em;
  margin: 0;
  text-wrap: balance;
}

.manifesto-page__body p {
  font-size: var(--step-1);
  font-weight: var(--w-read);
  line-height: 1.55;
  color: var(--ink-read);
}

/* останній абзац лишається темним — це звернення, а не опис
   the closing paragraph stays dark — it addresses you, it doesn't describe */
/* ⚠️ ЛІНІЇ ТУТ НЕМАЄ — І НЕ ПОВЕРТАТИ. заклик іде просто за текстом:
   він продовжує ту саму думку, а не починає нову секцію. відділяє його
   відступ, і цього досить
   ⚠️ no rule here, and don't put one back: the call continues the same
   thought rather than starting a new section — the space separates it */
.manifesto-page__end {
  max-width: 42rem;
  margin-top: clamp(3rem, 8vw, 5rem);
}

.manifesto-page__close {
  font-family: var(--font-head);
  font-size: clamp(1.3rem, 1rem + 1.2vw, 1.9rem);
  line-height: 1.25;
  letter-spacing: -0.018em;
  margin: 0 0 clamp(1.75rem, 4vw, 2.5rem);
  text-wrap: balance;
}

/* ------------------------------------------------------------- footer --- */

.site-footer { border-top: var(--hair) solid var(--line); padding-block: clamp(3rem, 7vw, 5rem); }

.site-footer .wrap {
  display: flex; flex-wrap: wrap; align-items: end;
  justify-content: space-between; gap: 2rem;
}

.site-footer__meta { font-size: var(--step--1); color: var(--ink-soft); display: grid; gap: 0.3rem; }

.site-footer a { border-bottom: var(--hair) solid transparent; }
.site-footer a:hover { border-bottom-color: currentColor; }

/* -------------------------------------------------------------- empty --- */

.empty {
  min-height: 40vh;
  display: grid; place-content: center; text-align: center;
  gap: 0.75rem;
}
.empty__note {
  font-weight: var(--w-read);
  color: var(--ink-read);
  max-width: 26rem;
  margin-inline: auto;
}

/* ------------------------------------------------------- motion / a11y -- */

.reveal { opacity: 0; transform: translateY(12px); }
.reveal.is-in { opacity: 1; transform: none; transition: opacity 0.7s ease, transform 0.7s ease; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal, .reveal.is-in { opacity: 1; transform: none; transition: none; }
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }


/* ==========================================================================
   ⬛ ЕКСПЕРИМЕНТ ЗІ ШРИФТОМ: URBANIST — 10.09.2026
   ==========================================================================
   ЩОБ ПОВЕРНУТИ ЯК БУЛО: видали цей блок цілком і прибери <link> на
   google fonts із <head> усіх п'яти сторінок. більше нічого чіпати не
   треба — усе, що змінює шрифт, лежить ТІЛЬКИ тут.

   ЯК БУЛО ДО ЕКСПЕРИМЕНТУ:
     --font-body: "Helvetica Neue", Helvetica, Arial, sans-serif;
     --font-head: "Helvetica Neue", Helvetica, Arial, sans-serif;

   TO REVERT: delete this whole block and the google fonts <link> from
   the five pages. nothing else was touched.

   --------------------------------------------------------------------
   ЧОМУ ЦЕЙ ШРИФТ ЛЯГАЄ РІВНО ТУДИ, ДЕ БУЛА ДІРКА

   urbanist — варіативний геометричний гротеск, повний діапазон ваг
   100–900 плюс курсив.
   це закриває головну проблему, яку ми досі мали записаною як
   обмеження: --w-read: 300 працював тільки на mac, бо light-начерк був
   лише в helvetica neue, а windows і android падали на arial без light.
   тепер light приходить із самим шрифтом — абзаци виглядають однаково
   скрізь, і та різниця в начерку між абзацом і заголовком, заради якої
   все й робилося, нарешті існує на всіх системах.

   тому --w-read НЕ перевизначається: він і далі 300, і вперше це 300
   по-справжньому.

   ТРЕКІНГ теж не чіпаю. від'ємні значення підбиралися під гротеск, і
   urbanist — теж гротеск, тільки геометричний. ⚠️ але це єдине, що
   варто перевірити оком: у геометричних шрифтів круглі знаки ширші,
   і -0.035em на великому заголовку кейсу може виявитись зайвим.
   якщо злипається — піднімай саме .case-hero__title, не всі одразу.

   urbanist is variable, 100–900 plus italic. that closes the one gap we
   had written down as a limitation: --w-read: 300 only worked on mac,
   because the light cut lived in helvetica neue and windows/android fell
   back to arial, which has none. the light now ships with the font, so
   the weight contrast between paragraph and heading finally exists
   everywhere. --w-read is therefore left alone at 300.
   the tracking is left alone too — the negative values were tuned for a
   grotesque, and this is one, geometric. ⚠️ that is the single thing to
   check by eye: geometric faces have wider round glyphs, so -0.035em on
   the big case title may prove too tight. loosen that one rule, not all.

   ⚠️ ЗАВАНТАЖУЮТЬСЯ ТІЛЬКИ ТРИ НАЧЕРКИ: 300, 400 і курсив 400.
   у шрифті їх дев'ять, але сайт використовує саме ці три (курсив —
   у кейсі omm, "miłego dnia"). тягнути всі дев'ять означало б
   18 файлів замість трьох. додаси на сайт нову вагу — не забудь
   дописати її в url, інакше браузер підмінить найближчою наявною.

   only three cuts are fetched: 300, 400 and italic 400 — the three the
   site actually uses. pulling all nine would be 18 files. if you start
   using another weight, add it to the url or the browser will silently
   substitute the nearest one.
   ========================================================================== */

:root {
  --font-body: "Urbanist", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-head: "Urbanist", "Helvetica Neue", Helvetica, Arial, sans-serif;
}

/* ========================= кінець експерименту / end of experiment ====== */
