/* Палитра и вёрстка веб-доступа.
 *
 * Цвета — те же, что в настольном клиенте (theme.py): человек переходит из
 * программы в браузер и не должен видеть чужой продукт.
 *
 * Шрифты только системные. Внешние подключать нельзя: сторонние сервисы вроде
 * Google Fonts на каждый заход отдают IP посетителя за границу, и по 152-ФЗ
 * это трансграничная передача. Системный набор к тому же не тормозит и не
 * мигает подменой шрифта.
 *
 * Никаких фреймворков и сборки: один файл, обычный CSS, переменные темы.
 */

/* Марка набрана Space Grotesk Light. Шрифт лежит рядом со стилями, подрезан
   до заглавной латиницы (1,8 КБ): страница ни за чем в сеть не ходит, это
   запрещено законодательством. Адрес относительный, поэтому работает и в
   корне сайта, и под префиксом программы. */
@font-face {
  font-family: "Northen Mark";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: local("Space Grotesk Light"), local("SpaceGrotesk-Light"),
       url("northen-mark.woff2") format("woff2");
}

:root {
  --bg: #FFFFFF;
  --panel: #F6F6F7;
  /* Сила зелёной подложки в полосе загрузки: на белом зелёный тёмный, и
     18 % хватает; на чёрном он светлый и тонет, там 26 % (решение Артемия,
     20.09.2026, по прототипу в обеих темах). */
  --up-tint: 18%;
  --content-bg: #EEEEF0;
  --card: #FFFFFF;
  --text: #1A1A1A;
  --muted: #75757A;
  --border: #DCDCE0;
  --border-strong: #B4B4BA;
  --hover: #F0F0F1;
  --rail-sel: #ECECEE;
  --seg-on: #FFFFFF;   /* выбранная кнопка переключателя «Сеть» */
  --accent: #2F6FEB;
  --focus-ring: rgba(26, 26, 26, 0.13);
  --primary-bg: #1A1A1A;
  --primary-text: #FFFFFF;
  --primary-hover: #333333;
  --back-hover: #4A4A4A;
  --success-bg: rgba(46, 160, 67, 0.12);
  --success-border: rgba(46, 160, 67, 0.55);
  --success-text: #1E7B34;
  --error-bg: rgba(220, 64, 52, 0.10);
  --error-border: rgba(220, 64, 52, 0.50);
  --error-text: #B4271B;
  --warn-bg: rgba(212, 160, 23, 0.14);
  --warn-border: rgba(212, 160, 23, 0.45);
  --warn-text: #7A5B06;
  --shadow: 0 2px 12px rgba(0, 0, 0, 0.13);
}

/* Тёмная — по настройке системы; переключатель в шапке перебивает её. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { color-scheme: dark; }
}
:root:not([data-theme="light"]) {
  --dark-bg: #161616;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #161616; --panel: #101010; --card: #1C1C1E;
    --content-bg: #1C1C1C;
    --up-tint: 26%;
    --text: #ECECEC; --muted: #9E9EA6; --border: #36363C;
    --border-strong: #55555E; --hover: #242426; --rail-sel: #242426; --seg-on: #48484C;
    --accent: #5B8DEF;
    --focus-ring: rgba(236, 236, 236, 0.18);
    --primary-bg: #ECECEC; --primary-text: #161616; --primary-hover: #FFFFFF;
    --back-hover: #BFBFBF;
    --success-bg: rgba(46, 160, 67, 0.16); --success-border: rgba(46, 160, 67, 0.60);
    --success-text: #6FD98A;
    --error-bg: rgba(220, 64, 52, 0.16); --error-border: rgba(220, 64, 52, 0.55);
    --error-text: #F0857A;
    --warn-bg: rgba(224, 177, 42, 0.16); --warn-border: rgba(224, 177, 42, 0.45);
    --warn-text: #E4C15F;
    --shadow: 0 2px 12px rgba(0, 0, 0, 0.42);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #161616; --panel: #101010; --card: #1C1C1E;
  --content-bg: #1C1C1C;
  --up-tint: 26%;
  --text: #ECECEC; --muted: #9E9EA6; --border: #36363C;
  --border-strong: #55555E; --hover: #242426; --rail-sel: #242426; --seg-on: #48484C;
  --accent: #5B8DEF;
  --focus-ring: rgba(236, 236, 236, 0.18);
  --primary-bg: #ECECEC; --primary-text: #161616; --primary-hover: #FFFFFF;
  --back-hover: #BFBFBF;
  --success-bg: rgba(46, 160, 67, 0.16); --success-border: rgba(46, 160, 67, 0.60);
  --success-text: #6FD98A;
  --error-bg: rgba(220, 64, 52, 0.16); --error-border: rgba(220, 64, 52, 0.55);
  --error-text: #F0857A;
  --warn-bg: rgba(224, 177, 42, 0.16); --warn-border: rgba(224, 177, 42, 0.45);
  --warn-text: #E4C15F;
  --shadow: 0 2px 12px rgba(0, 0, 0, 0.42);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* Chrome на телефоне сам раздувает шрифт, и заданные размеры перестают
     значить что-либо. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: "Segoe UI", "Inter", system-ui, -apple-system, "Noto Sans", Arial, sans-serif;
  font-size: 14px;
  line-height: 1.45;
}

a { color: var(--accent); }

/* ---- каркас: рейл слева, содержимое справа ---- */

.shell {
  display: flex;
  /* dvh, а не vh: на телефоне адресная строка съезжает, и 100vh даёт высоту
     больше видимой — страница дёргается при прокрутке. vh оставлен запасом
     для браузеров постарше. */
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--content-bg);
}

.rail {
  width: 210px;
  flex: 0 0 210px;
  /* Панель остаётся на месте, пока прокручивается содержимое. Без этого на
     длинной странице низ панели с переключателем темы уезжает за экран. */
  position: sticky;
  top: 0;
  align-self: flex-start;
  height: 100vh;
  overflow-y: auto;
  background: var(--panel);
  border-right: 1px solid var(--border);
  padding: 18px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.rail .brand {
  font-weight: 600;
  padding: 0 10px 14px;
  font-size: 15px;
}
.rail a {
  display: block;
  padding: 9px 12px;
  border-radius: 8px;
  color: var(--text);
  text-decoration: none;
}
.rail a:hover { background: var(--hover); }
.rail a.on { background: var(--rail-sel); font-weight: 600; }
.rail .spacer { flex: 1; }
.rail-foot { border-top: 1px solid var(--border); padding-top: 8px; margin-top: 8px; }
.rail-foot a { color: var(--muted); }
.rail-foot a:hover { color: var(--text); }

/* min-width: 0 — не косметика: без него флекс-элемент не сжимается ниже
   собственного содержимого, и широкая таблица внутри растягивала всю
   страницу, хотя сама лежала в обёртке с прокруткой. */
.content { flex: 1; min-width: 0; padding: 28px 40px 48px; max-width: 1100px; }
h1 { font-size: 22px; font-weight: 600; margin: 0 0 6px; }
.hint { color: var(--muted); font-size: 13px; margin: 0 0 22px; }
.muted { color: var(--muted); }

/* ---- карточки ---- */

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px;
}
.card + .card { margin-top: 12px; }
.col { max-width: 440px; }

/* Вход — такая же вкладка, как остальные: панель на месте, а карточка узкая и
   по центру области. Поля по бокам берёт у области, своих не держит. */
.gate { margin: 0 auto; max-width: 420px; padding-top: 8vh; }
/* Вход — самая простая страница сайта, и потому единственная, где виден каждый
   пиксель. Ритм задаём здесь, а не разметкой: у первой подписи был свой отступ
   сверху, и она отходила от края карточки на 28px против 16px у низа — форма
   заметно съезжала вверх. */
.gate .card { padding: 24px; }
.gate label { margin: 0 0 7px; }
.gate input + label { margin-top: 18px; }   /* воздух между полями */
.gate input { padding: 10px 12px; }
.gate-go { margin-top: 22px; }

/* Две формы, видна одна. Какая, решает data-gate-mode на обёртке; без JS
   его ставит сервер по ?mode=, с JS меняет gate.js. */
.gate-form, .gate-flip { display: none; }
/* Смена формы: уходящая гаснет и чуть уезжает, приходящая проявляется.
   Классы ставит gate.js на время перехода; reduce-motion гасит всё это
   общим правилом ниже по файлу. */
.gate .gate-form { transition: opacity .16s ease, transform .16s ease; }
.gate.gate-leaving .gate-form  { opacity: 0; transform: translateY(6px); }
.gate.gate-arriving .gate-form { animation: gate-in .22s ease both; }
@keyframes gate-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
.gate[data-gate-mode="login"]    .gate-form[data-gate="login"],
.gate[data-gate-mode="login"]    .gate-form[data-gate="login"] + .gate-flip,
.gate[data-gate-mode="register"] .gate-form[data-gate="register"],
.gate[data-gate-mode="register"] .gate-form[data-gate="register"] + .gate-flip { display: block; }
.gate-flip { text-align: center; margin: 14px 0 0; font-size: 13px; }
.gate-flip a { color: var(--muted); }
.gate-flip a:hover { color: var(--text); }

/* Подсказка под ключом и полоса надёжности: заполняет gate.js. */
.gate-note { min-height: 18px; margin: 6px 0 0; font-size: 12px; color: var(--muted); }
.gate-note.ok  { color: var(--success-text); }
.gate-note.bad { color: var(--error-text); }
.gate-strength { display: flex; align-items: center; gap: 6px; margin: 8px 0 0; }
.gate-strength .bar { width: 28px; height: 4px; border-radius: 2px; background: var(--border); }
.gate-strength[data-score="1"] .bar:nth-child(-n+1),
.gate-strength[data-score="2"] .bar:nth-child(-n+2),
.gate-strength[data-score="3"] .bar:nth-child(-n+3) { background: var(--success-text); }
.gate-strength[data-score="1"] .bar:nth-child(1) { background: var(--warn-text); }
.gate-strength-text { font-size: 12px; color: var(--muted); margin-left: 4px; }

/* ---- поля и кнопки ---- */

input[type="text"], input[type="password"], input[type="email"], input[type="search"], textarea, select {
  width: 100%;
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 8px 10px;
  font: inherit;
}
/* Поле в работе обводится цветом текста, а не синим: синий тут был единственным
   чужим цветом на всю страницу. Чистый белый брать нельзя — на светлой теме он
   исчез бы на белой карточке. Мягкий ободок вокруг вместо контура: он не
   прыгает по вёрстке и читается спокойнее. */
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--text);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
textarea { resize: vertical; min-height: 76px; }
label { display: block; margin: 12px 0 4px; font-size: 13px; }
/* Флажок с подписью в одну строку: обычная подпись стоит блоком над полем. */
label.check { display: flex; align-items: center; gap: 8px; margin: 6px 0; font-size: 14px; }
label.check input { width: auto; }
/* Форма ключа в админке: узкая, как карточка входа, а не во всю ширину. */
.key-form { max-width: 420px; margin-bottom: 14px; }

.btn {
  display: inline-block;
  background: var(--primary-bg);
  color: var(--primary-text);
  border: none;
  border-radius: 8px;
  padding: 9px 18px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { background: var(--primary-hover); }
.btn:disabled { background: transparent; color: var(--muted); border: 1px solid var(--border-strong); cursor: default; }

.btn-ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 8px 14px;
  font: inherit;
  cursor: pointer;
  text-decoration: none;
}
.btn-ghost:hover { border-color: var(--accent); background: var(--hover); }
/* Форма в ячейке таблицы: поле и кнопка в одну строку, без переносов. */
form.inline { display: flex; gap: 6px; align-items: center; margin: 0; }
form.inline input { width: 140px; padding: 6px 8px; }

/* Кнопка скачивания зеленеет под курсором — как в программе. */
.btn-dl {
  display: inline-block; box-sizing: border-box;
  background: transparent; color: var(--text);
  border: 1px solid var(--border-strong); border-radius: 8px;
  padding: 8px 14px; font: inherit; line-height: 1.2; cursor: pointer; text-decoration: none;
}

/* Действия у готового чертежа: «Скачать» с меню и «Предпросмотр». Пункты с
   воздухом, меню закрывается кликом мимо (menu.js). */
.acts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.menu-dl { position: relative; }
.menu-dl > summary { list-style: none; user-select: none; }
.menu-dl > summary::-webkit-details-marker { display: none; }
.menu-dl > summary::after { content: ""; display: inline-block; width: 6px; height: 6px; margin-left: 9px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg); opacity: .7; }
.menu-dl[open] > summary { border-color: var(--accent); }
.menu-dl-list {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 30;
  min-width: 300px; padding: 8px;
  background: var(--card); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 18px 44px -18px rgba(30, 32, 38, .4), 0 0 0 1px rgba(0, 0, 0, .04);
}
.menu-dl-list a { display: block; padding: 11px 14px; border-radius: 9px; color: var(--text);
  text-decoration: none; font-size: 14px; white-space: nowrap; }
.menu-dl-list a:hover { background: var(--hover); }
.menu-dl-list a + a { margin-top: 2px; }
.btn-dl:hover {
  color: var(--success-text);
  border-color: var(--success-border);
  background: var(--success-bg);
}

/* ---- уведомления ---- */

.notice {
  border-radius: 10px;
  padding: 10px 12px;
  margin: 12px 0;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--muted);
}
.notice.success { background: var(--success-bg); border-color: var(--success-border); color: var(--success-text); }
.notice.error   { background: var(--error-bg);   border-color: var(--error-border);   color: var(--error-text); }
.notice.warn    { background: var(--warn-bg);    border-color: var(--warn-border);    color: var(--warn-text); }
.notice span { opacity: .85; }

/* ---- зона загрузки ---- */

/* ---- фон содержимого: зерно и мягкий свет (Артемий, 21.09.2026) ----
   Плоская заливка выглядела дёшево. Под содержимым (не под панелью: у неё
   своя непрозрачная подложка, и она лежит выше) два слоя:
   1. зерно — SVG-шум в data-URI, растрируется один раз как картинка фона,
      фильтров в реальном времени нет; серое зерно на цвете фона читается
      как его же зернистость, сила около 16 %;
   2. свет — два очень мягких пятна градиентом (не blur()): правый верхний
      угол и левый низ, будто угол чуть подсвечен. Никаких «50×50»: пятна
      разного размера и не по центру, и внимания на себя не тянут.
   Раньше зерно было только на странице загрузки — теперь везде. */
.shell { position: relative; isolation: isolate; }
.shell::before, .shell::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
}
.shell::before {
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .5 0 0 0 0 .5 0 0 0 .16 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
  opacity: 1;
}
.shell::after {
  background:
    radial-gradient(ellipse 620px 440px at 100% 0%, var(--glow-1), transparent 70%),
    radial-gradient(ellipse 480px 360px at 0% 100%, var(--glow-2), transparent 70%),
    radial-gradient(ellipse 360px 260px at 62% 38%, var(--glow-3), transparent 70%);
}
:root {
  /* Без цвета (Артемий): на светлой теме свет — это тень, чёрные пятна. */
  --glow-1: rgba(0, 0, 0, 0.055);
  --glow-2: rgba(0, 0, 0, 0.040);
  --glow-3: rgba(0, 0, 0, 0.022);
}
:root[data-theme="dark"] {
  --glow-1: rgba(255, 255, 255, 0.040);
  --glow-2: rgba(255, 255, 255, 0.028);
  --glow-3: rgba(255, 255, 255, 0.018);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --glow-1: rgba(255, 255, 255, 0.040);
    --glow-2: rgba(255, 255, 255, 0.028);
    --glow-3: rgba(255, 255, 255, 0.018);
  }
}
/* Колонка программы стоит по центру, под маркой дока. Раньше она прижималась
   к левому краю области в 1180px и на широком экране читалась «чуть левее
   середины». Крошки лежат внутри колонки и выровнены вместе с плитками. */
.upload { max-width: 640px; margin-left: auto; margin-right: auto; }
.utile {
  display: flex; flex-direction: column; gap: 6px;
  padding: 22px 24px;
  border-radius: 22px;
  background: var(--card);
  color: var(--text);
  box-shadow: 0 18px 44px -18px rgba(30, 32, 38, .28), 0 0 0 1px rgba(0, 0, 0, .05);
  transition: transform .2s ease, box-shadow .2s ease, background .2s ease;
}
.utile-title { font-size: 17px; font-weight: 600; letter-spacing: -.01em; }
.utile-text { color: var(--muted); font-size: 14px; line-height: 1.45; }
.utile-drop { grid-column: 1 / -1; cursor: pointer; min-height: 168px; }
.utile-drop input[type="file"] { position: absolute; width: 0; height: 0; opacity: 0; }
.utile-drop:hover, .utile-drop.drag { transform: translateY(-2px);
  box-shadow: 0 24px 54px -18px rgba(30, 32, 38, .36), 0 0 0 2px var(--focus-ring); }
.utile-drop:focus-within { box-shadow: 0 18px 44px -18px rgba(30, 32, 38, .28), 0 0 0 2px var(--focus-ring); }
.utile-pick { display: flex; align-items: center; gap: 12px; margin-top: auto; padding-top: 14px; flex-wrap: wrap; }
.utile-pick .btn-ghost { pointer-events: none; }   /* клик ловит вся плитка (label) */
.utile-file { font-size: 13px; overflow-wrap: anywhere; }
/* Атрибут hidden сильнее любой раскладки: display:flex у .utile иначе
   показал бы скрытую плитку. */
[hidden] { display: none !important; }

.utile-go {
  grid-column: 1 / -1;   /* во всю ширину: выбор формата скрыт, спорить не с кем */
  align-items: center; justify-content: center;
  font: inherit; font-size: 16px; font-weight: 600; cursor: pointer; border: 0;
  background: var(--primary-bg); color: var(--primary-text);
  min-height: 72px;
}
.utile-go:hover { background: var(--primary-hover); transform: translateY(-1px); }
.utile-go:disabled { background: var(--card); color: var(--muted); cursor: default; transform: none; }
.utile-go:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }

/* ---- «Продолжить» становится полосой загрузки ----
   Та же кнопка, тот же размер: она не пропадает и не меняет ширины, только
   фон мягко уходит с чёрного на цвет карточки, а слева направо растёт
   зелёная подложка с линией по нижнему краю. Внутри — скорость и
   «примерно: … с», справа проценты. Страница при этом не дёргается. */
.utile-go { position: relative; overflow: hidden;
  transition: background .35s ease, color .35s ease, transform .2s ease, box-shadow .2s ease; }
.utile-go.is-uploading, .utile-go.is-uploading:disabled {
  background: var(--card); color: var(--text); cursor: default; transform: none;
  flex-direction: row; justify-content: space-between; align-items: center; gap: 16px;
  padding: 15px 24px; text-align: left; }
.utile-go .up-fill, .utile-go .up-line { position: absolute; left: 0; width: 0;
  pointer-events: none; transition: width .25s linear; }
.utile-go .up-fill { top: 0; bottom: 0;
  background: color-mix(in srgb, var(--success-text) var(--up-tint), transparent); }
.utile-go .up-line { bottom: 0; height: 3px; background: var(--success-text); border-radius: 0 3px 3px 0; }
.utile-go .up-text { position: relative; display: flex; flex-direction: column; gap: 3px; min-width: 0;
  animation: up-in .35s ease; }
