/* ==========================================================================
   The Body Edit — блоки после сцены: CTA, цитата, MINUSTA, отзывы, форма
   Сцена (scene.css / scene.js) не задействована.
   Токены (--ink-body, --quote, --body), .cta--quiet, подвал, правовая
   страница и печать — в base.css, он подключён первым.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Отступ якорных переходов — один раз на все секции с id.
   Раньше он стоял поимённо у .yrityksille и .yhteys; с появлением
   переключателя языка якорем может стать любая секция, и поимённый список
   разъехался бы при первом же добавлении блока. Число по-прежнему живёт
   только в --anchor-offset (base.css), здесь его нет.
   -------------------------------------------------------------------------- */

section[id],
.stage[id] { scroll-margin-top: var(--anchor-offset); }

/* --------------------------------------------------------------------------
   Появление
   -------------------------------------------------------------------------- */

[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .7s cubic-bezier(.22, .61, .36, 1),
              transform .7s cubic-bezier(.22, .61, .36, 1);
}

/* у кнопки свой переход на наведение — иначе он был бы затёрт целиком */
.cta[data-reveal] {
  transition: opacity .7s cubic-bezier(.22, .61, .36, 1),
              transform .7s cubic-bezier(.22, .61, .36, 1),
              background-color .35s ease,
              color .35s ease;
}

[data-reveal-group].is-in [data-reveal] {
  opacity: 1;
  transform: none;
}

/* Принудительное раскрытие по таймеру покоя (content.js): мгновенно,
   иначе рендерер снимет страницу посреди 0,7 с. Именно transition-duration,
   а не transition: none — у .cta[data-reveal] в том же списке живёт
   наведение (background-color, color по .35s), и none убил бы его почти
   везде, потому что таймер срабатывает у большинства посетителей.
   Четыре длительности во втором правиле идут по порядку свойств
   из .cta[data-reveal] выше: появление 0, наведение как было. */
[data-reveal-group].is-in--instant [data-reveal] {
  transition-duration: 0s;
}

[data-reveal-group].is-in--instant .cta[data-reveal] {
  transition-duration: 0s, 0s, .35s, .35s;
}

/* Дублируем отмену в CSS на случай, если стиль применится раньше,
   чем отработает JS. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* --------------------------------------------------------------------------
   Страховка: JS недоступен
   За появление отвечает content.js, и до него 95% текста страницы (замерено:
   4934 символа из 5193) стоит на opacity: 0. Без страховки любая причина, по
   которой скрипт не отработал, гасит всю страницу ниже сцены.

   Классов ТРИ, и они не взаимозаменяемы — у отказов разные источники:

     no-js      — скриптов нет вовсе. Стоит в разметке, снимается первым
                  инлайновым скриптом в <head>. Покрывает и выключенный JS,
                  и запрет инлайна политикой CSP (во втором случае <noscript>
                  не сработал бы вовсе: скриптинг-то включён).
     no-reveal  — не доехал content.js. Ставит onerror у его тега.
                  Гасит ТОЛЬКО появление: сцена в это время жива.
     no-scene   — не доехал scene.js. Ставит onerror у его тега.
                  Убирает локап и рельсы, делает шапку статичной.

   Разделение стоило одной итерации: пока onerror у content.js возвращал
   общий no-js, вместе с появлением отключались и рельсы сцены — а scene.js
   при этом работал и считал прогресс по рельсам нулевой высоты.

   Что классами НЕ покрывается: файл доехал и бросил исключение. От этого
   защищает сам content.js — блок появления стоит в нём первым и обёрнут
   в try/catch.

   <noscript> с копией этих правил здесь БЫЛ и убран намеренно: класс no-js
   покрывает все те же случаи и вдобавок запрет инлайна политикой CSP, где
   <noscript> не срабатывает вовсе. Он не добавлял ни одного сценария, зато
   держал пять селекторов во втором месте — в разметке обеих главных.
   -------------------------------------------------------------------------- */

.no-js [data-reveal],
.no-reveal [data-reveal] {
  opacity: 1;
  transform: none;
  transition: none;
}

/* --------------------------------------------------------------------------
   Услуга и прайс
   Имена .service__price / .service__prices, а не .price / .prices: последние
   заняты сценой, где у .price стоит opacity:0 и золотая линейка через ::after.
   -------------------------------------------------------------------------- */

.service {
  background: var(--shadow);
  padding: 14vh 0 6vh;   /* низ срезан: дальше .cta-strip со своим отступом */
}

.service__inner {
  max-width: 60rem;
  margin: 0 auto;
  padding: 0 6vw;      /* горизонтальные отступы здесь, а не на секции */
}

.service__intro { max-width: 34rem; }

.service__foot {
  max-width: 34rem;
  margin-top: 4rem;
}

/* Надстрочник и h2 — в общих списках селекторов ниже по файлу */

.service__intro p {
  margin: 0;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.72;
}

/* --- техники -------------------------------------------------------------- */

/* Колонки задаются ШИРИНОЙ, а не количеством: число колонок браузер считает
   сам по доступному месту. Жёсткое columns: 2 и было причиной переносов —
   оно требовало 523px там, где их было 370. */
