/* Prokhorov® — v7. A three-column page after a quiet editorial portfolio layout:
   who on the left, facts on the right, both held still on desktop while the
   work scrolls through the middle. One family (Inter), one weight, two greys,
   hairlines for structure. */

@font-face {
  font-family: "Inter";
  src: url("../assets/fonts/inter-opsz.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
/* Cyrillic, for /ru/: the same Inter build (4.001, opsz + wght) as its own file, which a browser
   fetches only when a page has Russian letters on it, so the English pages never load it */
@font-face {
  font-family: "Inter";
  src: url("../assets/fonts/inter-opsz-cyrillic.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* Arial sized to Inter while the font loads. Checked for Latin and Cyrillic alike (rendered width
   within 0.3% of Inter on /, /info/, /ru/, /ru/info/), so one rule serves both languages */
@font-face { font-family: "Inter Fallback"; src: local("Arial"); size-adjust: 107%; ascent-override: 90%; }

/* ---------- Tokens ---------- */
:root {
  --bg: #ffffff;
  --fg: #121212;
  --mute: #757575;
  --line: #e3e3e3;
  --soft: #f2f2f2;

  --bar: 44px;
  --pad: 20px;               /* outer edge */
  --in: 16px;                /* inside the middle column */
  --top: clamp(56px, 10.6svh, 120px);

  --t-hello: clamp(40px, 3.9vw, 72px);
  --t-state: clamp(20px, 1.8vw, 32px);
  --t-head: clamp(17px, 1.25vw, 22px);
  --t-base: clamp(15px, 1vw, 17px);
  --t-talk: clamp(56px, 5.6vw, 104px);

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}
@media (max-width: 767px) {
  :root { --pad: 12px; --in: 12px; }
}
/* Short laptop screens: less air above the columns so the sticky sides still fit */
@media (min-width: 1200px) and (max-height: 800px) {
  :root { --top: 48px; }
  .side .socials-wrap { margin-top: 28px; }
  .side .ask-wrap { margin-top: 20px; }
}

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--bar) + 16px);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: "Inter", "Inter Fallback", system-ui, sans-serif;
  font-size: var(--t-base);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.03em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin: 0; padding: 0; }
h1, h2, h3, h4 { font-weight: 500; font-size: inherit; }
ul, ol { list-style: none; }
img { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; letter-spacing: inherit; background: none; border: 0; padding: 0; cursor: pointer; text-align: inherit; }
a, button, summary { -webkit-tap-highlight-color: transparent; }
:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; }
::selection { background: var(--fg); color: var(--bg); }

.mute { color: var(--mute); }
.small { font-size: 0.875em; }
.r { font-size: 0.5em; vertical-align: 0.82em; margin-left: 0.04em; letter-spacing: 0; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.dot { display: inline-block; flex: none; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Logotype: star + wordmark, in the proportions of the master file (48 units
   high; the wordmark is 22 units high, 6 units after the star). Sized so the
   wordmark capitals match the capitals of the text beside it. */
.logo {
  --lh: calc(var(--t-base) * 1.59);
  display: inline-flex; align-items: center; gap: calc(var(--lh) * 6 / 48);
  color: var(--fg); line-height: 0;
}
.logo svg { fill: currentColor; flex: none; display: block; }
.logo__mark { width: calc(var(--lh) * 47 / 48); height: var(--lh); }
.logo__word { width: calc(var(--lh) * 181 / 48); height: calc(var(--lh) * 22 / 48); }

.skip {
  position: fixed; left: var(--pad); top: 8px; z-index: 100;
  padding: 10px 14px; background: var(--fg); color: var(--bg);
  transform: translateY(-200%);
}
.skip:focus { transform: none; }

a, button { transition: color 0.3s var(--ease); }

/* ---------- Top bar ----------
   Same columns as the page, so each cell sits on the column below it. */
.bar {
  position: fixed; inset: 0 0 auto; z-index: 20;
  height: var(--bar);
  display: grid; align-items: center;
  grid-template-columns: 28fr 44fr 28fr;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.bar::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--fg); transform: scaleX(var(--progress, 0)); transform-origin: 0 50%;
}
.bar__brand { display: inline-flex; justify-self: start; margin-left: var(--pad); }
/* the logo greys on hover and focus, as the text links do (the shared colour transition) */
.bar__brand .logo { transition: color 0.3s var(--ease); }
.bar__brand:is(:hover, :focus-visible) .logo { color: var(--mute); }
/* page links as outlined, fully rounded buttons; the current page is filled */
.bar__nav { display: flex; align-items: center; gap: 6px; padding-left: var(--in); }
.bar__nav .comma { display: none; }
.bar__nav a {
  padding: 2px 10px; border: 1px solid var(--fg); border-radius: 999px;
  line-height: 1.3; color: var(--fg);
  transition: background-color 0.3s var(--ease), color 0.3s var(--ease);
}
.bar__nav a[aria-current="true"] { background: var(--fg); color: var(--bg); }
@media (hover: hover) {
  .bar__nav a:not([aria-current="true"]):hover { background: var(--soft); }
}
.bar__time { color: var(--mute); padding-left: var(--pad); display: none; }
.bar__end { justify-self: end; margin-right: var(--pad); display: flex; align-items: center; gap: 6px; }
/* the other language (EN / RU): an outlined pill, as the page links and the button beside it */
.bar__lang-link {
  display: block; padding: 2px 10px; border: 1px solid var(--fg); border-radius: 999px;
  line-height: 1.3; color: var(--fg);
  transition: background-color 0.3s var(--ease);
}
@media (hover: hover) {
  .bar__lang-link:hover { background: var(--soft); }
}
.bar__cta {
  padding: 2px 10px; border: 1px solid var(--fg); border-radius: 999px; line-height: 1.3;
  transition: background-color 0.3s var(--ease);
}
@media (hover: hover) {
  .bar__cta:hover { background: var(--soft); }
}
.bar__toggle { display: none; }

@media (min-width: 1200px) {
  .bar { grid-template-columns: 28fr 44fr 14fr 14fr; }
  .bar__time { display: block; }
}
@media (max-width: 767px) {
  .bar { grid-template-columns: 1fr auto auto; }
  .bar__nav, .bar__cta, .bar__time { display: none; }
  .bar__end { margin-right: 0; }
  /* the pill as drawn, a 44px-tall target around it */
  .bar__lang { display: flex; align-items: center; height: 44px; }
  /* a filled pill, as tall as the language pill beside it; the button around it stays a 44px-tall target */
  .bar__toggle {
    position: relative; isolation: isolate;
    display: flex; align-items: center; gap: 6px;
    height: 44px; padding: 0 10px; margin: 0 var(--pad) 0 6px;
    color: var(--bg);
  }
  .bar__toggle::before {
    content: ""; position: absolute; z-index: -1; left: 0; right: 0; top: 50%;
    height: calc(1.3em + 6px); translate: 0 -50%;
    border-radius: 999px; background: var(--fg);
  }
  /* "Menu" rolls up to "Close" while the icon turns from + to x */
  /* the pill fits the word showing: the other one is taken out of the flow */
  .bar__toggle-label { position: relative; display: inline-grid; overflow: hidden; line-height: 1.3; }
  .bar__toggle-label > span { grid-area: 1 / 1; transition: translate 0.55s var(--ease); }
  .bar__toggle-label > span + span { position: absolute; left: 0; translate: 0 110%; }
  .bar__toggle[aria-expanded="true"] .bar__toggle-label > span:first-child { position: absolute; left: 0; translate: 0 -110%; }
  .bar__toggle[aria-expanded="true"] .bar__toggle-label > span + span { position: static; }
  .bar__toggle[aria-expanded="true"] .bar__toggle-label > span + span { translate: 0 0; }
  .bar__toggle-icon { position: relative; display: grid; place-items: center; width: 14px; height: 14px; }
  .bar__toggle-icon i {
    position: absolute; width: 14px; height: 1.5px; background: currentColor;
    transition: rotate 0.55s var(--ease);
  }
  .bar__toggle-icon i:last-child { rotate: 90deg; }
  .bar__toggle[aria-expanded="true"] .bar__toggle-icon i:first-child { rotate: 45deg; }
  .bar__toggle[aria-expanded="true"] .bar__toggle-icon i:last-child { rotate: 135deg; }
}

/* ---------- Mobile menu ----------
   Three dark cards stacked over the portrait, sitting low so the face shows
   above them. Small text and pills; the pages in large type with chevrons. */
.menu {
  position: fixed; inset: var(--bar) 0 0; z-index: 19;
  background: var(--soft);
  overflow-y: auto; overscroll-behavior: contain;
}
.menu[hidden] { display: none; }
.menu__bg { position: fixed; inset: var(--bar) 0 0; }
.menu__bg img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 20%; }
.menu__cards {
  position: relative;
  min-height: 100%;
  display: flex; flex-direction: column; justify-content: flex-end; gap: 6px;
  padding: 96px var(--pad) calc(var(--pad) + env(safe-area-inset-bottom, 0px));
}
.mcard {
  border-radius: 22px;
  background: rgb(12 12 12 / 0.95);
  color: #fff;
  padding: 16px;
}
.mcard__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.mcard__text { font-size: 15px; line-height: 1.4; letter-spacing: -0.015em; padding-top: 2px; }
.mcard__top + .mpill { margin-top: 16px; }
.mcard--now { padding-block: 18px 20px; }
/* the other language (EN / RU), a pill at the card's top right, beside where and when */
.mcard--now { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }

.mpill {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  padding: 4px 10px; border-radius: 999px;
  border: 1px solid rgb(255 255 255 / 0.9); background: transparent; color: #fff;
  font-size: 15px; line-height: 1.3; letter-spacing: -0.015em; white-space: nowrap;
  transition: background-color 0.3s var(--ease), scale 0.3s var(--ease);
}
/* all three menu buttons are outlined; an open list fills its button faintly */
.mpill[aria-expanded="true"] { background: rgb(255 255 255 / 0.12); }
.mpill:active { scale: 0.96; background: rgb(255 255 255 / 0.12); }
/* a pill whose width never changes: the longest label sits invisibly in the same cell */
.mpill--fixed { display: inline-grid; justify-items: center; align-items: center; overflow: hidden; }
.mpill--fixed > span { grid-area: 1 / 1; }
.mpill__sizer { visibility: hidden; }
.mchev { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.6; flex: none; }
.mchev--down { transition: rotate 0.4s var(--ease); }
[aria-expanded="true"] > .mchev--down { rotate: 180deg; }

.mbig { margin: -2px 0 16px; }
.mbig a {
  display: inline-flex; align-items: center; gap: 0.3em;
  font-size: clamp(34px, 10.2vw, 46px); line-height: 1.06; letter-spacing: -0.05em;
}
.mbig .mchev { width: 0.3em; height: 0.3em; stroke-width: 1.4; transition: translate 0.4s var(--ease); }
.mbig a:active .mchev { translate: 4px 0; }

.mlinks { display: flex; flex-wrap: wrap; gap: 8px 22px; padding-top: 14px; }
.mlinks a { display: inline-flex; align-items: center; gap: 5px; font-size: 17px; }
/* menu socials: full names as plain text, on one line */
.mlinks--plain { flex-wrap: nowrap; gap: 20px; }
.mcard__more { overflow: hidden; }
.mcard__more[hidden] { display: none; }