.utile-go .up-title { font-size: 16px; font-weight: 600; letter-spacing: -.01em; }
.utile-go .up-meta { font-size: 13px; font-weight: 400; color: var(--muted);
  font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.utile-go .up-pct { position: relative; flex: 0 0 auto; font-size: 20px; font-weight: 600;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em; animation: up-in .35s ease; }
.utile-go.is-done .up-title { color: var(--success-text); }
.utile-go.is-error .up-title { color: var(--error-text); }
.utile-go.is-error .up-fill, .utile-go.is-error .up-line { opacity: 0; }
@keyframes up-in { from { opacity: 0; } to { opacity: 1; } }

/* Крошки вместо заголовка: «Главное меню / Сейчас в обработке / имя».
   Тем же кеглем, что h1; ссылки в цвет текста, текущий шаг жирный. */
.crumbs { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 18px; }
.crumbs a { color: var(--muted); text-decoration: none; font-weight: 500; }
.crumbs a:hover { color: var(--text); }
.crumbs span { color: var(--border-strong); font-weight: 400; }
.crumbs b { font-weight: 600; }

/* Разделитель и строка «Сейчас в обработке … N» под плитками. */
.sep { border: 0; border-top: 1px solid var(--border); margin: 22px 0 14px; }
.utile-row { flex-direction: row; align-items: baseline; gap: 12px; text-decoration: none; padding: 16px 24px; }
.utile-row:hover { transform: translateY(-1px); box-shadow: 0 24px 54px -18px rgba(30, 32, 38, .36), 0 0 0 2px var(--focus-ring); }
.utile-row .leader { flex: 1; }   /* просто пустота: подпись слева, число справа */
.utile-row b { font-size: 17px; }
.utile-more {
  margin-top: 4px; text-decoration: none;
  width: 50%; min-height: 48px; padding: 12px 20px; font-size: 15px; font-weight: 500;
  background: var(--card); color: var(--text); box-shadow: 0 0 0 1px var(--border-strong);
}
.utile-more:hover { background: var(--hover); transform: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.more-row { display: flex; gap: 14px; margin-top: 14px; }   /* строка кнопок стоит от плиток на тот же зазор */
.more-row .utile-more { flex: 0 1 calc(50% - 7px); width: auto; }   /* одна кнопка не растягивается на всю ширину */

/* Крестик у выполненной задачи: видим при наведении, на телефоне всегда.
   Зона нажатия 40px, сам знак маленький. */
.utile.job { position: relative; }
.job-x {
  position: absolute; top: 10px; right: 10px; width: 40px; height: 40px;
  border: 0; border-radius: 12px; background: transparent; color: var(--muted);
  font: inherit; font-size: 22px; line-height: 1; cursor: pointer;
  opacity: 0; transition: opacity .15s ease, background .15s ease, color .15s ease;
}
.utile.job:hover .job-x, .job-x:focus-visible { opacity: 1; }
.job-x:hover { background: var(--hover); color: var(--text); }
@media (hover: none) { .job-x { opacity: .7; } }

/* Подтверждение: маленькая плитка посередине, две негромкие кнопки. */
dialog.confirm { border: 0; padding: 0; background: transparent; }
dialog.confirm::backdrop { background: rgba(0, 0, 0, .45); }
.confirm-box { width: min(420px, 92vw); max-width: 100%; padding: 20px 22px; border-radius: 18px; margin: 0;
  background: var(--card); color: var(--text); box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .5), 0 0 0 1px var(--border); }
/* Имя чертежа — одно длинное слово: «Планировка_2017_Измайловское_ш_71_РКР_
   Лайт_от_06_08_26г_ФИНАЛ.dwg». Подчёркивание переносом не считается, поэтому
   строка уезжала за скруглённый край, а у самого окна появлялась
   горизонтальная полоса прокрутки. Разрешаем перенос внутри слова. */
.confirm-text { margin: 0 0 16px; font-size: 14px; line-height: 1.5;
  overflow-wrap: anywhere; word-break: break-word; }
.confirm-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* задача: та же плитка, полоса выше и с плавным ходом */
.utile.job { grid-column: 1 / -1; gap: 10px; }
.upload > .utile + .utile { margin-top: 14px; }
.job-step { font-size: 14px; }
.job-eta { font-size: 13px; }
.job-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }

/* ---- переключатель ---- */

.switch { display: flex; align-items: center; gap: 10px; margin: 14px 0; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track {
  position: relative;
  margin: 0;   /* общее правило label дало бы 12px сверху и перекос */
  flex: 0 0 auto;
  width: 48px; height: 26px;
  border-radius: 13px;
  background: var(--panel);
  border: 1px solid var(--border-strong);
  transition: background .14s ease, border-color .14s ease;
  cursor: pointer;
}
.switch .track::after {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: #FFFFFF;
  border: 1px solid var(--border-strong);
  transition: transform .14s ease, border-color .14s ease;
}
.switch input:checked + .track {
  background: var(--success-text);
  border-color: var(--success-text);
}
.switch input:checked + .track::after { transform: translateX(22px); border-color: transparent; }
.switch input:focus-visible + .track { outline: 2px solid var(--accent); outline-offset: 2px; }
.switch .text b { font-size: 13px; font-weight: 600; display: block; }
.switch .text small { color: var(--muted); font-size: 12px; }

/* ---- список работ и история ---- */

/* Просмотр чертежа: во всю область содержимого, панель сверху, сцена ниже.
   Фон сцены тёмный по умолчанию, как в AutoCAD; переключатель меняет и
   цвет линий цвета 7 (viewer.js); толщину линий он же держит около пикселя при любом зуме. */
.viewer { display: flex; flex-direction: column; height: calc(100vh - 48px); min-height: 480px; margin: -8px 0 0; }
.viewer-bar { display: flex; align-items: center; gap: 10px; padding: 0 0 10px; flex-wrap: wrap; }
.viewer-title { font-weight: 600; overflow-wrap: anywhere; }
.viewer-note { color: var(--muted); font-size: 12px; flex: 1; }
.viewer-tools { display: flex; flex-wrap: wrap; gap: 6px; margin-left: auto; }
.viewer-stage { flex: 1; min-height: 0; border: 1px solid var(--border); border-radius: 12px; overflow: hidden;
  cursor: grab; touch-action: none; user-select: none; background: #000; }
.viewer[data-bg="light"] .viewer-stage { background: #fff; }
.viewer.dragging .viewer-stage { cursor: grabbing; }
.viewer-stage svg { display: block; width: 100%; height: 100%; }
/* Растр: страница открывается им, вид даёт CSS-трансформ (viewer.js). Точка
   отсчёта в левом верхнем углу — иначе сдвиг пришлось бы считать от середины
   и он зависел бы от масштаба. Размер по натуральному, не по сцене. */
.viewer-stage .viewer-img { display: block; max-width: none; transform-origin: 0 0;
  image-rendering: auto; user-select: none; -webkit-user-drag: none; cursor: grab; }
.viewer.dragging .viewer-stage .viewer-img { cursor: grabbing; }

/* Поиск по списку: поле во всю ширину карточки, счётчик справа. */
.find { display: flex; align-items: center; gap: 12px; margin: -8px 0 14px; }
.find input { flex: 1; max-width: 480px; }
.find [data-find-count] { font-size: 12px; white-space: nowrap; }
input[type="search"] { -webkit-appearance: none; appearance: none; }
input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; }

.row {
  display: flex; align-items: center; gap: 14px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 14px;
  margin-bottom: 10px;
}
.row:hover { border-color: var(--muted); }
.row .grow { flex: 1; min-width: 0; }
.row .name { font-weight: 600; overflow-wrap: anywhere; }
.row .meta { color: var(--muted); font-size: 12px; margin-top: 2px; }

.badge {
  border-radius: 6px; padding: 2px 8px; font-size: 12px;
  border: 1px solid var(--border); background: var(--panel); color: var(--muted);
  white-space: nowrap;
}
.badge.done  { background: var(--success-bg); border-color: var(--success-border); color: var(--success-text); }
.badge.error, .badge.failed { background: var(--error-bg);   border-color: var(--error-border);   color: var(--error-text); }
/* «Готовится» — жёлтая: разбор ещё идёт, скачать пока нечего. */
.badge.wait  { background: var(--warn-bg);    border-color: var(--warn-border);    color: var(--warn-text); }
/* Строка «Загрузок», сменившая «Готовится» на готовую, появляется мягко. */
.row.is-fresh { animation: tile-in .35s ease; }

/* ---- полоса выполнения ---- */

.bar {
  height: 12px; border-radius: 6px;
  background: var(--panel);
  border: 1px solid var(--border);
  padding: 2px;
  margin: 10px 0 2px;
  overflow: hidden;
}
.bar > i { display: block; height: 100%; border-radius: 4px; background: var(--success-text);
  transition: width .7s cubic-bezier(.2, .8, .2, 1); }

/* Разделы админки. Переносятся по строкам: на телефоне четыре в один ряд не
   влезали и вылезали за экран. */
.tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }

/* Обёртка для широких таблиц: катается сама, а не растягивает страницу. */
.scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.scroll table { min-width: 520px; }

table { border-collapse: collapse; width: 100%; font-size: 13px; }
th, td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--border); }
th { color: var(--muted); font-weight: 600; }

/* ---- плитки программ на главной ---- */

/* Марка в углу панели: своей гарнитурой, чтобы читалась как имя компании, а
   не как ещё один пункт меню. */
/* Марка теперь ссылка, и .rail a красил бы её как пункт панели. */
.rail a.brand:hover { background: none; opacity: .7; }
.rail .brand.mark {
  font-family: "Northen Mark", "Segoe UI", system-ui, sans-serif;
  font-size: 20px;
  font-weight: 300;
  letter-spacing: 2px;
}

/* Три в ряд, но плитка лежачая: в высоту ей нечего занимать, внутри только
   название и кнопка. */
.tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  max-width: 920px;
  margin-top: 20px;
}

.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 140px;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--card);
  color: inherit;
  text-decoration: none;
  transition: border-color .15s, background .15s;
}
.tile:hover { border-color: var(--border-strong); background: var(--hover); }

.tile-head { position: relative; z-index: 1; }
.tile-name { font-size: 16px; font-weight: 600; letter-spacing: .3px; }
.tile-note { font-size: 12px; color: var(--muted); margin-top: 2px; }

.tile-foot { margin-top: auto; display: flex; justify-content: flex-end; position: relative; z-index: 1; }
.tile-open {
  border: 1px solid var(--border-strong);
  border-radius: 18px;
  padding: 6px 16px;
  font-size: 13px;
  color: var(--text);
}
.tile:hover .tile-open {
  color: var(--success-text);
  border-color: var(--success-border);
  background: var(--success-bg);
}

@media (max-width: 900px) { .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .tiles { grid-template-columns: minmax(0, 1fr); } }

/* ---- отказ в доступе ---- */

/* Для отказа область отдаётся целиком: свой предел ширины и разные отступы
   сверху и снизу увели бы середину блока от середины видимой плоскости. */
.content:has(.denied) { max-width: none; padding: 0; display: flex; }
.denied {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  padding: 40px 20px;
  text-align: center;
}
.denied-mark {
  width: 56px; height: 56px;
  fill: none;
  stroke: var(--text);        /* чёрный на светлой теме, светлый на тёмной */
  stroke-width: 6;
  stroke-linecap: round;      /* те самые закруглённые края */
}
.denied-title { font-size: 28px; font-weight: 600; }
/* Кнопка та же, что везде: чёрная на светлой теме, белая на тёмной. Отличие
   одно - под курсором серая, а не ярче. */
.btn-back:hover { background: var(--back-hover); }

/* ---- о компании ---- */

/* Засечный шрифт и колонка в 640 px: страница читается, а не просматривается.
   Шрифты только системные - подключать чужие нельзя. */
.about {
  max-width: 640px;
  margin: 0 auto;
  padding: 56px 0 80px;
  font-family: "Times New Roman", Times, "Georgia", serif;
  font-size: 17px;
  line-height: 1.72;
}
.about-mark {
  font-family: "Northen Mark", "Segoe UI", system-ui, sans-serif;
  font-size: clamp(26px, 7.5vw, 40px);
  font-weight: 300;
  letter-spacing: clamp(2px, 1vw, 5px);
  text-align: center;
  margin: 0 0 14px;
}
.about-lead {
  text-align: center;
  color: var(--muted);
  font-size: 18px;
  margin: 0 0 44px;
}
.about p { margin: 0 0 20px; text-align: justify; hyphens: auto; }

/* ---- телефон ----
 *
 * Панель из боковой становится верхней и остаётся на месте при прокрутке.
 * Порядок в ней меняется, а не разметка: марка и переключатель темы в одну
 * строку, разделы — во вторую. Ничего не прячем за кнопкой-гамбургером: все
 * три-четыре пункта короткие и влезают, а спрятанное меню — лишнее нажатие
 * ради экономии, которой тут нет.
 *
 * Воздуха на телефоне больше, а не меньше: поля по бокам держат строку
 * читаемой, а заголовок наверху крупнее, чем на широком экране.
 */
@media (max-width: 720px) {
  .menu-dl-list { left: 0; right: auto; min-width: 260px; }   /* у левого края экрана меню растёт вправо */
  .utile { padding: 18px 18px; border-radius: 18px; }
  .shell { flex-direction: column; }

  .rail {
    width: auto;
    flex: none;
    height: auto;
    align-self: stretch;
    z-index: 5;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 6px;
    padding: 14px 16px 12px;
    border-right: none;
    border-bottom: 1px solid var(--border);
    overflow: visible;
  }
  /* Разделы идут последними, но в разметке стоят до распорки: порядок задаём
     здесь, чтобы не держать два варианта разметки. */
  .rail > a { order: 4; }
  .rail > a.brand { order: 1; margin-right: auto; padding: 0; }
  .rail > .rail-foot { order: 2; border-top: none; margin: 0; padding: 0; }
  .rail > .spacer { order: 3; flex: 0 0 100%; height: 0; }

  /* Палец — не курсор: 44px по высоте, меньше промахов. */
  .rail > a:not(.brand), .rail-foot a { padding: 12px 12px; }

  .content { padding: 28px 20px 56px; }
  h1 { font-size: 26px; margin-bottom: 8px; }
  .hint { margin-bottom: 26px; }
  .col { max-width: none; }

  /* Меньше 16px — и iOS сам увеличивает страницу, когда встаёшь в поле. */
  input[type="text"], input[type="password"], input[type="email"],
  textarea, select { font-size: 16px; padding: 11px 12px; }
  .btn, .btn-ghost, .btn-dl { padding: 12px 18px; }

  /* Имя, метка и кнопки в истории перестают тесниться в одну строку. */
  .row { flex-wrap: wrap; }
  .row .grow { flex: 1 1 100%; }

  .tile { min-height: 0; padding: 18px; }
  .tiles { margin-top: 16px; }

  /* Колонка узкая, а слова в русском длинные: выключка по формату рвала бы
     строку пробелами. */
  .about { padding: 36px 0 64px; font-size: 16px; }
  .about p { text-align: left; }
  .about-lead { font-size: 17px; margin-bottom: 32px; }

  .card { padding: 14px; }
  .gate { padding-top: 4vh; }
}

/* Совсем узкие экраны: боковые поля ещё меньше, иначе строка становится
   короче читаемой. */
@media (max-width: 400px) {
  .content { padding: 24px 14px 48px; }
  .rail { padding: 12px 14px 10px; }
}

/* Кому анимация мешает — тому её нет. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* Ходить по сайту с клавиатуры должно быть видно. */
a:focus-visible, button:focus-visible, .btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* У полей своя обводка выше; без этой строки поверх неё ложился синий контур. */
input:focus-visible, textarea:focus-visible, select:focus-visible { outline: none; }

/* --- пункт «скачать PDF предпросмотра» -----------------------------------
   Он стоит в меню всегда, потому что файл дорисовывается фоном уже после
   отчёта. Цветом говорим, что с ним: красный — ещё готовится, зелёный —
   готов. Вторая строка поясняет словами, чтобы цвет не был единственным
   носителем смысла. */
.dl-pdf.is-wait { color: var(--error-text); }
.dl-pdf.is-ready { color: var(--success-text); }
.dl-pdf small { display: block; font-size: 12px; line-height: 1.3; opacity: .85; }
/* В строке просмотрщика пункт — кнопка, и заливка там своя: перекрашиваем
   рамку и текст, а не фон, иначе кнопка перестанет читаться. */
.btn-dl.dl-pdf.is-wait { border-color: var(--error-border); }
.btn-dl.dl-pdf.is-ready { border-color: var(--success-border); }

/* ---- переключатель темы и значок аккаунта в подвале панели ----
   Тема — двухпозиционный тумблер со значками: солнце слева, луна справа,
   активная позиция подсвечена ярко (жёлтое солнце, синяя луна), чтобы
   состояние читалось с первого взгляда, а не по подписи. Значок аккаунта
   показывается только на телефоне вместо пунктов «Войти»/«Аккаунт»; на
   компьютере остаются пункты, а значок спрятан. */
