/*
 * Кирпич · списки.
 *
 * Основной блок: почти всё содержимое страницы — это список. Класс стоит
 * только на контейнере, внутри голые теги.
 *
 * Строка — прямой ребёнок контейнера: div, если это просто блок, или a,
 * если строка ведёт куда-то. Внутри по порядку:
 *   strong — термин или подзаголовок строки (необязателен),
 *   p — текст, абзацев может быть несколько.
 *
 * Селектор берёт любого прямого ребёнка, поэтому тег выбирается по смыслу,
 * а не диктуется стилями.
 */
.rows {
    /*
     * Отбивка снизу: список отделяется от того, что идёт следом. Поле
     * держит он сам, а не сосед, поэтому за списком можно поставить что
     * угодно и оно не прилипнет.
     */
    margin: 0 0 var(--gap);
    padding: 0;
}

/*
 * Разделитель рисуется фоном строки, а не бордером: у первой фон
 * снимается, поэтому гасить линию у последней не нужно и край списка
 * всегда чистый.
 */
.rows > :not(.rows) {
    display: block;
    padding: var(--step) 0;
    background: var(--separator-line) 0 0 / 100% var(--hairline) no-repeat;
    color: inherit;
    font-size: var(--text);
    line-height: var(--line);
    text-decoration: none;
}

/*
 * Подчёркивания нет и под указателем: строка это ссылка целиком, и линия
 * прочертила бы её во всю ширину.
 */
.rows > a:hover {
    text-decoration: none;
}

.rows > :first-child {
    background: none;
}

/*
 * Термин строки: та же строка, только жирная.
 *
 * Блок, а не инлайн: под термином обычно идёт подпись, и в потоке между
 * ними вставал бы межстрочный интервал. Внутри флекса этого не видно, а
 * в обычной строке подпись отодвигалась на десяток пикселей.
 */
.rows strong {
    display: block;
    font-weight: 700;
}

/*
 * Название без выделения весом: каталог, прайс, перечень, где строк много
 * и подряд идущий жирный превращается в частокол.
 *
 * Остаётся термином, а не становится абзацем: смысл тот же, меняется одна
 * краска. Тег здесь ещё и держит вёрстку — у абзаца снизу стоит просвет
 * между абзацами, и название отъезжало бы от своего описания на строку.
 */
.rows strong.plain {
    font-weight: 400;
}

/*
 * Приписка строки: дата над названием и подробности под ним.
 *
 * Мелкая приглушённая строка описана в base.css, здесь остаётся только
 * отступ — у даты снизу, потому что она стоит перед названием, у подписи
 * сверху, потому что идёт следом.
 *
 * Теги разные, потому что означают разное: time — когда это было, и
 * машина разбирает дату из атрибута datetime; small — подпись к строке,
 * телефон или режим работы.
 *
 *   .rows > a > time + strong + p
 *   .rows > a > strong + small
 */
.rows time {
    padding-bottom: 2px;
}

.rows small {
    padding-top: 2px;
}

/*
 * Ряд коротких значений в подписи: телефоны и почта подразделения. Идут
 * в строку и переносятся, если не помещаются.
 *
 * Отдельный класс нужен, потому что без него ссылки слипаются: между
 * инлайновыми элементами в разметке нет пробела, и «(495) 181-73-99» и
 * «info@» читаются как одно слово.
 *
 * Имя составное, а не короткое .links: короткие слова в проекте уже
 * заняты, и класс молча получил бы чужие отступы.
 */
.rows small.row-line {
    display: flex;
    flex-flow: row wrap;
    gap: 2px var(--icon-space);
}

/*
 * Ссылка в подписи красится акцентом: строка целиком гасит цвет ссылок,
 * иначе телефон внутри неё выглядел бы обычным текстом и не читался
 * как действие.
 */
.rows small a {
    color: var(--color-accent);
    text-decoration: none;
}

/*
 * Абзац текста. Своих полей у него нет: расстояние до края строки держит
 * поле самой строки, и всё, что абзац добавит снизу, станет к нему
 * прибавкой — строка снизу окажется просторнее, чем сверху.
 */
.rows p {
    margin: 0;
}

/*
 * Приглушённый абзац: второстепенный текст, который не должен спорить с
 * названием строки.
 *
 * Отдельный класс, а не small: small уменьшает кегль, а тут нужен
 * обычный текст, только тише по цвету. Размер и строка остаются
 * абзацными, меняется одна краска.
 */
