/* Точечные переопределения кита (публичные классы .chip/.badge/.tag-input):
аперкейс кит ставит всем бейджам/чипам глобально, но теги должны читаться
в оригинальном регистре (имена тегов регистрозависимы по смыслу). Прочие
бейджи (статус/приоритет/проект/время) остаются как в ките. */
.tag-input .chip,
.tag-suggest .chip,
.tag-badge {
text-transform: none;
}
/* Спиннер обновления данных: под топбаром в правом углу, контент не сдвигает */
.refresh-loader {
position: fixed;
top: 66px;
right: 18px;
z-index: 500;
}
/* --- iOS Safari (типовые проблемы, 2026-09-23) --- */
/* Зум полей при фокусе: iOS Safari автоматически зумит страницу, если у поля
font-size < 16px, и зум застревает после blur (ломает весь layout). У кита
поля/селекты 15px (наследуют body), поле тегов 13px — на touch-устройствах
поднимаем до 16px; на десктопе вид не меняется. */
@media (hover: none) and (pointer: coarse) {
.form-group .label .input,
.form-group .label .select-wrap .select,
.tag-input .tag-input-field {
font-size: 16px;
}
}
/* 100vh в iOS Safari выше видимой области (учитывает свёрнутую панель) — низ
drawer'ов/модалок уезжает под панель браузера и индикатор «домой». dvh —
фактическая высота (Safari 15.4+); на старых Safari невалидное значение
игнорируется и остаётся китовский 100vh. */
.drawer .drawer-panel {
min-height: 100dvh;
}
.nav-drawer {
max-height: 100dvh;
padding-bottom: env(safe-area-inset-bottom);
}
.modal .modal-dialog {
max-height: calc(100dvh - 48px);
}
/* viewport-fit=cover: контент уходит под жест-индикатор «домой» и «чёлку».
Тосты прижаты к bottom:15px кита — поднимаем на safe-area; в браузерном
Safari inset снизу = 0, поведение не меняется. */
.toast.a-show {
bottom: calc(15px + env(safe-area-inset-bottom));
}
/* контент drawer'а не упирается в индикатор «домой» */
.drawer .drawer-body {
padding-bottom: calc(18px + env(safe-area-inset-bottom));
}
/* в PWA (standalone) верх страницы уходит под статус-бар: топбар отступает
(в браузере inset = 0) */
.nav-topbar {
padding-top: env(safe-area-inset-top);
}
/* спиннер обновления держится ниже топбара, который в PWA становится выше */
.refresh-loader {
top: calc(66px + env(safe-area-inset-top));
}
/* Серая тап-подсветка WebKit поверх кнопок/чипов/ссылок — не в стиле кита.
Активное состояние у кита есть (media hover:none) — подсветка не нужна. */
a,
button,
.chip,
.badge,
.tag-input-wrap {
-webkit-tap-highlight-color: transparent;
}
/* iOS иногда раздувает текст при повороте в landscape — запрещаем */
html {
-webkit-text-size-adjust: 100%;
}
/* --- Скругление полос прокрутки (0.76, пересмотрено 0.79) --- */
/* Толщину и цвета оставляем китовыми: 10px, дорожка #16161e, бегунок #414868,
кнопок нет (у кита это глобальное правило; своя полоса — только у
.nav-drawer-body: 8px, #7aa2f7 по #1f2335). Просили «цвет оставить как было,
просто скруглить» — здесь только радиус и ничего больше.
`scrollbar-width` / `scrollbar-color` НЕ выставляем: как только они заданы,
Chromium игнорирует `::-webkit-scrollbar-*` целиком, и скругление пропадает
(в 0.76 так и вышло: тонкая полоса акцентного цвета с прозрачной дорожкой —
акцентный бегунок читался поверх шапки). У `.tabs-list` и `.nav-drawer-body`
эти свойства уже стоят у кита, там полосы остаются как были. */
::-webkit-scrollbar-thumb {
border-radius: 999px;
}
/* --- Полоса прокрутки у табов (0.80, общее правило с 0.82) --- */
/* Ряд табов прокручивается по горизонтали (у кита .tabs-list{overflow-x:auto}),
но видимая полоса тут только шумит: обрезанный крайний таб и так читается как
«дальше есть». Прячем у всех табов сразу — правило было только у фильтра списка
задач, а табы статуса на странице проекта оставались с полосой. Скролл остаётся.
`scrollbar-width: none` достаточно Chromium и Firefox; `::-webkit-scrollbar`
держим для Safari — там первое свойство не поддерживается */
.tabs-list {
scrollbar-width: none;
}
.tabs-list::-webkit-scrollbar {
display: none;
}
/* --- Подвал карточки (0.76, отступы выровнены в 0.81) --- */
/* У кита `.card .card-footer` — просто полоса с отступами: ни границы, ни
раскладки, ни своего размера (flex/13px есть только у горизонтального
варианта), да ещё и отступы несимметричны — 8px сверху против 15px снизу,
из-за чего строка подвала висела на 3.5px выше середины полосы. Здесь общий
вид подвала для карточек задач и проектов: черта-разделитель во всю ширину
карточки (1px — как у прочих внутренних разделителей в приложении; рамка
самой карточки 2px и с ней не спорит), мягкий текст мельче основного и равные
отступы сверху и снизу, поэтому строка стоит ровно по центру полосы.
12px, а не точные 11.5px — округление; полоса от этого выше прежней ровно на
пиксель (43px → 44px).
min-height держит полосу одной строкой: у проекта без задач и без срока она
пустая и иначе схлопнулась бы в 25px (отступы + черта), а черта встала бы на
разной высоте у соседей по ряду. 25px — это те же 12px сверху и снизу плюс
черта: min-height у рамки, а не у содержимого (кит ставит box-sizing:border-box) */
.card .card-footer {
padding: 12px 15px;
min-height: calc(1.6em + 25px);
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
border-top: 1px solid var(--gn-border-color-muted, rgba(192, 202, 245, 0.24));
color: var(--gn-color-text-medium, #a9b1d6);
font-size: 0.85em;
}
/* Иконка в строке подвала (0.83). Китовое `.ph.normalize` (top:.15em) рассчитано на
общую базовую линию: оно опускает глиф из 0.5em над базовой линией до середины
прописных. Но строка подвала («2 недели назад») — флекс с align-items:center, и
коробка иконки центрируется сама; у иконочного шрифта нет нижнего выноса
(fontBoundingBoxDescent = 0), поэтому низ коробки ложится на 0.125em ниже базовой
линии текста. Поправка складывается с этим центрированием, и глиф уезжает вниз на
0.135em ≈ 1.6px — низ глифа уходит под базовую линию, и иконка читается провалившейся
(замер по пикселям на одной и той же строке: центр глифа на 1.3px ниже центра цифры
«2», рядом с которой он и стоит). Без поправки низ глифа Phosphor садится ровно на
базовую линию, как у буквы: расчёт даёт 0.375em против 0.36em середины прописных, то
есть 0.2px — иконке в текстовой строке больше и не нужно */
.card-footer .ph.normalize {
top: 0;
}
/* --- Дропдауны на мобильных: шторка снизу (0.77) --- */
/* Кит позиционирует меню от левого края триггера (position:absolute; left:0,
min-width:220px) — на телефоне у триггеров у правого края меню уезжало за
вьюпорт, и координаты приходилось клампить инлайном на каждый клик
(dropdownClamp.ts). До 767px меню вместо этого становится шторкой снизу:
панель во всю ширину + затемнение под ней. Это штатная мобильная замена
выпадашки (так делают и нативные листы действий), и заодно снимает всю
возню с координатами. Затемняющий слой — статичный div в index.html.
Блок временный — как только кит отдаст шторку сам, он удаляется вместе со слоем
в index.html; задача оформлена в доке кита
(gnexus-ui-kit/docs/tasks/gn-dropdown-mobile-sheet.md) */
.dropdown-backdrop {
display: none;
}
@media (max-width: 767px) {
/* Затемнение. Слой в body (вне .dropdown) — поэтому клик по нему для китового
обработчика «вне меню», и меню закрывается само; показан ровно тогда, когда
где-то открыт дропдаун */
body:has(.dropdown.is-open) .dropdown-backdrop {
display: block;
position: fixed;
inset: 0;
z-index: 1090;
background: var(--gn-color-black, #16161e);
opacity: 0.72;
}
/* Китовый panel_boot оставляет у шапки transform (заливка both), а transform
делает элемент содержащим блоком для position:fixed: шторка «⋯» из шапки
раскладывалась бы по шапке, а не по экрану. Снимаем анимацию только на
мобильных — на десктопе она нужна как есть */
.app-content .page-header {
animation: none;
}
.dropdown.is-open .dropdown-menu {
position: fixed;
top: auto;
left: 0;
right: 0;
bottom: 0;
z-index: 1100;
min-width: 0;
max-width: none;
max-height: 70vh;
/* dvh — фактическая высота (Safari 15.4+); старые браузеры оставляют 70vh */
max-height: 70dvh;
overflow-y: auto;
/* прокрутка шторки не «протекает» в страницу под ней */
overscroll-behavior: contain;
/* панель прижата к низу экрана — нижняя рамка ушла бы под край */
border-bottom: 0;
/* акцентная полоса слева (6px) у панели во всю ширину читается обрубком */
border-left-width: var(--gn-border-width-base, 2px);
border-radius: 12px 12px 0 0;
/* снизу — запас под жест-индикатор «домой»; в браузере inset = 0. 12px, а не
китовые 5px: последний пункт у самого края экрана читается обрезанным */
padding: 6px 5px calc(12px + env(safe-area-inset-bottom, 0px));
animation: gn-dropdown-sheet 0.22s ease both;
}
/* «ручка» — примета листа снизу: без неё панель читается просто выпадашкой,
зачем-то прилипшей к низу */
.dropdown.is-open .dropdown-menu::before {
content: '';
display: block;
width: 36px;
height: 4px;
margin: 2px auto 6px;
border-radius: 999px;
background: var(--gn-border-color-muted, rgba(192, 202, 245, 0.24));
}
/* Под палец: китовые 34px и 13px мелковаты для шторки во всю ширину */
.dropdown.is-open .dropdown-menu .dropdown-item {
min-height: 44px;
padding: 12px 15px;
font-size: 14px;
}
@keyframes gn-dropdown-sheet {
from {
opacity: 0;
transform: translateY(100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}