.rail-foot { display: flex; align-items: center; gap: 8px; }
.rail-foot a.theme-switch, .rail-foot a.acct-icon {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px; border-radius: 999px; color: var(--muted);
}
.theme-switch {
  position: relative; width: 66px; height: 32px; padding: 0 !important;
  background: var(--hover); border: 1px solid var(--border-strong);
}
.theme-switch .ts-knob {
  position: absolute; top: 3px; left: 3px; width: 26px; height: 26px; border-radius: 50%;
  background: var(--panel); border: 1px solid var(--border-strong);
  transition: left .18s ease;
}
.theme-switch .ts-sun, .theme-switch .ts-moon {
  position: relative; z-index: 1; width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--muted); opacity: .55; transition: opacity .18s ease, color .18s ease;
}
.theme-switch svg { width: 18px; height: 18px; display: block; }
/* Светлая: солнце горит, кнопка слева. */
.theme-switch .ts-sun { color: #F0A500; opacity: 1; }
/* Тёмная: луна горит, кнопка справа. Та же пара правил, что и у палитры:
   явный выбор и системная тема без явного выбора. */
:root[data-theme="dark"] .theme-switch .ts-sun { color: var(--muted); opacity: .55; }
:root[data-theme="dark"] .theme-switch .ts-moon { color: #7FB0FF; opacity: 1; }
:root[data-theme="dark"] .theme-switch .ts-knob { left: 35px; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-switch .ts-sun { color: var(--muted); opacity: .55; }
  :root:not([data-theme="light"]) .theme-switch .ts-moon { color: #7FB0FF; opacity: 1; }
  :root:not([data-theme="light"]) .theme-switch .ts-knob { left: 35px; }
}
.theme-switch:hover { border-color: var(--accent); }
.acct-icon { display: none !important; width: 32px; height: 32px; border: 1px solid var(--border-strong); }
.acct-icon svg { width: 18px; height: 18px; display: block; }
.acct-icon.on { color: var(--accent); border-color: var(--accent); }
@media (max-width: 720px) {
  .rail-foot { gap: 10px; }
  .rail-foot a.theme-switch, .rail-foot a.acct-icon { padding: 0; }
  .acct-icon { display: inline-flex !important; }
  /* На телефоне вход и аккаунт — значок в подвале, пункты не дублируем. */
  .rail > a[data-key="login"], .rail > a[data-key="account"] { display: none; }
}

/* ---- док на телефоне: Главная слева, марка по центру, значки справа ----
   Три группы одной строки: пункт «Главная» и подвал со значками тянутся
   поровну, марка между ними остаётся ровно по центру. Остальные разделы
   переносятся на вторую строку за распоркой. */
@media (max-width: 720px) {
  .rail { justify-content: space-between; }
  .rail > a[data-key="hub"] { order: 1; flex: 1 0 0; text-align: left; padding-left: 0; }
  .rail > a.brand { order: 2; flex: 0 0 auto; margin: 0; text-align: center; }
  .rail > .rail-foot { order: 3; flex: 1 0 0; justify-content: flex-end; }
  .rail > .spacer { order: 4; }
  .rail > a:not(.brand):not([data-key="hub"]) { order: 5; }
}
@media (max-width: 720px) {
  /* Пункт тянется на треть строки ради центровки марки; подсветка на всю
     треть выглядела бы плашкой не по размеру — вместо неё жирный текст. */
  .rail > a[data-key="hub"].on { background: none; font-weight: 600; }
}
@media (max-width: 720px) {
  /* Края дока совпадают с краями содержимого (20px): «Главная» не липнет
     к левому обрезу, значки справа стоят на одной вертикали с плитками. */
  .rail > a[data-key="hub"] { padding-left: 6px; }
  .rail > .rail-foot { padding-right: 6px; }
}

/* Пиксельная выверка подвала: кнопка тумблера ровно по вертикальному центру
   (внутри 30px под рамкой — 26px кнопка, по 2px сверху и снизу), а на
   телефоне все три части дока одной высоты 44px, чтобы их центры лежали на
   одной целой линии и рамки значков не размывались на полупикселе. */
.theme-switch .ts-knob { top: 2px; }
@media (max-width: 720px) {
  .rail > a.brand, .rail > .rail-foot, .rail > a[data-key="hub"] {
    height: 44px; box-sizing: border-box; display: flex; align-items: center;
    padding-top: 0; padding-bottom: 0;
  }
  .rail > a.brand { justify-content: center; }
}

/* ---- тумблер без кружка, домик и марка-переключатель на телефоне ----
   Состояние темы показывает только свет: горит солнце или луна, общая
   рамка одна на оба значка. На телефоне тумблера нет вовсе — тему меняет
   касание марки NORTHEN на главной (menu.js), а «Главная» становится
   домиком. На компьютере всё как было. */
.theme-switch .ts-knob { display: none; }
.nav-home { display: none; width: 22px; height: 22px; }
@media (max-width: 720px) {
  .rail-foot .theme-switch { display: none !important; }
  .rail > a[data-key="hub"] { flex: 1 0 0; display: flex; align-items: center; }
  .rail > a[data-key="hub"] .nav-label { display: none; }
  .rail > a[data-key="hub"] .nav-home { display: block; }
  .rail > a[data-key="hub"].on .nav-home { color: var(--text); }
  .rail > a[data-key="hub"]:not(.on) .nav-home { color: var(--muted); }
}

/* Медиа-запросы идут последними (см. тест стилей): телефонный вариант
   пятен света — здесь, а не рядом с основным правилом. */
@media (max-width: 720px) {
  /* На телефоне область уже: пятна меньше, верхнее — ниже дока. */
  .shell::after {
    background:
      radial-gradient(ellipse 340px 260px at 100% 9%, var(--glow-1), transparent 70%),
      radial-gradient(ellipse 300px 220px at 0% 100%, var(--glow-2), transparent 70%);
  }
}

/* ---- домик и портрет одной семьи; марка без hover и чуть крупнее ----
   Оба значка на телефоне — одинаковые кружки 32px в серой рамке, сам
   рисунок приглушённый (цвет muted, 75 %), ничего яркого. Марка не
   реагирует на наведение цветом и стала на 2px крупнее. */
.rail a.brand:hover { opacity: 1; color: inherit; }
.rail .brand.mark { font-size: 22px; }
.acct-icon, .acct-icon.on { color: var(--muted); border-color: var(--border-strong); }
.acct-icon svg, .nav-home { opacity: .75; }
@media (max-width: 720px) {
  .rail > a[data-key="hub"] .nav-home {
    box-sizing: border-box; width: 32px; height: 32px; padding: 6px;
    border: 1px solid var(--border-strong); border-radius: 50%;
    color: var(--muted); opacity: 1;
  }
  .rail > a[data-key="hub"] .nav-home path { opacity: .75; }
  .rail > a[data-key="hub"].on .nav-home { color: var(--muted); }
  .rail > a.brand { font-size: 24px; }
}

/* ==== док сверху на всех экранах (Артемий, 21.09.2026) ====
   Панель слева ушла: и на компьютере, и на телефоне одна и та же полоса
   сверху — слева домик и (админу) значок админ-панели, по центру марка,
   справа портрет. Разделы программы («Загрузки») идут второй строкой.
   Тумблера темы нет нигде: тему меняет касание марки на главной. На
   компьютере всё то же, только крупнее. Значки темнее: цвет текста с
   рамкой в 45 % текста, чтобы не терялись. */
.shell { flex-direction: column; }
.rail {
  position: sticky; top: 0; z-index: 5;
  width: auto; flex: none; height: auto; align-self: stretch; overflow: visible;
  display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 8px 12px; padding: 16px 28px 14px;
  border-right: none; border-bottom: 1px solid var(--border);
}
.rail > .dock-left { order: 1; flex: 1 0 0; display: flex; align-items: center; gap: 12px; height: 48px; }
.rail > a.brand { order: 2; flex: 0 0 auto; margin: 0; padding: 0; height: 48px; display: flex; align-items: center; font-size: 26px; }
.rail > .rail-foot { order: 3; flex: 1 0 0; height: 48px; margin: 0; padding: 0; border: none;
  display: flex; align-items: center; justify-content: flex-end; gap: 12px; }
.rail > .dock-tabs { order: 4; flex: 0 0 100%; display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.rail > .dock-tabs:not(:has(a:not([data-key="login"]):not([data-key="account"]))) { display: none; }
.rail > .spacer { display: none; }
.rail > .dock-tabs a { padding: 8px 14px; }
.rail > .dock-tabs a[data-key="login"], .rail > .dock-tabs a[data-key="account"] { display: none; }
.rail a.dock-ico, .rail-foot a.acct-icon {
  display: inline-flex !important; align-items: center; justify-content: center;
  box-sizing: border-box; width: 40px; height: 40px; padding: 0; border-radius: 50%;
  color: var(--text); border: 1px solid color-mix(in srgb, var(--text) 45%, transparent);
  background: none;
}
.rail a.dock-ico svg, .rail-foot a.acct-icon svg { width: 22px; height: 22px; opacity: .88; display: block; }
.rail a.dock-ico:hover, .rail-foot a.acct-icon:hover { background: var(--hover); }
.rail a.dock-ico.on, .rail-foot a.acct-icon.on { background: var(--rail-sel); border-color: color-mix(in srgb, var(--text) 65%, transparent); }
.rail a.dock-ico .nav-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.rail-foot .theme-switch { display: none !important; }
.rail a.brand:hover { opacity: 1; }
.content { width: 100%; max-width: 1180px; margin: 0 auto; padding: 32px 40px 56px; }
/* «Открыть» шире остальных страниц на 120 px с каждой стороны: каждой
   колонке свода хватает места (Артемий, 02.10.2026). */
.content:has(.rpt) { max-width: 1420px; }
@media (max-width: 720px) {
  .rail { padding: 10px 16px 10px; gap: 6px 8px; }
  .rail > .dock-left, .rail > a.brand, .rail > .rail-foot { height: 44px; }
  .rail > a.brand { font-size: 24px; }
  .rail a.dock-ico, .rail-foot a.acct-icon { width: 34px; height: 34px; }
  .rail a.dock-ico svg, .rail-foot a.acct-icon svg { width: 19px; height: 19px; }
  .rail > .dock-tabs a { padding: 10px 12px; }
  .content { padding: 24px 16px 48px; }
}

/* ---- без hover на сенсорных экранах; док на компьютере тоньше ----
   На телефоне hover «залипает» после касания: кнопка остаётся подсвеченной
   и сдвинутой. Там, где наведения нет физически, все эти эффекты
   выключены; на компьютере остаются. Док на компьютере ужат на 15 %. */
@media (hover: none) {
  .rail a:hover, .rail-foot a:hover, .btn:hover, .btn-ghost:hover, .btn-dl:hover,
  .menu-dl-list a:hover, .tile:hover, .row:hover, .btn-back:hover, .job-x:hover,
  .rail a.dock-ico:hover, .rail-foot a.acct-icon:hover, .utile-more:hover {
    background: inherit; border-color: inherit; color: inherit; box-shadow: none; transform: none;
  }
  .btn:hover { background: var(--primary-bg); color: var(--primary-text); }
  .btn-ghost:hover, .btn-dl:hover, .btn-back:hover, .tile:hover, .row:hover { background: var(--card); }
  .rail a.dock-ico:hover, .rail-foot a.acct-icon:hover, .rail a:hover, .job-x:hover, .menu-dl-list a:hover, .utile-more:hover { background: none; }
  .utile-drop:hover, .utile-go:hover, .utile-row:hover { transform: none; box-shadow: none; }
  .utile-go:hover { background: var(--primary-bg); }
  .utile-drop:hover { box-shadow: none; }
  .tile:hover .tile-open { background: none; }
}
/* Вкладка «Аккаунт» второй строкой: портрет справа теперь — библиотека. */
.rail > .dock-tabs a[data-key="account"] { display: inline-block; }
.rail > .dock-tabs:not(:has(a:not([data-key="login"]))) { display: none; }
@media (min-width: 721px) {
  .rail { padding: 12px 24px 10px; gap: 6px 10px; }
  .rail > .dock-left, .rail > a.brand, .rail > .rail-foot { height: 41px; }
  .rail > a.brand { font-size: 22px; }
  .rail a.dock-ico, .rail-foot a.acct-icon { width: 34px; height: 34px; }
  .rail a.dock-ico svg, .rail-foot a.acct-icon svg { width: 19px; height: 19px; }
  .rail > .dock-tabs a { padding: 6px 12px; }
}

/* ---- полоса загрузки DWG → PDF: только заливка и проценты по центру ---- */
.utile-go.up-minimal { justify-content: center; }
.utile-go.up-minimal .up-text { display: none; }
.utile-go.up-minimal .up-pct { position: relative; flex: none; margin: 0 auto; font-size: 18px; }
.utile-go.up-minimal.is-error .up-pct { display: none; }
.utile-go.up-minimal.is-error .up-text { display: flex; align-items: center; text-align: center; }

/* ---- главная DWG → PDF в тонком виде ----
   Колонка по центру: «Бесплатный продукт», кнопка NORTHENCAD, значок
   загрузки и окно с одной кнопкой файла. Окно само становится полосой:
   те же правила, что у «Продолжить», только заливка и проценты. */
.pdf-home { max-width: 520px; margin: 6vh auto 0; text-align: center; }
.pdf-free { margin: 0 0 14px; color: var(--muted); font-size: 13px; letter-spacing: .12em; text-transform: uppercase; }
.btn-cad {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 200px; padding: 12px 26px; border-radius: 999px;
  background: var(--primary-bg); color: var(--primary-text); text-decoration: none;
  font-weight: 600; letter-spacing: .08em; font-size: 14px;
}
.btn-cad:hover { background: var(--primary-hover); }
.pdf-drop-ico { margin: 34px auto 12px; width: 40px; height: 40px; color: var(--muted); opacity: .85; }
.pdf-drop-ico svg { width: 100%; height: 100%; display: block; }
.pdf-form { display: block; }
.pdf-box { min-height: 0; padding: 22px 24px; align-items: center; justify-content: center; }
.pdf-box .utile-pick { margin: 0; padding: 0; justify-content: center; }
/* окно → полоса (копия правил .utile-go.is-uploading для окна) */
.pdf-box { position: relative; overflow: hidden; transition: background .35s ease, transform .2s ease, box-shadow .2s ease; }
.pdf-box.is-uploading { cursor: default; transform: none; flex-direction: row; justify-content: center; align-items: center; min-height: 72px; }
.pdf-box .up-fill, .pdf-box .up-line { position: absolute; left: 0; width: 0; pointer-events: none; transition: width .25s linear; }
.pdf-box .up-fill { top: 0; bottom: 0; background: color-mix(in srgb, var(--success-text) var(--up-tint), transparent); }
.pdf-box .up-line { bottom: 0; height: 3px; background: var(--success-text); border-radius: 0 3px 3px 0; }
.pdf-box .up-text { display: none; }
.pdf-box .up-pct { position: relative; font-size: 18px; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.pdf-box.is-error .up-fill, .pdf-box.is-error .up-line { opacity: 0; }
.pdf-box.is-error .up-pct { display: none; }
.pdf-box.is-error .up-text { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.pdf-box.is-error .up-title { color: var(--error-text); font-weight: 600; }
.pdf-box.is-error .up-meta { color: var(--muted); font-size: 13px; }
@media (max-width: 720px) { .pdf-home { margin-top: 3vh; } }
/* Окно главной — одна кнопка: имени файла и подписи нет, окно сразу
   становится полосой. */
.pdf-home .pdf-free { margin-bottom: 22px; }
/* Окно главной — большой прямоугольник; заголовок NORTHENCAD над ним. */
.pdf-home { max-width: 680px; margin-top: 4vh; }
.pdf-home .pdf-free { font-size: 15px; letter-spacing: .18em; color: var(--text); opacity: .8; margin-bottom: 20px; }
/* На главной NORTHENCAD — ссылка на /northencad/ (28.09.2026): вид тот же,
   что у подписи над формой, без подчёркивания и с полем под палец. */
a.pdf-free { display: inline-block; padding: 12px 16px; text-decoration: none; }
a.pdf-free:hover { opacity: 1; }
.pdf-box { min-height: 260px; }
.pdf-box.is-uploading { min-height: 260px; }
@media (max-width: 720px) {
  .pdf-home { max-width: none; margin-top: 2vh; }
  .pdf-box, .pdf-box.is-uploading { min-height: 220px; }
}
/* Кнопка «Файл» 600×160, окно обнимает её; на телефоне — во всю ширину. */
.pdf-home { max-width: 680px; }
.pdf-box, .pdf-box.is-uploading { min-height: 0; padding: 20px; }
.pdf-box.is-uploading { min-height: 200px; }
.pdf-box .pdf-pick {
  display: flex; align-items: center; justify-content: center;
  width: 600px; max-width: 100%; height: 160px; box-sizing: border-box;
  font-size: 20px; font-weight: 600; letter-spacing: .02em; border-radius: 16px;
}
@media (max-width: 720px) {
  .pdf-box, .pdf-box.is-uploading { min-height: 0; padding: 16px; }
  .pdf-box.is-uploading { min-height: 192px; }
  .pdf-box .pdf-pick { width: 100%; }
}

/* ==== загрузка нескольких чертежей (25.09.2026) ====
   Файл уходит на сервер сразу после выбора: у каждого своя строка в плитке
   под зоной загрузки — значок формата, имя целиком, дата изменения и
   размер, справа ход или галочка и круглый крестик («отменить» пока идёт,
   «убрать» после). Полоса — тонкая линия под текстом строки, в своей
   строке сетки: нижний отступ она не занимает (у края плитки она «съедала»
   низ), а когда файл загрузился, мягко сворачивается. Строки разделены
   волосяной линией с отступом под значок, как списки в iOS. «Продолжить»
   только запускает разбор загруженного. */
/* Зона загрузки — одно крупное слово посередине, без заголовка и кнопки. */
.files-word { font-size: 44px; font-weight: 600; letter-spacing: .08em; line-height: 1; }
.stage-rows { list-style: none; margin: 0; padding: 0; }
.stage-row {
  display: grid; grid-template-rows: 1fr;
  transition: grid-template-rows .22s ease, opacity .18s ease;
  animation: stage-in .24s ease;
}
.stage-row.is-leaving { grid-template-rows: 0fr; opacity: 0; }
.stage-in {
  position: relative; min-height: 0; overflow: hidden;
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto 44px;
  grid-template-rows: auto 0px;           /* вторая строка — под полосу */
  align-items: center; column-gap: 12px;
  padding: 12px 6px 12px 18px;
  transition: grid-template-rows .25s ease;
}
.stage-row[data-state="uploading"] .stage-in,
.stage-row[data-state="queued"] .stage-in { grid-template-rows: auto 11px; }
.stage-row + .stage-row .stage-in::before {
  content: ""; position: absolute; top: 0; left: 70px; right: 0;
  border-top: 1px solid var(--border);
}
.stage-ico {
  width: 40px; height: 40px; border-radius: 11px;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--text) 7%, transparent); color: var(--muted);
  font-size: 11px; font-weight: 700; letter-spacing: .04em;
}
.stage-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.stage-name { font-size: 15px; font-weight: 500; line-height: 1.3; overflow-wrap: anywhere; }
.stage-meta { font-size: 13px; line-height: 1.35; color: var(--muted); font-variant-numeric: tabular-nums; }
.stage-row[data-state="error"] .stage-meta { color: var(--error-text); }
.stage-row[data-state="error"] .stage-ico { color: var(--error-text); background: var(--error-bg); }
.stage-state {
  min-width: 36px; text-align: right; font-size: 13px; color: var(--muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.stage-state svg { width: 18px; height: 18px; display: block; margin-left: auto; color: var(--success-text); }
.stage-retry {
  border: 0; background: none; padding: 10px 4px; margin: -10px 0;
  font: inherit; font-size: 13px; font-weight: 600; color: var(--accent); cursor: pointer;
}
.stage-x {
  width: 44px; height: 44px; padding: 0; border: 0; border-radius: 50%;
  background: none; color: var(--muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: color .15s ease, background .15s ease;
}
.stage-x svg { width: 22px; height: 22px; display: block; }
.stage-x:hover { color: var(--text); background: var(--hover); }
.stage-bar {
  grid-column: 2 / 4; grid-row: 2; align-self: end;
  height: 3px; max-height: 100%; border-radius: 2px; overflow: hidden;
  background: color-mix(in srgb, var(--success-text) 16%, transparent);
  opacity: 0; transition: opacity .2s ease; pointer-events: none;
}
.stage-bar > i {
  display: block; height: 100%; width: 0; border-radius: inherit;
  background: var(--success-text); transition: width .25s linear;
}
.stage-row[data-state="uploading"] .stage-bar,
.stage-row[data-state="queued"] .stage-bar { opacity: 1; }
@keyframes stage-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
/* Тот же файл выбран второй раз: строка коротко подсвечивается — «он уже тут». */
.stage-row.is-flash .stage-in { animation: stage-flash .9s ease; }
@keyframes stage-flash { from { background: var(--hover); } to { background: transparent; } }
/* Разделы программы на её главной — две плитки рядом: «Сейчас в обработке»
   с числом и «Загрузки» с шевроном. Шеврон смотрит вправо: знак «перейти»,
   как в списках iOS. */
.utile-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
/* По центру, а не по базовой линии: у левой плитки крупное число, и подпись
   вставала на пару пикселей ниже, чем у соседней «Загрузок». */
.utile-pair .utile-row { align-items: center; }
.utile-row .chev {
  flex: 0 0 auto; align-self: center; width: 8px; height: 8px; margin-right: 4px;
  border-right: 2px solid var(--muted); border-top: 2px solid var(--muted);
  transform: rotate(45deg);
}
/* Крошки «Главное меню / имя чертежа» переносятся по словам имени. */
.crumbs b { overflow-wrap: anywhere; }

/* Плитка задачи: время готовности вместо полосы и шагов. Готовая плитка
   встаёт на место идущей мягким появлением, ушедшая — тает. */
.job-ready { font-size: 14px; color: var(--muted); font-variant-numeric: tabular-nums; }
.utile.job.is-fresh { animation: tile-in .35s ease; }
.utile.job.is-leaving, [data-jobs-empty].is-leaving { opacity: 0; transform: scale(.98);
  transition: opacity .2s ease, transform .2s ease; }
@keyframes tile-in { from { opacity: 0; } to { opacity: 1; } }

@media (max-width: 720px) {
  /* На телефоне имени нужна вся ширина: значок чуть меньше, правый край не
     держит место под проценты, которых нет. Крестик остаётся 44px — палец. */
  .stage-in { grid-template-columns: 36px minmax(0, 1fr) auto 44px; padding: 12px 2px 12px 14px; column-gap: 8px; }
  .stage-ico { width: 36px; height: 36px; border-radius: 10px; font-size: 10px; }
  .stage-state { min-width: 0; }
  .stage-row + .stage-row .stage-in::before { left: 58px; }
}
@media (hover: none) {
  .stage-x:hover { color: var(--muted); background: none; }
}
/* На узком экране две плитки разделов рядом не помещают подпись в строку. */
@media (max-width: 560px) {
  .utile-pair { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
  .files-word { font-size: 36px; }
}

/* ==== главная POLAIR: одна плитка загрузки (26.09.2026) ====
   «ФАЙЛЫ», загруженные файлы, «Сеть», ЦХМ и «Продолжить» лежат на одном
   фоне строками во всю ширину, между строками — волосяная линия, как в
   настройках iPhone. У строк одна высота, 64px; зона «ФАЙЛЫ» — ровно две
   строки. Шрифт один: 16px, жирность 600, крупное только «ФАЙЛЫ». Те же
   16px/600 и та же высота — у плиток разделов под линией. */
.utile.upload-card { padding: 0; gap: 0; overflow: hidden; }
.upload-card > :not(:first-child) { border-top: 1px solid var(--border); }
.uc-drop, .uc-row, .uc-go, .utile-pair .utile-row {
  font-size: 16px; font-weight: 600; letter-spacing: -.01em;
}
/* Строки-label: общее правило label (13px, поля сверху) им не подходит. */
.uc-drop, .uc-row { margin: 0; }

/* Зона «ФАЙЛЫ»: нажатие открывает выбор, файл можно бросить сюда или в
   любое место страницы. Наведение подсвечивает зону, файл над страницей
   обводит всю плитку. */
.uc-drop {
  position: relative; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 12px; padding: 20px 24px;
  min-height: 128px; cursor: pointer; transition: background .2s ease;
}
/* Подсказка под «ФАЙЛЫ» — тем же шрифтом, что строки плитки, серым. */
.files-hint { color: var(--muted); text-align: center; }
.uc-drop input[type="file"] { position: absolute; width: 0; height: 0; opacity: 0; }
.uc-drop:hover, .uc-drop.drag { background: var(--hover); }
.upload-card:has(.uc-drop.drag),
.upload-card:has(.uc-drop input:focus-visible) {
  box-shadow: 0 18px 44px -18px rgba(30, 32, 38, .28), 0 0 0 2px var(--focus-ring);
}

/* Загруженные файлы: те же строки, что и раньше, но отступ слева — как у
   остальных строк плитки, и имя тем же шрифтом. */
.uc-list { padding: 4px 0; }
.upload-card .stage-in { padding: 12px 13px 12px 24px; }
.upload-card .stage-row + .stage-row .stage-in::before { left: 76px; }
.upload-card .stage-name { font-size: 16px; font-weight: 600; }

/* Строка: подпись слева, выбор справа. */
.uc-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  min-height: 64px; padding: 10px 24px;
}

/* Выбор сети — две кнопки рядом, как переключатель в iOS: оба варианта
   видны сразу, выбор — одно нажатие. Системный список раскрывался своим
   квадратным меню, которое не подстроить под плитку. Скругление умеренное:
   12px у подложки, 9px у кнопки — вложенное меньше внешнего. */
.seg {
  display: inline-flex; flex: 0 0 auto; gap: 2px; padding: 3px; border-radius: 12px;
  background: color-mix(in srgb, var(--text) 7%, transparent);
}
.seg-opt { position: relative; display: flex; margin: 0; font-size: inherit; cursor: pointer; }
.seg-opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.seg-opt span {
  display: flex; align-items: center; min-height: 38px; padding: 0 16px; border-radius: 9px;
  color: var(--muted); white-space: nowrap;
  transition: background .2s ease, color .2s ease, box-shadow .2s ease;
}
.seg-opt:hover span { color: var(--text); }
.seg-opt input:checked + span {
  background: var(--seg-on); color: var(--text);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 0 0 .5px rgba(0, 0, 0, .04);
}
.seg-opt input:focus-visible + span { box-shadow: 0 0 0 3px var(--focus-ring); }
.seg-opt input:checked:focus-visible + span {
  box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 0 0 3px var(--focus-ring);
}

/* Флажок строкой: точка и подпись, нажимается вся строка. Точка 14px при
   кегле 16px, обводка 1,5px, заливка при включении. */
.uc-switch { justify-content: flex-start; gap: 12px; cursor: pointer; user-select: none;
  transition: background .2s ease; }
.uc-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.uc-switch .dot {
  flex: 0 0 auto; width: 14px; height: 14px; border-radius: 50%;
  border: 1.5px solid var(--text); background: transparent;
  transition: background .15s ease, box-shadow .15s ease;
}
.uc-switch input:checked + .dot { background: var(--text); }
.uc-switch input:focus-visible + .dot { box-shadow: 0 0 0 3px var(--focus-ring); }
.uc-switch:hover { background: var(--hover); }
/* Строку ЦХМ открывает и прячет выбор сети: она раскрывается и
   сворачивается по высоте, «Продолжить» под ней не прыгает. */
.uc-row.is-fresh, .uc-row.is-leaving { overflow: hidden; }
.uc-row.is-fresh { animation: uc-row-in .26s ease; }
.uc-row.is-leaving { animation: uc-row-out .2s ease forwards; pointer-events: none; }
@keyframes uc-row-in {
  from { min-height: 0; max-height: 0; padding-top: 0; padding-bottom: 0; border-top-width: 0; opacity: 0; }
  to { min-height: 64px; max-height: 96px; opacity: 1; }
}
@keyframes uc-row-out {
  from { min-height: 64px; max-height: 96px; opacity: 1; }
  to { min-height: 0; max-height: 0; padding-top: 0; padding-bottom: 0; border-top-width: 0; opacity: 0; }
}

/* «Продолжить» — нижняя строка плитки. Пока отправлять нечего, это тихая
   серая надпись; когда есть — строка заливается тёмным. */
.uc-go {
  display: flex; align-items: center; justify-content: center; width: 100%;
  min-height: 64px; padding: 10px 24px; margin: 0; border: 0;
  font-family: inherit; line-height: inherit; cursor: pointer;
  background: var(--primary-bg); color: var(--primary-text);
  transition: background .25s ease, color .25s ease;
}
.uc-go:hover { background: var(--primary-hover); }
.uc-go:disabled { background: transparent; color: var(--muted); cursor: default; }
/* Обводка внутрь: наружную срезал бы край плитки. */
.uc-go:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
/* Отказ «Продолжить» — полосой над кнопкой, во всю ширину плитки. */
.upload-card .stage-note {
  margin: 0; border-radius: 0; border-width: 1px 0 0; border-color: var(--border);
  padding: 14px 24px;
}

/* Плитки разделов — той же высоты, что строки плитки выше. */
.utile-pair .utile-row { min-height: 64px; padding: 10px 24px; }
.utile-pair .utile-row b { font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums; }

@media (max-width: 720px) {
  .uc-row, .uc-go, .upload-card .stage-note, .utile-pair .utile-row { padding-left: 18px; padding-right: 18px; }
  .upload-card .stage-in { padding: 12px 7px 12px 18px; }
  .upload-card .stage-row + .stage-row .stage-in::before { left: 62px; }
}
/* Совсем узкий экран: переключатель встаёт под подпись во всю ширину,
   кнопки делят её поровну. */
@media (max-width: 360px) {
  .uc-row:has(.seg) { flex-wrap: wrap; row-gap: 10px; padding-top: 12px; padding-bottom: 12px; }
  .uc-row .seg { flex: 1 1 100%; }
  .uc-row .seg-opt { flex: 1 1 0; }
  .uc-row .seg-opt span { flex: 1; justify-content: center; padding: 0 10px; }
}
@media (hover: none) {
  .uc-drop:hover, .uc-switch:hover { background: none; }
  .uc-go:not(:disabled):hover { background: var(--primary-bg); }
  .seg-opt:hover span { color: var(--muted); }
}

/* ---- genDWG: форма и набор (Артемий, 27.09.2026) ----
   Плитки форм крупные: контур видно сразу, под ним имя и размер; выбранная
   обведена цветом акцента. Набор — группы по бренду: заголовок сворачивает
   группу, в строке вид, размер и число. Толщину линий контура держит
   vector-effect: зал 8 м и магазин 120 м рисуются одинаково тонко. */
.gd { max-width: 920px; margin: 0 auto; }
.gd-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.gd-tabs .seg-opt span { min-height: 46px; padding: 0 24px; font-size: 16px; font-weight: 600; }
.gd-group { font-size: 13px; font-weight: 600; color: var(--muted); margin: 22px 0 10px; }
.gd-forms { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 14px; }
.gd-form {
  display: flex; flex-direction: column; gap: 6px; text-align: left;
  padding: 14px; border: 0; border-radius: 18px; cursor: pointer;
  background: var(--card); color: var(--text); font: inherit;
  box-shadow: 0 0 0 1px var(--border);
  transition: box-shadow .15s ease, transform .15s ease;
}
.gd-form:hover { transform: translateY(-1px); box-shadow: 0 0 0 1px var(--border-strong); }
.gd-form[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--accent); }
.gd-form:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }
.gd-shape { display: block; width: 100%; height: 130px; }
.gd-shape path { fill: none; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.gd-shape .gd-wall { stroke: var(--text); stroke-width: 2; }
.gd-shape .gd-zone { stroke: var(--border-strong); stroke-width: 1; }
.gd-shape .gd-hole { fill: var(--border-strong); }
/* Красные зоны формы — кассы и путь к выходу, туда оборудование не ставится. */
.gd-shape .gd-red { fill: color-mix(in srgb, var(--error-text) 30%, transparent); }
.gd-form-name { font-weight: 600; font-size: 14px; line-height: 1.3; }
.gd-form-size { color: var(--muted); font-size: 13px; }

.gd-chosen {
  display: flex; align-items: center; gap: 14px; width: 100%; margin: 14px 0 16px;
  padding: 12px 16px; border: 0; border-radius: 16px; cursor: pointer; text-align: left;
  background: var(--card); color: var(--text); font: inherit; box-shadow: 0 0 0 1px var(--border);
}
.gd-chosen-shape { flex: 0 0 72px; }
.gd-chosen-shape .gd-shape { height: 52px; }
.gd-chosen-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.gd-chosen-change { font-size: 13px; }

.gd-brand { background: var(--card); border-radius: 14px; box-shadow: 0 0 0 1px var(--border); margin-bottom: 10px; overflow: hidden; }
.gd-brand-head {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 14px 16px;
  border: 0; background: none; color: var(--text); font: inherit; text-align: left; cursor: pointer;
}
.gd-brand-name { font-weight: 600; font-size: 16px; overflow-wrap: anywhere; }
.gd-brand-sum { flex: 1; font-size: 13px; }
.gd-chev { color: var(--muted); font-size: 20px; line-height: 1; transition: transform .2s ease; }
.gd-brand-head[aria-expanded="true"] .gd-chev { transform: rotate(90deg); }
.gd-row { display: flex; align-items: center; gap: 10px; padding: 10px 16px; border-top: 1px solid var(--border); }
.gd-row-name { flex: 1; min-width: 0; }
.gd-step { display: inline-flex; align-items: center; gap: 6px; }
.gd-step button, .gd-x {
  width: 36px; height: 36px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--border-strong); background: transparent; color: var(--text);
  font: inherit; font-size: 18px; line-height: 1;
}
.gd-step button:disabled { color: var(--muted); border-color: var(--border); cursor: default; }
.gd-count {
  width: 64px; height: 36px; box-sizing: border-box; text-align: center; font: inherit; color: var(--text);
  background: var(--card); border: 1px solid var(--border-strong); border-radius: 10px;
}
.gd-x { border-color: transparent; color: var(--muted); }
.gd-x:hover { color: var(--error-text); }
.gd-auto-chip { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 14px; cursor: pointer; }
.gd-brand-add {
  display: block; width: 100%; padding: 10px 16px; border: 0; border-top: 1px solid var(--border);
  background: none; color: var(--muted); font: inherit; font-size: 14px; text-align: left; cursor: pointer;
}
.gd-add {
  width: 100%; min-height: 52px; margin: 4px 0 16px; border-radius: 14px; cursor: pointer;
  border: 1px dashed var(--border-strong); background: transparent; color: var(--text);
  font: inherit; font-size: 16px;
}
.gd-brand-add:hover, .gd-add:hover { color: var(--text); background: var(--hover); }
.gd-empty { margin: 4px 0 12px; }
.gd-new { gap: 12px; margin-bottom: 16px; }
.gd-new .gd-kind { align-self: flex-start; }
.gd-note { margin: 0; }
.gd-field { display: flex; flex-direction: column; gap: 6px; margin: 0; color: var(--muted); }
.gd-field input {
  width: 100%; box-sizing: border-box; padding: 10px 12px; font: inherit; font-size: 16px;
  color: var(--text); background: var(--card); border: 1px solid var(--border-strong); border-radius: 10px;
}
.gd-dims { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.gd-auto { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 14px; cursor: pointer; }
.gd-new-actions { display: flex; gap: 10px; }
.gd-go { width: 100%; margin-top: 8px; }
.gd-plan { display: block; width: 100%; height: auto; margin-top: 8px; border-radius: 12px; box-shadow: 0 0 0 1px var(--border); }
.row a.name { color: var(--text); text-decoration: none; }
.row a.name:hover { text-decoration: underline; }
/* Предпросмотр рядом с набором (Артемий, 27.09.2026): на компьютере набор
   слева, картинка справа и прилипает под верхней панелью (79 px), на
   телефоне — под карточкой формы. Две колонки — от 960 px: уже строка
   «Холодильник 2500 × 900» со степпером переносится. Вкладка «Оборудование»
   ради них шире, «Форма» — прежняя. */
.gd:has(> [data-gd-panel="set"]:not([hidden])) { max-width: 1120px; }
.gd-set {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-rows: auto 1fr; grid-template-areas: "chosen look" "list look";
  column-gap: 20px; align-items: start;
}
.gd-set > .gd-chosen { grid-area: chosen; }
.gd-set-list { grid-area: list; min-width: 0; }
.gd-look {
  grid-area: look; position: sticky; top: 96px; margin: 14px 0 16px; padding: 12px;
  border-radius: 16px; background: var(--card); box-shadow: 0 0 0 1px var(--border);
}
.gd-look-pic { display: flex; align-items: center; justify-content: center; min-height: 200px; }
/* Высота — чтобы и картинка, и подпись под ней были видны сразу при
   открытии: карточка начинается около 180 px от верха, ещё ~80 px — поля и
   подпись. Высокий зал 8 × 12 иначе уводил подпись за край экрана. */
.gd-look-img {
  display: block; max-width: 100%; max-height: calc(100vh - 260px); width: auto; height: auto;
  border-radius: 10px; box-shadow: 0 0 0 1px var(--border); transition: opacity .15s ease;
}
.gd-look-shape { display: block; width: 100%; }
.gd-look-shape .gd-shape { height: 200px; opacity: .55; }
.gd-look-note { margin: 10px 4px 2px; text-align: center; font-size: 14px; color: var(--muted); }
.gd-look[data-state="busy"] .gd-look-img, .gd-look[data-state="busy"] .gd-look-shape { opacity: .45; }
.gd-look[data-state="bad"] .gd-look-pic { display: none; }
.gd-look[data-state="bad"] .gd-look-note { padding: 72px 0; font-size: 16px; color: var(--text); }
@media (max-width: 959px) {
  .gd-set { grid-template-columns: minmax(0, 1fr); grid-template-rows: none;
            grid-template-areas: "chosen" "look" "list"; }
  .gd-look { position: static; margin-top: 0; }
  .gd-look-img { max-height: 60vh; }
}
@media (max-width: 620px) {
  .gd-top { flex-wrap: wrap; }
  .gd-forms { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .gd-shape { height: 96px; }
  .gd-dims { grid-template-columns: 1fr 1fr; }
}

/* ==== главная POLAIR: слева добавить в очередь, справа очередь ====
   По образцу genDWG (Артемий, 28.09.2026). Слева одна большая плитка
   (прилипает под доком, пока очередь листается) и под ней отдельная плитка
   действия «Добавить в очередь» со стрелкой вправо — проход в правую
   колонку. Справа всё одной плиткой: очередь — те же строки с волосяными
   линиями, шрифт 16px/600. Разделителя между колонками нет — его сняли:
   плиток достаточно. На телефоне очередь сверху, добавление снизу, стрелка
   смотрит вверх. Тот же раскрой — у NORTHENCAD. */
.pq {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 32px; align-items: start; max-width: 1120px; margin: 0 auto;
}
.pq-add { position: sticky; top: 96px; min-width: 0; }
.pq-queue { min-width: 0; }
.uc-go { gap: 10px; }
.go-arrow { flex: none; width: 18px; height: 18px; transition: transform .25s ease; }
.uc-go:not(:disabled):hover .go-arrow { transform: translateX(3px); }
/* «Добавить в очередь» — своей кнопкой под плиткой, с воздухом над ней:
   строкой плитки она липла к «Сети» и ЦХМ. Та же высота и скругление, что
   у плиток; неактивная — светлая, с тонкой обводкой. */
.pq-go {
  margin-top: 14px; border-radius: 22px;
  box-shadow: 0 18px 44px -18px rgba(30, 32, 38, .28), 0 0 0 1px rgba(0, 0, 0, .05);
}
.pq-go:disabled { background: var(--card); box-shadow: 0 0 0 1px var(--border); }
.pq-go:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }

/* Строка-пояснение в плитке загрузки (гостю NORTHENCAD — его лимит):
   тихая, по центру, не спорит со строками выбора. */
.uc-row.uc-note { justify-content: center; text-align: center; color: var(--muted);
  font-size: 14px; font-weight: 500; min-height: 52px; }

/* Сеть — меткой перед именем задачи: «Чижик» или «Перекрёсток». */
.job-chain {
  display: inline-block; margin-right: 8px; padding: 2px 8px; border-radius: 6px;
  background: color-mix(in srgb, var(--text) 7%, transparent); color: var(--muted);
  font-size: 13px; font-weight: 600; letter-spacing: 0; vertical-align: 2px;
}

/* Карточка очереди: шапка, пустая строка или задачи, «Очистить». Меню
   «Скачать» выходит за край карточки, поэтому без overflow: hidden, а
   скругление нижней строки задано ей самой. */
.utile.pq-card { padding: 0; gap: 0; }
.pq-card > :not(:first-child) { border-top: 1px solid var(--border); }
.pq-head {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  min-height: 64px; padding: 10px 24px;
}
.pq-title {
  display: flex; align-items: center; gap: 10px; min-width: 0; margin: 0;
  font-size: 16px; font-weight: 600; letter-spacing: -.01em; white-space: nowrap;
}
/* «Загрузки» — та же кнопка, что «История» у genDWG, но ровной высоты со
   строкой: иначе на телефоне она выдавливала заголовок во вторую строку. */
.pq-head .pq-uploads {
  flex: none; display: inline-flex; align-items: center; min-height: 40px; padding: 0 14px;
  font-size: 15px; font-weight: 600;
}
.pq-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 26px; padding: 0 8px; border-radius: 8px;
  background: color-mix(in srgb, var(--text) 7%, transparent);
  font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.pq-empty {
  display: flex; align-items: center; justify-content: center; min-height: 128px;
  padding: 20px 24px; color: var(--muted); font-size: 16px; font-weight: 600; text-align: center;
}
/* Задача — строка карточки. Место справа держим под крестик готовой, чтобы
   имя не переносилось заново, когда он появится. */
.pq-card > .utile.job {
  border-radius: 0; box-shadow: none; background: transparent;
  gap: 6px; padding: 16px 56px 16px 24px;
}
.pq-card .utile-title { font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }
.pq-card .job-actions { margin-top: 4px; }
.pq-card .menu-dl-list { left: 0; right: auto; }   /* меню растёт вправо, внутрь колонки */
.pq-clear {
  display: block; width: 100%; min-height: 56px; margin: 0; padding: 10px 24px; border: 0;
  border-radius: 0 0 22px 22px; background: transparent; color: var(--muted);
  font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;
  transition: background .2s ease, color .2s ease;
}
.pq-clear:hover { background: var(--hover); color: var(--text); }

/* Где задача: «В очереди» — пустое кольцо, «В работе» — точка цвета
   ссылок с тихой пульсацией. Рядом время готовности; сменилось — текст
   мягко проявляется заново. */
.job-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; }
.job-state {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 600; color: var(--muted); transition: color .3s ease;
}
.job-state::before {
  content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px currentColor; transition: background .3s ease;
}
.job-state[data-job-state="running"] { color: var(--accent); }
.job-state[data-job-state="running"]::before {
  background: currentColor; animation: pq-pulse 1.8s ease-out infinite;
}
@keyframes pq-pulse {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 45%, transparent); }
  to { box-shadow: 0 0 0 7px color-mix(in srgb, currentColor 0%, transparent); }
}
.job-ready.is-tick { animation: pq-tick .5s ease; }
@keyframes pq-tick { from { opacity: .3; } to { opacity: 1; } }
/* Новая задача въезжает сверху, раскрываясь по высоте; готовая встаёт на
   место идущей с короткой зелёной подсветкой. */
.pq-card > .utile.job.is-new { overflow: hidden; animation: pq-in .32s ease; }
@keyframes pq-in {
  from { opacity: 0; max-height: 0; padding-top: 0; padding-bottom: 0; }
  to { opacity: 1; max-height: 220px; }
}
.pq-card > .utile.job.is-fresh { animation: pq-done 1.4s ease; }
@keyframes pq-done { from { background: var(--success-bg); } to { background: transparent; } }

@media (max-width: 959px) {
  .pq { grid-template-columns: minmax(0, 1fr); row-gap: 18px; max-width: 640px; }
  .pq-queue { order: -1; }
  .pq-add { order: 1; position: static; }
  .go-arrow { transform: rotate(-90deg); }
  .uc-go:not(:disabled):hover .go-arrow { transform: rotate(-90deg) translateX(3px); }
  .pq-empty { min-height: 88px; }
}
@media (max-width: 720px) {
  .pq-head, .pq-clear { padding-left: 18px; padding-right: 18px; }
  .pq-card > .utile.job { padding: 14px 50px 14px 18px; }
  .pq-clear { border-radius: 0 0 18px 18px; }
  .pq-go { border-radius: 18px; }
}
@media (max-width: 360px) {
  .pq-title { white-space: normal; }   /* совсем узко: пусть переносится */
}

/* ---- Отчёт «Открыть» (report_open.html, report.js) ----
   Одна плитка во всю колонку: шапка, «Свод» таблицей, низ с «Скачать».
   По высоте плитка равна окну (report.js ставит max-height), прокручивается
   только таблица. Правленая ячейка — жёлтая, снятая строка — серая. */
.rpt {
  --rpt-edit-bg: rgba(250, 204, 21, .28); --rpt-edit-line: rgba(202, 138, 4, .55);
  --rpt-off-bg: #F1F1F3; --rpt-off-text: #A5A5AB;
  gap: 0; padding: 22px 26px 18px; min-height: 0; overflow: hidden;
}
:root[data-theme="dark"] .rpt {
  --rpt-edit-bg: rgba(250, 204, 21, .18); --rpt-edit-line: rgba(250, 204, 21, .5);
  --rpt-off-bg: #1B1B1D; --rpt-off-text: #5E5E64;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .rpt {
    --rpt-edit-bg: rgba(250, 204, 21, .18); --rpt-edit-line: rgba(250, 204, 21, .5);
    --rpt-off-bg: #1B1B1D; --rpt-off-text: #5E5E64;
  }
}
.rpt-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.rpt-tag {
  padding: 2px 9px; border-radius: 6px; background: color-mix(in srgb, var(--text) 7%, transparent);
  color: var(--muted); font-size: 13px; font-weight: 600;
}
.rpt-name { margin: 0; font-size: 20px; font-weight: 600; line-height: 1.3; letter-spacing: -.01em; word-break: break-word; }
/* Предпросмотр — файлом, справа от названия, контуром, как «Загрузки». */
.rpt-pdf {
  margin-left: auto; display: inline-flex; align-items: center; gap: 8px; height: 38px; padding: 0 16px;
  border: 1px solid var(--border); border-radius: 12px; color: var(--text); text-decoration: none;
  font-size: 14px; font-weight: 600; white-space: nowrap;
}
.rpt-pdf:hover, .rpt-pdf:focus-visible { background: var(--hover); outline: none; }
.rpt-pdf svg { width: 16px; height: 16px; }
.rpt-pdf.is-wait { color: var(--muted); }
.rpt-pdf small { font-weight: 400; color: var(--muted); }
/* «Скачать» чертежа — меню у правого края; список растёт влево, внутрь
   карточки, и на телефоне тоже: кнопка там стоит справа. */
.rpt-pdf-menu { margin-left: auto; }
.rpt-pdf-menu > summary.rpt-pdf { margin-left: 0; cursor: pointer; }
.rpt-pdf-menu .menu-dl-list { left: auto; right: 0; min-width: 240px; }
/* Отступ под шапкой больше, чем в макете: таблица не липнет к названию. */
.rpt-meta { margin: 6px 0 26px; color: var(--muted); font-size: 14px; }
.rpt-admin a { margin-left: 12px; color: var(--muted); font-size: 13px; }
.rpt > .notice { margin: 0 0 14px; }

.rpt-scroll { position: relative; flex: 1 1 auto; min-height: 0; overflow: auto; margin: 0 -6px; padding: 0 6px; }
.rpt-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.rpt-table col.rpt-c-n { width: 40px; }
.rpt-table col.rpt-c-qty { width: 84px; }
.rpt-table col.rpt-c-cold { width: 132px; }
.rpt-table col.rpt-c-plu { width: 96px; }
/* У Перекрёстка: наименование заказчика — всё свободное место, бренд шире
   позиции (Артемий, 02.10.2026). Доли — от ширины таблицы. */
.rpt-table col.rpt-c-pos { width: 19%; }
.rpt-table col.rpt-c-brand { width: 25%; }
.rpt-table col.rpt-c-gap { width: 28px; }
.rpt-table col.rpt-c-chk { width: 44px; }
.rpt-table th {
  position: sticky; top: 0; z-index: 1; background: var(--card); vertical-align: bottom;
  text-align: left; font-size: 12.5px; font-weight: 600; color: var(--muted);
  padding: 0 10px 8px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
.rpt-table td {
  padding: 0 10px; height: 38px; border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: background .12s, color .12s;
}
.rpt-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.rpt-table td.rpt-n { color: var(--muted); font-variant-numeric: tabular-nums; }
.rpt-table .rpt-gap { padding: 0; border-bottom-color: transparent; }
.rpt-table .rpt-chk { padding: 0; text-align: center; cursor: pointer; line-height: 0; }
.rpt-table tr.rpt-row:hover td:not(.rpt-gap) { background: var(--hover); }

.rpt-table [contenteditable] { cursor: text; outline: none; }
.rpt-table td[contenteditable]:focus, .rpt-table td:focus-within {
  box-shadow: inset 0 0 0 1.5px var(--muted); border-radius: 6px; text-overflow: clip;
}
.rpt-table td.rpt-chk:focus-within { box-shadow: none; }
.rpt-table td.rpt-edited, .rpt-table tr.rpt-row:hover td.rpt-edited {
  background: var(--rpt-edit-bg); box-shadow: inset 0 -2px 0 var(--rpt-edit-line);
}
.rpt-table td.rpt-edited:focus, .rpt-table td.rpt-edited:focus-within { box-shadow: inset 0 0 0 1.5px var(--rpt-edit-line); }
.rpt-table tr.rpt-off td:not(.rpt-gap), .rpt-table tr.rpt-off:hover td:not(.rpt-gap) {
  background: var(--rpt-off-bg); color: var(--rpt-off-text);
}
.rpt-table tr.rpt-off td.rpt-edited { box-shadow: none; }
.rpt-table tr.rpt-off .rpt-warn { opacity: .5; }
/* Текст ячейки с меткой обрезается сам, чтобы метка оставалась на виду. */
.rpt-ed {
  display: inline-block; max-width: calc(100% - 90px); overflow: hidden; text-overflow: ellipsis;
  vertical-align: bottom; min-width: 1em;
}
.rpt-warn {
  margin-left: 8px; padding: 1px 7px; border-radius: 6px; font-size: 12px; font-weight: 600;
  background: var(--warn-bg); color: var(--warn-text); vertical-align: 1px;
}
/* «проверить» — кнопка: по щелчку окно с тем, как позиция написана на чертеже. */
button.rpt-warn { border: 0; font-family: inherit; line-height: inherit; cursor: pointer; }
@media (hover: hover) { button.rpt-warn:hover { filter: brightness(.95); text-decoration: underline; } }
button.rpt-warn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rpt-why-list { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: 10px 14px; }
.rpt-why-list dt { font-size: 13px; color: var(--muted); }
.rpt-why-list dd { margin: 0; font-size: 14px; line-height: 1.4; overflow-wrap: anywhere; }
@media (max-width: 520px) {
  .rpt-why-list { grid-template-columns: 1fr; gap: 2px; }
  .rpt-why-list dd { margin-bottom: 10px; }
}
/* Раздел — своё название и своя строка колонок, как шапка таблицы. */
.rpt-table tr.rpt-section td {
  height: 45px; padding-top: 19px; font-size: 12.5px; font-weight: 600; color: var(--muted);
  letter-spacing: .02em; border-bottom: none; vertical-align: bottom;
}
/* Первая категория — у самого верха плитки: отступ над ней не нужен. */
.rpt-table tr.rpt-section.rpt-first td { height: 22px; padding-top: 0; }
.rpt-table tr.rpt-subhead td {
  height: 30px; font-size: 12.5px; font-weight: 600; color: var(--muted);
  border-bottom: 1px solid var(--border); vertical-align: bottom; padding-bottom: 8px;
}

.rpt-table tr.rpt-total td { height: 40px; font-weight: 600; border-bottom: none; }
.rpt-table tr.rpt-total td.rpt-lbl { white-space: normal; line-height: 1.25; }
/* «+ Добавить» — строка того же размера, при наведении светится как ячейка. */
.rpt-table tr.rpt-add td { padding: 0; border-bottom: none; }
.rpt-add-btn {
  display: block; width: 100%; height: 34px; text-align: left;
  border: none; background: none; padding: 0 10px; border-radius: 8px;
  font: inherit; color: var(--muted); cursor: pointer;
}
.rpt-add-btn:hover, .rpt-add-btn:focus-visible { background: var(--hover); color: var(--text); outline: none; }
.rpt-box {
  appearance: none; -webkit-appearance: none; margin: 0; cursor: pointer;
  width: 19px; height: 19px; border-radius: 6px; border: 1.5px solid var(--muted);
  display: grid; place-items: center; margin: 0 auto; transition: background .12s, border-color .12s;
}
.rpt-box:checked, .rpt-box:indeterminate { background: var(--primary-bg); border-color: var(--primary-bg); }
.rpt-box:checked::after {
  content: ""; width: 8px; height: 5px; margin-top: -2px;
  border-left: 2px solid var(--primary-text); border-bottom: 2px solid var(--primary-text);
  transform: rotate(-45deg);
}
.rpt-box:indeterminate::after { content: ""; width: 8px; height: 2px; background: var(--primary-text); }
/* Галочка в строке названий колонок стоит на той же линии, что и сами
   названия: снизу тот же отступ, что у текста, а не ноль (Артемий, 29.09.2026). */
.rpt-table th.rpt-chk, .rpt-table tr.rpt-subhead td.rpt-chk { vertical-align: bottom; padding-bottom: 7px; }
.rpt-box:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
/* В строке галочка стоит посреди ячейки, но глаз меряет по цифрам, а их
   середина выше середины строки текста: без сдвига галочка «липла к низу». */
.rpt-table tr.rpt-row td.rpt-chk .rpt-box { transform: translateY(-2px); }

.rpt-foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 16px; flex: none; }
.rpt-summary { color: var(--muted); font-size: 14px; }
.rpt-summary b { color: var(--text); font-weight: 600; }
.rpt-reset {
  border: none; background: none; padding: 0 0 0 12px; font: inherit; font-size: 13px;
  color: var(--muted); cursor: pointer; text-decoration: underline;
}
.rpt-reset[hidden] { display: none; }
.rpt-dl, .rpt-go {
  display: inline-flex; align-items: center; gap: 10px; height: 46px; padding: 0 26px; flex: none;
  border: none; border-radius: 14px; background: var(--primary-bg); color: var(--primary-text);
  font: inherit; font-weight: 600; font-size: 15px; cursor: pointer; text-decoration: none;
}
.rpt-dl:hover, .rpt-go:hover { background: var(--primary-hover); }
.rpt-dl:disabled, .rpt-go:disabled { opacity: .4; cursor: default; }
.rpt-dl.is-busy, .rpt-go.is-busy { opacity: .6; cursor: progress; }
.rpt-dl svg, .rpt-go svg { width: 18px; height: 18px; }
/* Перекрёсток (Артемий, 08.10.2026): главное — «Продолжить» в М&Д
   Перекрёсток, «Скачать» книгу — рядом, тихой кнопкой. */
.rpt-acts { display: flex; align-items: center; gap: 8px; flex: none; }
.rpt-dl.rpt-quiet { background: none; color: var(--muted); padding: 0 14px; font-weight: 500; }
.rpt-dl.rpt-quiet:hover { background: var(--hover); color: var(--text); }
/* Меню правой кнопки: одно действие — удалить строку. */
.rpt-menu {
  position: fixed; z-index: 20; min-width: 170px; padding: 6px; border-radius: 12px;
  background: var(--card); box-shadow: 0 18px 44px -18px rgba(0, 0, 0, .45), 0 8px 24px rgba(0, 0, 0, .18), 0 0 0 1px var(--border);
}
.rpt-menu[hidden] { display: none; }
/* Подбор новой строки по спецификации (report.js): то же стекло, что у меню. */
.rpt-suggest {
  position: fixed; z-index: 21; max-height: 340px; overflow: auto; padding: 6px; border-radius: 12px;
  background: var(--card); box-shadow: 0 18px 44px -18px rgba(0, 0, 0, .45), 0 8px 24px rgba(0, 0, 0, .18), 0 0 0 1px var(--border);
}
.rpt-suggest[hidden] { display: none; }
.rpt-suggest button {
  display: block; width: 100%; text-align: left; border: none; background: none; cursor: pointer;
  padding: 7px 10px; border-radius: 8px; font: inherit; color: var(--text);
}
.rpt-suggest button.on, .rpt-suggest button:hover { background: var(--hover); }
.rpt-sg-name { display: block; font-size: 14px; line-height: 1.3; }
.rpt-sg-sub { display: block; margin-top: 2px; font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.rpt-menu button {
  display: block; width: 100%; text-align: left; border: none; background: none; cursor: pointer;
  padding: 8px 12px; border-radius: 8px; font: inherit; font-size: 14px; color: var(--error-text);
}
.rpt-menu button:hover, .rpt-menu button:focus-visible { background: var(--hover); outline: none; }

/* Низкие экраны: строки плотнее, чтобы свод помещался без прокрутки. */
@media (max-height: 960px) {
  .rpt { padding: 18px 24px 14px; }
  .rpt-meta { margin-bottom: 20px; }
  .rpt-table td { height: 30px; }
  .rpt-table tr.rpt-total td { height: 32px; }
  .rpt-add-btn { height: 28px; }
  .rpt-table tr.rpt-section td { height: 39px; padding-top: 15px; }
  .rpt-table tr.rpt-section.rpt-first td { height: 20px; }
  .rpt-table tr.rpt-subhead td { height: 26px; padding-bottom: 6px; }
  .rpt-table tr.rpt-subhead td.rpt-chk { padding-bottom: 6px; }
  .rpt-foot { margin-top: 12px; }
  .rpt-dl, .rpt-go { height: 42px; }
}
@media (max-height: 800px) {
  .rpt { padding: 14px 22px 12px; }
  .rpt-name { font-size: 18px; }
  .rpt-meta { margin: 2px 0 16px; font-size: 13px; }
  .rpt-pdf { height: 32px; padding: 0 12px; font-size: 13px; }
  .rpt-table { font-size: 14px; }
  .rpt-table th { padding-bottom: 6px; }
  .rpt-table th.rpt-chk { padding-bottom: 5px; }
  .rpt-table td { height: 27px; }
  .rpt-table tr.rpt-total td { height: 29px; }
  .rpt-add-btn { height: 25px; }
  .rpt-table tr.rpt-section td { height: 33px; padding-top: 11px; }
  .rpt-table tr.rpt-section.rpt-first td { height: 18px; }
  .rpt-table tr.rpt-subhead td { height: 22px; padding-bottom: 4px; }
  .rpt-table tr.rpt-subhead td.rpt-chk { padding-bottom: 3px; }
  .rpt-foot { margin-top: 10px; }
  .rpt-dl, .rpt-go { height: 38px; }
}
@media (max-height: 680px) {
  .rpt { padding: 12px 22px 10px; }
  .rpt-meta { margin: 0 0 12px; }
  .rpt-table th { padding-bottom: 4px; }
  .rpt-table th.rpt-chk { padding-bottom: 3px; }
  .rpt-table td { height: 25px; }
  .rpt-add-btn { height: 23px; }
  .rpt-table tr.rpt-total td { height: 26px; }
  .rpt-table tr.rpt-section td { height: 29px; padding-top: 9px; }
  .rpt-table tr.rpt-section.rpt-first td { height: 16px; }
  .rpt-table tr.rpt-subhead td { height: 20px; padding-bottom: 3px; }
  .rpt-table tr.rpt-subhead td.rpt-chk { padding-bottom: 2px; }
  .rpt-foot { margin-top: 8px; }
  .rpt-dl, .rpt-go { height: 36px; }
}
@media (max-width: 760px) {
  .rpt { padding: 18px 14px 14px; border-radius: 18px; }
  .rpt-table col.rpt-c-cold { width: 76px; }
  .rpt-table col.rpt-c-gap { width: 8px; }
  .rpt-foot { flex-direction: column; align-items: stretch; }
  .rpt-dl, .rpt-go { justify-content: center; }
  .rpt-acts { flex-direction: column-reverse; align-items: stretch; }
}
@media (max-width: 520px) {
  .rpt-table col.rpt-c-n { width: 30px; }
  .rpt-table col.rpt-c-qty { width: 58px; }
  .rpt-table col.rpt-c-chk { width: 36px; }
  .rpt-table td, .rpt-table th { padding-left: 6px; padding-right: 6px; }
}
@media (hover: none) {
  .rpt-table tr.rpt-row:hover td:not(.rpt-gap) { background: inherit; }
  .rpt-add-btn:hover, .rpt-pdf:hover { background: none; }
}

/* ==== главное меню, библиотека и аккаунт (редизайн, 29–30.09.2026) ====
   Три вкладки общей части сайта собраны из одних деталей, как экраны
   iPhone: переходишь с вкладки на вкладку – язык не меняется.
   - Список группами (.glist): подложка карточки со скруглением, строки
     от 60 px под палец, волосяная линия между строками начинается от
     текста, а не от края, как в настройках. Значков у строк нет (просьба
     владельца сайта, 30.09.2026).
   - Одна главная кнопка на экран: на слайде заявки «Отправить заявку»,
     на слайде EQS «Связаться», на слайде NORTHENCAD «Открыть», в
     аккаунте «Выйти». В библиотеке кнопок нет – ссылка вся карточка.
   Общие классы (.tile, .card, .btn*, h1, .notice) не тронуты: ими
   рисуются программы и админка. Всё новое – под своими именами, здесь.
   Исключения, о которых просил владелец сайта (30.09.2026), – шапка,
   зона загрузки в программах и чёрная тёмная тема – отдельными блоками
   ниже, у каждого сказано, что он задевает. */

/* Колонка аккаунта – 560 px по центру: на компьютере строка не
   растягивается на весь экран, и глаз не бегает от подписи слева к
   шеврону справа; на телефоне колонка во всю ширину. Библиотеке – 720 px:
   в ней две карточки рядом. */
.acc-home { max-width: 560px; margin: 0 auto; }
.lib-home { max-width: 720px; margin: 0 auto; }

/* Заголовок вкладки – крупный и слева, как большой заголовок в iPhone:
   док над ним общий для всех вкладок и не говорит, где ты. Глобальный h1
   (22 px) рассчитан на страницы программ и админки, его не трогаем. */
.tab-title {
  margin: 4px 0 18px; padding: 0 2px;
  font-size: 30px; font-weight: 700; line-height: 1.15; letter-spacing: -.02em;
}

/* Группа строк. Кольцо, а не рамка: не прибавляет к размерам. И без него
   нельзя: в тёмной теме подложка карточки (#1C1C1E) почти совпадает с
   фоном (#1C1C1C), группа растворялась бы. */
.glist {
  display: block; margin: 0 0 22px; padding: 0; overflow: hidden;
  background: var(--card); border-radius: 16px;
  box-shadow: 0 0 0 1px var(--border);
}
.glist > form { display: block; margin: 0; }
/* Строка – ссылка, кнопка формы или просто строка с данными; вид один.
   Отступ слева у строки, справа – у тела: так тело ровно по середине
   строки, и выровненная по центру надпись («Выйти») стоит по центру. */
.glist-row {
  display: flex; align-items: stretch; gap: 14px;
  width: 100%; margin: 0; padding: 0 0 0 16px;
  border: 0; border-radius: 0; background: none; color: var(--text);
  font: inherit; text-align: left; text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
a.glist-row, button.glist-row { cursor: pointer; transition: background .15s ease; }
.glist-body {
  flex: 1; min-width: 0; min-height: 60px;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px 10px 0;
  border-top: 1px solid transparent;
}
/* Линия – сверху у тела, со второй строки. Кнопка выхода лежит внутри
   формы, поэтому соседей считаем по детям группы, а не по строкам. */
.glist > * + * .glist-body { border-top-color: var(--border); }

.glist-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* Имя программы бывает одним длинным словом – переносим внутри слова,
   чтобы строка не уезжала за скругление. */
.glist-title { font-size: 16px; font-weight: 600; line-height: 1.3; letter-spacing: -.01em;
  overflow-wrap: anywhere; }
.glist-sub { font-size: 13px; line-height: 1.35; color: var(--muted); }
.glist-val { flex: 0 0 auto; font-size: 16px; color: var(--muted); font-variant-numeric: tabular-nums; }
/* Шеврон «перейти» – той же формы, что у «Загрузок» в программе
   (.utile-row .chev), только тише: он подсказка, а не кнопка. */
.glist-chev {
  flex: 0 0 auto; width: 8px; height: 8px; margin: 0 4px 0 0;
  border-right: 2px solid var(--muted); border-top: 2px solid var(--muted);
  transform: rotate(45deg); opacity: .6;
}

/* Выход – красным, как в iPhone: действие обрывает сеанс. */
.glist-danger .glist-title { color: var(--error-text); font-weight: 500; }
.glist-center .glist-text { align-items: center; text-align: center; }

/* Отклик. Наведение – только там, где есть мышь: на телефоне подсветка
   «залипала» бы после касания. Нажатие видно везде, темнее наведения.
   Обводка фокуса внутрь: наружную срезало бы скругление группы. Именно
   outline, а не тень: в контрастной теме Windows тени пропадают, а
   обводка остаётся. */
@media (hover: hover) {
  a.glist-row:hover, button.glist-row:hover { background: var(--hover); }
}
a.glist-row:active, button.glist-row:active { background: color-mix(in srgb, var(--text) 8%, transparent); }
.glist-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---- аккаунт: карточка владельца ----
   Буква логина в круге – как аватар в Telegram, когда фото нет. */
.acc-me .glist-body { min-height: 80px; }
.acc-ava {
  flex: 0 0 auto; align-self: center;
  width: 52px; height: 52px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text);
  font-size: 22px; font-weight: 600; line-height: 1;
}
.acc-login { font-size: 19px; font-weight: 600; line-height: 1.25; letter-spacing: -.01em;
  overflow-wrap: anywhere; }
/* Строки аккаунта – в одну строку и без подписи: жирное имя тут не
   отделяет себя от подписи, а только давит. Та же насыщенность, что у
   красных кнопок выхода рядом, – группа читается ровно. */
.acc-home .glist-title { font-weight: 500; }

/* ---- библиотека пуста ---- */
.lib-empty {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 56px 20px; text-align: center; color: var(--muted);
}
.lib-empty p { margin: 0; font-size: 15px; }

/* ---- библиотека: программа – именем (30.09.2026) ----
   У заказчика почти всегда одна программа, реже две (слова владельца
   сайта). В ячейке только короткое имя – EQS, CAD – антиквой и тише
   цветом, как EQS на главной: чёрный и белый значки с буквами были
   слишком резкими, полное имя NORTHEN… и кнопка «Открыть» – лишними
   (просьба владельца сайта, 30.09.2026). Ссылка – вся ячейка, значков в
   ней нет. Одна ячейка – шириной 600 px, две – рядом, у администратора –
   по две в ряд. Снимка программы и подписи «Открыта вам» нет. */
.lib-home:has(.lib-card:only-child) { max-width: 600px; }
.lib-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; }
.lib-card {
  position: relative; display: flex; align-items: center; justify-content: center; min-width: 0; min-height: 210px;
  padding: 32px 28px; border-radius: 24px; text-align: center;
  background: var(--card); color: var(--text); text-decoration: none;
  box-shadow: 0 0 0 1px var(--border), 0 26px 60px -40px rgba(20, 22, 28, .5);
  -webkit-tap-highlight-color: transparent; transition: box-shadow .2s ease, background .2s ease;
}
.lib-home:has(.lib-card:only-child) .lib-card { min-height: 240px; }
/* Имя – контуры антиквы (sv-g): размер задаёт font-size. Длинное имя
   (DWG to PDF) – мельче, чтобы стояло в одну-две строки. */
.lib-mark { max-width: 100%; font-size: 54px; line-height: 1.1; color: color-mix(in srgb, var(--text) 82%, transparent); }
.lib-card-long .lib-mark { font-size: 40px; }
.lib-card-xl .lib-mark { font-size: 30px; }
/* EQS — две строки: «EQS» и сеть, по центру друг под другом. */
.lib-card-lines .lib-mark { display: flex; flex-direction: column; align-items: center; gap: .12em; }
.lib-line { display: block; line-height: 1; }
@media (hover: hover) {
  .lib-card:hover { box-shadow: 0 0 0 1px var(--border-strong), 0 30px 64px -38px rgba(20, 22, 28, .55); }
}
.lib-card:active { background: var(--hover); }
.lib-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* «Нужна другая программа?» – строкой под ячейками. */
.lib-more { margin-top: 16px; }

/* ---- аккаунт: окно «Связаться с нами» (30.09.2026) ----
   Как поле сообщения в мессенджере (окно сменили по просьбе владельца
   сайта): сверху – кому пишешь, ниже поле на мягкой подложке, в его
   нижнем углу счётчик знаков и «Отправить». На компьютере – карточка по
   центру, на телефоне – у верхнего края экрана (телефонный блок ниже):
   клавиатура снизу её не закрывает. Поле – 16 px (меньше – и iOS увеличит
   страницу). */
.acc-dialog {
  width: min(520px, calc(100% - 32px)); max-width: none; max-height: calc(100% - 32px);
  margin: auto; padding: 0; border: 0; border-radius: 26px;
  background: var(--card); color: var(--text);
  box-shadow: 0 0 0 1px var(--border), 0 40px 90px -40px rgba(0, 0, 0, .6);
}
.acc-dialog[open] { animation: acc-in .2s ease-out; }
.acc-dialog::backdrop { background: rgba(10, 10, 12, .45); }
@keyframes acc-in { from { opacity: 0; transform: translateY(8px) scale(.985); } }
.acc-dialog-in { margin: 0; padding: 18px 20px 20px; }
.acc-dialog-head { display: flex; align-items: flex-start; gap: 12px; margin: 0 0 16px; }
.acc-dialog-text { flex: 1; min-width: 0; padding-top: 2px; }
.acc-dialog-title { margin: 0; font-size: 17px; font-weight: 600; line-height: 1.3; letter-spacing: -.01em; }
.acc-dialog-note { margin: 2px 0 0; font-size: 13px; line-height: 1.35; color: var(--muted); }
/* Закрыть – словом, как в листах iPhone: значков в окне нет (просьба
   владельца сайта, 30.09.2026). Мишень 44 px в высоту. */
.acc-dialog-x {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; height: 44px; margin: -10px -10px 0 0; padding: 0 10px; border: 0; border-radius: 12px;
  background: none; color: var(--muted); font: inherit; font-size: 15px; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) { .acc-dialog-x:hover { background: var(--hover); color: var(--text); } }
.acc-dialog-x:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Поле и кнопка – одна подложка. У самого поля рамки нет, при вводе
   обводится подложка – так же, как любое поле сайта (цвет текста и мягкий
   ободок). */
.acc-compose {
  border-radius: 20px; background: color-mix(in srgb, var(--text) 5%, var(--card));
  box-shadow: inset 0 0 0 1px var(--border); transition: box-shadow .15s ease;
}
.acc-compose:focus-within { box-shadow: inset 0 0 0 1px var(--text), 0 0 0 3px var(--focus-ring); }
.acc-compose textarea, .acc-compose textarea:focus {
  display: block; width: 100%; min-height: 150px; max-height: 50vh; margin: 0; padding: 14px 16px 4px;
  border: 0; border-radius: 20px 20px 0 0; background: transparent; box-shadow: none; outline: none;
  color: var(--text); font: inherit; font-size: 16px; line-height: 1.5; resize: none;
}
.acc-compose-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 4px 8px 8px 16px; }
.acc-count { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.acc-send {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 20px; border: 0; border-radius: 999px;
  background: color-mix(in srgb, var(--text) 16%, var(--card)); color: var(--text);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text) 8%, transparent);
  font: inherit; font-size: 15px; font-weight: 600; line-height: 1.2; white-space: nowrap; cursor: pointer;
  -webkit-tap-highlight-color: transparent; transition: background .15s ease, opacity .15s ease;
}
@media (hover: hover) { .acc-send:hover { background: color-mix(in srgb, var(--text) 21%, var(--card)); } }
.acc-send:active { background: color-mix(in srgb, var(--text) 26%, var(--card)); }
.acc-send:disabled { opacity: .55; cursor: default; }
.acc-send:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Пока поле пустое, кнопка приглушена: отправлять нечего (нажать можно –
   браузер подскажет заполнить поле). */