.techniques {
  margin: 4rem 0 0;
  padding: 0;
  list-style: none;
  columns: 16rem;
  column-gap: 2rem;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.6;
}

.techniques li {
  position: relative;
  padding-left: 1.1rem;
  margin-bottom: .55rem;
  break-inside: avoid;   /* иначе многоколоночная раскладка рвёт пункт */
}

.techniques li::before {
  content: '';
  position: absolute;
  left: 0;
  top: .62em;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--gold);
}

/* --- прайс ---------------------------------------------------------------- */

/* Кегль текстовый, гротеск как у окружения, без линеек: этот прайс не должен
   спорить со сценой. Золото только на сумме. */
.service__prices {
  margin: 0 0 1.4rem;
  font-size: var(--body);
}

.service__price {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1em;
  margin-bottom: .6rem;
}

.service__price dt { color: var(--ink-body); }

.service__price dd {
  margin: 0;
  color: var(--gold);
}

.service__note {
  margin: 0;
  color: var(--ink-body);
  font-size: .82rem;
  line-height: 1.6;
}

/* Медиазапроса у .service нет: max-width схлопываются сами,
   а columns по ширине сам уходит в одну колонку на узком экране. */

/* --------------------------------------------------------------------------
   CTA
   -------------------------------------------------------------------------- */

.cta-strip {
  background: var(--shadow);
  padding: 6vh 6vw 14vh;  /* верх срезан, низ держит отбивку от .quote-band */
  text-align: center;
}

.cta-strip__note {
  margin: 1.9rem 0 0;
  color: var(--ink-body);
  font-size: .72rem;
  font-weight: 400;
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   Цитата — общее оформление для .quote-band и MINUSTA
   Кавычек нет намеренно: линейка и подпись задают жанр однозначно.
   -------------------------------------------------------------------------- */

.quote {
  margin: 0;
  max-width: 26rem;
  padding-left: 1.75rem;
  /* Приглушена только линейка, не текст: opacity на элементе увела бы
     за собой и цитату. Первая строка — запасной вариант для браузеров
     без color-mix. */
  border-left: 1px solid rgb(var(--gold-rgb) / .4);
  border-left-color: color-mix(in srgb, var(--gold) 40%, transparent);
  color: var(--gold);
  font-family: var(--serif);
  font-size: var(--quote);
  line-height: 1.32;
  text-align: left;
}

/* Финские кавычки: U+201D с обеих сторон, не английская пара и не ёлочки.
   Открывающая висит в поле padding-left, иначе первая строка уехала бы
   вправо на полкегля и разошлась с остальными по левому краю у линейки.
   Отрицательное поле в em, чтобы масштабировалось вместе с --quote. */
.quote--marked::before {
  content: "\201D";
  margin-left: -.44em;
}

.quote--marked::after {
  content: "\201D";
}

/* --------------------------------------------------------------------------
   Цитата с кадром
   Ни рамки, ни тени: края снимка сами уходят в чёрное.
   Порядок в разметке — <img> перед <figcaption>, поэтому на узком экране
   картинка идёт первой сама собой, а на десктопе колонки расставлены явно.
   -------------------------------------------------------------------------- */

.quote-band {
  background: var(--shadow);
  padding: 12vh 6vw;
}

.quote-band figure {
  margin: 0 auto;
  max-width: 64rem;
  display: grid;
  gap: 2.5rem;
  justify-items: center;
}

.quote-band img {
  grid-column: 1;
  display: block;
  width: 100%;
  max-width: 260px;
  height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: 2px;
}

.quote-band .quote {
  grid-column: 1;
  justify-self: start;
}

/* После базовых правил — иначе одинаковая специфичность и базовые значения
   перебивали бы десктопные (та же грабля, что с aspect-ratio портрета). */
@media (min-width: 960px) {
  .quote-band figure {
    grid-template-columns: 1.15fr .85fr;
    gap: 5rem;
    align-items: center;
    justify-items: stretch;
  }

  /* grid-row обязателен обоим: без него авто-размещение увело бы
     figcaption во вторую строку, потому что курсор уже прошёл колонку 2. */
  .quote-band img {
    grid-column: 2;
    grid-row: 1;
    max-width: 320px;
    justify-self: end;
  }

  .quote-band .quote {
    grid-column: 1;
    grid-row: 1;
  }
}

/* --------------------------------------------------------------------------
   MINUSTA — одна колонка по центру, портрета нет
   -------------------------------------------------------------------------- */

.minusta {
  background: var(--shadow);
  padding: 10vh 6vw 6vh;   /* низ срезан: дальше .miksi со своим отступом */
}


/* Портрет убран по решению клиентки: MINUSTA — одна колонка по центру.
   Вместе с ним ушли двухколоночный грид, sticky и все его ограничения. */
.minusta__text {
  max-width: 34rem;
  margin: 0 auto;
}

/* Надстрочник — в общем списке селекторов ниже по файлу */

.minusta__text h2 {
  margin: 0 0 1.9rem;
  color: var(--ink);
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.6rem, 3.6vw, 2.4rem);
  line-height: 1.24;
  letter-spacing: .005em;
}