.mmix { font-size: clamp(30px, 9vw, 40px); line-height: 1.06; letter-spacing: -0.05em; }
.mmix__line { display: block; }
.mmix__dim { color: #8a8a8a; }
html.menu-open { overflow: hidden; }

/* ---------- Page: three columns ---------- */
.page {
  display: grid;
  grid-template-columns: 28fr 44fr 28fr;
  grid-template-areas: "intro feed info";
  padding-top: var(--bar);
  min-height: 100svh;
}
.side--intro { grid-area: intro; }
.side--info { grid-area: info; }
.feed { grid-area: feed; border-inline: 1px solid var(--line); min-width: 0; }

.side { position: sticky; top: var(--bar); align-self: start; height: calc(100svh - var(--bar)); min-width: 0; }
.side__in {
  height: 100%;
  display: flex; flex-direction: column; justify-content: space-between; gap: 40px;
  padding: var(--top) var(--pad) var(--pad);
}

/* Left */
.hello { font-size: var(--t-hello); line-height: 1; letter-spacing: -0.05em; }

/* "Artem" ⇄ "Hello!": two words stacked in one cell, letters masked so they
   can roll. Each letter of "Hello!" has its own colour. */
.swap {
  display: inline-grid; vertical-align: top;
  overflow: hidden; padding-bottom: 0.1em; margin-bottom: -0.1em;
  cursor: default;
}
.swap > span { grid-area: 1 / 1; display: inline-flex; }
.swap i { display: inline-block; font-style: normal; }
.swap__b i { translate: 0 110%; }
.swap__b i::before { content: attr(data-c); }   /* drawn, not written: the heading's text stays the name */
.swap__b i:nth-child(1) { color: #ff4d00; }
.swap__b i:nth-child(2) { color: #ff2d87; }
.swap__b i:nth-child(3) { color: #7b61ff; }
.swap__b i:nth-child(4) { color: #0091ff; }
.swap__b i:nth-child(5) { color: #00b37e; }
.swap__b i:nth-child(6) { color: #ffb800; }
.swap__b i:nth-child(7) { color: #ff4d00; }   /* "Привет!" has a seventh: the colours start over */
.swap:hover .swap__a i, .swap.is-on .swap__a i { translate: 0 -110%; }
.swap:hover .swap__b i, .swap.is-on .swap__b i { translate: 0 0; }
.motion-ok .swap i { transition: translate 0.7s var(--ease) calc(var(--l) * 40ms); }
.hello__sub { margin-top: 14px; color: var(--mute); }
.hero-cta, .facts__where { display: none; }
.side__label {
  display: flex; justify-content: space-between;
  font-size: 13px; color: var(--mute); letter-spacing: 0;
  padding-bottom: 6px; border-bottom: 1px solid var(--line);
}
.services-wrap, .socials-wrap { width: min(100%, 360px); }
.socials-wrap { margin-top: 40px; }

/* Ask AI about me: three inline links, same behaviour as Socials */
.ask-wrap { margin-top: 28px; }
.ask { display: flex; flex-wrap: wrap; column-gap: 20px; }
.ask .social { justify-content: flex-start; gap: 5px; }

/* Socials: name on the left, an up-right arrow on the right. On hover or focus
   the name rolls, the arrow leaves to the top right while a fresh one arrives
   from the bottom left, and the row's hairline darkens from the left. */
.socials li { border-bottom: 1px solid var(--line); }
.social {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding-block: 6px 5px;
}
.social::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--fg); transform: scaleX(0); transform-origin: 100% 50%;
  transition: transform 0.6s var(--ease);
}
.social__arrow { position: relative; width: 0.72em; height: 0.72em; overflow: hidden; flex: none; color: var(--mute); transition: color 0.3s var(--ease); }
.social__arrow svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: square;
}
.social__arrow svg + svg { translate: -100% 100%; }
.social:is(:hover, :focus-visible) .social__arrow { color: var(--fg); }
.social:is(:hover, :focus-visible)::after { transform: scaleX(1); transform-origin: 0 50%; }
.motion-ok .social__arrow svg { transition: translate 0.55s var(--ease); }
.motion-ok .social:is(:hover, :focus-visible) .social__arrow svg:first-child { translate: 100% -100%; }
.motion-ok .social:is(:hover, :focus-visible) .social__arrow svg + svg { translate: 0 0; }
.motion-ok .social:is(:hover, :focus-visible) .roll > span:first-child { translate: 0 -105%; }
.motion-ok .social:is(:hover, :focus-visible) .roll > span + span { translate: 0 0; }
.motion-ok .social:active .social__arrow { scale: 0.8; }
.motion-ok .social__arrow { transition: color 0.3s var(--ease), scale 0.3s var(--ease); }
.social:focus-visible { outline-offset: 2px; }
.services__no { display: inline-block; width: 2.2rem; color: var(--mute); font-size: 13px; letter-spacing: 0; font-variant-numeric: tabular-nums; }
.services { width: 100%; }
.services li { padding-block: 6px 5px; border-bottom: 1px solid var(--line); }
.services li:last-child { border-bottom: 0; padding-bottom: 0; }
/* /info/: a grey line under each service, aligned with its name */
.info-do__desc { display: block; padding: 2px 0 4px 2.2rem; color: var(--mute); }
.info-do + .prose { margin-top: clamp(20px, 2vw, 32px); }   /* a service page: the price lines, then their small print */

/* Right */
.facts { display: grid; grid-template-columns: 1fr minmax(0, 46%); gap: 16px var(--pad); align-items: start; }
.facts__status { white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; }
.facts__status { gap: 8px; }
.facts__status .dot { color: var(--fg); }
.facts__portrait { grid-column: 2; aspect-ratio: 3 / 4; overflow: hidden; background: var(--soft); }
.facts__portrait picture, .facts__portrait img { width: 100%; height: 100%; }
.facts__portrait img { object-fit: cover; object-position: 50% 40%; }
.spec { grid-column: 1 / -1; margin: 12px 0 0; }
.spec > div { display: flex; justify-content: space-between; gap: 16px; padding-block: 5px 4px; border-top: 1px solid var(--line); }
.spec > div:last-child { border-bottom: 1px solid var(--line); }
.spec dt { color: var(--mute); }
.spec dd { margin: 0; text-align: right; }
.reach { display: flex; justify-content: space-between; align-items: end; gap: 16px; }
.reach__mail { font-size: 1.07em; }
a.reach__mail:hover { color: var(--mute); }
.reach__copy { color: var(--mute); }
.reach__copy:hover { color: var(--fg); }

/* ---------- Middle column ---------- */
.statement {
  position: sticky; top: calc(var(--bar) + var(--top));
  margin-top: var(--top);
  padding-inline: var(--in);
  font-size: var(--t-state); line-height: 1.1; letter-spacing: -0.04em;
}
.feed__body {
  position: relative; z-index: 1;
  margin-top: clamp(120px, 30svh, 340px);
  background: var(--bg);
}

.block { padding: 0 var(--in) clamp(72px, 10svh, 120px); }
.block__head {
  display: flex; justify-content: space-between; align-items: baseline;
  padding-bottom: 10px;
  font-size: var(--t-head); letter-spacing: -0.035em;
}
.block__head h2 { display: flex; align-items: baseline; }
.block__no, .count { font-size: 13px; color: var(--mute); letter-spacing: 0; font-variant-numeric: tabular-nums; }
/* a section's index, "(01)": the heading's own size, in grey */
.block__no { width: 2.9em; flex: none; font-size: inherit; letter-spacing: -0.02em; }

/* Work feed */
.work { display: grid; gap: clamp(48px, 5vw, 80px); }
.item__link { display: block; width: 100%; }
.item__media { display: block; overflow: hidden; background: var(--soft); aspect-ratio: 4 / 3; }
.item__media picture, .item__media img { width: 100%; height: 100%; }
.item__media img { object-fit: cover; object-position: var(--pos, 50% 50%); transition: scale 1.1s var(--ease); }
/* a cover video (<pic … video>) lies over its poster and shows once it plays (js/main.js) */
.item__media:has(> .vid) { position: relative; }
.vid { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: var(--pos, 50% 50%); opacity: 0; pointer-events: none; transition: opacity 0.6s var(--ease); }
.vid.is-playing { opacity: 1; }
/* no zoom on hover: a playing video scaled shakes, and it moves enough on its own */
.item__link:hover .item__media:has(> .vid) img { scale: 1 !important; }
.item__cap { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding-top: 14px; }
/* the project's name leads the caption, at the size of the statement; its tags, quieter,
   are listed down the right edge with air between them, the first on the name's line */
.item__name { min-width: 0; font-size: var(--t-state); line-height: 1.1; letter-spacing: -0.04em; }
.item__tags { display: flex; flex-direction: column; align-items: flex-end; gap: 0.3em; flex-shrink: 0; text-align: right; color: var(--mute); line-height: 1.35; }
.item__tag { white-space: nowrap; }
@media (hover: hover) {
  .item__link:hover .item__media img { scale: 1.035; }
}

/* Rows */
.rows li { border-top: 1px solid var(--line); }
.rows li:last-child { border-bottom: 1px solid var(--line); }
.row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) 6.5em; gap: 16px;
  width: 100%; padding-block: 10px;
}
.row__c { text-align: right; }
.row__b, .row__c { color: var(--mute); }
button.row .row__a { display: inline-flex; gap: 0.4em; }
button.row .row__a::after { content: "→"; transition: translate 0.4s var(--ease); }
button.row:hover .row__a::after { translate: 4px 0; }

/* Info prose */
.prose { display: grid; gap: 16px; max-width: 36em; }
/* a link inside running text (to a blog post): the hairline under it, as in an article */
.prose a, .cs-lead a, .info-do__desc a { background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 100% 1px; transition: color 0.3s var(--ease); }
@media (hover: hover) { .prose a:hover { color: var(--mute); } :is(.cs-lead, .info-do__desc) a:hover { color: var(--fg); } }
.lede { font-size: var(--t-state); line-height: 1.1; letter-spacing: -0.04em; margin-bottom: 16px; }

/* Process: one continuous sequence (see main.js). Desktop: the section holds
   still (sticky) while a runway below it scrolls by; the row of rounded cards
   slides across and the phase line runs. Each step has its own quiet pastel
   and its own 16 × 16 pixel icon. --d is how current a card is, 0 to 1, set
   continuously from the scroll position: the nearer the card, the fuller its
   colour, its icon and its list, so one step hands over to the next instead
   of switching. Phone: the cards stack, and the one in the middle of the
   screen comes forward. */
.process__pin { position: sticky; top: calc(var(--bar) + 24px); }
.process__runway { height: calc(4 * 36svh); }
.process__meter {
  position: relative; display: flex; justify-content: space-between;
  margin-bottom: 20px; padding-bottom: 6px; border-bottom: 1px solid var(--line);
  font-size: 13px; color: var(--mute); letter-spacing: 0; font-variant-numeric: tabular-nums;
}
.process__meter i {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--fg); transform: scaleX(var(--p, 0)); transform-origin: 0 50%;
}
.process__now { color: var(--fg); }

/* five quiet surfaces: blue, sage, clay, sand, lilac */
.process__step { --d: 0; --c: #e3e8f7; }
.process__step:nth-child(2) { --c: #e2eadf; }
.process__step:nth-child(3) { --c: #f3e3dc; }
.process__step:nth-child(4) { --c: #efe9da; }
.process__step:nth-child(5) { --c: #e9e3f1; }
.process__step.is-active { --d: 1; }            /* until the script sets it continuously */

/* the row runs edge to edge of the column, cut by its hairlines */
.process__view { overflow: clip; margin-inline: calc(var(--in) * -1); padding-block: 4px; container-type: inline-size; }
.process__track { display: flex; align-items: stretch; gap: 12px; width: max-content; padding-inline: var(--in); will-change: transform; }

/* a card: the pixel icon in its own lighter field, then the number, the name,
   one line on what happens, and what the stage includes */
.process__step {
  position: relative; display: flex; flex-direction: column; flex: none;
  width: 62cqi; padding: 14px 14px 22px;
  border-radius: 10px;
  background: var(--c);
  background: color-mix(in srgb, var(--c) calc(30% + 70% * var(--d)), #f6f6f5);
  scale: calc(0.97 + 0.03 * var(--d)); transform-origin: 50% 100%;
  transition: background-color 0.35s var(--ease), scale 0.45s var(--ease);
}
.process__step > * { opacity: calc(0.5 + 0.5 * var(--d)); transition: opacity 0.35s var(--ease); }
@media (hover: hover) { .process__step:hover > * { opacity: 1; } }
.process__step > :not(.process__art) { margin-inline: 10px; }
.process__art {
  display: grid; place-items: center;
  block-size: clamp(140px, 30cqi, 210px); border-radius: 6px;
  background: rgb(255 255 255 / 0.42);
}
.process__px {
  display: block; width: clamp(88px, 19cqi, 128px); height: auto;
  translate: 0 calc((1 - var(--d)) * 8px); scale: calc(0.9 + 0.1 * var(--d));
  transition: translate 0.5s var(--ease), scale 0.5s var(--ease);
}
.px-k { fill: var(--fg); }
.px-w { fill: #fff; }
.px-a { fill: var(--fg); opacity: 0; transition: opacity 0.25s steps(1, end); }
.process__step.is-active .px-a { opacity: 1; }
/* the caret blinks; the flag's edge flutters (two frames, like the footer invader) */
.motion-ok .process__step.is-active:is(:nth-child(4), :nth-child(5)) .px-a { animation: px-blink 1.1s steps(1, end) infinite; }
@keyframes px-blink { 50% { opacity: 0; } }

.process__tile { display: flex; flex-direction: column; align-items: flex-start; margin-top: 22px; text-align: left; }
.process__no { font-size: var(--t-state); line-height: 1; letter-spacing: -0.04em; color: color-mix(in srgb, var(--mute), var(--fg) 30%); font-variant-numeric: tabular-nums; }   /* --mute alone is under 4.5:1 on the tinted cards */
.process__name { margin-top: 8px; font-size: clamp(28px, 2.4vw, 42px); line-height: 1; letter-spacing: -0.05em; }
.process__desc { margin-top: 14px; font-size: var(--t-head); line-height: 1.3; letter-spacing: -0.03em; text-wrap: pretty; }
/* what the stage includes: square pixel bullets; on the current card the
   lines fill in one after another as it arrives */
.process__list { margin-top: 18px; }
.process__list li {
  display: flex; align-items: baseline; gap: 10px;
  padding-block: 8px; border-top: 1px solid rgb(18 18 18 / 0.1);
  font-size: clamp(16px, 1.15vw, 19px); line-height: 1.25; letter-spacing: -0.02em;
  opacity: clamp(0.4, calc(var(--d) * 6 - var(--i, 0)), 1);
  transition: opacity 0.3s var(--ease);
}
.process__list li::before { content: ""; flex: none; width: 6px; height: 6px; background: var(--fg); translate: 0 -0.2em; }

/* the cards rise one after another as the section arrives */
.motion-ok .process__step { opacity: 0; translate: 0 16px; transition: opacity 0.9s var(--ease), translate 1s var(--ease), background-color 0.35s var(--ease), scale 0.45s var(--ease); transition-delay: calc(var(--k, 0) * 70ms), calc(var(--k, 0) * 70ms), 0s, 0s; }
.motion-ok .process.is-in .process__step { opacity: 1; translate: 0 0; }

/* short desktop windows: a little less air so the held card still fits */
@media (min-width: 768px) and (max-height: 820px) {
  .process__art { block-size: clamp(110px, 22cqi, 150px); }
  .process__px { width: clamp(72px, 15cqi, 104px); }
  .process__tile { margin-top: 16px; }
  .process__list li { padding-block: 6px; }
}

@media (max-width: 767px) {
  .process__pin { position: static; }
  .process__runway, .process__meter { display: none; }
  .process__view { overflow: visible; margin: 0; padding: 0; container-type: normal; }
  .process__track { flex-direction: column; width: auto; gap: 10px; padding: 0; transform: none !important; }
  .process__step { width: auto; padding: 12px 12px 20px; scale: none; transition: background-color 0.6s var(--ease); }
  .process__step > * { opacity: 1; }
  .process__step > :not(.process__art) { margin-inline: 8px; }
  .process__art { block-size: 156px; }
  .process__px { width: 104px; }
  .process__tile { margin-top: 18px; }
  .process__no { font-size: 22px; }
  .process__name { font-size: 32px; }
  .process__desc { font-size: 17px; }
  .process__list li { font-size: 16px; opacity: clamp(0.55, calc(var(--d) * 6 - var(--i, 0)), 1); transition: opacity 0.5s var(--ease) calc(var(--i, 0) * 70ms); }
  .motion-ok .process__step { transition: opacity 0.9s var(--ease), translate 1s var(--ease), background-color 0.6s var(--ease); }
}
@media (prefers-reduced-motion: reduce) {
  .process__step, .process__step > *, .process__px, .process__list li, .px-a { transition: none; }
}

/* Clients: one long sentence set as a type specimen — large, tight, justified
   so the lines run edge to edge, with numbered pills, brackets and a pilcrow
   in the text. The companies are in full ink; everything else is grey. */
.names {
  font-size: clamp(26px, 2.4vw, 44px); line-height: 1.16; letter-spacing: -0.04em;
  text-align: justify; hyphens: none;
  color: var(--mute);
}
/* A phone column is too narrow to justify without wide gaps. */
@media (max-width: 767px) { .names { text-align: left; } }
.client { white-space: nowrap; color: var(--fg); }
/* the narrowest phones: a long name may wrap rather than push the page sideways */
@media (max-width: 359px) { .client { white-space: normal; } }
.names .nw { white-space: nowrap; }
/* Russian words run longer and the client pills never break: justified, the lines open wide holes
   even with hyphenation (checked in Edge), so the Russian text sets ragged-right */
.names:lang(ru) { text-align: left; }
/* Two-digit number in a pill, like ⑭ in a type specimen: the pill is exactly as tall
   as the capitals around it (baseline to cap height), with semibold figures ~0.6
   of that height. --k is the figures' size relative to the text.
   Inside: with line-height 1, Inter's figures are centred in the line box, and the
   grid centres the line box in the pill. Outside: the pill sits on the figures'
   baseline, so its centre is 0.5cap up; the text's capitals are 1/k cap tall, so
   lifting it by (0.5/k − 0.5)cap puts the pill from their baseline to their top. */
.num {
  --k: 0.58;
  display: inline-grid; place-items: center; box-sizing: border-box;
  padding: 0 0.32em; margin-right: 0.42em;
  height: 1.266em;                                             /* Inter, without the cap unit */
  height: calc(1cap / var(--k));
  border: 0.13em solid currentColor; border-radius: 1em;
  font-size: calc(var(--k) * 1em); font-weight: 600; line-height: 1; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  vertical-align: 0.266em;                                     /* Inter, without the cap unit */
  vertical-align: calc((0.5 / var(--k) - 0.5) * 1cap);
}
.num--fill { background: var(--fg); border-color: var(--fg); color: var(--bg); }
.g { letter-spacing: 0; }
/* Profile links at the end of the sentence: grey like the text, underlined, ink on hover. */
.names__link {
  text-decoration: underline; text-decoration-thickness: 0.05em; text-underline-offset: 0.12em;
}
@media (hover: hover) { .names__link:hover { color: var(--fg); } }
.names__link:focus-visible { color: var(--fg); }
.names__sup { font-size: 0.42em; vertical-align: 1.1em; line-height: 0; letter-spacing: 0; margin-left: 0.06em; }
/* Reviews: cards in a sideways row; one fills most of the column and the
   next one shows at the edge. Swipe, trackpad, drag or the arrows. */
.reviews__nav { display: flex; align-items: center; gap: 4px; }
.reviews__count { font-size: 13px; color: var(--mute); letter-spacing: 0; font-variant-numeric: tabular-nums; margin-right: 10px; }
.reviews__count [data-reviews-index] { color: var(--fg); }
.reviews__btn {
  display: grid; place-items: center; width: 32px; height: 32px;
  border: 1px solid var(--line); border-radius: 50%;
  transition: border-color 0.3s var(--ease), color 0.3s var(--ease);
}
.reviews__btn:disabled { color: var(--line); cursor: default; }
.reviews__btn:not(:disabled):is(:hover, :focus-visible) { border-color: var(--fg); }
.arrow { position: relative; width: 12px; height: 12px; overflow: hidden; }
.arrow svg { position: absolute; inset: 0; width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.4; }
.arrow--prev { rotate: 180deg; }
.arrow svg + svg { translate: -100% 0; }
.motion-ok .arrow svg { transition: translate 0.5s var(--ease); }
.motion-ok .reviews__btn:not(:disabled):is(:hover, :focus-visible) .arrow svg:first-child { translate: 100% 0; }
.motion-ok .reviews__btn:not(:disabled):is(:hover, :focus-visible) .arrow svg + svg { translate: 0 0; }
.motion-ok .reviews__btn:not(:disabled):active { scale: 0.92; }
.reviews__btn { transition: border-color 0.3s var(--ease), color 0.3s var(--ease), scale 0.3s var(--ease); }

.reviews__track {
  /* a card is never wider than a comfortable line of the quote (~35–45 characters at this size) */
  --t-quote: clamp(20px, 1.6vw, 30px);
  --rp: clamp(28px, 2.6vw, 48px);              /* padding inside a card */
  display: grid; grid-auto-flow: column; grid-auto-columns: min(82%, calc(var(--t-quote) * 21 + var(--rp) * 2)); gap: 12px;
  margin: 6px calc(var(--in) * -1) 0; padding: 0 var(--in) 4px;
  overflow-x: auto; overscroll-behavior-x: contain;
  position: relative;                          /* clips the cards' visually-hidden text too: without it, before main.js (or without JS) it widened the phone page to ~2600px */
  scroll-snap-type: x mandatory; scroll-padding-inline: var(--in);
  scrollbar-width: none;
}
.reviews__track::-webkit-scrollbar { display: none; }
.reviews__track:focus-visible { outline-offset: -2px; }
.reviews__track.is-dragging { scroll-snap-type: none; cursor: grabbing; user-select: none; }
/* with a mouse the row is dragged, so pressing must not start a text selection or an image drag */
@media (hover: hover) and (pointer: fine) {
  .reviews__track { cursor: grab; user-select: none; -webkit-user-select: none; }
  .reviews__track img { -webkit-user-drag: none; }
}

/* A review is a soft card: who wrote it at the top, the quote at the bottom,
   open space between. Cards in the row share one height, so the quotes of
   different lengths still end on one line. */
.review {
  scroll-snap-align: start;
  display: flex; flex-direction: column;
  min-width: 0; min-height: clamp(300px, 26vw, 420px);
  padding: var(--rp);
  border-radius: 20px;
  background: #f7f7f7;
}
/* who wrote it: avatar, name, role (placeholders until supplied) */
.review__by { display: flex; align-items: center; gap: clamp(14px, 1.2vw, 20px); }
.review__avatar {
  flex: none; width: clamp(52px, 4.2vw, 72px); height: clamp(52px, 4.2vw, 72px); border-radius: 50%; overflow: hidden;
  display: grid; place-items: end center; background: #ececec; color: #cdcdcd;
}
.review__avatar svg { width: 78%; height: 78%; fill: currentColor; }
img.review__avatar { object-fit: cover; }
.review__avatar img { width: 100%; height: 100%; object-fit: cover; }
/* a review shortened from a longer one: the way to the full text, under the quote */
.review__more { align-self: flex-start; white-space: nowrap; margin-top: clamp(28px, 2.4vw, 40px); font-size: 15px; letter-spacing: -0.01em; color: var(--mute); transition: color 0.3s var(--ease); }
.review__net { color: var(--fg); }      /* the network's name in the text colour, the rest muted */
@media (hover: hover) { .review__more:hover { color: var(--fg); } }
.review__who { display: grid; gap: 2px; min-width: 0; font-size: var(--t-head); line-height: 1.2; letter-spacing: -0.03em; }
.review__name { color: var(--fg); }
.review__role { color: var(--mute); }
/* an anonymous review: the label in the role's grey */
.review__name--anon { color: var(--mute); }
/* the verified seal, at the start of the line under the quote */
.review__badge { width: 1.05em; height: 1.05em; margin-right: 0.4em; vertical-align: -0.17em; fill: var(--fg); }
/* the quote, in its own marks: key phrases in the dark text colour, the rest dimmed */
.review__text { margin-top: auto; padding-top: clamp(48px, 5vw, 88px); font-size: var(--t-quote); line-height: 1.2; letter-spacing: -0.035em; text-wrap: pretty; color: var(--mute); }
.review__text p::before { content: "“"; }
.review__text p::after { content: "”"; }
.review__text mark { background: none; color: var(--fg); }
/* a translated review: a quiet line right under the quote */
.review__note { margin-top: 12px; font-size: 13px; letter-spacing: 0; color: var(--mute); }
[data-todo] { color: var(--mute); }

/* Cards slide in one after another as the section arrives. The contents move,
   not the card: snap points are measured from the card's own box. */
.motion-ok .review > * { opacity: 0; translate: 32px 0; transition: opacity 0.9s var(--ease), translate 1.1s var(--ease); transition-delay: calc(var(--k, 0) * 90ms); }
.motion-ok .reviews.is-in .review > * { opacity: 1; translate: 0 0; }

/* FAQ */
.faq__item { border-top: 1px solid var(--line); }
.faq__item:last-child { border-bottom: 1px solid var(--line); }
.faq__item summary {
  list-style: none; cursor: pointer;
  display: flex; justify-content: space-between; gap: 16px; padding-block: 10px;
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary::after { content: "+"; color: var(--mute); transition: rotate 0.5s var(--ease); }
.faq__item[open]:not([data-closing="true"]) summary::after { rotate: 45deg; }
.faq__answer { overflow: hidden; }
.faq__answer p { color: var(--mute); padding-bottom: 14px; max-width: 34em; }

/* Contact */
.talk { border-top: 1px solid var(--line); padding-block: clamp(96px, 18svh, 200px) clamp(64px, 10svh, 120px); }
.marquee {
  display: flex; overflow: hidden;
  border-block: 1px solid var(--line);
  padding-block: 0.1em 0.14em;
  font-size: var(--t-talk); line-height: 1; letter-spacing: -0.045em;
}
.marquee__track { display: flex; align-items: center; gap: 0.36em; padding-right: 0.36em; flex: none; white-space: nowrap; }
.marquee__track .dot { width: 0.14em; height: 0.14em; color: var(--mute); }
.marquee__track span { transition: color 0.4s var(--ease); }
.marquee:hover .marquee__track span { color: var(--mute); }
.talk__lead { padding: 20px var(--in) 0; max-width: 34em; }
.foot {
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  padding: 0 var(--in) var(--pad);
}
/* Footer easter egg: the door to a future 8-bit game. The invader is inline
   SVG traced from assets/img/invader.svg (one unit per pixel, see
   invader-mono.svg), filled with the text colour. */
/* sits in the footer row between the copyright and "Back to top", in the same type */
.foot__game { display: flex; align-items: baseline; }
.foot__play { display: inline-flex; flex-direction: row; align-items: baseline; gap: 0.45em; color: var(--mute); }
/* a full em tall (the art's 22:16 ratio), so it holds its own beside the label */
.foot__invader { display: block; flex: none; align-self: center; width: auto; height: 1em; aspect-ratio: 22 / 16; }
.foot__play:hover, .foot__play:focus-visible { color: var(--fg); }
/* 8-bit sprite: two frames (the asset and its arms-up twin) swap on a hard
   cut while the invader marches a pixel step at a time, no easing in between.
   On hover it speeds up, like the last invader left on the screen. */
.foot__frame--b { visibility: hidden; }
@keyframes invader-march {
  0%   { translate: 0 0; }
  25%  { translate: 2px 0; }
  50%  { translate: 0 0; }
  75%  { translate: -2px 0; }
}
@keyframes invader-frame { 50% { visibility: hidden; } }
@keyframes invader-frame-b { 50% { visibility: visible; } }
@keyframes invader-hop {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -2px; }
}
.motion-ok .foot__invader { animation: invader-march 2.4s steps(1, end) infinite; }
.motion-ok .foot__frame { animation: invader-frame 1.2s steps(1, end) infinite; }
.motion-ok .foot__frame--b { animation-name: invader-frame-b; }
.motion-ok .foot__play:is(:hover, :focus-visible) .foot__invader { animation: invader-hop 0.3s steps(1, end) infinite; }
.motion-ok .foot__play:is(:hover, :focus-visible) .foot__frame { animation-duration: 0.3s; }
.motion-ok .foot__play:active .foot__invader { animation: none; translate: 0 1px; }
/* The label rolls like every other link on the site. */
.motion-ok .foot__play:is(:hover, :focus-visible) .roll > span:first-child { translate: 0 -105%; }
.motion-ok .foot__play:is(:hover, :focus-visible) .roll > span + span { translate: 0 0; }

/* ---------- Tablet: two columns, facts above the feed ---------- */
@media (min-width: 768px) and (max-width: 1199px) {
  .bar { grid-template-columns: 36fr 64fr; }
  .bar__end { position: absolute; right: 0; }
  .page {
    grid-template-columns: 36fr 64fr;
    grid-template-areas: "intro info" "intro feed";
    grid-template-rows: auto 1fr;
  }
  .side--intro { grid-row: 1 / 3; }
  .feed { border-right: 0; }
  .side--info { position: static; height: auto; border-left: 1px solid var(--line); }
  .side--info .side__in { padding: 56px var(--in) 0; gap: 24px; }
  .facts { grid-template-columns: minmax(0, 1fr) 30%; }
  .spec { grid-column: 1; grid-row: 2; margin: 0; align-self: end; }
  .facts__portrait { grid-row: 1 / 3; }
  .reach { display: none; }
  .statement { position: static; margin-top: 56px; }
  .feed__body { margin-top: 72px; }
}

/* ---------- Phone: one simple column ----------
   The three columns dissolve (display: contents) so their pieces can be put
   in one reading order. First screen: portrait and status, name, what I do,
   two actions; the first project starts just below. Then the work, the info,
   and the ways to reach me at the end. Ask AI lives in the menu. */
@media (max-width: 767px) {
  .page {
    grid-template-columns: 92px minmax(0, 1fr);
    grid-template-areas: none;
    grid-auto-flow: row dense;
    column-gap: 16px;
    padding-top: calc(var(--bar) + 28px);
  }
  .side, .side__in, .side__top, .facts, .feed, .feed__body { display: contents; }

  /* first screen */
  .facts__portrait { order: 1; grid-column: 1; margin-left: var(--pad); aspect-ratio: 3 / 4; }
  .facts__status {
    order: 2; grid-column: 2; align-self: end; margin-right: var(--pad);
    align-items: flex-start; gap: 8px;
  }
  .facts__status .dot { margin-top: 0.55em; }
  .facts__where { display: block; color: var(--mute); }
  .hello { order: 3; grid-column: 1 / -1; margin: 32px var(--pad) 0; font-size: clamp(44px, 13vw, 56px); }
  .hello__sub { display: none; }
  .statement { order: 4; grid-column: 1 / -1; margin-top: 16px; position: static; font-size: 20px; }
  .hero-cta {
    order: 5; grid-column: 1 / -1; display: flex; gap: 28px;
    margin: 24px var(--pad) 0; padding-top: 10px; border-top: 1px solid var(--line);
  }
  .hero-cta .social { justify-content: flex-start; gap: 6px; padding: 0; }
  .hero-cta .social::after { display: none; }

  /* then the work and the rest, in reading order */
  #work { order: 6; grid-column: 1 / -1; margin-top: 56px; }
  .feed__body > .block, .talk, .foot { grid-column: 1 / -1; }
  #about { order: 8; }
  .spec { order: 9; grid-column: 1 / -1; margin: -56px var(--pad) 72px; }
  .services-wrap { order: 10; grid-column: 1 / -1; width: auto; margin: 0 var(--pad) 72px; }
  [aria-labelledby="process-title"] { order: 12; }
  [aria-labelledby="clients-title"] { order: 13; }
  [aria-labelledby="reviews-title"] { order: 14; }
  [aria-labelledby="faq-title"] { order: 15; }
  .talk { order: 16; }
  .socials-wrap { order: 17; grid-column: 1 / -1; width: auto; margin: 0 var(--pad) 32px; }
  .reach { order: 18; grid-column: 1 / -1; margin: 0 var(--pad) 40px; }
  .foot { order: 19; }
  .ask-wrap { display: none; }               /* in the menu instead */
  /* socials: their full names, on one line */
  .socials { display: flex; justify-content: space-between; border-bottom: 1px solid var(--line); }
  .socials-wrap .socials li, .motion-ok .socials-wrap .socials li { border: 0; background: none; }
  .socials .social { gap: 6px; padding-block: 10px 9px; }

  /* the pinned-sheet effect is a desktop idea */
  .feed__body { margin: 0; }
  .row { grid-template-columns: minmax(0, 1fr) auto; row-gap: 2px; }
  .row__b { grid-column: 1 / -1; grid-row: 2; }
  .reviews__track { --t-quote: 21px; --rp: 24px; grid-auto-columns: min(88%, 30em); gap: 8px; }
  .review { min-height: 340px; border-radius: 18px; }
  .review__who { font-size: 16px; }
  .review__avatar { width: 48px; height: 48px; }
  .review__more { margin-top: 24px; font-size: 14px; }
  .talk { padding-block: 56px 48px; }
  [aria-labelledby="faq-title"] { padding-bottom: 56px; }
}

/* Short desktop windows: the side columns scroll with the page */
@media (min-width: 1200px) and (max-height: 620px) {
  .side { position: static; height: auto; }
  .side__in { min-height: calc(100svh - var(--bar)); }
}

/* ---------- Motion ----------
   Load: words come into focus one after another, the rest settles in.
   Scroll: images arrive softly as they enter. Hover: images lean in. */
.motion-ok [data-words] .w {
  display: inline-block;
  opacity: 0; filter: blur(10px); translate: 0 0.15em;
  transition: opacity 0.9s var(--ease), filter 0.9s var(--ease), translate 0.9s var(--ease);
  transition-delay: calc(var(--d, 0s) + var(--i) * 45ms);
}
.motion-ok.is-loaded [data-words] .w { opacity: 1; filter: blur(0); translate: 0 0; }
.motion-ok .statement { --d: 0.3s; }

.motion-ok :is(.bar, .socials-wrap, .hello__sub, .services-wrap, .side--info .side__in > *, .feed__body) {
  opacity: 0; transition: opacity 1.2s var(--ease) 0.6s;
}
.motion-ok .bar { transition-delay: 0s; }
.motion-ok.is-loaded :is(.bar, .socials-wrap, .hello__sub, .services-wrap, .side--info .side__in > *, .feed__body) { opacity: 1; }
/* coming back to the home page (js/main.js): the same entrance, quicker and lighter, with the
   word-by-word wait capped, and the rest settling in together with the words */
.motion-ok.is-return:not(.is-loaded) [data-words] .w { filter: blur(6px); translate: 0 0.08em; }
.motion-ok.is-return [data-words] .w {
  transition-duration: 0.6s;
  transition-delay: calc(var(--d, 0s) + min(var(--i) * 16ms, 0.32s));
}
.motion-ok.is-return .statement { --d: 0.06s; }
.motion-ok.is-return :is(.socials-wrap, .hello__sub, .services-wrap, .side--info .side__in > *, .feed__body) { transition-duration: 0.7s; transition-delay: 0.12s; }

.motion-ok .item__media img { opacity: 0; scale: 1.08; transition: opacity 1s var(--ease), scale 1.6s var(--ease); }
.motion-ok .item.is-in .item__media img { opacity: 1; scale: 1; }
/* the portrait on /info/ settles in the same way */
.motion-ok .doc__figure img { opacity: 0; scale: 1.08; transition: opacity 1s var(--ease), scale 1.6s var(--ease); }
.motion-ok .doc__figure.is-in img { opacity: 1; scale: 1; }
/* the page's first, high-priority picture (its LCP) is there from the start; it still settles in scale */
.motion-ok img[fetchpriority="high"] { opacity: 1; }
@media (hover: hover) {
  .motion-ok .item.is-in .item__link:hover .item__media img { scale: 1.035; transition: scale 1.1s var(--ease); }
}

.marquee__track { animation: marquee 22s linear infinite; }
@keyframes marquee { to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) {
  .marquee__track { animation: none; }
}

/* ---------- Microinteractions ----------
   One small vocabulary, used everywhere: labels roll, stars turn, hairlines
   draw from the left, things give a little when pressed. All of it only
   with motion allowed. */

/* Text roll: links and buttons swap their label for an identical one from below */
.roll { display: inline-grid; overflow: hidden; }
.roll > span { grid-area: 1 / 1; }
.roll > span + span { translate: 0 105%; }
.motion-ok .roll > span { transition: translate 0.6s var(--ease); }
@media (hover: hover) {
  .motion-ok :hover > .roll > span:first-child { translate: 0 -105%; }
  .motion-ok :hover > .roll > span + span { translate: 0 0; }
}

/* Copy email: the label rolls to "Copied!", stars burst out (see main.js) */
[data-copy-label] { display: inline-block; }
.spark { position: fixed; z-index: 50; pointer-events: none; fill: var(--fg); will-change: transform, opacity; }
.motion-ok .reach__copy:active { scale: 0.96; }
.reach__copy { transition: color 0.3s var(--ease), scale 0.3s var(--ease); }

/* Email address: an underline draws in from the left, leaves to the right */
.motion-ok a.reach__mail {
  background: linear-gradient(currentColor, currentColor) no-repeat 100% 100% / 0% 1px;
  transition: background-size 0.6s var(--ease);
}
.motion-ok a.reach__mail:hover { color: var(--fg); background-size: 100% 1px; background-position: 0 100%; }

/* The status dot breathes like a live light */
.motion-ok .facts__status .dot { animation: breathe 2.4s ease-in-out infinite; }
@keyframes breathe { 50% { opacity: 0.35; scale: 0.7; } }

/* Clock: the colon pulses */
.motion-ok .colon { animation: pulse 2s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.4; } }

/* Work items: the tags darken, the image gives a little when pressed */
.motion-ok .item__tags { transition: color 0.3s var(--ease); }
.motion-ok .item__media { transition: scale 0.5s var(--ease); }
@media (hover: hover) {
  .motion-ok .item__link:hover .item__tags { color: var(--fg); }
}
.motion-ok .item__link:active .item__media { scale: 0.985; }

/* Portrait leans in */
@media (hover: hover) {
  .motion-ok .facts__portrait img { transition: scale 1.2s var(--ease); }
  .motion-ok .facts__portrait:hover img { scale: 1.05; }
}

/* Ruled lists: hairlines draw in one after another, text follows */
.motion-ok :is(.rows, .faq, .quotes) > *,
.motion-ok :is(.rows, .faq, .quotes) > :last-child {
  border-color: transparent;
  background: linear-gradient(var(--line), var(--line)) no-repeat 0 0 / 0% 1px;
  transition: background-size 1.2s var(--ease) calc(var(--k, 0) * 80ms);
}
.motion-ok :is(.rows, .faq) > :last-child {
  background-image: linear-gradient(var(--line), var(--line)), linear-gradient(var(--line), var(--line));
  background-position: 0 0, 0 100%;
  background-size: 0% 1px, 0% 1px;
}
.motion-ok .is-in :is(.rows, .faq, .quotes) > * { background-size: 100% 1px; }
.motion-ok .is-in :is(.rows, .faq) > :last-child { background-size: 100% 1px, 100% 1px; }
.motion-ok :is(.rows, .faq, .quotes) > * > * {
  opacity: 0; translate: 0 8px;
  transition: opacity 0.9s var(--ease), translate 0.9s var(--ease);
  transition-delay: calc(0.15s + var(--k, 0) * 80ms);
}
.motion-ok .is-in :is(.rows, .faq, .quotes) > * > * { opacity: 1; translate: 0 0; }
.motion-ok .faq__answer { translate: none !important; }

/* Services: the same hairlines, drawn once on load */
.motion-ok .services li {
  border-color: transparent;
  background: linear-gradient(var(--line), var(--line)) no-repeat 0 100% / 0% 1px;
  transition: background-size 1.2s var(--ease) calc(0.7s + var(--k, 0) * 90ms);
}
.motion-ok.is-loaded .services li { background-size: 100% 1px; }
.motion-ok .services li:last-child { background: none; }
.motion-ok .socials li {
  border-color: transparent;
  background: linear-gradient(var(--line), var(--line)) no-repeat 0 100% / 0% 1px;
  transition: background-size 1.2s var(--ease) calc(0.6s + var(--k, 0) * 90ms);
}
.motion-ok.is-loaded .socials li { background-size: 100% 1px; }

/* Rows: the hovered row darkens its line */
@media (hover: hover) {
  .motion-ok button.row:hover .row__b { color: var(--fg); }
  .motion-ok .faq__item summary:hover::after { rotate: 90deg; color: var(--fg); }
}

/* Mobile menu motion runs from main.js (setMenu) so it can be reversed mid-way */
.menu__bg, .mcard, .mbig li { will-change: opacity, transform; }

@media (prefers-reduced-motion: reduce) {
  .spark { display: none; }
}

/* ---------- Cursor ----------
   A white disc with "View" over projects, "Drag" over the reviews (most project images are dark). Scales in from the pointer.
   Over a review's source link the disc becomes a white pill that says "Check confirmation" (.is-check). */
.cursor {
  position: fixed; left: 0; top: 0; z-index: 60;
  pointer-events: none;
  translate: -50% -50%;
}
.cursor span {
  display: grid; place-items: center;
  width: 76px; height: 76px; border-radius: 999px;
  background: var(--bg); color: var(--fg);
  box-shadow: 0 0 0 1px rgb(18 18 18 / 0.08);
  font-size: 14px; letter-spacing: -0.02em;
  scale: 0;
  transition: scale 0.45s var(--ease), width 0.45s var(--ease), height 0.45s var(--ease), background-color 0.3s var(--ease), color 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.cursor span { grid-template-columns: minmax(0, 1fr); }
.cursor span > * { grid-area: 1 / 1; justify-self: center; }
.cursor__text, .cursor__check { font-style: normal; white-space: nowrap; transition: opacity 0.25s var(--ease); }
.cursor__check { opacity: 0; }
.cursor.is-check span { width: 162px; height: 40px; }
.cursor.is-check.is-dark span { background: var(--bg); color: var(--fg); box-shadow: 0 0 0 1px rgb(18 18 18 / 0.08), 0 6px 18px rgb(18 18 18 / 0.1); }
.cursor.is-check .cursor__text { opacity: 0; }
.cursor.is-check .cursor__check { opacity: 1; }
.cursor.is-on span { scale: 1; }
.cursor.is-on.is-pressed span { scale: 0.82; }
.cursor.is-dark span { background: var(--fg); color: var(--bg); box-shadow: none; }
html.has-cursor :is(.item__link, .reviews__track, .reviews__track.is-dragging, .reviews__track a) { cursor: none; }
html:not(.has-cursor) .cursor { display: none; }

/* ---------- Preloader ----------
   A white sheet with a counter that runs while the font and
   first images load. It lifts away and the page comes into focus beneath. */
.loader {
  position: fixed; inset: 0; z-index: 90;
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 0 var(--pad) var(--pad);
  background: var(--bg); color: var(--fg);
  animation: loader-safety 0.4s ease 6s forwards;   /* if the script never runs */
}
.no-js .loader { display: none; }
@keyframes loader-safety { to { opacity: 0; visibility: hidden; } }
.loader__top { height: var(--bar); display: flex; align-items: center; justify-content: space-between; }
.loader__brand { display: inline-flex; }
.loader__time { color: var(--mute); }
.loader__bottom { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; }
.loader__count {
  font-size: clamp(88px, 13vw, 240px); line-height: 0.78; letter-spacing: -0.06em;
  font-variant-numeric: tabular-nums;
}
html.is-loading { overflow: hidden; }
@media (prefers-reduced-motion: reduce) { .loader { display: none; } }

/* ---------- Case pages ---------- */
/* full-width pages (src/layouts/full.html): /work/ and one page per case, under the site's bar */
.full { padding-top: var(--bar); }
/* the project picture a case grows out of (js/main.js): its box goes from the
   card's to the case's cover (under the bar, as .case__cover), the crop and
   scale to the cover's, and the cover's shade comes up */
.case-grow { position: fixed; top: var(--bar); left: 0; right: 0; bottom: 0; z-index: 70; overflow: hidden; pointer-events: none; }
.case-grow__veil { position: absolute; inset: 0; background: var(--bg); opacity: 0; }
.case-grow__pic { position: absolute; top: 0; left: 0; width: 100%; height: calc(100svh - var(--bar)); overflow: hidden; }
.case-grow__img, .case-grow__img picture, .case-grow__img img { position: absolute; inset: 0; width: 100%; height: 100%; }
.case-grow__img img { object-fit: cover; }
.case-grow__shade {
  position: absolute; inset: 45% 0 0;
  background: linear-gradient(to top, rgb(0 0 0 / 0.7), rgb(0 0 0 / 0.34) 45%, rgb(0 0 0 / 0));
}
@media (max-width: 767px) { .case-grow__pic { height: 72svh; } }
.case__cover { height: calc(100svh - var(--bar)); overflow: hidden; background: var(--soft); }
.case__cover picture, .case__cover img { width: 100%; height: 100%; }
.case__cover img { object-fit: cover; }
/* the title sits on the cover: large, bottom left, over a hairline with the
   project's number, kind and platform. White on dark pictures, ink on light. */
.case__cover { position: relative; }
.case__over {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  display: grid; gap: clamp(14px, 2svh, 22px); padding: 0 var(--pad) clamp(18px, 3svh, 30px);
  pointer-events: none;
}
.case__over .case__title { padding: 0; font-size: clamp(64px, 10.5vw, 210px); line-height: 0.86; letter-spacing: -0.065em; pointer-events: auto; }
.case__meta {
  display: grid; grid-template-columns: 28fr 44fr 28fr; gap: 12px;
  padding-top: 10px; box-shadow: inset 0 1px currentColor;
  font-size: var(--t-head); letter-spacing: -0.03em;
}
.case__meta > span:first-child { font-variant-numeric: tabular-nums; }
.case__meta > span:last-child { text-align: right; }
/* a case still being written: a quiet status in the hero's line of facts,
   with the "Working worldwide" dot breathing slowly */
.case__meta:has(.case__status) { grid-template-columns: 28fr 30fr 14fr 28fr; }
/* below desktop the line of facts takes two rows: what it is, then type and year */
@media (max-width: 1199px) {
  .case__meta, .case__meta:has(.case__status) { grid-template-columns: minmax(0, 1fr) auto; row-gap: 4px; }
  .case__meta > span:first-child { grid-column: 1 / -1; }
  .case__cover--panel .case__meta { grid-template-columns: minmax(0, 1fr) auto; }
}
.case__status { display: inline-flex; align-items: center; gap: 8px; }
.case__status .dot { width: 6px; height: 6px; }
.motion-ok .case__status .dot { animation: case-breathe 3.6s ease-in-out infinite; }
@keyframes case-breathe { 50% { opacity: 0.3; scale: 0.7; } }
/* white on the picture, over a soft shade rising from the bottom edge, so the title
   reads on a light picture as on a dark one */
.case__cover--dark::after {
  content: ""; position: absolute; inset: 45% 0 0; pointer-events: none;
  background: linear-gradient(to top, rgb(0 0 0 / 0.7), rgb(0 0 0 / 0.34) 45%, rgb(0 0 0 / 0));
}
.case__cover--dark .case__over { color: #fff; }
.case__cover--dark .case__title { text-shadow: 0 2px 48px rgb(0 0 0 / 0.28); }
.case__cover--dark .case__meta { box-shadow: inset 0 1px rgb(255 255 255 / 0.45); }
.case__cover--light .case__over { color: var(--fg); }
.case__cover--light .case__meta { box-shadow: inset 0 1px rgb(18 18 18 / 0.25); }
/* on a light, busy picture the title sits in a notch of page white cut into
   the corner, so it never fights the screenshot underneath */
.case__cover--panel .case__over { right: auto; max-width: calc(100% - var(--pad)); padding: clamp(16px, 2.4svh, 26px) clamp(20px, 2.6vw, 40px) clamp(18px, 3svh, 30px) var(--pad); background: var(--bg); color: var(--fg); }
.case__cover--panel .case__meta { grid-template-columns: auto auto auto; gap: 24px; box-shadow: inset 0 1px var(--line); }
.case__cover--panel .case__meta > span:last-child { text-align: left; }
/* a case without a picture gets a cover set in type: what the work covered */
.case__type {
  display: grid; align-content: start; padding: clamp(24px, 5svh, 56px) var(--pad);
  font-size: clamp(40px, 6.4vw, 120px); line-height: 0.95; letter-spacing: -0.06em; color: #cdcdcd;
}
.case__title:focus { outline: none; }
/* ® / © on the display titles: a small raised mark, not half the title's height */
.case__title .r { font-size: 0.24em; vertical-align: 2.6em; }

/* ---------- Case study: the narrative ----------
   The markup is in src/pages/work/_template.html. Each case opens on its
   cover (the title on the picture), then a project overview: a numbered label
   in the left half; in the right half the project's own line set large, a
   short paragraph in grey and the details as a list of rows. In each part that
   follows, the pictures come first, full width and large, and the text that
   belongs to them follows close underneath on the same two equal halves (label
   left, text right; the text lines up with the right picture of a pair). A part
   with pictures and no text is headed by its label. A case only has the parts
   its text supports. */
.cs-part { padding-top: clamp(112px, 18svh, 220px); }
.cs-top { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: var(--in); align-items: baseline; margin: 0 var(--pad); }
.cs-label { display: flex; gap: 0.5em; font-size: var(--t-head); letter-spacing: -0.03em; color: var(--mute); }
.cs-label > span { font-variant-numeric: tabular-nums; }
.cs-text, .cs-overview__body { grid-column: 2; grid-row: 1; }
.cs-text { display: grid; gap: 16px; max-width: 40em; font-size: var(--t-head); line-height: 1.4; letter-spacing: -0.03em; }
.cs-top + .cs-row { margin-top: clamp(48px, 8svh, 104px); }
/* the text of a part sits close under its pictures: it reads as theirs */
.cs-row + .cs-top { margin-top: clamp(40px, 6svh, 72px); }
/* overview: the line, the paragraph, then (Details) */
.cs-head { max-width: 15em; font-size: clamp(34px, 4.2vw, 84px); line-height: 1; letter-spacing: -0.055em; text-wrap: balance; }
.cs-lead { max-width: 22em; font-size: clamp(28px, 3vw, 56px); line-height: 1.08; letter-spacing: -0.05em; text-wrap: pretty; }
.cs-head + .cs-lead { max-width: 36em; margin-top: clamp(32px, 4.4vw, 72px); font-size: var(--t-head); line-height: 1.4; letter-spacing: -0.03em; color: var(--mute); }
.cs-details-label { margin-top: clamp(64px, 10svh, 128px); font-size: var(--t-head); letter-spacing: -0.03em; }
.cs-facts { max-width: 52em; margin-top: clamp(20px, 2.4vw, 32px); border-bottom: 1px solid var(--line); }
.cs-facts > div { display: flex; justify-content: space-between; align-items: baseline; gap: 24px; padding: clamp(20px, 2.4vw, 34px) 0; border-top: 1px solid var(--line); font-size: var(--t-head); line-height: 1.3; letter-spacing: -0.03em; }
.cs-facts dd { margin: 0; text-align: right; }

.cs-row { display: grid; gap: var(--in); margin: var(--in) var(--pad) 0; }
.cs-fig__frame { position: relative; }
.cs-row--pair { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.cs-fig__frame { aspect-ratio: var(--r); overflow: hidden; background: var(--soft); }
.cs-fig__frame picture, .cs-fig__frame img { width: 100%; height: 100%; }
.cs-fig__frame img { object-fit: cover; }
.cs-fig--contain .cs-fig__frame img { object-fit: contain; padding: 6%; }
.cs-fig figcaption { padding-top: 8px; font-size: 13px; color: var(--mute); letter-spacing: 0; }

.cs-metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--in); margin-top: 24px; }
.cs-metrics > div { display: flex; flex-direction: column-reverse; justify-content: flex-end; gap: 6px; padding-top: 12px; border-top: 1px solid var(--line); }
.cs-metrics dd { margin: 0; font-size: clamp(32px, 3.2vw, 56px); line-height: 1; letter-spacing: -0.05em; }
.cs-metrics dt { font-size: 13px; color: var(--mute); letter-spacing: 0; line-height: 1.4; }

/* a case whose full study is still being written: after the overview, the statement
   set as large as the cover title (two lines, the second grey and indented, ending
   on a breathing dot), a hairline with a segment running along it, then a short
   note and the way to ask */
.cs-wip__title {
  display: grid; margin: 0 var(--pad);
  font-size: clamp(72px, 15vw, 300px); font-weight: inherit; line-height: 0.86; letter-spacing: -0.07em;
}
.cs-wip__line { display: block; overflow: clip; padding-bottom: 0.06em; }
.cs-wip__line > span { display: inline-flex; align-items: flex-end; }
.cs-wip__line + .cs-wip__line { padding-left: 1.1em; color: var(--mute); }
.cs-wip__dot { flex: none; width: 0.14em; height: 0.14em; margin: 0 0 0.16em 0.08em; border-radius: 50%; background: var(--fg); }
.cs-wip__bar { position: relative; height: 1px; margin: clamp(48px, 8svh, 104px) var(--pad) clamp(40px, 6svh, 72px); overflow: hidden; background: var(--line); }
.cs-wip__bar::after { content: ""; position: absolute; inset: 0 auto 0 0; width: 18%; background: var(--fg); }
.cs-wip__link { display: inline-flex; gap: 0.4em; }
@media (hover: hover) { .cs-wip__link:hover { color: var(--mute); } }

.motion-ok .cs-wip__line > span { translate: 0 105%; transition: translate 1.4s var(--ease); }
.motion-ok .cs-wip__line + .cs-wip__line > span { transition-delay: 0.12s; }
.motion-ok .cs-wip__title.is-in .cs-wip__line > span { translate: none; }
.motion-ok .cs-wip__bar::after { translate: -100% 0; }
.motion-ok .cs-wip__bar.is-in::after { animation: wip-run 2.8s var(--ease) infinite; }
@keyframes wip-run { from { translate: -100% 0; } to { translate: 560% 0; } }

/* motion, in the order the case is read: the text of each part rises; each
   picture opens from its lower edge as it arrives and its image settles;
   wide pictures drift slightly with the scroll; the detail rows follow one
   another */
.motion-ok .cs-top > *, .motion-ok .cs-overview__body > *, .motion-ok .cs-facts > div { opacity: 0; translate: 0 28px; transition: opacity 1s var(--ease), translate 1.2s var(--ease); }
.motion-ok .cs-top > :nth-child(2) { transition-delay: 0.08s; }
.motion-ok .cs-top > :nth-child(3) { transition-delay: 0.16s; }
.motion-ok .cs-overview__body > :nth-child(2) { transition-delay: 0.1s; }
.motion-ok .cs-facts > div { transition-delay: calc(0.1s + var(--k, 0) * 80ms); }
.motion-ok :is(.cs-top, .cs-facts).is-in > *, .motion-ok .cs-top.is-in .cs-overview__body > * { opacity: 1; translate: none; }
.motion-ok .cs-fig .cs-fig__frame { clip-path: inset(12% 0 0 0); transition: clip-path 1.3s var(--ease); }
.motion-ok .cs-fig figcaption { opacity: 0; transition: opacity 0.8s var(--ease) 0.4s; }
.motion-ok .cs-fig .cs-fig__frame img { scale: 1.08; transition: scale 1.8s var(--ease); }
.motion-ok .cs-row > .cs-fig:nth-child(2) :is(.cs-fig__frame, img) { transition-delay: 0.12s; }
.motion-ok .cs-fig.is-in .cs-fig__frame { clip-path: inset(0); }
.motion-ok .cs-fig.is-in figcaption { opacity: 1; }
.motion-ok .cs-fig.is-in .cs-fig__frame img { scale: 1; }
@supports (animation-timeline: view()) {
  @media (min-width: 768px) {
    .motion-ok .cs-row--wide .cs-fig.is-in .cs-fig__frame img { animation: cs-drift linear both; animation-timeline: view(); animation-range: cover; }
  }
}
@keyframes cs-drift { from { scale: 1.06; translate: 0 -2%; } to { scale: 1.06; translate: 0 2%; } }

/* a case page closes on the inner pages' grid, as /work/ (src/layouts/full.html):
   where this is and the way back to all work on the left; the next case as a
   work card under a numbered heading; then the way to get in touch and the footer */
.doc.doc--end { min-height: 0; margin-top: clamp(96px, 16svh, 180px); padding-top: 0; border-top: 1px solid var(--line); }
.doc__all { margin-top: 24px; }

@media (max-width: 767px) {
  .case__cover { height: 72svh; }
  .case__over .case__title { font-size: clamp(48px, 15vw, 76px); }
  .case__meta { font-size: 14px; }
  .case__type { font-size: clamp(34px, 11vw, 52px); }
  .cs-part { padding-top: 96px; }
  .cs-top { grid-template-columns: minmax(0, 1fr); }
  .cs-label { margin-bottom: 14px; }
  .cs-row + .cs-top { margin-top: 28px; }
  .doc--end { margin-top: 72px; }
  .cs-text, .cs-overview__body { grid-column: 1; grid-row: auto; padding-left: 0; }
  .cs-head { font-size: clamp(34px, 10vw, 46px); }
  .cs-text { font-size: 17px; }
  .cs-lead { font-size: clamp(26px, 7.6vw, 34px); }
  .cs-head + .cs-lead, .cs-details-label, .cs-facts > div { font-size: 17px; }
  .cs-details-label { margin-top: 56px; }
  .cs-facts > div { padding: 18px 0; }
  .cs-top + .cs-row { margin-top: 40px; }
  .cs-row--pair { grid-template-columns: 1fr; }
  .cs-metrics { grid-template-columns: 1fr; }
  .cs-wip__title { font-size: clamp(56px, 18vw, 92px); }
  .cs-wip__line + .cs-wip__line { padding-left: 0.5em; }
  .cs-wip__bar { margin: 40px var(--pad) 28px; }
}

/* ---------- Work (/work/) ----------
   The home page's work section on a wide inner page (.doc--wide): the same
   cards, captions, hover and arrival, given the room of the middle and right
   columns. Two to a row, with every third one across the full width at 16:9;
   a project without a picture shows its tags and a
   line instead (.item__media--text). On a phone, one column, as on the home page. */
.doc--work { --vz: #f2f2f1; }
.work--all { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(56px, 5.5vw, 96px) 20px; }
.work--all .item--wide { grid-column: 1 / -1; }
.work--all .item--wide .item__media { aspect-ratio: 16 / 9; }
.work--all .item--wide .item__media--text { aspect-ratio: 21 / 8; }
@media (hover: hover) {
  .work--all .item__link:hover .item__media--text { background: var(--soft); }
}
@media (max-width: 767px) {
  .work--all { grid-template-columns: minmax(0, 1fr); gap: 48px; }
  .work--all .item--wide .item__media { aspect-ratio: 4 / 3; }
  .work--all .item--wide .item__media--text { aspect-ratio: auto; min-height: 220px; }
}

/* ---------- Inner pages (about, contact, blog …) ----------
   The home page's three columns again: what this page is on the left (sticky),
   the content in the middle, the way to get in touch on the right (sticky).
   Built by scripts/build-html.py around src/layouts/page.html. */
.bar__nav a[aria-current="page"] { background: var(--fg); color: var(--bg); }
@media (hover: hover) {
  .bar__nav a[aria-current="page"]:hover { background: var(--fg); }
}
.mbig a[aria-current="page"] { color: #8a8a8a; }

.doc {
  display: grid;
  grid-template-columns: 28fr 44fr 28fr;
  grid-template-areas: "side main aside" "side foot aside";
  grid-template-rows: 1fr auto;
  padding-top: var(--bar);
  min-height: 100svh;
}
.doc__side { grid-area: side; }
.doc__main { grid-area: main; padding-top: var(--top); min-width: 0; }
.doc__aside { grid-area: aside; }
.doc__foot { grid-area: foot; }
.doc__main, .doc__foot { border-inline: 1px solid var(--line); }
.doc__side, .doc__aside { position: sticky; top: var(--bar); align-self: start; max-height: calc(100svh - var(--bar)); overflow: auto; min-width: 0; scrollbar-width: none; }
.doc__side-in, .doc__aside-in { padding: var(--top) var(--pad) var(--pad); }

.crumbs ol { display: flex; flex-wrap: wrap; gap: 4px 0; font-size: 13px; color: var(--mute); letter-spacing: 0; margin-bottom: 20px; }
.crumbs li + li::before { content: "/"; padding-inline: 6px; color: var(--line); }
.crumbs a { transition: color 0.3s var(--ease); }
.crumbs a:hover { color: var(--fg); }
.doc__title { font-size: var(--t-hello); line-height: 1; letter-spacing: -0.05em; overflow-wrap: anywhere; hyphens: auto; }
.doc__lead { margin-top: 14px; color: var(--mute); max-width: 24em; }
.doc__lead:empty { display: none; }
.doc__meta { display: flex; flex-direction: column; margin-top: 28px; padding-top: 8px; border-top: 1px solid var(--line); color: var(--mute); font-size: 0.875em; }
.doc__meta a { color: var(--fg); }

/* middle column */
.doc__intro { padding: 0 var(--in) clamp(56px, 8svh, 96px); font-size: var(--t-state); line-height: 1.1; letter-spacing: -0.04em; max-width: 22em; }
.doc__intro--mail { padding-bottom: 0; }
.doc__mail { overflow-wrap: anywhere; background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 0% 1px; transition: background-size 0.6s var(--ease); }
.doc__mail:hover { background-size: 100% 1px; }
.doc__copy { padding: 10px var(--in) clamp(56px, 8svh, 96px); }
.doc__figure { margin: 0 var(--in) 24px; background: var(--soft); overflow: hidden; }
.doc__figure img { width: 100%; height: 100%; object-fit: cover; }
.doc__figure--portrait { aspect-ratio: 3 / 4; }
@media (min-width: 1200px) { .doc__figure--portrait { aspect-ratio: 1 / 1; } }   /* desktop: square */
.doc__figure--portrait img { object-position: 50% 30%; }
/* Info: the statement, with a small portrait beside it at the column's right edge */
.info-top { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; column-gap: clamp(16px, 2vw, 32px); }
.info-top .doc__intro { padding-right: 0; }
.info-top__photo { width: clamp(96px, 12.5vw, 180px); margin: 0 var(--in) clamp(28px, 3.4vw, 48px) 0; }
.doc__figure--portrait.info-top__photo { aspect-ratio: 3 / 4; }
.doc-empty { color: var(--mute); padding-block: 10px; border-block: 1px solid var(--line); }
.doc-cta { padding-top: clamp(24px, 4svh, 48px); }
.doc-cta__title { font-size: var(--t-talk); line-height: 0.95; letter-spacing: -0.055em; }
.doc-cta__link { display: inline-flex; gap: 0.4em; margin-top: 18px; padding: 2px 10px; border: 1px solid var(--fg); border-radius: 999px; line-height: 1.3; transition: background-color 0.3s var(--ease); }
@media (hover: hover) { .doc-cta__link:hover { background: var(--soft); } }
/* Contact: the way to the project form, under its few lines */
.contact-form .doc-cta__link { margin-top: 24px; }
/* the page already lists the email and the profiles: the right column keeps only where and when */
.doc--contact :is(.doc__reach, .doc__socials) { display: none; }

/* links between the home page and the inner pages (shown once those are live) */
.more { margin-top: 14px; }
.more__link { display: inline-flex; gap: 0.4em; color: var(--mute); transition: color 0.3s var(--ease); }
.more__link:hover { color: var(--fg); }

/* rows that are links: the arrow sits at the right end, after the date or status */
a.row .row__c { white-space: nowrap; }
a.row .row__c::after { content: "→"; display: inline-block; margin-left: 0.5em; color: var(--fg); transition: translate 0.4s var(--ease); }
a.row:hover .row__c::after { translate: 4px 0; }
@media (hover: hover) { .motion-ok a.row:hover .row__b { color: var(--fg); } }


/* placeholders: what is still to be written, visible on purpose */
.todo { font-size: var(--t-base); line-height: 1.45; color: var(--mute); border-left: 2px solid var(--line); padding-left: 14px; max-width: 36em; }
:is(.rows, .faq) + .todo { margin-top: 16px; }
.todo__tag { display: block; font-size: 13px; color: var(--fg); letter-spacing: 0; margin-bottom: 2px; }

/* right column */
.doc__aside-in { display: grid; gap: 40px; align-content: start; }
.doc__status { display: flex; align-items: baseline; gap: 8px; }
.doc__status .dot { translate: 0 -0.15em; }
.doc__reach { display: grid; gap: 12px; }
.doc__reach-lead { color: var(--mute); max-width: 22em; }
.doc__aside .socials-wrap { margin-top: 0; }

/* "Back to site" in place of the breadcrumbs (front matter back: yes) */
.back { margin-bottom: 20px; font-size: 13px; letter-spacing: 0; }
.back__link { display: inline-flex; gap: 6px; color: var(--mute); transition: color 0.3s var(--ease); }
.back__link > span:first-child { transition: translate 0.4s var(--ease); }
.back__link:hover { color: var(--fg); }
.back__link:hover > span:first-child { translate: -3px 0; }
.motion-ok .back__link:hover .roll > span:first-child { translate: 0 -105%; }
.motion-ok .back__link:hover .roll > span + span { translate: 0 0; }

/* ---------- Get in touch: the project intake (/get-in-touch/) ----------
   Seven questions on one system: a hairline, a small number, the question,
   then its answers — every question with the same type, the same gaps and
   the same answer height. Answers are the bar's rounded buttons, larger:
   outlined in the hairline grey, darker on hover, filled once chosen (only
   colours change, never sizes). Fields are a line to write on.
     --q-space  between two questions
     --q-gap    from a question to its answers
     --q-opt    one answer's height */
.intake {
  --warn: #c4380a;
  --q-space: clamp(48px, 7svh, 72px);
  --q-gap: 20px;
  --q-opt: 2.5em;
}
fieldset.q { margin: 0; border: 0; min-width: 0; }
.block.q { padding: 0 var(--in) var(--q-space); }
.q__title {
  float: left; width: 100%;                /* a legend that behaves like a heading */
  display: flex; align-items: baseline;
  padding: 14px 0 var(--q-gap); border-top: 1px solid var(--line);
  font-size: var(--t-state); line-height: 1.15; letter-spacing: -0.04em;
}
.q__title + * { clear: both; }
.q__no { flex: none; width: 2.2rem; font-size: 13px; color: var(--mute); letter-spacing: 0; font-variant-numeric: tabular-nums; transition: color 0.4s var(--ease); }
.q.is-answered .q__no { color: var(--fg); }
/* one line of help under a question, in body text */
.q__hint { display: block; margin-top: 6px; font-size: var(--t-base); line-height: 1.4; letter-spacing: -0.03em; color: var(--mute); }

.q__opts { display: flex; flex-wrap: wrap; gap: 8px; }
.opt { position: relative; display: inline-flex; max-width: 100%; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.opt input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.opt > span {
  display: inline-flex; align-items: center;
  min-height: var(--q-opt); padding: 0.3em 0.95em; border: 1px solid var(--line); border-radius: calc(var(--q-opt) / 2);
  font-size: var(--t-head); line-height: 1.2; letter-spacing: -0.03em;
  transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease), color 0.3s var(--ease), scale 0.3s var(--ease);
}
@media (hover: hover) { .opt:hover > span { border-color: var(--fg); } }
.opt input:checked + span { background: var(--fg); border-color: var(--fg); color: var(--bg); }
.opt input:focus-visible + span { outline: 2px solid var(--fg); outline-offset: 3px; }
.motion-ok .opt:active > span { scale: 0.97; }

/* the project text and the contact fields: a line to write on, in the answers' size */
.q__text, .field__input {
  display: block; width: 100%;
  border: 0; border-radius: 0; padding: 0; margin: 0;
  background: linear-gradient(var(--fg), var(--fg)) no-repeat 0 100% / 0% 1px, linear-gradient(var(--line), var(--line)) no-repeat 0 100% / 100% 1px;
  color: var(--fg); font: inherit; font-size: var(--t-head); line-height: 1.4; letter-spacing: -0.03em;
  transition: background-size 0.6s var(--ease);
}
.q__text:focus, .field__input:focus { outline: none; background-size: 100% 1px, 100% 1px; }
.q__text::placeholder { color: var(--mute); opacity: 1; }
.q__text { padding-bottom: 12px; min-height: calc(4 * 1.4em + 12px); resize: none; field-sizing: content; }
.fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--q-gap) var(--in); }
.field { display: grid; gap: 6px; min-width: 0; }
.field--wide { grid-column: 1 / -1; }
.field__label { font-size: var(--t-base); color: var(--mute); }
/* a required answer: an asterisk after its question or label (the controls say "required" themselves) */
.req { margin-left: 0.1em; color: var(--fg); }
.field__input { padding: 4px 0 10px; }
.field__input:-webkit-autofill { -webkit-box-shadow: 0 0 0 100px var(--bg) inset; -webkit-text-fill-color: var(--fg); }

/* notes: only after a send attempt, in a warm red, gone as soon as it is answered */
.q__error, .field__error { font-size: 0.875em; color: var(--warn); }
.q__error:empty, .field__error:empty { display: none; }
.q__error { padding-top: 12px; }
.q.is-invalid .q__no { color: var(--warn); }
.q.is-invalid .opt > span { border-color: #f0c2b0; }
.field.is-invalid .field__input { background-image: linear-gradient(var(--warn), var(--warn)), linear-gradient(var(--warn), var(--warn)); }

/* the close: the same hairline and gaps, the ask one step larger */
.intake-end { margin: 0 var(--in); padding: 14px 0 var(--q-space); border-top: 1px solid var(--line); }
.intake-end__title { padding-top: 24px; font-size: clamp(30px, 2.8vw, 48px); line-height: 1; letter-spacing: -0.05em; }
.intake-end__lead { margin-top: 12px; max-width: 30em; font-size: var(--t-head); letter-spacing: -0.03em; }
.consent { display: grid; gap: 6px; margin-top: 28px; }
.consent__label { position: relative; display: inline-flex; align-items: baseline; gap: 10px; cursor: pointer; justify-self: start; }
.consent__label input { position: absolute; left: 0; top: 0.2em; width: 16px; height: 16px; margin: 0; opacity: 0; cursor: pointer; }
.consent__box {
  flex: none; position: relative; top: 0.16em; width: 16px; height: 16px;
  border: 1px solid var(--mute); border-radius: 4px;
  transition: background-color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.consent__box::after {
  content: ""; position: absolute; left: 4.5px; top: 1.5px; width: 5px; height: 9px;
  border: solid var(--bg); border-width: 0 1.5px 1.5px 0; rotate: 45deg; scale: 0;
  transition: scale 0.3s var(--ease);
}
.consent__label:hover .consent__box { border-color: var(--fg); }
.consent__label input:checked + .consent__box { background: var(--fg); border-color: var(--fg); }
.consent__label input:checked + .consent__box::after { scale: 1; }
.consent__label input:focus-visible + .consent__box { outline: 2px solid var(--fg); outline-offset: 3px; }
.consent.is-invalid .consent__box { border-color: var(--warn); }
.consent a { background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 100% 1px; }
.intake-end__send { margin-top: var(--q-gap); }
/* the one filled button: an answer's type, a little taller */
.send {
  display: inline-flex; align-items: center; gap: 0.5em;
  min-height: calc(var(--q-opt) * 1.2); padding: 0.3em 1.3em; border-radius: 999px;
  background: var(--fg); color: var(--bg);
  font-size: var(--t-head); line-height: 1.2; letter-spacing: -0.03em;
  transition: background-color 0.3s var(--ease), scale 0.3s var(--ease), opacity 0.3s var(--ease);
}
.send__arrow { transition: translate 0.45s var(--ease); }
@media (hover: hover) {
  .send:hover { background: #333; }
  .send:hover .send__arrow { translate: 5px 0; }
}
.motion-ok .send:active { scale: 0.97; }
.send.is-busy { opacity: 0.6; cursor: progress; }
.send.is-busy .send__arrow { animation: send-wait 1s var(--ease) infinite; }
@keyframes send-wait { 50% { translate: 5px 0; } }
.intake__msg { margin-top: 16px; max-width: 30em; color: var(--warn); }
.intake__msg:empty { display: none; }
.intake__msg a { background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 100% 1px; }

/* after sending: in the place of the form, at the close's size */
.intake-done { padding: 0 var(--in) clamp(96px, 16svh, 200px); }
.intake-done:focus { outline: none; }
.intake-done__title { font-size: clamp(30px, 2.8vw, 48px); line-height: 1; letter-spacing: -0.05em; }
.intake-done__text { margin-top: 12px; max-width: 28em; font-size: var(--t-head); letter-spacing: -0.03em; }
.intake-done__back { margin-top: 28px; }
.intake-done__back .more__link, .intake-done__actions .more__link { display: inline-flex; gap: 0.4em; }
.intake-done__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px 28px; margin-top: 28px; }

/* answers arrive one after another as each question comes in */
.motion-ok .q__opts > * { opacity: 0; translate: 0 10px; transition: opacity 0.8s var(--ease), translate 0.9s var(--ease); transition-delay: calc(0.1s + var(--k, 0) * 45ms); }
.motion-ok .q.is-in .q__opts > * { opacity: 1; translate: 0 0; }
@media (prefers-reduced-motion: reduce) {
  .opt > span, .consent__box, .consent__box::after { transition: none; }
}

@media (max-width: 767px) {
  .intake { --q-space: 48px; --q-gap: 16px; }
  .fields { grid-template-columns: 1fr; }
  .send { width: 100%; justify-content: center; }
}

/* footer of every inner page */
.doc__foot { align-items: baseline; padding-top: 40px; }
/* the Privacy Policy link, beside the copyright in its grey; darker on hover, as the game button */
.foot__legal:hover, .foot__legal:focus-visible { color: var(--fg); }
/* the other language (EN / RU), after the legal links in the same grey */
.lang__link { color: var(--mute); transition: color 0.3s var(--ease); }
.lang__link:hover, .lang__link:focus-visible { color: var(--fg); }

/* /privacy/: the dates, then the PDF, quieter than the text */
.legal__dates { color: var(--mute); font-size: var(--t-base); }
.legal__pdf { font-size: var(--t-base); }

/* blog posts */
.article { padding: 0 var(--in) clamp(72px, 10svh, 120px); max-width: 40em; font-size: var(--t-head); line-height: 1.45; letter-spacing: -0.02em; }
.article > * + * { margin-top: 1em; }
.article h2 { margin-top: 2.2em; font-size: var(--t-state); line-height: 1.15; letter-spacing: -0.04em; }
.article h3 { margin-top: 1.8em; font-weight: 500; }
.article :is(h2, h3) + * { margin-top: 0.6em; }
.article a { background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 100% 1px; }
.article a:hover { color: var(--mute); }
.article ul, .article ol { display: grid; gap: 0.35em; padding-left: 1.1em; }
.article ul { list-style: disc; }
.article ol { list-style: decimal; }
.article figure { margin-block: 2em; }
.article figure img { width: 100%; height: auto; background: var(--soft); }
.article figcaption { margin-top: 8px; font-size: 13px; color: var(--mute); letter-spacing: 0; }
.article blockquote { margin-block: 1.6em; padding-left: 16px; border-left: 2px solid var(--fg); font-size: var(--t-state); line-height: 1.2; letter-spacing: -0.04em; }
.article code { font-size: 0.9em; background: var(--soft); padding: 0.1em 0.3em; border-radius: 4px; }

.article__hero { margin: 0 0 2em; aspect-ratio: 3 / 2; overflow: hidden; background: var(--soft); }
.article__hero picture, .article__hero img { width: 100%; height: 100%; }
.article__hero img { object-fit: cover; object-position: var(--pos, 50% 50%); }

/* ---------- Wide inner pages (blog, work) ----------
   The content takes the middle and right columns together; the contact
   column moves below it, as on a tablet. */
@media (min-width: 1200px) {
  .doc--wide { grid-template-columns: 28fr 72fr; grid-template-areas: "side main" "side aside" "side foot"; grid-template-rows: auto auto 1fr; }
  .doc--wide .doc__main, .doc--wide .doc__foot { border-right: 0; }
  .doc--wide .doc__aside { position: static; max-height: none; border-left: 1px solid var(--line); border-top: 1px solid var(--line); }
  .doc--wide .doc__aside-in { padding: 40px var(--in); grid-template-columns: 1fr 1fr; }
  .doc--wide .doc__status { grid-column: 1 / -1; }
  .doc--wide .doc__foot { align-self: end; }
}

/* The contact column and the footer under a wide page (and on a tablet): one grid.
   The contact column in two halves — where and how on the left, the socials across the
   whole right half; the footer on the same halves — who made it and the copyright under the email, the
   game under the start of the socials and Back to Top at their end. */
@media (min-width: 768px) and (max-width: 1199px) {
  .doc { --foot-gap: clamp(24px, 3vw, 48px); }
  .doc__aside-in { column-gap: var(--foot-gap); }
  .doc__aside-in > :not(.doc__socials) { grid-column: 1; }
  .doc__aside .doc__socials { grid-column: 2; grid-row: 1 / span 2; width: 100%; }
  /* too narrow for quarters: the game and Back to Top under each other in the right half */
  .doc__foot { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px var(--foot-gap); }
  .doc__foot > p { grid-column: 1; }
  .doc__foot > :is(.foot__game, .foot__top) { grid-column: 2; }
  .doc__foot > .foot__game { grid-row: 1; }
  .doc__foot > .foot__top { grid-row: 2; justify-self: start; }
}
@media (min-width: 1200px) {
  .doc--wide { --foot-gap: clamp(24px, 3vw, 48px); }
  .doc--wide .doc__aside-in { column-gap: var(--foot-gap); }
  .doc--wide .doc__aside-in > :not(.doc__socials) { grid-column: 1; }
  .doc--wide .doc__aside .doc__socials { grid-column: 2; grid-row: 1 / span 2; width: 100%; }
  .doc--wide .doc__foot { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px var(--foot-gap); }
  .doc--wide .doc__foot .foot__top { justify-self: end; }
  .doc--wide .doc__foot > p { grid-column: 1 / 3; }
  .doc--wide .doc__foot > :is(.foot__game, .foot__top) { grid-row: 1; }
  .doc--wide .doc__foot > .foot__game { grid-column: 3; }
  .doc--wide .doc__foot > .foot__top { grid-column: 4; }
}

/* ---------- Blog (/blog/) ----------
   An editorial index: one article large, then the rest as ruled rows, picture
   on the left and words on the right, on one shared grid (the featured
   article's words start where every row's words start). The content takes the
   middle and right columns together (.doc--wide). The title's link covers
   each card, so the whole card answers:
   the picture leans in, the title underlines, the arrow goes out and comes
   back. Phone: the featured article stays large; the rows become a quick list
   of titles with a small square picture. */
.doc--blog { --b-cols: minmax(0, 0.9fr) minmax(0, 1.1fr); --b-gap: clamp(24px, 3.4vw, 56px); }
.doc--blog .doc__lead { max-width: 20em; }

.post { position: relative; }
.post__media { overflow: hidden; background: var(--soft); aspect-ratio: 5 / 3; }
.post__media picture, .post__media img { width: 100%; height: 100%; }
.post__media img { object-fit: cover; object-position: var(--pos, 50% 50%); transition: scale 1.1s var(--ease); }
.post__body { display: flex; flex-direction: column; min-width: 0; }
.post__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; font-size: 13px; letter-spacing: 0; color: var(--mute); font-variant-numeric: tabular-nums; }
.post__cat { display: inline-flex; align-items: baseline; gap: 6px; color: var(--fg); }
.post__cat::before { content: ""; width: 4px; height: 4px; border-radius: 50%; background: currentColor; align-self: center; transition: scale 0.5s var(--ease); }
.post__draft { padding: 0 6px; border-radius: 999px; background: var(--soft); color: var(--fg); }
/* a post's topic (TOPICS in site_config.py): its colour as a dot, the same colour as its cover */
.topic { display: inline-flex; align-items: baseline; gap: 6px; color: var(--fg); }
.topic::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--t-back, currentColor); align-self: center; }
.post__cat.topic::before { width: 7px; height: 7px; }

/* /blog/: the topics as one line of large words, "All, Web Design, …": the chosen one in ink,
   the others muted (js/main.js); without the script the line stays hidden and every post shows */
.topics { padding: 0 var(--in); margin-bottom: clamp(40px, 4.4vw, 72px); }   /* in line with the blocks below */
.topics ul { --tsz: clamp(30px, 3.1vw, 56px); display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 0.3em; row-gap: 0.02em; font-size: var(--tsz); line-height: 1.04; letter-spacing: -0.045em; }
.topics li { display: inline-flex; align-items: baseline; }
.topics__btn { padding: 0; color: var(--mute); letter-spacing: inherit; cursor: pointer; transition: color 0.6s var(--ease); }
.topics__btn[aria-pressed="true"] { color: var(--fg); }
/* the count of articles, always shown, right at the word's edge at the top of the line;
   the comma sits under it, on the line itself */
.topics__tail { position: relative; display: inline-block; vertical-align: top; margin-left: 0.05em; padding-top: 0.2em; }
.topics__n { display: block; font-size: 13px; line-height: 1; letter-spacing: 0; font-variant-numeric: tabular-nums; color: var(--mute); }
.topics li:not(:last-child) .topics__tail::after { content: ","; position: absolute; left: 0; top: 0; font-size: var(--tsz); line-height: 1.04; letter-spacing: 0; color: var(--mute); }
.topics__btn:focus-visible { outline: 2px solid var(--fg); outline-offset: 4px; border-radius: 2px; }
@media (hover: hover) and (pointer: fine) {
  .topics__btn:not([aria-pressed="true"]):hover { color: #4a4a4a; }
}
[data-topic-hidden] { display: none !important; }
.post__title { font-size: var(--t-state); line-height: 1.1; letter-spacing: -0.04em; text-wrap: balance; }
.post__link { outline: none; }
.post__link::after { content: ""; position: absolute; inset: 0; z-index: 1; }   /* the whole card is the link */
.post:has(.post__link:focus-visible) { outline: 2px solid var(--fg); outline-offset: 6px; }
/* the title underlines from the left, line by line */
.post__line {
  background: linear-gradient(currentColor, currentColor) no-repeat 100% 100% / 0% 1px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
  transition: background-size 0.6s var(--ease);
}
.post__lead { margin-top: 12px; max-width: 30em; color: var(--mute); text-wrap: pretty; }
.post__more { display: inline-flex; align-items: center; gap: 8px; align-self: flex-start; margin-top: 20px; }

/* featured: a wide picture; under it the meta on the left, the words where the rows' words are */
.post--feature { display: grid; gap: 20px; }
.post--feature .post__media { aspect-ratio: 16 / 9; }
.post--feature .post__body { display: grid; grid-template-columns: var(--b-cols); gap: 12px var(--b-gap); }
.post--feature .post__text { grid-column: 2; grid-row: 1 / span 2; }
.post--feature .post__more { grid-column: 2; }
.post--feature .post__title { font-size: clamp(30px, 2.8vw, 48px); line-height: 1; letter-spacing: -0.05em; }
.post--feature .post__lead { margin-top: 16px; font-size: var(--t-head); letter-spacing: -0.03em; }
.post--feature .post__meta { flex-direction: column; gap: 2px; }

/* the rows: ruled like the reference, words spread over the picture's height */
.posts > li { position: relative; padding-block: 24px; }
.posts > li::before, .posts > li:last-child::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--line);
  transform-origin: 0 50%;
}
.posts > li:last-child::after { top: auto; bottom: 0; }
.post:not(.post--feature) { display: grid; grid-template-columns: var(--b-cols); gap: var(--b-gap); }
.post:not(.post--feature) .post__body { justify-content: space-between; gap: 20px; padding-block: 2px; }
.post:not(.post--feature) .post__more { margin-top: 0; }

@media (hover: hover) {
  .post:hover .post__line { background-size: 100% 1px; background-position: 0 100%; }
  .post:hover .post__media img { scale: 1.035; }
  .post:hover .post__cat::before { scale: 1.5; }
  .post:hover .social__arrow { color: var(--fg); }
  .motion-ok .post:hover .social__arrow svg:first-child { translate: 100% -100%; }
  .motion-ok .post:hover .social__arrow svg + svg { translate: 0 0; }
}
.motion-ok .post .social__arrow svg { transition: translate 0.55s var(--ease); }
.motion-ok .post .post__media { transition: scale 0.5s var(--ease); }
.motion-ok .post:active .post__media { scale: 0.985; }

/* arriving: the picture settles in, the words follow, the rule draws from the left */
.motion-ok .post .post__media img { opacity: 0; scale: 1.08; transition: opacity 1s var(--ease), scale 1.6s var(--ease); }
.motion-ok .post.is-in .post__media img { opacity: 1; scale: 1; }
@media (hover: hover) {
  .motion-ok .post.is-in:hover .post__media img { scale: 1.035; transition: scale 1.1s var(--ease); }
}
.motion-ok .post .post__body > * { opacity: 0; translate: 0 10px; transition: opacity 0.9s var(--ease), translate 0.9s var(--ease); }
.motion-ok .post .post__body > :nth-child(2) { transition-delay: 0.08s; }
.motion-ok .post .post__body > :nth-child(3) { transition-delay: 0.16s; }
.motion-ok .post.is-in .post__body > * { opacity: 1; translate: none; }   /* none, not 0 0: a transform would trap the card-wide link inside the text */
.motion-ok .posts > li::before, .motion-ok .posts > li:last-child::after { transform: scaleX(0); transition: transform 1.2s var(--ease); }
.motion-ok .posts > li:has(.is-in)::before, .motion-ok .posts > li:last-child:has(.is-in)::after { transform: scaleX(1); }

@media (max-width: 767px) {
  .post--feature { gap: 16px; }
  .post--feature .post__media { aspect-ratio: 4 / 3; margin-inline: calc(var(--in) * -1); }
  .post--feature .post__body { display: flex; }
  .post--feature .post__meta { flex-direction: row; gap: 4px 16px; }
  .post--feature .post__text { margin-top: 14px; }
  .post--feature .post__title { font-size: 30px; }
  .post--feature .post__lead { font-size: 17px; }
  /* the rows: a quick list — title first, a small square picture on the right */
  .posts > li { padding-block: 16px; }
  .post:not(.post--feature) { grid-template-columns: minmax(0, 1fr) 30%; gap: 16px; align-items: start; }
  .post:not(.post--feature) .post__media { order: 2; aspect-ratio: 1; }
  .post:not(.post--feature) .post__body { justify-content: flex-start; gap: 8px; padding: 0; }
  .post:not(.post--feature) .post__text { order: -1; }
  .post:not(.post--feature) .post__title { font-size: 20px; line-height: 1.15; }
  .post:not(.post--feature) :is(.post__lead, .post__more) { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .post__media img, .post__line { transition: none; }
}

/* ---------- Shared inner-page parts ---------- */
.svc-intro { padding-bottom: clamp(28px, 3.4vw, 48px); }
.faq__answer a { color: var(--fg); background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 100% 1px; }

/* small labels */
.tags { display: flex; flex-wrap: wrap; gap: 6px; }
.tag { padding: 3px 10px; border-radius: 999px; background: var(--vz); font-size: 13px; line-height: 1.35; letter-spacing: 0; }

/* a work card without a picture: its tags and a short line instead */
.item__media--text {
  display: flex; flex-direction: column; justify-content: space-between; gap: 24px; padding: clamp(20px, 2.6vw, 44px);
  background: var(--vz); transition: background-color 0.4s var(--ease);
  font-size: var(--t-state); line-height: 1.15; letter-spacing: -0.04em; text-wrap: pretty;
}
.item__media--text > span { max-width: 24em; }
@media (max-width: 767px) {
  .item__media--text { padding: 20px; font-size: 20px; }
}
@media (prefers-reduced-motion: reduce) {
  .item__media--text { transition: none; }
}

/* ---------- Services, full width (/services/, src/layouts/services.html) ----------
   The case pages' full width, with a lot of air: the title in the middle of the first
   screen; a statement that lights up word by word; the services' names drifting past in
   two quiet rows; then each service as a scene — its number and a hairline dropping into
   its name, set across the page, the line under it, the picture and everything about the
   service as one ruled list. Driven by js/main.js ("Services, full width"):
   --h the first screen scrolled away (0 → 1), --t the statement lit (0 → 1),
   --p a scene's way through the screen (0 entering at the foot, 1 leaving at the top). */
.sw { --air: clamp(160px, 20vw, 360px); }
.sw-hero {
  position: relative; min-height: calc(88svh - var(--bar));
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 0 var(--pad); text-align: center;
}
.sw-hero__title { font-size: clamp(88px, 15vw, 280px); line-height: 0.9; letter-spacing: -0.06em; margin-left: -0.06em; }
.sw-hero__lead { max-width: 24em; margin-top: clamp(16px, 2vw, 32px); color: var(--mute); text-wrap: balance; }
.sw-hint { margin-top: 20px; font-size: 13px; letter-spacing: 0; color: var(--mute); white-space: nowrap; }
.motion-ok .sw-hero > :not(.sw-hint) { opacity: calc(1 - var(--h, 0) * 1.4); translate: 0 calc(var(--h, 0) * -16vh); }
.motion-ok .sw-hint { opacity: calc(1 - var(--h, 0) * 4); }
.motion-ok .sw-hero__title { animation: sw-rise 1.4s var(--ease) 0.15s both; }
.motion-ok :is(.sw-hero__lead, .sw-hint) { animation: sw-fade 1s var(--ease) 0.6s both; }
@keyframes sw-rise { from { clip-path: inset(0 0 100% 0); transform: translateY(0.3em); } to { clip-path: inset(0 0 -10% 0); transform: none; } }
@keyframes sw-fade { from { opacity: 0; } }

/* the ring around the title (js/main.js "Orbit"): one 3D scene holding the title and
   the ring, so the cards in front pass over the title and those behind pass under it.
   Each card is a few upright strips bent along the ring; the ring leans and turns. */
.sw-hero { overflow: clip; }
.orbit {
  position: relative; width: calc(100% + 2 * var(--pad)); margin-inline: calc(-1 * var(--pad)); height: clamp(340px, 46vw, 700px);
  perspective: 2600px; perspective-origin: 50% 46%;
  touch-action: pan-y; user-select: none; -webkit-user-select: none;
}
.js .orbit { cursor: grab; }
.orbit.is-dragging { cursor: grabbing; }
.orbit__scene { position: absolute; inset: 0; transform-style: preserve-3d; }
.orbit .sw-hero__title { position: absolute; left: 50%; top: 50%; translate: -50% -50%; margin: 0; white-space: nowrap; }
.orbit__ring { position: absolute; left: 50%; top: 50%; transform-style: preserve-3d; }
.orbit__card { position: absolute; left: 0; top: 0; transform-style: preserve-3d; }
.orbit__strip {
  position: absolute; left: 0; top: 0;
  background-color: var(--soft); background-repeat: no-repeat;
}
.motion-ok .orbit__strip { opacity: 0; }
/* the sources: loaded, never shown */
.orbit__src { position: absolute; left: 0; top: 0; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none; }
.orbit__src img { width: 1px; height: 1px; }
.no-js .orbit { height: auto; padding-block: 12vh; }
.no-js .orbit .sw-hero__title { position: static; translate: none; }

.sw-intro {
  max-width: 22em; margin: 0 auto; padding: var(--air) var(--pad) 0;
  font-size: clamp(28px, 3.4vw, 60px); line-height: 1.08; letter-spacing: -0.05em; text-align: center; text-wrap: balance;
}
.sw .wd, .sw .ch { display: inline-block; }
.sw .wk { display: inline-block; white-space: nowrap; }
.motion-ok .sw-intro .wd { opacity: clamp(0.14, calc(var(--t, 1) * var(--n, 1) - var(--i, 0)), 1); transition: opacity 0.25s linear; }

/* the names drifting past: quiet, behind everything else in weight */
.sw-ticker { padding-top: var(--air); overflow: hidden; display: grid; gap: 0.06em; font-size: clamp(56px, 8vw, 150px); line-height: 1; letter-spacing: -0.05em; color: #dcdcdc; }
.sw-ticker__row { display: flex; align-items: center; gap: 0.3em; width: max-content; white-space: nowrap; }
.sw-ticker__row--back { color: #ebebeb; }
.sw-ticker__star { width: 0.42em; height: 0.42em; fill: currentColor; flex: none; }
.motion-ok .sw-ticker__row { transform: translateX(calc(var(--p, 0.5) * -32%)); will-change: transform; }
.motion-ok .sw-ticker__row--back { transform: translateX(calc(-32% + var(--p, 0.5) * 32%)); }

/* a service */
.sw-list { padding: 0 var(--pad); }
.sw-svc { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: clamp(16px, 2vw, 32px); padding-top: var(--air); text-align: center; }
.sw-svc__no { grid-column: 1 / -1; font-size: 13px; letter-spacing: 0; color: var(--mute); font-variant-numeric: tabular-nums; }
.sw-svc__no::after { content: ""; display: block; width: 1px; height: clamp(64px, 9vw, 160px); margin: 14px auto 0; background: var(--fg); transform-origin: 50% 0; }
.motion-ok .sw-svc__no::after { transform: scaleY(clamp(0, calc(var(--p, 1) * 3 - 0.4), 1)); }
.sw-svc__title {
  grid-column: 1 / -1; margin-top: clamp(8px, 1vw, 16px); overflow: hidden;
  font-size: clamp(44px, 6.4vw, 120px); line-height: 0.9; letter-spacing: -0.05em; padding-bottom: 0.16em; margin-bottom: -0.1em; text-wrap: balance;
}
.sw-svc__word { display: inline-block; }
.sw-svc__link { display: inline-block; transition: opacity 0.4s var(--ease); }
@media (hover: hover) and (pointer: fine) { .sw-svc__link:hover { opacity: 0.55; } }
.sw-svc__lead { grid-column: 3 / 11; margin-top: clamp(20px, 2.4vw, 40px); font-size: var(--t-state); line-height: 1.15; letter-spacing: -0.04em; color: var(--mute); text-wrap: balance; }
.sw-svc__row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; align-items: start; margin-top: clamp(64px, 8vw, 140px); text-align: left; }
.sw-svc__fig { grid-column: 1 / 6; display: grid; gap: 8px; }
.sw-svc__frame { position: relative; display: block; overflow: hidden; aspect-ratio: 4 / 3; background: var(--soft); }
.sw-svc__frame picture { position: absolute; inset: -8% 0; }
.sw-svc__frame img { width: 100%; height: 100%; object-fit: cover; }
.motion-ok .sw-svc__frame picture { transform: translateY(calc((0.5 - var(--p, 0.5)) * 12%)); }
.sw-svc__fig figcaption { font-size: 13px; letter-spacing: 0; color: var(--mute); }
.sw-svc__fig figcaption a { transition: color 0.3s var(--ease); }
.sw-svc__fig figcaption a:hover { color: var(--fg); }
.sw-svc__list { position: relative; grid-column: 8 / -1; border-top: 1px solid var(--line); }
.sw-svc__list li {
  position: relative; display: flex; align-items: center; padding: 10px 16px 9px 0; border-bottom: 1px solid var(--line);
  font-size: var(--t-head); line-height: 1.25; letter-spacing: -0.02em;
}
.sw-svc__list li::after { content: ""; flex: none; width: 6px; height: 6px; margin-left: auto; border-radius: 50%; background: var(--fg); translate: 16px 0; }
.sw-svc__k { margin-right: 0.3em; color: var(--mute); }
/* a service with a page of its own: the way to it, apart from the list, under it */
.sw-svc__row { grid-template-rows: auto 1fr; }   /* the picture beside both: what it adds goes under the link */
.sw-svc__fig { grid-row: span 2; }
.sw-svc__more { grid-column: 8 / -1; }
.sw-svc__more a { display: inline-flex; gap: 0.4em; margin-top: 20px; font-size: var(--t-head); color: var(--mute); transition: color 0.3s var(--ease); }
@media (hover: hover) { .sw-svc__more a:hover { color: var(--fg); } }
.motion-ok .sw-svc__more { opacity: 0; transition: opacity 1s var(--ease) 1.1s; }
.motion-ok .sw-svc.is-in .sw-svc__more { opacity: 1; }

/* arriving: the name letter by letter out of its line, the line under it word by word,
   the picture opening from below (grey until it reaches the middle of the screen),
   the list's hairlines drawn from the left with each row after its line */
.motion-ok .sw-svc .ch { translate: 0 112%; transition: translate 1.2s var(--ease); transition-delay: calc(0.15s + var(--i, 0) * 28ms); }
.motion-ok .sw-svc__lead .wd { opacity: 0; translate: 0 0.4em; transition: opacity 0.9s var(--ease), translate 1s var(--ease); transition-delay: calc(0.45s + var(--i, 0) * 30ms); }
.motion-ok .sw-svc__frame { clip-path: inset(16% 0 0 0); transition: clip-path 1.4s var(--ease) 0.3s; }
.motion-ok .sw-svc__frame img { scale: 1.1; filter: grayscale(clamp(0, calc((0.42 - var(--p, 1)) * 4), 1)); transition: scale 1.9s var(--ease) 0.3s; }
.motion-ok .sw-svc__fig figcaption { opacity: 0; transition: opacity 1s var(--ease) 0.8s; }
.motion-ok :is(.sw-svc__list, .sw-svc__list li) { border-color: transparent; }
.motion-ok :is(.sw-svc__list, .sw-svc__list li)::before {
  content: ""; position: absolute; left: 0; right: 0; height: 1px; background: var(--line);
  scale: 0 1; transform-origin: 0 50%; transition: scale 1.2s var(--ease);
}
.motion-ok .sw-svc__list::before { top: 0; transition-delay: 0.35s; }
.motion-ok .sw-svc__list li::before { bottom: 0; transition-delay: calc(0.42s + var(--k, 0) * 70ms); }
.motion-ok .sw-svc__list li { opacity: 0; translate: 0 10px; transition: opacity 0.9s var(--ease), translate 1.1s var(--ease); transition-delay: calc(0.5s + var(--k, 0) * 70ms); }
.motion-ok .sw-svc.is-in :is(.ch, .sw-svc__lead .wd, .sw-svc__list li) { translate: 0 0; opacity: 1; }
.motion-ok .sw-svc.is-in .sw-svc__frame { clip-path: inset(0); }
.motion-ok .sw-svc.is-in .sw-svc__frame img { scale: 1; }
.motion-ok .sw-svc.is-in .sw-svc__fig figcaption { opacity: 1; }
.motion-ok .sw-svc.is-in :is(.sw-svc__list, .sw-svc__list li)::before { scale: 1 1; }
@media (hover: hover) and (pointer: fine) {
  .motion-ok .sw-svc.is-in a.sw-svc__frame:hover img { scale: 1.035; transition: scale 1.1s var(--ease); }
}
@media (max-width: 1199px) {
  .sw-svc__lead { grid-column: 2 / 12; }
  .sw-svc__fig { grid-column: 1 / 7; }
  .sw-svc__list, .sw-svc__more { grid-column: 7 / -1; }
}
@media (max-width: 767px) {
  .sw { --air: 140px; }
  .sw-hero__title { font-size: clamp(64px, 22vw, 110px); }
  .sw-svc__lead { font-size: 20px; }
  .sw-svc__lead, .sw-svc__fig, .sw-svc__list, .sw-svc__more { grid-column: 1 / -1; }
  .sw-svc__fig { grid-row: auto; }
  .sw-svc__row { margin-top: 48px; }
  .sw-svc__list { margin-top: 32px; }
}

/* drafts: a reminder that this page is not published */
.draft-flag {
  position: fixed; left: var(--pad); bottom: var(--pad); z-index: 30;
  padding: 4px 10px; border-radius: 999px;
  background: var(--fg); color: var(--bg); font-size: 13px; letter-spacing: 0;
  pointer-events: none;
}
.menu-open .draft-flag { display: none; }

/* tablet: title on the left, everything else on the right */
@media (min-width: 768px) and (max-width: 1199px) {
  .doc { grid-template-columns: 36fr 64fr; grid-template-areas: "side main" "side aside" "side foot"; grid-template-rows: auto auto 1fr; }
  .doc__main, .doc__foot { border-right: 0; }
  .doc__aside { position: static; max-height: none; border-left: 1px solid var(--line); border-top: 1px solid var(--line); }
  .doc__aside-in { padding: 40px var(--in); grid-template-columns: 1fr 1fr; }
  .doc__status { grid-column: 1 / -1; }
  .doc__foot { align-self: end; }
}

/* phone: one column — title, content, contact, footer */
@media (max-width: 767px) {
  .doc { grid-template-columns: minmax(0, 1fr); grid-template-areas: "side" "main" "aside" "foot"; grid-template-rows: auto; }
  .doc__side, .doc__aside { position: static; max-height: none; }
  .doc__side-in { padding: 28px var(--pad) 40px; }
  .doc__title { font-size: clamp(44px, 13vw, 56px); }
  .doc__main { padding-top: 0; }
  .doc__main, .doc__foot { border-inline: 0; }
  .doc__aside { border-top: 1px solid var(--line); }
  .doc__aside-in { padding: 32px var(--pad) 40px; }
  .doc__intro { font-size: 20px; }
  .doc-cta__title { font-size: clamp(56px, 17vw, 80px); }
  .article { font-size: 17px; }
  .doc__aside .socials-wrap { margin: 0; width: auto; }
}

/* page to page: three colour sheets (css/transitions.css, js/transitions.js) */

/* returning in the same session: no preloader (the class is set before the first paint) */
.is-seen .loader { display: none; }
/* returning visit: the bar is simply there (it only fades in from under the preloader) */
.motion-ok.is-seen .bar { opacity: 1; transition: none; }