.rows p.grey {
    color: var(--color-muted);
}

/*
 * Картинка в начале строки: значок, логотип, обложка. Лежит в теге i
 * первым ребёнком строки, внутри img.
 *
 *   .rows > div > i > img + Название
 *
 * Строка при этом становится флексом, поэтому текст встаёт рядом с
 * картинкой, а не под ней. Квадрат фиксированный: строки выравниваются
 * по одной линии независимо от пропорций картинки.
 */
.rows > * > i:first-child {
    display: flex;
    overflow: hidden;
    width: var(--icon-size);
    height: var(--icon-size);
    flex: none;
    margin-right: var(--icon-space);
    align-items: center;
    justify-content: center;
    border-radius: var(--icon-radius);
    background: var(--color-surface);
}

/*
 * Внутри может лежать и картинка, и готовый svg: иконки набором отдают
 * второе, и подменять их на img ради стилей неправильно.
 *
 * Свои размеры svg при этом снимаются: набор проставляет их атрибутами,
 * и значок вылезал бы за квадрат.
 */
.rows > * > i:first-child > img,
.rows > * > i:first-child > svg {
    display: block;
    width: auto;
    height: auto;
    max-width: 60%;
    max-height: 60%;
}

/*
 * Строка с картинкой встаёт в ряд, содержимое выравнивается по центру.
 *
 * Растягивается название: flex: 1 на нём, и всё, что идёт следом —
 * значение, срок, статус — само оказывается у правого края. Разводить
 * их space-between не нужно, иначе значение встало бы в середину.
 */
.rows > *:has(> i:first-child) {
    display: flex;
    align-items: center;
}

.rows > *:has(> i:first-child) > span,
.rows > *:has(> i:first-child) > div {
    min-width: 0;
    flex: 1;
}

/* Значение после названия к краю: оно не растягивается. */
.rows > *:has(> i:first-child) > span + span,
.rows > *:has(> i:first-child) > div + span {
    flex: none;
}

/*
 * Список-переход: у каждой строки справа шеврон. Ставится на весь список,
 * потому что вперемешку со строками без шеврона он читается как ошибка.
 *
 * Шеврон рисуется фоном на псевдоэлементе, а не значком в разметке: он
 * служебный, содержанием строки не является и в текст попадать не должен.
 *
 * Размеры внутренние, переменных им не заведено: они держат сам значок,
 * а не ритм страницы, и снаружи их крутить незачем. Поле справа шире
 * шеврона, чтобы текст не подходил к нему вплотную.
 *
 * Цвет зашит в сам svg: фоновая картинка не наследует currentColor, а
 * подставлять переменную внутрь data-url нечем. Шеврон служебный и от
 * палитры сайта не зависит, поэтому это устраивает.
 */
.rows.arrow > :not(.rows) {
    position: relative;
    padding-right: 26px;
}

.rows.arrow > :not(.rows)::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 14px;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 24'%3E%3Cpath d='M0,22.15a1.92,1.92,0,0,1,.58-1.36l9-8.78-9-8.8A1.92,1.92,0,0,1,0,1.85,1.85,1.85,0,0,1,3.15.53l10.17,10A1.93,1.93,0,0,1,14,12a2.09,2.09,0,0,1-.68,1.49l-10.17,10A1.8,1.8,0,0,1,1.86,24,1.85,1.85,0,0,1,0,22.15Z' style='fill:%23c4c4c8'/%3E%3C/svg%3E")
        right center / 7px 12px no-repeat;
}

/*
 * Строка из двух частей: название слева, значение справа. Разводит их
 * space-between, поэтому значение прижимается к правому краю само,
 * без ширины и отступов.
 *
 * Класс ставится либо на весь список, либо на отдельную строку:
 *
 *   .rows.two > div > Цена + span
 *   .rows > div.two > Итого + span
 *
 * Длинное название переносится на свою строку, а не ломает раскладку.
 */
.rows.two > *,
.rows > .two {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
}

/* Значение не разрывается посередине: «2 621 ₽» остаётся одной строкой. */
.rows.two > * > span,
.rows > .two > span {
    white-space: nowrap;
}