.acc-compose:has(textarea:placeholder-shown) .acc-send { opacity: .5; }
.acc-dialog-error { margin: 10px 2px 0; font-size: 13px; line-height: 1.45; color: var(--error-text); }
.acc-dialog-sent { padding: 18px 0 2px; text-align: center; }
.acc-dialog-sent:focus { outline: none; }
.acc-dialog-done { margin: 0; font-size: 18px; font-weight: 600; line-height: 1.3; }
.acc-dialog-sent .main-go { width: 100%; margin-top: 20px; }
.acc-dialog-in[style*="min-height"] { display: flex; flex-direction: column; }
.acc-dialog-in[style*="min-height"] .acc-dialog-sent { margin: auto 0; }

/* ==== шапка: только марка и стрелка назад (30.09.2026) ====
   По просьбе владельца сайта из шапки убраны все значки, в том числе в
   программах: видны марка NORTHEN GROUP (клик меняет тему, как раньше) и
   стрелка «назад» (.dock-back) – всегда слева. Сами значки остаются в
   разметке base.html, здесь их только прячем. Вторая строка шапки с
   разделами программы тоже скрыта (на сайте она сейчас пустая): вернуть –
   убрать третье правило. */
/* Селекторы длиннее прежних: те показывают значки с !important. */
.rail > .dock-left > a:not(.dock-back), .rail > .dock-left > a.dock-ico { display: none !important; }
.rail > .rail-foot a.acct-icon { display: none !important; }
.rail > .dock-tabs { display: none !important; }
.rail a.brand .svt { white-space: nowrap; }
/* Стрелка – шеврон без круга, как «назад» в iPhone: одной кнопке шапки
   рамка не нужна. Мишень 44 px; поле сдвинуто влево так, что шеврон
   стоит по краю страницы, а не отступает от него. */