/* Цепочка :not() — не украшение, а плата за селектор на голый <p>.
   Правило «блок p» имеет специфичность (0,1,1) и молча перебивает КЛАСС
   на самом абзаце: сначала так потерялась .quote, потом надстрочник
   (замерено: рисовался 16.96px цветом --ink-body вместо 11.52px золотом,
   в обеих языковых версиях, и заметили это только аудитом).
   :not(.eyebrow), а не имя одного конкретного надстрочника, — тогда
   следующий такой же защищён сам собой.
   Добавляешь сюда классованный <p> — дописывай его в цепочку тем же движением.
   См. CLAUDE.md, «Что легко сломать». */
.minusta__text p:not(.quote):not(.eyebrow) {
  margin: 0 0 1.35rem;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.72;
}

/* Оформление — из общего .quote, здесь только отбивка по месту.
   Подписи нет: цитата стоит внутри её же рассказа, авторство очевидно. */
.minusta__text .quote { margin: 3rem 0; }

/* --------------------------------------------------------------------------
   Раскрытие хвоста MINUSTA
   Высота нигде не измеряется и не кешируется — состояние живёт в атрибутах,
   поэтому ресайз сбросить его не может в принципе.
   -------------------------------------------------------------------------- */

.more {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 420ms cubic-bezier(.22, .61, .36, 1);
}

.more[data-open] { grid-template-rows: 1fr; }

/* overflow именно на внутреннем элементе: на самом гриде приём не работает.
   Он же создаёт BFC, поэтому отступы абзацев не выходят наружу. */
.more__inner {
  overflow: hidden;
  min-height: 0;
}

.more__inner > p:first-child { margin-top: 2rem; }
.more__inner > p:last-child  { margin-bottom: 0; }

.more__toggle {
  display: inline-block;
  margin-top: 1rem;
  padding: 0 0 .5rem;
  background: none;
  border: 0;
  /* Линия непрозрачная: это единственная опора кнопки. У линейки цитаты 0.4
     нужна была, чтобы не спорить с текстом рядом, здесь спорить не с чем. */
  border-bottom: 1px solid var(--gold);
  color: var(--gold);
  font: inherit;
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
}

.more__toggle:focus-visible {
  outline: 1px solid var(--gold);
  outline-offset: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .more { transition: none; }
}

/* Без этого свёрнутый текст не попал бы на печать: 0fr режет его физически. */
@media print {
  .more { grid-template-rows: 1fr; }
  .more__inner { overflow: visible; }
  .more__toggle { display: none; }
}

/* --------------------------------------------------------------------------
   Надстрочники и заголовки второго уровня — общие для блоков после сцены.
   Собраны в один список селекторов, чтобы не расползались копиями.
   -------------------------------------------------------------------------- */

/* Один класс на все надстрочники страницы. Отдельного .minusta__eyebrow
   больше нет: он существовал только в MINUSTA, попадал под правило на голый
   <p> в том же блоке и молча терял и цвет, и кегль. Общий класс закрывает
   и этот случай, и будущие — цепочка :not() в правиле абзацев названа по
   нему, а не по имени конкретной секции. */
.eyebrow {
  margin: 0 0 1.5rem;
  color: var(--gold);
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
}

.service__intro h2,
.koulutus h2,
.kenelle h2,
.miksi h2,
.arviot h2,
.yrityksille h2,
.yhteys h2 {
  margin: 0 0 1.6rem;
  color: var(--ink);
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.6rem, 3.6vw, 2.4rem);
  line-height: 1.24;
  letter-spacing: .02em;
}

/* --------------------------------------------------------------------------
   Koulutus ja osaaminen
   Все правила адресуются классам: ни одного селектора на голый <p>, чтобы
   `блок p` (0,1,1) не перебил класс на самом абзаце. См. CLAUDE.md.
   -------------------------------------------------------------------------- */

.koulutus {
  background: var(--shadow);
  padding: 6vh 0;   /* по 6vh с обеих сторон: между секциями везде 12vh */
}

.koulutus__inner {
  max-width: 64rem;
  margin: 0 auto;
  padding: 0 6vw;   /* горизонтальные отступы здесь, а не на секции */
}

.koulutus__lead {
  margin: 0;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.72;
}

/* Третий приём оформления списка на странице: в .service точки и колонки,
   в .miksi линия над заголовком, здесь — разделители между строками.
   Список закрыт с обеих сторон: border-top у каждого пункта плюс
   border-bottom последнему. Длинные пункты переносятся спокойно —
   разделители держат ритм, в отличие от columns. */
.koulutus__list {
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.5;
}

.koulutus__list li {
  padding: .9rem 0;
  border-top: 1px solid rgb(var(--gold-rgb) / .18);
}

.koulutus__list li:last-child {
  border-bottom: 1px solid rgb(var(--gold-rgb) / .18);
}

.koulutus__tail {
  max-width: 34rem;
  margin-top: 5rem;
}

.koulutus__text {
  margin: 0 0 1.35rem;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.72;
}

.koulutus__text:last-child { margin-bottom: 0; }