/*
 * Строка с описанием и значением справа: название, под ним текст, сбоку
 * цена или срок. Каталог услуг, прайс, перечень с ценами.
 *
 *   .rows.aside > a > strong + p + span
 *
 * Грид, а не флекс: у .two всё содержимое лежит в одном ряду, и значение
 * встаёт в строку с названием. Как только под названием появляется абзац,
 * ряд ломается — значение уезжает под текст или растягивает строку. Здесь
 * колонки заданы явно, поэтому описание занимает левую, а значение стоит
 * в правой независимо от того, сколько строк заняло описание.
 *
 * Значение прижато к верху: оно относится к названию, а не к концу абзаца.
 *
 * Правая колонка берёт по тексту, но не шире половины строки. Потолок
 * стоит на самой колонке: без него «от 45 000 ₽ за один объект» на экране
 * телефона занимал двести пикселей из трёхсот, выдавливал строку за край,
 * и вбок ехала вся страница.
 *
 * Держать потолок max-width на самом значении нельзя, хотя соблазнительно:
 * процент от строки заставляет грид мерить ряд по самому узкому переносу,
 * где «от 12 000 ₽» ломается на четыре строки. Ряд раздувался до высоты
 * этого переноса, и между названием и описанием повисал лишний просвет,
 * хотя значение стояло в одну строку. Ограничение на колонке считается до
 * раскладки содержимого и такого следа не оставляет.
 */
.rows.aside > * {
    display: grid;
    grid-template-columns: 1fr minmax(0, max-content);
    align-items: start;
    column-gap: var(--icon-space);
}

/*
 * Всё, кроме значения, остаётся в левой колонке: строк там сколько угодно,
 * поэтому колонка задаётся явно, а не порядком элементов.
 */
.rows.aside > * > :not(span) {
    grid-column: 1;
}

/*
 * Значение: правая колонка, во всю высоту строки. Тег span выбран потому,
 * что strong, p и small внутри строки уже заняты смыслом, а значение —
 * это просто значение.
 *
 * Кегль подписи, а не текста: цена рядом с названием не должна спорить
 * с ним весом. Цвет обычный — приглушать значение незачем, за этим есть
 * .grey на самом span.
 *
 * Переносится по словам, в отличие от .two: там значение всегда короткое,
 * а здесь это ещё и срок или условие — «от 45 000 ₽ за один объект».
 * Ломается оно только когда упирается в потолок колонки, то есть на
 * узком экране.
 *
 * Число внутри разрывать нечем: пробелы в «45 000 ₽» пишутся в разметке
 * неразрывными, иначе на телефоне цена расходится на две строки, а вслед
 * за ней растёт и ряд с названием.
 */
.rows.aside > * > span {
    grid-column: 2;
    grid-row: 1 / -1;
    font-size: var(--text-small);
    line-height: var(--line-small);
    text-align: right;
}

/* Приглушённое значение: «По запросу» вместо цены. */
.rows.aside > * > span.grey {
    color: var(--color-muted);
}

/*
 * Список-переход со значением: шеврон и цена одновременно. Поле справа
 * держит шеврон, поэтому значение к нему не подходит вплотную.
 */
.rows.aside.arrow > * {
    padding-right: 26px;
}

/*
 * Вложенный список: подраздел под своим разделом. Карта сайта, структура
 * учреждения, дерево документов.
 *
 *     .rows
 *         div Раздел
 *         .rows
 *             div Подраздел
 *
 * Вложенный список лежит прямым ребёнком внешнего, а не внутри строки:
 * он продолжает перечень, а не является частью пункта. Строка со своим
 * названием стоит перед ним соседом.
 *
 * Отступ рисуется полем самих строк, а не полем списка. Разница видна на
 * разделителе: линия рисуется фоном строки, и поле на списке сжимало бы
 * её вместе с текстом. Список из четырёх уровней превращался в лесенку
 * обрубков разной длины, каждый следующий короче предыдущего. Линия
 * отделяет строку от строки и потому обязана идти во всю ширину списка,
 * независимо от того, насколько глубоко лежит сама строка.
 *
 * Уровни перечислены поштучно, и это вынужденно. Копить глубину
 * наследуемой переменной нельзя: её пришлось бы считать от себя самой,
 * прибавляя ступень, а такую ссылку браузер считает замкнутой и
 * отбрасывает вместе с отступом. Поле на строке тоже не складывается:
 * строка третьего уровня — прямой ребёнок только своего списка и о
 * внешних не знает.
 *
 * Поэтому селектор на каждый уровень свой. Четырёх хватает: дерево
 * глубже читается хуже любого отступа, и упереться в потолок здесь
 * полезнее, чем незаметно потерять ступень.
 */