.rail > .dock-left > a.dock-back {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 44px; height: 44px; margin: 0 0 0 -12px; padding: 0; border: 0; border-radius: 50%;
  color: var(--text); background: none; text-decoration: none;
  -webkit-tap-highlight-color: transparent; transition: background .15s ease;
}
.rail > .dock-left > a.dock-back svg { width: 24px; height: 24px; display: block; }
@media (hover: hover) { .rail > .dock-left > a.dock-back:hover { background: var(--hover); } }
.rail > .dock-left > a.dock-back:active { background: color-mix(in srgb, var(--text) 8%, transparent); }
.rail > .dock-left > a.dock-back:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Марка на компьютере 41 px в высоту (док там ужат), а на планшете её
   нажимают пальцем: мишень добираем до 44 px прозрачным полем сверху и
   снизу, сама шапка не растёт. */
.rail > a.brand { position: relative; }
.rail > a.brand::after { content: ""; position: absolute; inset: -2px 0; }

/* ==== надписи контурами (30.09.2026) ====
   Текст главной и марка в шапке – контуры букв Inter и марки
   (templates/_svgtext.html, собирает preview/svgtext.py): выделить и
   скопировать их нельзя. Каждое слово – свой SVG высотой в кегль строки,
   ширина – в em, поэтому размер задаёт обычный font-size родителя, а
   строки переносит браузер. SVG опущен на глубину нижних выносных:
   базовая линия букв совпадает с базовой линией обычного текста рядом
   (логин в приветствии). Числа – из метрик шрифтов: у Inter 2728 и 680 на
   2816, у марки 984 и 292 на 1000. */