/* После базовых правил: специфичность та же, при обратном порядке
   одноколоночная раскладка перебила бы двухколоночную. */
@media (min-width: 880px) {
  .koulutus__grid {
    display: grid;
    grid-template-columns: .85fr 1.15fr;
    gap: 5rem;
    align-items: start;
  }
}

/* --------------------------------------------------------------------------
   Kenelle palveluni sopivat

   Оболочка повторяет .koulutus почти дословно: 6vh сверху и снизу (в сумме
   с соседями те же 12vh), inner 64rem, две колонки от 880px, хвост 34rem.
   Пропорции сетки при этом другие — .75fr 1.25fr вместо .85fr 1.15fr,
   причина у медиазапроса в конце блока.
   Селекторы свои, а не сгруппированы с .koulutus: правые
   колонки у блоков разного состава — там список с разделителями, здесь
   подводка плюс список без маркеров, — и общий селектор пришлось бы
   расщеплять при первой же правке.

   Все правила адресуются классам: ни одного селектора на голый <p>,
   чтобы `блок p` (0,1,1) не перебил класс на самом абзаце. См. CLAUDE.md.
   -------------------------------------------------------------------------- */

.kenelle {
  background: var(--shadow);
  padding: 6vh 0;   /* по 6vh с обеих сторон: между секциями везде 12vh */
}

.kenelle__inner {
  max-width: 64rem;
  margin: 0 auto;
  padding: 0 6vw;   /* горизонтальные отступы здесь, а не на секции */
}

.kenelle__lead {
  margin: 0;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.72;
}

/* Отбивка нужна только на одной колонке: в гриде столбцы разведены gap.
   У .koulutus этот зазор держит border-top первого пункта списка,
   здесь границ нет, и без правила подводка прилипла бы к вводному абзацу. */
.kenelle__aside { margin-top: 3rem; }

/* Подводка к списку. Золото и обычный регистр: капса нет намеренно —
   иначе строка читалась бы вторым надстрочником. */
.kenelle__cue {
  margin: 0 0 1.8rem;
  color: var(--gold);
  font-size: calc(var(--body) * 1.15);
  line-height: 1.5;
}

/* Четвёртый приём оформления списка на странице, и он не повторяет три
   предыдущих: в .service точки и колонки, в .koulutus разделители между
   строками, в .miksi линия над заголовком. Здесь маркеров нет вовсе,
   а единственная линия вертикальная — между колонками.

   Колонки задаются ШИРИНОЙ, а не количеством (та же причина, что
   у .techniques): на узком экране колонка остаётся одна, и column-rule
   исчезает сам — отдельного медиазапроса под линию не нужно.

   12.5rem — не «на глаз». Список набран СИСТЕМНЫМ гротеском, а не нашим
   Cormorant: на Windows это Segoe UI, на macOS SF, на Android Roboto,
   и ширина строки от машины к машине разная. Поэтому число подобрано так,
   чтобы колонка не могла оказаться уже пункта ни при какой ширине окна:
   у многоколоночной раскладки used column-width НИКОГДА не меньше
   заданной, значит 200px — жёсткий пол. Самый длинный пункт при предельном
   кегле 19.5px: «Hidas palautuminen» 172.1px, «Restricted movement»
   182.7px (замер на Segoe UI) — запас 9.5% на чужие метрики.
   Не уменьшать: при 11rem на 1920px колонка выходила 185px против
   183px английского пункта, то есть перенос ловился бы на любой машине
   с чуть более широким гротеском.

   Кегль поднят множителем от --body, а не выписан числом: связь с общим
   токеном должна оставаться явной. */
.kenelle__signs {
  margin: 0;
  padding: 0;
  list-style: none;
  columns: 12.5rem;
  column-gap: 2.5rem;
  column-rule: 1px solid rgb(var(--gold-rgb) / .18);
  color: var(--ink-body);
  font-size: calc(var(--body) * 1.15);
  line-height: 1.5;
}

/* Никакого text-transform: заглавные стоят в самом тексте <li>.
   Список — главный поисковый вход в блок, люди ищут по симптомам,
   и текст должен уходить в индекс ровно в том виде, в каком написан. */
.kenelle__signs li {
  margin-bottom: .9rem;
  break-inside: avoid;   /* иначе многоколоночная раскладка рвёт пункт */
}

.kenelle__tail {
  max-width: 34rem;
  margin-top: 5rem;
}

.kenelle__text {
  margin: 0 0 1.35rem;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.72;
}

.kenelle__text:last-child { margin-bottom: 0; }

/* После базовых правил: специфичность та же, при обратном порядке
   одноколоночная раскладка перебила бы двухколоночную.

   .75fr 1.25fr, а не .85fr 1.15fr как у .koulutus, и разница вынужденная.
   `.kenelle__inner` при 64rem всё равно теряет 12vw на паддинги, поэтому
   на широком экране правая колонка не растёт, а СЖИМАЕТСЯ: при 1920px
   пропорции .koulutus дают ей 410px, а двум колонкам по 200px с зазором
   нужно 440px — линия между колонками пропала бы ровно на самом
   распространённом десктопе. При .75/1.25 правой достаётся 446px,
   и две колонки держатся на всём диапазоне ~890–2140px. Левой при этом
   остаётся 296px против 336px: надстрочник, заголовок в две-три строки
   и абзац в неё укладываются.

   За пределами диапазона список сам становится одноколоночным — это
   штатное поведение, а не поломка: линия исчезает вместе со второй
   колонкой. */