.rows .rows > :not(.rows) {
    padding-left: var(--nest-space);
}

.rows .rows .rows > :not(.rows) {
    padding-left: calc(var(--nest-space) * 2);
}

.rows .rows .rows .rows > :not(.rows) {
    padding-left: calc(var(--nest-space) * 3);
}

/*
 * Первая строка вложенного списка линию не гасит, в отличие от первой
 * строки внешнего: она отделяет подраздел от его собственного названия,
 * которое стоит выше соседом.
 */
.rows .rows > :first-child {
    background: var(--separator-line) 0 0 / 100% var(--hairline) no-repeat;
}

/*
 * Вложенные строки мельче: так уровень читается даже там, где отступ
 * съеден узким экраном, а на широком подчёркивает подчинённость.
 *
 * Кегль задан строкам, а не самому списку: внутри строки лежат приписки
 * со своим размером, и наследование сбило бы их вместе с названием.
 */
.rows .rows > :not(.rows) {
    font-size: var(--text-small);
    line-height: var(--line-small);
}

/*
 * На узком экране поле уменьшается: три уровня по 20px съедают половину
 * строки телефона, и названия начинают переноситься по одному слову.
 */
@media (max-width: 480px) {
    .rows .rows > :not(.rows) {
        padding-left: calc(var(--nest-space) / 2);
    }

    .rows .rows .rows > :not(.rows) {
        padding-left: var(--nest-space);
    }

    .rows .rows .rows .rows > :not(.rows) {
        padding-left: calc(var(--nest-space) / 2 * 3);
    }
}

/*
 * Строка, которая раскрывается: частые вопросы, условия, состав услуги.
 * Отдельного блока не заводится — это тот же список, только строка в нём
 * взята тегом details:
 *
 *     .rows
 *         details
 *             summary Нужна ли справка о доходах?
 *             p Ответ.
 *
 * Собрано на details, поэтому раскрытие работает без единой строки
 * поведения, а закрытый ответ не попадает в поиск по странице. Тот же
 * приём стоит в меню под «Ещё», правила у них разные только потому, что
 * там список ложится поверх страницы, а здесь раздвигает её.
 *
 * Строкой details становится сам, наравне с div и a: селектор списка
 * берёт любого прямого ребёнка.
 */
.rows > details > summary {
    cursor: pointer;
    /* Треугольник браузера убирается: свой значок рисуется ниже. */
    list-style: none;
    font-weight: 700;
}

.rows > details > summary::-webkit-details-marker {
    display: none;
}

/*
 * Галка справа, а не слева: слева стоит значок строки, и два знака рядом
 * читаются как один блок. Рисуется псевдоэлементом — она служебная и
 * содержанием строки не является.
 *
 * Позиционируется абсолютно, поэтому длинный вопрос переносится под неё,
 * а не обтекает.
 */
.rows > details > summary {
    position: relative;
    padding-right: 26px;
}

.rows > details > summary::after {
    content: '';
    position: absolute;
    top: 6px;
    right: 4px;
    width: 7px;
    height: 7px;
    border-right: 1.5px solid var(--color-muted);
    border-bottom: 1.5px solid var(--color-muted);
    transform: rotate(45deg);
}

.rows > details[open] > summary::after {
    top: 9px;
    transform: rotate(-135deg);
}

/*
 * Ответ отбивается от вопроса, но не от следующей строки: снизу поле
 * держит сама строка списка, и своё поле прибавлялось бы к нему.
 *
 * Поле снизу у вопроса, а не сверху у ответа: расстояние держит тот,
 * кто стоит выше. У закрытого details ответа нет, и поле вопроса тогда
 * не нужно, поэтому оно появляется только у раскрытого.
 */
.rows > details[open] > summary {
    margin-bottom: 6px;
}

/*
 * Список с картинками: миниатюра слева, текст справа.
 *
 * Так устроены каталоги товаров: по виду вещь узнают быстрее, чем по
 * названию. У записи без обложки картинка не рендерится вовсе — пустая
 * колонка в текстовом каталоге только отнимает место у содержания.
 */
/*
 * Флекс, а не грид: у карточки два блока, картинка и текст. Гридом
 * название и описание расходились бы по разным рядам, и между ними
 * вставал просвет высотой с картинку.
 */