.sv { display: inline-block; height: 1.2102em; vertical-align: -0.2415em; fill: currentColor; overflow: visible; }
.sv-k { height: 1.276em; vertical-align: -0.292em; }
/* Антиква Spectral (приветствие вошедшего): строка 900 и 250 на 1000 –
   ровно под буквы, иначе коробка из метрик шрифта раздвинула бы строки. */
.sv-g { height: 1.15em; vertical-align: -0.25em; }
.sv-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
/* Пробелы между словами-картинками – обычный текст: не даём выделять и
   их, иначе мышь рисовала бы пустые полосы. Поля ввода – как обычно. */
.main-home, .main-deck, .rail a.brand { -webkit-user-select: none; user-select: none; }
.main-deck input { -webkit-user-select: text; user-select: text; }

/* ==== главная вошедшего: «С возвращением» (30.09.2026) ====
   Вид второй сдачи вернули по просьбе владельца сайта: страница в экран,
   слева крупное приветствие антиквой, справа разделы карточкой, всё по
   середине высоты. Слайдов и нижней панели у вошедшего нет. */
.content:has(.main-home) { flex: 1 1 auto; display: flex; max-width: none; margin: 0; padding: 0; }
.main-home { flex: 1 1 auto; display: flex; min-width: 0; }
.main-home-in {
  margin: auto; width: 100%; max-width: 1080px; padding: 40px 40px 64px;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 400px); gap: 64px; align-items: center;
}
/* Буквы – контуры, шрифт ниже нужен только знакам логина, которых нет в
   таблице: они выйдут текстом, похожей антиквой. */
.main-welcome {
  margin: 0; font-size: 60px; line-height: 1.08; font-weight: 400; overflow-wrap: anywhere;
  font-family: "Palatino Linotype", Palatino, "Book Antiqua", ui-serif, Georgia, serif;
}
.main-home .glist { margin: 0; }
/* Логин – одно слово-картинка, переноса внутри нет. Слишком длинный на
   узком телефоне сжимается под ширину строки, а не вылезает за край. */
.main-welcome .sv { max-width: 100%; height: auto; }

/* ==== главная гостя – три слайда (29–30.09.2026) ====
   Страница ровно в экран: листается лента слайдов, а длинный слайд
   прокручивается внутри себя. Иначе у короткого слайда внизу была бы
   пустота высотой с самый длинный. Шапка наверху не двигается. */