@media (min-width: 880px) {
  .kenelle__grid {
    display: grid;
    grid-template-columns: .75fr 1.25fr;
    gap: 5rem;
    align-items: start;
  }

  .kenelle__aside { margin-top: 0; }
}

/* --------------------------------------------------------------------------
   Miksi Ventell
   -------------------------------------------------------------------------- */

.miksi {
  background: var(--shadow);
  padding: 6vh 6vw 14vh;   /* верх срезан, см. .minusta выше */
}

.miksi__inner {
  max-width: 64rem;
  margin: 0 auto;
}

.miksi__grid {
  margin-top: 3.5rem;
  display: grid;
  gap: 3.5rem;
}

/* Линия непрозрачная: у линейки цитаты 0.4 нужна была, чтобы не спорить
   с текстом рядом, здесь линия одна и мелкая. */
.reason::before {
  content: '';
  display: block;
  width: 32px;
  height: 1px;
  background: var(--gold);
  margin-bottom: 1.25rem;
}

.reason h3 {
  margin: 0;
  max-width: 22rem;
  color: var(--ink);
  font-family: var(--serif);
  font-weight: 400;
  font-size: calc(var(--body) * 1.3);
  line-height: 1.34;
}

/* --------------------------------------------------------------------------
   Отзывы
   Горизонтальная полоса с карточками + ряд миниатюр-оригиналов под ней.

   Смещение карточек разделено на два слоя, и это не украшательство:
   базовый «летающий» вид (--ty, --rot) держит CSS, параллакс из JS пишет
   только --py и --ps. Собери transform в JS целиком — инлайновый стиль
   затёр бы nth-child, и при выключенном JS или reduced-motion карточки
   встали бы ровным рядом.
   -------------------------------------------------------------------------- */

/* Без @property невалидная подстановка убила бы весь transform, а не одну
   свою часть: --py и --ps участвуют в calc() и в scale(). initial-value
   заодно даёт корректный первый кадр до того, как JS что-то записал. */