.rows.cards > * {
    display: flex;
    /* По центру, а не по верху: у строки с двумя строками текста
       картинка иначе висит вверху и строка выглядит незакончённой. */
    align-items: center;
    gap: var(--gap-small, 12px);
}

/* Картинка своей ширины, текст забирает остаток. */
.rows.cards .rows__image {
    flex: 0 0 var(--card-image);
}

.rows.cards .rows__image {
    display: grid;
    overflow: hidden;
    aspect-ratio: 1;
    border-radius: var(--surface-radius);
    background: #fff;
    place-items: center;
}

.rows.cards .rows__image img {
    width: 100%;
    height: 100%;
    /* Вписывается целиком: у товаров разные пропорции, и обрезка съела
       бы им края. */
    object-fit: contain;
}

/* Текст занимает остаток строки, название и описание идут вплотную. */
.rows__text {
    min-width: 0;
    flex: 1;
}

.rows__text strong,
.rows__text p {
    display: block;
    margin: 0;
}

/*
 * Описание обрезается двумя строками: строки списка должны стоять
 * ровным рядом, а не разъезжаться по длине текста. Полное описание
 * читают на самой странице записи.
 */
.rows__text p {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}

/*
 * Плитка: те же карточки, но в несколько колонок.
 *
 * Картинка становится крупной и встаёт над текстом — в плитке решает
 * она, а описание читают уже после выбора.
 */
.rows.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--tile-width), 1fr));
    gap: var(--gap, 24px);
}

.rows.tiles > * {
    display: flex;
    flex-direction: column;
    /* Разделителя нет: в плитке строки не следуют друг за другом, и
       линия резала бы карточку пополам. */
    padding: 0;
    background: none;
    gap: 6px;
}

.rows.tiles .rows__image {
    display: grid;
    overflow: hidden;
    aspect-ratio: 1;
    border-radius: var(--surface-radius);
    background: #fff;
    place-items: center;
}

.rows.tiles .rows__image img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.lazy-image {
    background: var(--fill-secondary, #f5f5f7);
}

/*
 * Подпись плитки убирается настройкой: название и описание по
 * отдельности. На витрине работ решает картинка, и строка «CLIENT: …»
 * под каждой только дробит ряд.
 */
.rows.tiles .rows__text strong {
    display: var(--tile-title-display);
}

.rows.tiles .rows__text p {
    display: var(--tile-lead-display);
}



/*
 * Строка заголовка страницы: название слева, переключатель вида справа.
 */
.page-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    /* Отбивка снизу переезжает с заголовка на строку: у h1 её снимают
       ниже, иначе она прибавлялась бы к отступу самой строки. */
    margin-block-end: var(--page-title-space-under);
    gap: var(--gap-small, 12px);
}

/*
 * Заголовок забирает свободное место, а кнопки прижаты к правому краю.
 * Без этого заголовок сжимается по своему тексту, и переключатель
 * прилипает к нему посреди строки.
 */
.page-head h1 {
    flex: 1;
    margin-block-end: 0;
}

/*
 * Переключатель вида. Показывается скриптом: без него кнопки ничего не
 * делают, и место занимать им незачем.
 */
.view-switcher {
    display: flex;
    flex-shrink: 0;
    gap: 2px;
}

.view-switcher[hidden] {
    display: none;
}

.view-switcher button {
    display: grid;
    width: 36px;
    height: 36px;
    padding: 0;
    border: 0;
    border-radius: var(--surface-radius);
    background: none;
    color: var(--color-muted);
    cursor: pointer;
    place-items: center;
}

.view-switcher button:hover {
    background: var(--fill-secondary, #f5f5f7);
    color: var(--color-text);
}

/* Выбранный вид залит: так же показано нажатое состояние в админке. */
.view-switcher button[aria-pressed='true'] {
    background: var(--fill-secondary, #f5f5f7);
    color: var(--color-text);
}

.view-switcher svg {
    width: 22px;
    height: 22px;
}

/*
 * Витрина по категориям: несколько списков подряд, у каждого свой
 * заголовок. Показывается, пока посетитель не выбрал категорию.
 */
.showcase__group + .showcase__group {
    margin-block-start: var(--gap);
}

.showcase__group .section-title a {
    color: inherit;
    text-decoration: none;
}

.showcase__group .section-title a:hover {
    text-decoration: underline;
}