.shell:has(.main-deck) { height: 100vh; height: 100dvh; min-height: 0; overflow: hidden; }
.content:has(.main-deck) {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  max-width: none; margin: 0; padding: 0;
}
.main-deck { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

/* Лента: прокрутка вбок с привязкой к слайдам. Свайп, тачпад и
   Shift+колесо листают сами, без скрипта; полосу прокрутки прячем – где
   ты, показывает нижняя панель. */
.main-slides {
  flex: 1 1 auto; min-height: 0; display: flex;
  overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scrollbar-width: none;
}
.main-slides::-webkit-scrollbar { display: none; }
.main-slide {
  flex: 0 0 100%; min-width: 0; display: flex;
  overflow-x: hidden; overflow-y: auto; overscroll-behavior-y: contain;
  scroll-snap-align: start; scroll-snap-stop: always;
}
/* margin: auto – по середине, пока слайд помещается; длинный начинается
   сверху и прокручивается, а не уходит верхом за край. */
.main-slide-in { margin: auto; width: 100%; max-width: 1080px; padding: 32px 40px 20px; }

/* Кегли. Буквы – SVG, размер им задаёт font-size; жирность выбрана
   шрифтом контура (Inter 400, 500, 600), font-weight на них не влияет. */
.main-title { margin: 0; font-size: 52px; line-height: 1.12; }
.main-mark { margin: 0; font-size: 64px; line-height: 1.05; }
.main-mark-xl { font-size: 58px; }
/* EQS – антиквой и тише цветом: прежняя геометрическая марка на этом
   слайде была слишком резкой (слова владельца сайта). */
.main-mark-soft { font-weight: 400; color: color-mix(in srgb, var(--text) 82%, transparent); }
.main-lead { margin: 20px 0 0; max-width: 34em; font-size: 16px; line-height: 1.6; color: var(--muted); }
.main-eyebrow { margin: 0 0 16px; font-size: 12px; line-height: 1.3; color: var(--muted); }
/* Пункты NORTHENCAD – только на телефоне: на компьютере вместо них
   абзац. Карточку «Очные встречи» убрали совсем (просьба владельца сайта,
   30.09.2026). */
.main-facts { display: none; }

/* Главная кнопка – капсула графитового тона (30.09.2026): чёрная на
   светлой теме и белая на чёрной слишком выделялись (слова владельца
   сайта). Цвет смешан из токенов – 13 % текста на подложке карточки:
   светло-серая в светлой теме, тёмно-серая в чёрной; видна и на карточке,
   и на фоне, но не кричит. Тонкое кольцо держит край. Главные кнопки
   программ (.btn, .uc-go) не тронуты. */
.main-go {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 220px; min-height: 50px; padding: 0 32px; border: 0; border-radius: 999px;
  background: color-mix(in srgb, var(--text) 13%, var(--card)); color: var(--text);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text) 8%, transparent);
  font-family: inherit; font-size: 16px; line-height: 1.2; text-decoration: none; white-space: nowrap; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background .15s ease, opacity .15s ease;
}
@media (hover: hover) { .main-go:hover { background: color-mix(in srgb, var(--text) 18%, var(--card)); } }
.main-go:active { background: color-mix(in srgb, var(--text) 23%, var(--card)); }
.main-go:disabled { opacity: .55; cursor: default; }
.main-go:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* «Готово» под ответом на заявку закрывает окно заявки. */
.main-go.main-sheet-done { margin-top: 18px; }
/* «Отправить заявку» и «Отправляем…» – две надписи, видна одна: контуры
   скрипт не перепишет, как текст, он только ставит форме класс is-busy. */
.main-go-busy, .is-busy .main-go-idle { display: none; }
.is-busy .main-go-busy { display: inline; }

/* ---- слайд 1: о нас и заявка ----
   Заявка – кнопкой (просьба владельца сайта, 01.10.2026): на слайде
   заголовок, направления, абзац и «Оставить заявку» – одной колонкой по
   центру, форма открывается окном. Заголовок не шире 11 em – две ровные
   строки, без одинокого слова во второй. */
.main-about { display: flex; flex-direction: column; align-items: center; text-align: center; }
.main-about .main-intro { display: flex; flex-direction: column; align-items: center; max-width: 860px; }
.main-about .main-title { max-width: 11em; text-wrap: balance; }
.main-about .main-dirs { justify-content: center; }
.main-about .main-lead { margin-left: auto; margin-right: auto; }
.main-apply-cta { display: flex; flex-direction: column; align-items: center; margin-top: 32px; }
.main-apply-cta .main-go { min-width: 240px; }
/* Направления – строчными плашками: видно сразу, без чтения абзаца. */
.main-dirs { display: flex; flex-wrap: wrap; gap: 8px; margin: 24px 0 0; padding: 0; list-style: none; }
.main-dirs li {
  display: flex; align-items: center; min-height: 36px; padding: 0 14px; border-radius: 999px;
  font-size: 14px; line-height: 1.2; color: var(--text); box-shadow: inset 0 0 0 1px var(--border-strong);
}

/* Заявка – окном (01.10.2026): кнопка «Оставить заявку» открывает форму
   по центру экрана, остальная страница затемнена и не нажимается (main.js,
   класс is-applying на слайде). Закрытое окно невидимо и вне фокуса
   (visibility). На телефоне окно – у верхнего края (телефонный блок ниже).
   Вид (просьба владельца сайта, 30.09.2026): значков нет – заголовок,
   подпись, поля, согласие, кнопка; «Закрыть» – словом в углу. Поля –
   мягкие серые, без рамок, подпись над каждым; ФИО и должность – в одну
   строку, они про одного человека. При вводе поле светлеет до цвета
   карточки и обводится цветом текста с мягким ободком – как любое поле
   сайта. Шрифт ввода 16 px: меньше – и iOS увеличит страницу. */
.main-invite {
  position: fixed; z-index: 60; top: 50%; left: 50%;
  width: min(460px, calc(100% - 32px)); max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px);
  overflow-y: auto; overscroll-behavior: contain;
  margin: 0; padding: 26px 24px 18px; border-radius: 26px; text-align: left;
  background: var(--card); color: var(--text);
  box-shadow: 0 0 0 1px var(--border), 0 40px 90px -40px rgba(0, 0, 0, .6);
  visibility: hidden; opacity: 0; transform: translate(-50%, calc(-50% + 10px)) scale(.985);
  transition: opacity .2s ease, transform .2s ease, visibility 0s linear .2s;
}
.main-about.is-applying .main-invite {
  visibility: visible; opacity: 1; transform: translate(-50%, -50%);
  transition: opacity .2s ease, transform .2s ease;
}
.main-sheet-bg {
  position: fixed; inset: 0; z-index: 55; background: rgba(10, 10, 12, .45);
  visibility: hidden; opacity: 0; transition: opacity .2s ease, visibility 0s linear .2s;
}
.main-about.is-applying .main-sheet-bg { visibility: visible; opacity: 1; transition: opacity .2s ease; }
/* «Закрыть» – словом в правом верхнем углу, мишень 44 px. */
.main-sheet-x {
  position: absolute; top: 14px; right: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; height: 44px; padding: 0 10px; border-radius: 12px;
  color: var(--muted); font-size: 15px; text-decoration: none; -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) { .main-sheet-x:hover { background: var(--hover); color: var(--text); } }
.main-sheet-x:active { background: var(--hover); }
.main-sheet-x:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.main-invite-title { margin: 0; padding-right: 88px; font-size: 22px; line-height: 1.25; }
.main-invite-sub { margin: 4px 0 18px; font-size: 14px; line-height: 1.45; color: var(--muted); }
.main-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 10px; margin: 0 0 8px; }
.main-field-wide { grid-column: 1 / -1; }
.main-invite .main-field { display: block; min-width: 0; margin: 0; font-size: inherit; }
.main-field-label { display: block; margin: 0 0 6px; font-size: 12.5px; line-height: 1.3; color: var(--muted); }
.main-invite .main-fields input[type="text"] {
  display: block; width: 100%; height: 46px; margin: 0; padding: 0 14px;
  border: 0; border-radius: 12px; background: color-mix(in srgb, var(--text) 5%, var(--card));
  box-shadow: none; color: var(--text); font-size: 16px; text-overflow: ellipsis;
  transition: background .15s ease, box-shadow .15s ease;
}
.main-invite .main-fields input[type="text"]:focus {
  background: var(--card); box-shadow: inset 0 0 0 1.5px var(--text), 0 0 0 3px var(--focus-ring);
}
.main-invite .main-fields input::placeholder { color: var(--muted); opacity: .7; }

/* Согласие – квадратная галочка: так её узнают с первого взгляда. Сам
   флажок прозрачный, но остаётся в форме: без галочки браузер заявку не
   отправит и сам подскажет. Мишень – вся строка, 44 px. */
.main-consent {
  position: relative; display: flex; align-items: center; gap: 12px;
  min-height: 44px; margin: 0 0 12px; font-size: 13px; line-height: 1.35; color: var(--muted); cursor: pointer;
}
.main-consent input { position: absolute; left: 0; top: 50%; width: 22px; height: 22px; margin: -11px 0 0; opacity: 0; }
.main-check {
  position: relative; flex: 0 0 auto; width: 22px; height: 22px; border-radius: 7px;
  border: 1.5px solid var(--border-strong); transition: background .15s ease, border-color .15s ease;
}
.main-check::after {
  content: ""; position: absolute; left: 6.5px; top: 2.5px; width: 5px; height: 10px;
  border: solid var(--primary-text); border-width: 0 2px 2px 0; transform: rotate(45deg); opacity: 0;
}
.main-consent input:checked + .main-check { background: var(--text); border-color: var(--text); }
.main-consent input:checked + .main-check::after { opacity: 1; }
.main-consent input:focus-visible + .main-check { outline: 2px solid var(--accent); outline-offset: 2px; }

.main-invite .main-go { width: 100%; }
.main-apply-error { margin: 0 0 12px; font-size: 14px; line-height: 1.45; color: var(--error-text); }

/* Ответ встаёт на место полей: заголовок, строка и «Готово», без
   значков. Окно становится короче – оно поверх страницы, ей прыгать
   нечему. */
.main-invite.is-sent { display: flex; flex-direction: column; justify-content: center; }
.main-sent { padding: 20px 0 2px; text-align: center; }
.main-sent:focus { outline: none; }
.main-sent .main-invite-title { padding-right: 0; }
.main-sent .main-invite-sub { margin: 6px 0 0; }

/* Вход и регистрация по ключу – тихими ссылками под кнопкой: значка входа
   в шапке больше нет. */
.main-keys { display: flex; justify-content: center; flex-wrap: wrap; gap: 0 12px; margin: 6px 0 0; }
.main-keys a {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 6px;
  font-size: 14px; color: var(--muted); text-decoration: none;
}
@media (hover: hover) { .main-keys a:hover { color: var(--text); } }

/* ---- слайд 2: кейс EQS (30.09.2026) ----
   Имя по центру, под ним – для кого и что делает. Ниже в строку – как
   работает: четыре шага на одной линии. Внизу экран программы и карточка
   условий с кнопкой к заявке. Текст нейтральный, без «вы» и «мы».
   Вид слайда – седьмой сдачи (владелец сайта вернул его, 30.09.2026), новая
   только линия шагов: номер – в кружке, кружки соединяет тонкая линия, как
   остановки на схеме маршрута; под кружком – название и подпись. */
.main-eqs-head { margin: 0 0 20px; text-align: center; }
.main-eqs-head .main-lead { margin: 8px auto 0; max-width: 42em; }
.main-flow { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px; margin: 0 0 22px; padding: 0; list-style: none; }
.main-flow li { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 3px; min-width: 0; }
/* Цифры – контуры (SVG) блоком: у коробки контура высота – от верхних до
   нижних выносных, у цифр выносных нет, и их середина совпадает с
   серединой коробки – в кружке они стоят ровно по центру. */
.main-flow-n {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--card); box-shadow: inset 0 0 0 1px var(--border-strong);
  font-size: 11px; line-height: 1; color: var(--text);
}
.main-flow-n .svt { display: flex; }
.main-flow-n .sv { display: block; }
.main-flow li:not(:last-child)::after {
  content: ""; position: absolute; top: 15px; left: 38px; right: -16px; height: 1px;
  background: var(--border-strong);
}
.main-flow-title { margin-top: 10px; font-size: 16px; line-height: 1.3; color: var(--text); }
.main-flow-sub { font-size: 14px; line-height: 1.45; color: var(--muted); }
.main-eqs-body { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: 32px; align-items: center; }
/* Условия – карточкой, как заявка на первом слайде: подпись слева,
   значение справа, строки через волосяную линию. */
.main-terms {
  padding: 20px 22px 20px; border-radius: 22px; background: var(--card);
  box-shadow: 0 0 0 1px var(--border), 0 24px 60px -36px rgba(20, 22, 28, .45);
}
.main-terms-title { margin: 0 0 4px; font-size: 18px; line-height: 1.3; }
.main-terms-list { margin: 0 0 16px; }
.main-terms-list > div { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 12px; padding: 8px 0; border-top: 1px solid var(--border); }
.main-terms-list > div:first-child { border-top: 0; }
.main-terms-k, .main-terms-v { margin: 0; font-size: 14px; line-height: 1.45; }
.main-terms-k { color: var(--muted); }
.main-terms-v { color: var(--text); }
.main-terms-ask { margin: 0 0 12px; font-size: 14px; line-height: 1.45; color: var(--muted); }
.main-terms .main-go { width: 100%; min-width: 0; }

/* Скриншот – в рамке окна: скругление и волосяная линия, чтобы светлый
   снимок не растворялся на светлом фоне, а тёмный – на тёмном. Снимков по
   два, под каждую тему, виден один. Пропорция задана заранее: пока
   картинка грузится, место под неё уже занято. */
.main-shot {
  position: relative; overflow: hidden; aspect-ratio: 1200 / 476; border-radius: 14px;
  background: var(--content-bg);
  box-shadow: 0 22px 50px -30px rgba(20, 22, 28, .5), 0 0 0 1px var(--border);
}
.main-shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; }
/* Та же пара правил, что у палитры: явный выбор темы и системная тема без
   выбора. Скрытый снимок с loading="lazy" браузер не скачивает. */
.main-shot .is-dark { display: none; }
:root[data-theme="dark"] .main-shot .is-light { display: none; }
:root[data-theme="dark"] .main-shot .is-dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .main-shot .is-light { display: none; }
  :root:not([data-theme="light"]) .main-shot .is-dark { display: block; }
}

/* ---- слайд 3: бесплатный NORTHENCAD ----
   Колонки поровну; марка мельче, чем EQS: NORTHENCAD – одно длинное
   слово, переноса у него нет. Справа – окно «Файлы», кнопки «Открыть
   NORTHENCAD» нет (просьба владельца сайта, 01.10.2026). */
.main-free { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 56px; align-items: center; }
.main-free .main-mark { font-size: 54px; }
/* Окно «Файлы» – то же поле, что в самой программе: белая карточка,
   внутри пунктирная рамка, по центру значок файла и «Файлы». Наведение –
   рамка цвета текста; когда файл несут над окном – рамка сплошная, с
   лёгкой подложкой. Значок – та же маска, что в программах (.uc-drop),
   цвет – цвет текста. Нажимается всё окно. */