@property --py {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

@property --ps {
  syntax: '<number>';
  inherits: false;
  initial-value: 1;
}

.arviot {
  background: var(--shadow);
  padding: 12vh 0;   /* по горизонтали ноль: полоса идёт от края до края */
}

.arviot__inner {
  max-width: 64rem;
  margin: 0 auto;
  padding: 0 6vw;
}

/* position: relative — чтобы offsetParent карточек была именно полоса:
   JS считает центры через offsetLeft и сравнивает их со scrollLeft, а это
   одна и та же система координат только при позиционированном предке.

   padding-block 4.5rem — не отбивка. overflow-x: auto вычисляет overflow-y
   в auto, а не в visible: без запаса поднятые карточки обрезались бы, и
   внутри полосы появился бы вертикальный скролл. Худший случай — 28px
   базы + 30px параллакса + ~5px от поворота, 72px закрывают.

   touch-action НЕ задаём: pan-x убил бы вертикальную прокрутку страницы
   пальцем по карточкам. */
.arviot__scroller {
  position: relative;
  overflow-x: auto;
  padding-block: 4.5rem;
  /* иначе свайп влево у края уводит назад по истории */
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: calc((100% - min(400px, 74vw)) / 2);
  scrollbar-width: none;
}

.arviot__scroller::-webkit-scrollbar { display: none; }

.arviot__scroller:focus-visible {
  outline: 1px solid var(--gold);
  outline-offset: 4px;
}

/* width: max-content обязателен. У блочного флекс-контейнера ширина равна
   100% полосы, карточки просто вылезают вправо — и конечный padding-inline
   оказывается не после последней карточки, а на 100%-й отметке. Тогда
   последняя карточка не встаёт по центру: доехать до неё нечем. */
.arviot__list {
  display: flex;
  width: max-content;
  margin: 0;
  padding-inline: calc((100% - min(400px, 74vw)) / 2);
  gap: 2rem;
  list-style: none;
}

/* На 360px карточка занимает 266px, по бокам остаётся по 46.8px — при
   зазоре 2rem соседняя выглядывала бы на 7.6px, то есть никак. 1.5rem
   поднимают просвет до 22.8px. Порог 719px тот же, что у --narrow
   в scene.css: при его правке чинить оба места. */
@media (max-width: 719px) {
  .arviot__list { gap: 1.5rem; }
}

.arvio {
  --ty: 0px;
  --rot: 0deg;

  flex: 0 0 auto;
  width: min(400px, 74vw);
  scroll-snap-align: center;
  padding: 2.5rem 2rem;
  background: rgb(var(--gold-rgb) / .04);
  border: 1px solid rgb(var(--gold-rgb) / .16);
  border-radius: 2px;
  transform: translateY(calc(var(--ty) + var(--py))) rotate(var(--rot)) scale(var(--ps));
}

/* Поворот и масштаб идут от центра, поэтому позиции привязки к прокрутке
   не смещаются: снап остаётся ровно там же, где был бы без наклона. */
.arvio:nth-child(1) { --ty:   0px; --rot: -1.2deg; }
.arvio:nth-child(2) { --ty: -28px; --rot:   .8deg; }
.arvio:nth-child(3) { --ty:  14px; --rot:  -.5deg; }
.arvio:nth-child(4) { --ty: -18px; --rot:  1.4deg; }
.arvio:nth-child(5) { --ty:   6px; --rot:   -1deg; }
.arvio:nth-child(6) { --ty: -10px; --rot:   .6deg; }

.arvio blockquote { margin: 0; }

/* Правило на голый p — но других абзацев внутри цитаты не бывает.
   Появится классованный — дописывать :not(), см. CLAUDE.md. */
.arvio blockquote p {
  margin: 0;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.72;
}

/* Кавычки те же, что у .quote--marked, но своим правилом: тот класс тянет
   за собой линейку слева и кегль --quote, а здесь рамка уже есть и два
   приёма конфликтуют. Открывающая висит в поле padding, иначе первая
   строка уехала бы вправо на полкегля. */
.arvio blockquote p::before {
  content: "\201D";
  margin-left: -.44em;
}

.arvio blockquote p::after {
  content: "\201D";
}

.arvio cite {
  display: block;
  margin-top: 1.5rem;
  color: var(--gold);
  font-size: .72rem;
  font-style: normal;
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* --- оригиналы ------------------------------------------------------------
   .5rem, а не 5rem: 4.5rem запаса под карточки уже стоят в padding-block
   полосы, вместе выходит ровно 5rem от нижнего края карточек.
   Горизонтальных полей у блока нет: полоса миниатюр идёт от края до края,
   отступ держит надстрочник и padding-inline самого ряда. */
.arviot__originals {
  margin: .5rem 0 0;
  text-align: center;
}

.arviot__originals .eyebrow { padding: 0 6vw; }

/* Вторая полоса прокрутки на странице — служебная, без параллакса.
   padding-block .75rem — запас под аутлайн фокуса: overflow-x: auto
   вычисляет overflow-y в auto, и обводка с offset 4px иначе обрезалась бы. */
.arviot__shots-scroller {
  overflow-x: auto;
  padding-block: .75rem;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 6vw;
  scrollbar-width: none;
}

.arviot__shots-scroller::-webkit-scrollbar { display: none; }

.arviot__shots-scroller:focus-visible {
  outline: 1px solid var(--gold);
  outline-offset: 4px;
}

/* min-width: max-content, а не width: у карточек выше ряд заведомо шире
   экрана, здесь все шесть миниатюр помещаются на десктопе. При width
   пришлось бы городить пустые поля по краям и прокрутка появлялась бы
   на пустом месте. С min-width ряд равен 100% пока помещается — и тогда
   работает justify-content: center, — и растёт до max-content, когда нет;
   padding-inline при этом попадает в область прокрутки с обеих сторон. */
.arviot__shots {
  display: flex;
  justify-content: center;
  min-width: max-content;
  gap: 1rem;
  margin: 0;
  padding-inline: 6vw;
  list-style: none;
}

/* Флекс-элемент — <li>, а не кнопка внутри: снап и запрет на сжатие
   должны стоять на нём, иначе при переполнении миниатюры сожмутся. */
.arviot__shots > li {
  flex: 0 0 auto;
  scroll-snap-align: center;
}

.arviot__shot {
  display: block;
  width: 150px;
  padding: 0;
  border: 1px solid rgb(var(--gold-rgb) / .16);
  border-radius: 2px;
  background: none;
  line-height: 0;
  cursor: pointer;
}

.arviot__shot:focus-visible {
  outline: 1px solid var(--gold);
  outline-offset: 4px;
}

/* Прозрачность на картинке, а не на кнопке: на кнопке она приглушила бы
   заодно и рамку. */
.arviot__shot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  /* left, а не только top: у широких снимков (до 2.20) cover режет по
     горизонтали, и центрированный кадр показывал бы среднюю полосу текста
     вместо начала строк. По вертикали крой всё так же идёт от верха. */
  object-position: left top;
  opacity: .55;
  transition: opacity .35s ease;
}

.arviot__shot:hover img,
.arviot__shot:focus-visible img { opacity: 1; }

/* --- лайтбокс -------------------------------------------------------------
   Диалог растянут во весь экран и прозрачен, фон рисует ::backdrop. Так
   клик мимо картинки приходит на сам <dialog> — по этому и закрываем.
   display задаётся ТОЛЬКО под [open]: без этого правило перебило бы
   display:none, которым браузер прячет закрытый диалог. */
.lightbox {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  overflow: hidden;
}

.lightbox[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Подложка диалога — на --veil-rgb, а не на своих числах. Раньше здесь
   стоял литерал rgb(5 20 15), не совпадавший ни с одним цветом палитры;
   при альфе .92 разница с вуалью составляла 1–2 из 255, то есть её не
   существовало на глаз, зато цвет жил отдельной жизнью от токенов. */
.lightbox::backdrop { background: rgb(var(--veil-rgb) / .92); }

/* max-width обязателен рядом с max-height: у снимков соотношение сторон
   гуляет от 0.78 до 2.20, и широкий кадр при 88svh вылез бы за экран. */
.lightbox__img {
  display: block;
  width: auto;
  height: auto;
  max-width: min(92vw, 900px);
  max-height: 88svh;
}

.lightbox__close {
  position: absolute;
  top: .5rem;
  right: .5rem;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font-family: var(--serif);
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox__close:focus-visible {
  outline: 1px solid var(--gold);
  outline-offset: 2px;
}

/* Селектор удвоен намеренно: у .arvio:nth-child() специфичность (0,2,0),
   и одиночный .arvio отсюда проиграл бы базовым смещениям.

   !important у --py и --ps — ровно из-за того, что их пишет JS в инлайн:
   при загрузке с включённой настройкой параллакс не стартует и они и так
   нулевые, но если настройку включить ПОСРЕДИ сессии, инлайновые значения
   застыли бы на последних. Проверено: без !important --py оставался 10.23px. */
@media (prefers-reduced-motion: reduce) {
  .arviot__list .arvio {
    --ty: 0px;
    --rot: 0deg;
    --py: 0px !important;
    --ps: 1 !important;
  }

  .arviot__scroller { scroll-snap-type: none; }

  .arviot__shot img { transition: none; }
}

/* --------------------------------------------------------------------------
   Yrityksille
   Только контур, без заливки: блок отделяется от соседей рамкой, а не фоном.
   -------------------------------------------------------------------------- */

.yrityksille {
  background: var(--shadow);
  padding: 12vh 6vw;
}

.yrityksille__box {
  max-width: 52rem;
  margin: 0 auto;
  padding: 3rem 1.75rem;
  border: 1px solid rgb(var(--gold-rgb) / .22);
  border-radius: 2px;
}

/* Дочерний комбинатор `>` вместо потомка: правило бьёт только по абзацам
   верхнего уровня и не дотягивается внутрь формы. Иначе каждый <p> внутри
   формы пришлось бы добавлять в цепочку :not() — та самая ловушка
   «блок p перебивает класс на абзаце» из CLAUDE.md.
   :not(...) всё равно нужен для .eyebrow и .yrityksille__phone: они тоже
   прямые дети. */
.yrityksille__box > p:not(.eyebrow):not(.yrityksille__phone) {
  margin: 0 0 1.35rem;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.72;
}

/* Абзац перед кнопкой не добавляет свой отступ к её margin-top:
   margin-bottom и margin-top не схлопываются через inline-block.
   Цепочка :not() повторена, чтобы специфичность была выше правила выше. */
.yrityksille__box > p:not(.eyebrow):not(.yrityksille__phone):has(+ .cta) {
  margin-bottom: 0;
}

.yrityksille__box .cta { margin-top: 3rem; }

/* --------------------------------------------------------------------------
   Sijainti ja yhteystiedot — точка конверсии
   -------------------------------------------------------------------------- */

.yhteys {
  background: var(--shadow);
  padding: 14vh 6vw 10vh;
}

.yhteys__inner {
  max-width: 64rem;
  margin: 0 auto;
}

.yhteys__grid {
  margin-top: 3.5rem;
  display: grid;
  gap: 3rem;
}

.yhteys h3 {
  margin: 0 0 1.25rem;
  color: var(--ink);
  font-family: var(--serif);
  font-weight: 400;
  font-size: calc(var(--body) * 1.3);
  line-height: 1.3;
}

/* :not(.channels) обязателен — см. правило про абзацы с классом в CLAUDE.md:
   селектор блок + p имеет специфичность (0,1,1) и молча перебивает любой
   класс на самом абзаце. */
.yhteys__col p:not(.channels) {
  margin: 0 0 1.35rem;
  color: var(--ink-body);
  font-size: var(--body);
  line-height: 1.72;
}

.yhteys__col p:not(.channels):last-of-type { margin-bottom: 0; }

.contacts {
  font-style: normal;   /* <address> по умолчанию курсивный */
  margin: 0;
}

.contacts dl { margin: 0; }

.contact { margin-bottom: 1.6rem; }

.contact dt {
  margin-bottom: .45rem;
  color: var(--gold);
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
}

.contact dd {
  margin: 0;
  font-size: calc(var(--body) * 1.15);
}

.contact a {
  color: var(--ink);
  text-decoration: none;
}

.contact a:hover,
.contact a:focus-visible {
  text-decoration: underline;
  text-underline-offset: .2em;
}

.yhteys__col .cta { margin-top: 3rem; }

/* Запасные каналы под главной кнопкой.
   Скрыты, пока JS не проставил адреса через data-mirror: ссылка без href
   не ссылка, а «Soita» простым текстом только сбивает с толку.
   Главный путь (кнопка WhatsApp) и контакты в <dl> работают и без JS. */
.channels {
  display: none;
  margin: 1.25rem 0 0;
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
}

.channels.is-live { display: block; }

/* Зона нажатия — псевдоэлементом, приём тот же, что у .langs__item.
   Замерено до правки: «Soita» рисуется 39.8×15.3, то есть ниже минимума
   24×24 по высоте. По горизонтали растягиваем осторожно: между двумя
   ссылками стоит разделитель с полями .6em (≈6.9px с каждой стороны при
   кегле 11.52px), и при -6px зоны почти сомкнулись бы. -4px оставляют
   между ними просвет, замер после правки — в _work/CLAUDE-perustelut.md. */
.channels__link {
  position: relative;
  color: var(--gold);
  text-decoration: none;
}

.channels__link::after {
  content: '';
  position: absolute;
  inset: -6px -4px;
}

.channels__link:hover,
.channels__link:focus-visible {
  text-decoration: underline;
  text-underline-offset: .25em;
}

.channels__sep {
  color: var(--ink-body);
  margin: 0 .6em;
}

/* Через .yrityksille__box — иначе правило абзацев выше перебьёт */
.yrityksille__box .yrityksille__phone {
  margin: 1.25rem 0 0;
  color: var(--ink-body);
  font-size: .82rem;
  line-height: 1.6;
}

/* --------------------------------------------------------------------------
   Форма запроса предложения
   Живёт внутри раскрытия .more, поэтому в закрытом виде её поля не в Tab
   (за это отвечает inert) и не печатаются (за это — @media print у .more).
   -------------------------------------------------------------------------- */

/* .cta задуман для <a>; на <button> нужно снять фон и наследовать шрифт */
button.cta--quiet,
button.cta {
  background: none;
  font-family: inherit;
  cursor: pointer;
}

.form {
  display: grid;
  gap: 1.6rem 2rem;
  margin-top: 2.5rem;
}

.form__field--wide { grid-column: 1 / -1; }

.form__field label {
  display: block;
  margin-bottom: .4rem;
  color: var(--gold);
  font-size: .72rem;          /* тот же кегль, что у меток контактов */
  letter-spacing: .16em;
  text-transform: uppercase;
}

.form__req { color: var(--gold); }

/* Ни рамок сверху и по бокам, ни скруглений, ни заливки — только линия снизу.

   Альфа .55, а не .3: линия — единственная видимая граница поля ввода,
   то есть попадает под WCAG 1.4.11 с порогом 3:1. При .3 контраст к фону
   был 1.89:1. Замерено в браузере: .5 даёт 3.02:1 (впритык), .55 — 3.37:1. */
.form__field input,
.form__field textarea {
  width: 100%;
  padding: .5rem 0;
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgb(var(--gold-rgb) / .55);
  border-radius: 0;
  color: var(--ink);
  font-family: inherit;
  font-size: var(--body);
  line-height: 1.5;
}

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

.form__field input:focus-visible,
.form__field textarea:focus-visible {
  border-bottom-color: var(--gold);
  outline: 1px solid var(--gold);
  outline-offset: 2px;
}

.form__consent {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
}

.form__consent input {
  flex: 0 0 auto;
  margin-top: .15em;
  width: 1rem;
  height: 1rem;
  accent-color: var(--gold);
}

.form__consent label {
  color: var(--ink-body);
  font-size: .82rem;
  line-height: 1.6;
}

.form__consent a {
  color: var(--gold);
  text-decoration: underline;
  text-underline-offset: .2em;
}

.form__note {
  margin: 0;
  color: var(--ink-body);
  font-size: .74rem;
  letter-spacing: .04em;
}

.form__status {
  margin: 1.5rem 0 0;
  color: var(--ink);
  font-size: var(--body);
  line-height: 1.6;
}

.form__status:empty { margin: 0; }

.form__status.is-error { color: var(--gold); }

@media (min-width: 760px) {
  .form { grid-template-columns: 1fr 1fr; }
}

.yrityksille__phone a {
  color: var(--ink-body);
  text-decoration: none;
  white-space: nowrap;
}

.yrityksille__phone a:hover,
.yrityksille__phone a:focus-visible {
  color: var(--gold);
  text-decoration: underline;
  text-underline-offset: .2em;
}

/* --------------------------------------------------------------------------
   Кнопка: единственная залитая — модификатор к базовому .cta из base.css
   (.cta--quiet живёт там же, он нужен и странице политики).
   content.css подключён позже, поэтому переопределение работает.
   -------------------------------------------------------------------------- */

/* Единственная залитая кнопка на странице */
.cta--solid {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--shadow);
}

.cta--solid:hover,
.cta--solid:focus-visible {
  background: var(--gold-hover);
  border-color: var(--gold-hover);
  color: var(--shadow);
}

/* --------------------------------------------------------------------------
   Медиазапросы новых блоков — после базовых правил
   -------------------------------------------------------------------------- */

@media (min-width: 760px) {
  .miksi__grid {
    grid-template-columns: 1fr 1fr;
    column-gap: 4rem;
    row-gap: 3.5rem;
  }

  .yrityksille__box { padding: 5rem 4rem; }
}

@media (min-width: 880px) {
  .yhteys__grid {
    grid-template-columns: 1fr 1fr;
    gap: 5rem;
  }
}