.main-drop-card {
  position: relative; padding: 14px; border-radius: 22px; background: var(--card);
  box-shadow: 0 0 0 1px var(--border), 0 26px 60px -38px rgba(20, 22, 28, .5);
}
.main-drop {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  min-height: 250px; border-radius: 16px; border: 1.5px dashed var(--border-strong);
  color: var(--text); text-decoration: none; cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: border-color .2s ease, background .2s ease, opacity .2s ease;
}
.main-drop-ico {
  display: block; width: 30px; height: 30px; background: currentColor; opacity: .85;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 3v5h5'/%3E%3Cpath d='M12 17.5v-6'/%3E%3Cpath d='M9.5 14 12 11.5 14.5 14'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 3v5h5'/%3E%3Cpath d='M12 17.5v-6'/%3E%3Cpath d='M9.5 14 12 11.5 14.5 14'/%3E%3C/svg%3E") center / contain no-repeat;
}
.main-drop-word { font-size: 17px; line-height: 1.3; }
@media (hover: hover) { .main-drop:hover { border-color: var(--text); } }
.main-drop:active { background: color-mix(in srgb, var(--text) 4%, transparent); }
.main-drop.is-over { border-style: solid; border-color: var(--text); background: color-mix(in srgb, var(--text) 5%, transparent); }
.main-drop.is-busy { opacity: .55; cursor: progress; }
.main-drop:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
/* Поле выбора файла – невидимое, его открывает main.js по нажатию на окно. */
.main-drop-input { position: absolute; left: 0; top: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* ---- нижняя панель слайдов (30.09.2026) ----
   Плавающая капсула: подписи слайдов, текущий подсвечен мягкой
   подложкой. Значков нет – убраны по просьбе владельца сайта. Капсула
   полупрозрачная, фон под ней чуть размыт; стоит под слайдами, а не
   поверх них, и ничего не закрывает. Горит слайд, на котором стоит лента
   (main.js); без скрипта горит первый. */
.main-pager {
  flex: 0 0 auto; display: flex; justify-content: center;
  padding: 6px 16px calc(14px + env(safe-area-inset-bottom, 0px));
}
.main-pager-in {
  display: flex; gap: 2px; padding: 4px; border-radius: 999px;
  background: color-mix(in srgb, var(--card) 80%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(1.3); backdrop-filter: blur(16px) saturate(1.3);
  box-shadow: 0 0 0 1px var(--border), 0 16px 36px -22px rgba(20, 22, 28, .5);
}
.main-pager a {
  display: flex; align-items: center; justify-content: center;
  min-width: 112px; min-height: 44px; padding: 0 18px; border-radius: 999px;
  color: var(--muted); font-size: 13px; line-height: 1.1; white-space: nowrap; text-decoration: none;
  -webkit-tap-highlight-color: transparent; transition: color .2s ease, background .2s ease;
}
.main-pager a[aria-current="true"] { color: var(--text); background: color-mix(in srgb, var(--text) 9%, transparent); }
@media (hover: hover) { .main-pager a:not([aria-current="true"]):hover { color: var(--text); } }
.main-pager a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ==== зона загрузки в программах: новый вид (30.09.2026) ====
   По просьбе владельца сайта. В NORTHENEQS и NORTHENCAD место для файлов
   было крупным словом «ФАЙЛЫ». Теперь это поле переноса: пунктирная
   рамка, значок файла и подпись «Файлы» обычным кеглем, вместе по
   центру поля. Серая подсказка («Выберите файл(-ы) .dwg, .dxf») с экрана
   убрана, но осталась для экранного диктора: uploads.js пишет в неё
   только эти слова, ошибки у него – в строках файлов. Слова – из шаблонов
   программ, их не трогаем; «ФАЙЛЫ» только пишется как «Файлы». Когда
   файл несут над полем, рамка становится сплошной. Задевает обе
   программы – для того и сделано. Значок – маска, цвет – цвет текста. */
.uc-drop { gap: 10px; min-height: 150px; padding: 30px 32px; }
.uc-drop::before {
  content: ""; flex: 0 0 auto; width: 30px; height: 30px; margin: 0;
  background: currentColor; opacity: .85;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 3v5h5'/%3E%3Cpath d='M12 17.5v-6'/%3E%3Cpath d='M9.5 14 12 11.5 14.5 14'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 3v5h5'/%3E%3Cpath d='M12 17.5v-6'/%3E%3Cpath d='M9.5 14 12 11.5 14.5 14'/%3E%3C/svg%3E") center / contain no-repeat;
}
.uc-drop::after {
  content: ""; position: absolute; inset: 14px; border-radius: 16px;
  border: 1.5px dashed var(--border-strong); pointer-events: none;
  transition: border-color .2s ease, background .2s ease;
}
.uc-drop .files-word { font-size: 17px; font-weight: 600; letter-spacing: 0; line-height: 1.3; text-transform: lowercase; }
.uc-drop .files-word::first-letter { text-transform: uppercase; }
.uc-drop .files-hint {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.uc-drop:hover, .uc-drop.drag { background: none; }
.uc-drop:hover::after { border-color: var(--text); }
.uc-drop.drag::after { border-style: solid; border-color: var(--text); background: color-mix(in srgb, var(--text) 5%, transparent); }

/* ==== NORTHENCAD: только поле «Файлы» (30.09.2026) ====
   По просьбе владельца сайта в NORTHENCAD нет кнопки «Добавить в очередь»
   и пустой карточки «Сейчас в обработке»: на странице одно поле «Файлы»,
   по центру. Чертёж уходит в разбор сам, сразу после выбора (main.js шлёт
   форме submit, uploads.js ставит его в очередь, как делала кнопка).
   Карточка очереди появляется, только когда в ней есть чертёж: без неё не
   было бы видно ни хода работы, ни готового PDF. Колонка одна – карточка
   встаёт под поле, ничего не прыгает вбок. Страница узнаётся по адресу
   загрузки, шаблон программы не тронут. Лучше, конечно, сделать это в
   самом шаблоне NORTHENCAD – тогда эти правила не нужны. */
.pq:has(> .pq-add[data-stage-url$="/northencad/uploads"]) {
  grid-template-columns: minmax(0, 1fr); row-gap: 18px; max-width: 560px; margin-top: 4vh;
}
.pq-add[data-stage-url$="/northencad/uploads"] { position: static; order: 0; }
.pq-add[data-stage-url$="/northencad/uploads"] > .pq-go { display: none; }
.pq:has(> .pq-add[data-stage-url$="/northencad/uploads"]) > .pq-queue { order: 1; }
.pq:has(> .pq-add[data-stage-url$="/northencad/uploads"]) > .pq-queue:has([data-jobs-empty]:not([hidden])) { display: none; }
.pq-add[data-stage-url$="/northencad/uploads"] .uc-drop { min-height: 200px; }

/* ==== тёмная тема – чёрная (30.09.2026) ====
   По просьбе владельца сайта тёмная гамма перекрашена в чёрную: фон
   чисто чёрный, плитки чуть светлее фона, линии тише, главные кнопки
   белые. Светлая тема не тронута, переключение – по клику на марку, как
   раньше. Правила повторяют пару из начала файла (явный выбор темы и
   системная тема без выбора) и стоят после неё, поэтому перекрывают её,
   не трогая исходные строки. Токены общие: программы в тёмной теме тоже
   чёрные. */
:root[data-theme="dark"] {
  --bg: #000000; --panel: #000000; --card: #0F0F10; --content-bg: #000000; --dark-bg: #000000;
  --text: #F2F2F3; --muted: #8E8E95; --border: #26262A; --border-strong: #3C3C42;
  --hover: #18181A; --rail-sel: #18181A; --seg-on: #2C2C30;
  --focus-ring: rgba(255, 255, 255, 0.22);
  --primary-bg: #FFFFFF; --primary-text: #000000; --primary-hover: #E2E2E4; --back-hover: #CFCFD2;
  --shadow: 0 2px 12px rgba(0, 0, 0, 0.7);
  --glow-1: rgba(255, 255, 255, 0.030); --glow-2: rgba(255, 255, 255, 0.020); --glow-3: rgba(255, 255, 255, 0.012);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #000000; --panel: #000000; --card: #0F0F10; --content-bg: #000000; --dark-bg: #000000;
    --text: #F2F2F3; --muted: #8E8E95; --border: #26262A; --border-strong: #3C3C42;
    --hover: #18181A; --rail-sel: #18181A; --seg-on: #2C2C30;
    --focus-ring: rgba(255, 255, 255, 0.22);
    --primary-bg: #FFFFFF; --primary-text: #000000; --primary-hover: #E2E2E4; --back-hover: #CFCFD2;
    --shadow: 0 2px 12px rgba(0, 0, 0, 0.7);
    --glow-1: rgba(255, 255, 255, 0.030); --glow-2: rgba(255, 255, 255, 0.020); --glow-3: rgba(255, 255, 255, 0.012);
  }
}

/* Плитки программ (.utile) обведены чёрной тенью: на чёрном фоне она не
   видна, и плитка растворялась. В тёмной теме – волосяная линия цвета
   границ. :where не добавляет веса: подсветка при наведении и переносе
   файла (.utile-drop:hover, .upload-card:has(…)) остаётся своей. */
:where(:root[data-theme="dark"]) .utile { box-shadow: 0 0 0 1px var(--border); }
/* Зерно фона на чёрном заметнее, чем на сером, – делаем его тише, чтобы
   фон оставался чёрным, а не рябым. */
:where(:root[data-theme="dark"]) .shell::before { opacity: .6; }
@media (prefers-color-scheme: dark) {
  :where(:root:not([data-theme="light"])) .utile { box-shadow: 0 0 0 1px var(--border); }
  :where(:root:not([data-theme="light"])) .shell::before { opacity: .6; }
}

/* ==== медиа-запросы новых блоков – последними ==== */

/* Планшет и узкое окно: колонкам тесно, NORTHENCAD заходил бы на снимок
   – колонки встают друг под друга. */
@media (max-width: 960px) {
  .main-free, .main-eqs-body, .main-home-in { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .main-side { width: 100%; max-width: 460px; }
  .main-drop-card, .main-eqs-body .main-shot, .main-terms { max-width: 640px; }
  .main-welcome { font-size: 52px; }
  /* Шаги кейса – по два в ряд; линия ведёт только к соседу справа. */
  .main-flow { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 20px; }
  .main-flow li:nth-child(2n)::after { display: none; }
}

/* ==== телефон (30.09.2026) ====
   Своя раскладка под смартфон, собранная заново (просьба владельца
   сайта): слайды листаются вбок, как на компьютере, но каждый экран –
   свой, телефонный. Значков нет нигде (тоже просьба владельца сайта):
   держат текст, тонкие линии и белые карточки. Списки – строками через
   волосяную линию, крупным кеглем; главная кнопка – внизу, под большим
   пальцем. Форма заявки не лежит на слайде – «Оставить заявку» открывает
   её окном у верхнего края экрана, клавиатура снизу его не закрывает
   (main.js; без скрипта форма стоит на слайде, см. noscript в main.html).
   Мишени нажатия не меньше 44 px, поля ввода – 16 px (меньше – и iOS
   увеличит страницу). */
@media (max-width: 720px) {
  /* Шапка: марка мельче, высота прежняя – 44 px под палец. */
  .rail > a.brand { font-size: 20px; }

  /* ---- слайды: экран приложения ---- */
  .main-slide-in { display: flex; flex-direction: column; margin: 0; min-height: 100%; padding: 20px 16px 16px; }
  .main-about, .main-eqs, .main-free { display: flex; flex-direction: column; align-items: stretch; gap: 0; }
  .main-title { font-size: 30px; line-height: 1.15; }
  .main-lead { margin-top: 12px; font-size: 15px; line-height: 1.5; }
  .main-lead-more { display: none; }
  .main-eyebrow { margin-bottom: 10px; font-size: 11px; }
  .main-go { width: 100%; min-width: 0; min-height: 50px; font-size: 16px; }
  .main-shot { border-radius: 14px; }
  /* Слайд – колонка flex. Блоки с overflow: hidden (снимок, карточка
     условий) колонка иначе сжала бы по высоте и обрезала бы на невысоком
     телефоне – пусть лучше слайд прокрутится. */
  .main-dirs, .main-flow, .main-terms, .main-facts, .main-shot { flex-shrink: 0; }

  /* Строки через волосяную линию – направления «О нас» и пункты
     NORTHENCAD: крупный кегль, линия сверху и снизу каждой строки. */
  .main-dirs, .main-facts { display: block; margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--border); }
  .main-dirs li, .main-facts li {
    display: flex; align-items: center; min-height: 54px; padding: 0 2px;
    border-radius: 0; box-shadow: none; border-bottom: 1px solid var(--border);
    font-size: 17px; line-height: 1.3; color: var(--text);
  }

  /* «О нас»: заголовок, одна фраза и направления строками – по середине
     экрана, по левому краю; кнопка заявки внизу, во всю ширину, под ней
     вход и регистрация по ключу. */
  .main-about { text-align: left; }
  .main-about > .main-intro { display: flex; flex-direction: column; align-items: stretch; max-width: none; margin: auto 0; }
  .main-about .main-title { max-width: none; }
  .main-about .main-lead { order: 1; margin-left: 0; margin-right: 0; }
  .main-dirs { order: 2; margin-top: 26px; }
  .main-apply-cta { align-items: stretch; margin-top: 0; padding-top: 24px; }
  .main-apply-cta .main-go { min-width: 0; }
  .main-apply-cta .main-keys { margin-top: 4px; }
  .main-keys a { font-size: 13px; }

  /* Окно заявки – у верхнего края экрана, во всю ширину: клавиатура
     снизу его не закрывает. Открытое выезжает сверху. */
  .main-invite {
    top: calc(12px + env(safe-area-inset-top, 0px)); left: 12px; right: 12px; width: auto;
    max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px);
    padding: 18px 16px 12px; border-radius: 24px;
    box-shadow: 0 0 0 1px var(--border), 0 30px 70px -30px rgba(0, 0, 0, .55);
    transform: translateY(-12px);
  }
  .main-about.is-applying .main-invite { transform: none; }
  .main-sheet-x { top: 8px; right: 6px; }
  .main-invite-title { font-size: 19px; padding-right: 84px; }
  .main-invite-sub { margin: 3px 0 14px; font-size: 13px; }
  .main-fields { gap: 10px 8px; }
  .main-field-label { margin-bottom: 5px; font-size: 12px; }
  .main-invite .main-fields input[type="text"] { height: 44px; padding: 0 12px; }
  .main-consent { gap: 10px; margin-bottom: 10px; font-size: 12px; }
  .main-apply-error { font-size: 13px; }
  .main-sent { padding: 18px 0 6px; }

  /* EQS: имя и фраза по центру, шаги – той же линией с кружками, только
     сверху вниз; ниже карточка условий с кнопкой. Снимок программы на
     телефоне не нужен – его видно на компьютере. */
  .main-mark-xl { font-size: 42px; }
  .main-eqs-head { margin-bottom: 20px; }
  .main-eqs-head .main-lead { margin-top: 6px; }
  .main-flow { display: block; margin: 0 0 22px; padding: 0 2px; }
  .main-flow li { display: grid; grid-template-columns: 28px minmax(0, 1fr); column-gap: 12px; align-items: start; padding: 0 0 14px; }
  .main-flow li:last-child { padding-bottom: 0; }
  .main-flow-n { grid-row: 1 / span 2; width: 28px; height: 28px; }
  .main-flow-title { grid-column: 2; margin: 3px 0 0; font-size: 15px; line-height: 22px; }
  .main-flow-sub { grid-column: 2; font-size: 12.5px; line-height: 1.4; }
  .main-flow li:not(:last-child)::after { top: 32px; bottom: 4px; left: 14px; right: auto; width: 1px; height: auto; }
  .main-eqs-body { display: flex; flex-direction: column; flex: 1 1 auto; gap: 0; }
  .main-eqs-body .main-shot { display: none; }
  .main-terms { margin-top: auto; padding: 16px; border-radius: 20px; }
  .main-terms-title { font-size: 16px; }
  .main-terms-list { margin-bottom: 12px; }
  .main-terms-list > div { grid-template-columns: 76px minmax(0, 1fr); gap: 10px; padding: 7px 0; }
  .main-terms-k, .main-terms-v { font-size: 13px; line-height: 1.4; }
  .main-terms-ask { display: none; }

  /* NORTHENCAD: марка, три пункта строками, ниже окно «Файлы» – на всё
     оставшееся место, как поле в самой программе. Кнопки нет. */
  .main-free .main-mark { font-size: clamp(26px, 9.5vw, 38px); }
  .main-lead-desk { display: none; }
  .main-facts { margin-top: 20px; }
  .main-facts li { min-height: 50px; font-size: 16px; }
  .main-drop-card { display: flex; flex: 1 1 auto; min-height: 200px; margin-top: 22px; padding: 12px; border-radius: 20px; }
  .main-drop { flex: 1 1 auto; min-height: 0; border-radius: 14px; }

  /* Панель слайдов – по размеру подписей и по центру. */
  .main-pager { padding: 4px 16px calc(10px + env(safe-area-inset-bottom, 0px)); }
  .main-pager-in { max-width: 100%; }
  .main-pager a { flex: 0 1 auto; min-width: 88px; padding: 0 10px; font-size: 12px; }

  /* ---- вошедший: приветствие и разделы, по середине экрана ----
     Разделы – белыми карточками во всю ширину, одна под другой: крупное
     название и подпись, без значков. Блок чуть выше середины: на глаз
     середина экрана выше геометрической. */
  .main-home-in { margin: auto; align-content: center; gap: 28px; padding: 28px 16px 72px; }
  .main-welcome { font-size: 42px; line-height: 1.08; }
  .main-home .glist { display: grid; gap: 12px; margin: 0; background: none; box-shadow: none; border-radius: 0; overflow: visible; }
  .main-home .glist-row {
    align-items: center; min-height: 104px; padding: 18px 20px; border-radius: 22px;
    background: var(--card); box-shadow: 0 0 0 1px var(--border), 0 16px 36px -28px rgba(20, 22, 28, .45);
  }
  .main-home .glist-body { min-height: 0; padding: 0; border-top: 0; }
  .main-home .glist > * + * .glist-body { border-top-color: transparent; }
  .main-home .glist-text { gap: 4px; }
  .main-home .glist-title { font-size: 21px; }
  .main-home .glist-sub { font-size: 13px; }
  .main-home .glist-chev { display: none; }

  /* ---- списки, библиотека, аккаунт ---- */
  .tab-title { margin: 0 0 14px; font-size: 25px; }
  .glist { border-radius: 14px; margin-bottom: 16px; }
  .glist-row { gap: 12px; padding-left: 16px; }
  .glist-body { min-height: 52px; padding: 8px 14px 8px 0; }
  .glist-title { font-size: 15px; }
  .glist-sub { font-size: 12px; }
  .glist-val { font-size: 15px; }
  .acc-me .glist-body { min-height: 68px; }
  .acc-ava { width: 44px; height: 44px; font-size: 19px; }
  .acc-login { font-size: 17px; }
  /* Библиотека: одна программа – ячейка на полэкрана, две и больше –
     квадратами по две в ряд, как значки приложений. */
  .lib-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .lib-home:has(.lib-card:only-child) .lib-grid { grid-template-columns: minmax(0, 1fr); }
  .lib-card { min-height: 0; aspect-ratio: 1 / 1; padding: 16px; border-radius: 22px; }
  .lib-home:has(.lib-card:only-child) .lib-card { aspect-ratio: auto; min-height: 42vh; }
  .lib-mark { font-size: 40px; }
  .lib-card-long .lib-mark { font-size: 28px; }
  .lib-card-xl .lib-mark { font-size: 21px; }
  .lib-card-lines .lib-mark { font-size: 24px; }   /* «Перекресток» в плитку телефона */
  .lib-home:has(.lib-card:only-child) .lib-mark { font-size: 60px; }
  .lib-home:has(.lib-card:only-child) .lib-card-long .lib-mark { font-size: 44px; }
  .lib-home:has(.lib-card:only-child) .lib-card-xl .lib-mark { font-size: 32px; }
  .lib-more { margin-top: 12px; }

  /* Окно связи – у верхнего края экрана: клавиатура снизу его не закроет. */
  .acc-dialog { width: calc(100% - 24px); margin: calc(12px + env(safe-area-inset-top, 0px)) auto auto; border-radius: 22px; max-height: calc(100dvh - 24px); }
  .acc-dialog-in { padding: 16px 16px 16px; }
  .acc-dialog-head { gap: 10px; margin-bottom: 12px; }
  .acc-dialog-title { font-size: 17px; }
  .acc-dialog-note { font-size: 12px; }
  .acc-compose { border-radius: 18px; }
  .acc-compose textarea, .acc-compose textarea:focus { min-height: 110px; padding: 12px 14px 4px; }
  .acc-send { font-size: 14px; padding: 0 18px; }
  .acc-dialog-done { font-size: 17px; }

  /* ---- программы: поле «Файлы» ---- */
  .uc-drop { min-height: 140px; padding: 26px 24px; }
  .uc-drop::after { inset: 12px; border-radius: 14px; }
  .uc-drop .files-word { font-size: 16px; }
  .pq-add[data-stage-url$="/northencad/uploads"] .uc-drop { min-height: 170px; }
}

/* Невысокий телефон (iPhone SE, старые Android – до 700 px в высоту):
   отступы плотнее, чтобы кнопки и почти весь EQS помещались без
   прокрутки. Что не влезло – прокручивается внутри слайда. */
@media (max-width: 720px) and (max-height: 700px) {
  .main-slide-in { padding-top: 14px; padding-bottom: 12px; }
  .main-title { font-size: 28px; }
  .main-dirs { margin-top: 18px; }
  .main-dirs li { min-height: 48px; font-size: 16px; }
  .main-apply-cta { padding-top: 16px; }
  .main-eqs-head { margin-bottom: 14px; }
  .main-mark-xl { font-size: 36px; }
  .main-flow { margin-bottom: 16px; }
  .main-flow li { padding-bottom: 10px; }
  .main-terms { padding: 14px; }
  .main-terms-list > div { padding: 6px 0; }
  .main-eyebrow { margin-bottom: 6px; }
  .main-free .main-mark { font-size: clamp(26px, 9vw, 34px); }
  .main-facts { margin-top: 14px; }
  .main-facts li { min-height: 46px; }
  .main-drop-card { min-height: 170px; margin-top: 14px; }
  .main-welcome { font-size: 36px; }
  .main-home .glist-row { min-height: 88px; }
}

/* Невысокое окно ноутбука (ниже 800 px, как у экранов 1366 × 768 и
   1440 × 900 за вычетом панелей браузера): слайды плотнее, чтобы кнопки и
   описания помещались над нижней панелью, а не прятались в прокрутке. */
@media (min-width: 721px) and (max-height: 800px) {
  .main-slide-in { padding-top: 12px; padding-bottom: 8px; }
  .main-title { font-size: 40px; }
  .main-dirs { margin-top: 18px; }
  .main-lead { margin-top: 16px; }
  .main-invite { padding: 20px 22px 16px; }
  .main-invite-title { font-size: 20px; }
  .main-invite-sub { margin-bottom: 14px; }
  .main-fields { gap: 10px; }
  .main-field-label { margin-bottom: 4px; }
  .main-invite .main-fields input[type="text"] { height: 42px; }
  .main-consent { margin-bottom: 8px; }
  .main-keys { margin-top: 2px; }
  .main-mark-xl { font-size: 48px; }
  .main-eqs-head { margin-bottom: 10px; }
  /* В кейсе – пояснение и шаги плотнее, условия мельче. */
  .main-eqs-head .main-lead { margin-top: 6px; font-size: 15px; line-height: 1.5; }
  .main-flow { margin-bottom: 10px; }
  .main-flow-n { width: 26px; height: 26px; }
  .main-flow li:not(:last-child)::after { top: 13px; left: 34px; }
  .main-flow-title { margin-top: 4px; font-size: 15px; }
  .main-flow-sub { font-size: 13px; line-height: 1.4; }
  .main-eqs-body { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 28px; }
  .main-terms { padding: 14px 18px 16px; }
  .main-terms-title { font-size: 17px; }
  .main-terms-list { margin-bottom: 10px; }
  .main-terms-list > div { padding: 6px 0; }
  .main-terms-k, .main-terms-v, .main-terms-ask { font-size: 13px; line-height: 1.4; }
  .main-terms-ask { margin-bottom: 8px; }
  .main-terms .main-go { min-height: 44px; }
  .main-pager { padding-bottom: 10px; }
}

/* Где наведения нет (телефон), рамка поля загрузки не «залипает»
   подсвеченной после касания. */
@media (hover: none) {
  .uc-drop:hover::after { border-color: var(--border-strong); }
}
