/* ============================================================================
   VentCalc — компонентні стилі. Кольори/простір/типографіка беруться ВИКЛЮЧНО
   з design-tokens.css (сирих hex тут бути не повинно).
   ============================================================================ */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--t-base);
    line-height: var(--lh-base);
    color: var(--c-text);
    background: var(--c-bg);
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { line-height: var(--lh-tight); font-weight: 650; letter-spacing: -0.012em; margin: 0; }
h1 { font-size: var(--t-xl); }
h2 { font-size: var(--t-lg); }
h3 { font-size: var(--t-md); }
p { margin: 0 0 var(--s-3); }

a { color: var(--c-brand); text-decoration-color: color-mix(in srgb, var(--c-brand) 35%, transparent); text-underline-offset: 2px; }
a:hover { color: var(--c-brand-hover); }

h1:focus, h2:focus, h3:focus { outline: none; }  /* FocusOnNavigate фокусує h1 — рамка тут зайва */
:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; border-radius: var(--r-sm); }

/* Числа (ціни, розміри, аеро) — моноширинні цифри, щоб колонки не «стрибали» */
.price-total, .price-lines, .quote-table, .aero-card dd, .variant-price,
.variant-aero dd, .saved-table, .field input { font-variant-numeric: tabular-nums; }
/* Одиниці на кшталт «м³/год» не рвуться переносом після «/» — розірвана
   одиниця читається як одрук (UI/UX-прохід 18.08). */
.nowrap { white-space: nowrap; }

/* Пошук у кабінеті (зріз 50) і рядок «показано N з M»: фільтр, що звузив список
   до нуля, без цього рядка читався б як «збережене зникло». */
.account-search { max-width: 420px; margin-bottom: var(--s-4); }
.account-filtered { margin: 0 0 var(--s-2); color: var(--c-muted); font-size: var(--t-sm); }
/* === Елементи форми ====================================================== */
.field { display: flex; flex-direction: column; gap: var(--s-1); margin-bottom: var(--s-4); min-width: 0; }
.field > span, .field-label { font-weight: 600; font-size: var(--t-sm); color: var(--c-text-soft); }
.field small, .field-label small { font-weight: 400; color: var(--c-muted); }
/* У вузькій колонці (1000–1240 px із сайдбаром, або мобільний) підказка рвала
   підпис посеред дужок — «Довжина L, мм (100-1000, / крок 5)». Даємо їй рядок. */
@media (max-width: 1240px) {
    .field > span small, .field-label small { display: block; margin-top: 1px; }
}

/* Чекбокс-поле: без цього правила чекбокс успадковував колонку .field і
   width:100%/min-height:42px від .field input — величезний квадрат по центру
   з підписом ПІД ним (аудит UI: «Пленум-бокс PB», «Лише ADPI-комфорт»). */
.field-check { flex-direction: row; align-items: baseline; gap: var(--s-2); cursor: pointer; }
.field-check input[type="checkbox"] {
    width: 1.05rem; height: 1.05rem; min-height: 0; flex: none;
    margin: 0; transform: translateY(2px); accent-color: var(--c-brand);
    cursor: pointer;
}
.field-check > span { font-weight: 600; font-size: var(--t-sm); color: var(--c-text-soft); }

/* Поле, на яке скаржиться валідатор: саме повідомлення живе внизу форми, і без
   підсвітки зв'язок «крок 5 мм» ↔ «Довжина L» доводилось вгадувати. */
.field input.field-invalid, .field select.field-invalid {
    border-color: var(--err-600); background: color-mix(in srgb, var(--err-600) 5%, var(--c-surface));
}
.field input.field-invalid:focus { outline-color: var(--err-600); }

.field input, .field select, .field textarea, .save-quote-title {
    width: 100%;
    padding: 0.55rem 0.7rem;
    min-height: 42px;
    font: inherit;
    color: var(--c-text);
    background: var(--c-surface);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--r-sm);
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--c-muted); }
.field input:focus, .field select:focus, .field textarea:focus, .save-quote-title:focus {
    outline: none; border-color: var(--c-brand); box-shadow: var(--ring);
}
.field select {
    appearance: none;
    /* Трикрапка, а не кліп посеред слова: на 390 px «…LDT (з лам» читалось як
       битий текст, і розрізнювальний суфікс виконання зникав без сліду
       (UI/UX-прохід 18.08; Chromium підтримує text-overflow на select). */
    text-overflow: ellipsis;
    background-image: linear-gradient(45deg, transparent 50%, var(--c-muted) 50%),
                      linear-gradient(135deg, var(--c-muted) 50%, transparent 50%);
    background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    padding-right: var(--s-8);
}
.field select:disabled { background-color: var(--c-surface-sunk); color: var(--c-muted); }
/* Ряд полів. Підписи різної висоти зсували СВОЇ інпути: «Колір RAL (9016 — у
   базовій ціні)» переносився на другий рядок, і його поле опинялось нижче за
   сусідні «Кут ламелей» і «Кількість, шт». Медіазапит нижче лікував це лише до
   1240 px (там підказка отримує власний рядок у КОЖНОГО поля) — на широкому
   екрані ряд і далі розсипався.
   Тепер рядки «підпис / контрол / дельта» вирівняні між полями через subgrid:
   скільки б рядків не зайняв будь-який підпис, усі інпути стоять на одній лінії.
   Без підтримки subgrid лишається попередня поведінка з вирівнюванням по низу. */
.field-row { display: flex; gap: var(--s-4); flex-wrap: wrap; align-items: flex-end; }
.field-row .field { flex: 1 1 140px; margin-bottom: var(--s-4); }

/* Підбір за витратою: п'ять полів із підказками різної довжини. Підказка завжди
   своїм рядком і мінімум 170 px на колонку — інакше «Довжина L, м (до стіни / пів
   відстані…)» ламалась на 3 рядки, а поля пливли по вертикалі (аудит UI). */
.select-inputs .field > span small { display: block; margin-top: 1px; }
/* min-width, а не лише flex-basis: у контейнері 620px четверо полів у ряд
   стискались до 143px, і селект різав власну опцію — «Тихий (≤25 д…»
   (UI/UX-огляд 03.08). З мінімумом зайве поле переноситься в наступний ряд.
   200 px і явний міжколонковий проміжок — після зрізу 04.08 (14 полів):
   на 170 px підпис «Висота решітки ≤, мм» упирався в сусідній «Приєднання
   Ду, мм» без зазору, а селект призначення різав «Приплив (розда…». */
.select-inputs .field { flex-basis: 200px; min-width: 200px; }
/* Подвійний селектор не для краси: у subgrid-браузерах шаблон колонок задає
   .field-row (minmax 140px) ПІЗНІШЕ у файлі, і одинарний .select-inputs йому
   програвав за порядком — колонки стискались до 140 px, підписи наїжджали
   на сусідні («…ммПриєднання Ду, мм»). */
.field-row.select-inputs {
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    column-gap: var(--s-4);
}

/* Фото-інпут: нативний «Choose File» англійською і не в стилі застосунку —
   ховаємо контрол (лишаючи його фокусованим), клік по label відкриває діалог. */
.photo-drop { position: relative; }
.photo-drop input[type="file"] {
    position: absolute; width: 1px; height: 1px; opacity: 0;
    overflow: hidden; clip: rect(0 0 0 0);
}
.photo-drop .photo-pick { align-self: flex-start; pointer-events: none; }
.photo-drop input[type="file"]:focus-visible ~ .photo-pick {
    outline: 2px solid var(--c-brand); outline-offset: 2px;
}
/* Drop-зона: рамка є ЗАВЖДИ (інакше про перетягування не здогадатись),
   підсвічується під файлом (клас ставить initPhotoDrop в App.razor). */
.photo-drop {
    padding: var(--s-4); border: 2px dashed var(--c-border-strong);
    border-radius: var(--r-lg); transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.photo-drop.drop-over { border-color: var(--c-brand); background: color-mix(in srgb, var(--c-brand) 6%, transparent); }
.photo-example { width: 168px; max-width: 60%; border-radius: var(--r-sm); border: 1px solid var(--c-border); }
/* Приклад — окремим блоком ПІД дроп-зоною, не всередині: там він читався як
   прев'ю «сюди ляже ваш знімок» (UI/UX-прохід 18.08). */
.photo-example-block { margin: var(--s-3) 0 0; }
.photo-example-block figcaption { font-size: var(--t-xs); color: var(--c-muted); margin-top: var(--s-1); }

@supports (grid-template-rows: subgrid) {
    .field-row {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
        grid-template-rows: auto auto auto;
        align-items: stretch;
        margin-bottom: var(--s-4);
    }

    .field-row .field {
        display: grid;
        grid-row: span 3;
        grid-template-rows: subgrid;
        row-gap: var(--s-1);
        margin-bottom: 0;
    }
}
.field-delta { color: var(--warn-600); font-size: var(--t-xs); font-weight: 600; }
.face-note { color: var(--c-muted); font-size: var(--t-xs); }
/* Перехресний місток між конфігураторами (зріз 162). Окремий клас, а не
   .face-note: тести й скрипти адресують .face-note як ЄДИНУ примітку серії,
   і другий елемент того самого класу ламає строгі локатори. */
.cross-link { color: var(--c-muted); font-size: var(--t-xs); margin: 0 0 var(--s-3); }
/* Величина дальності біля числа (L₀,₂ у KQ) і примітка про змішані величини
   у видачі /select — рішення власника 29.08 «позначка величини скрізь». */
.throw-magnitude { color: var(--c-text-soft); font-weight: 600; margin-right: 2px; }
.throw-mixed-note { color: var(--c-muted); font-size: var(--t-xs); margin: 0 0 var(--s-2); }
/* Кнопка, що виглядає лінком (вихід із розділу на /accessory): дія рахується
   на сервері в момент кліку — на відміну від href, який рендер міг лишити
   застарілим (жива гонка, спіймана e2e локально і на шарді CI). */
.link-inline { background: none; border: 0; padding: 0; font: inherit;
  color: var(--c-brand); text-decoration: underline; cursor: pointer; }
.link-inline:hover { color: var(--c-brand-hover); }

/* === Кнопки ============================================================== */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
    min-height: 42px; padding: 0.5rem 1rem;
    font: inherit; font-weight: 600; font-size: var(--t-sm);
    border-radius: var(--r-sm); border: 1px solid transparent;
    cursor: pointer; text-decoration: none; white-space: nowrap;
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
                color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.btn:active { transform: translateY(1px); }

/* Базовий вигляд — вторинна кнопка. Доти `.btn` без модифікатора не мав ні тла,
   ні рамки (`border: 1px solid transparent`), тож «Зберегти в кабінет» на /quote
   і «Вийти» в кабінеті малювались голим текстом і на кнопки не скидались.
   Модифікатори нижче перекривають це за порядком у файлі. */
.btn { background: var(--c-surface); color: var(--c-text-soft); border-color: var(--c-border-strong); }
.btn:hover { border-color: var(--c-brand); color: var(--c-brand); }

.btn-primary { background: var(--c-brand); color: var(--c-on-brand); border-color: var(--c-brand); box-shadow: var(--sh-xs); }
.btn-primary:hover { background: var(--c-brand-hover); border-color: var(--c-brand-hover); color: var(--c-on-brand); }
.btn-ghost { background: var(--c-surface); color: var(--c-text-soft); border-color: var(--c-border-strong); }
.btn-ghost:hover { border-color: var(--c-brand); color: var(--c-brand); }
.btn-danger { background: var(--c-surface); color: var(--err-600); border-color: var(--c-border-strong); }
.btn-danger:hover { background: var(--err-50); border-color: var(--err-600); }
.btn-small { min-height: 30px; padding: 0.15rem 0.5rem; font-size: var(--t-xs); }
.btn-request { margin-top: var(--s-2); }
.actions { display: flex; gap: var(--s-2); margin-top: var(--s-4); flex-wrap: wrap; }

/* === Чипи типорозмірів =================================================== */
.preset-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
@media (max-width: 1180px) {
    /* До ~1180 px колонка форми лишається вузькою (350–450 px), і 21 типорозмір
       розкладається в 6–7 рядів чипів, які ховають усе інше. Горизонтальна стрічка
       тримає блок в один ряд; згасання праворуч показує, що ряд продовжується. */
    .preset-chips {
        flex-wrap: nowrap; overflow-x: auto; scroll-snap-type: x proximity;
        padding-bottom: var(--s-1); scrollbar-width: thin;
        mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
    }
    .preset-chips .chip { flex: 0 0 auto; scroll-snap-align: start; }

    /* КОРОТКИЙ ряд (≤8 чипів) краще загорнути, ніж гортати: два ряди на 390 px
       читаються цілком, а стрічка ховала половину варіантів за згасанням. На
       /ldt ДВІ сусідні стрічки (щілини + довжини) ще й зливались в одну — їх
       плутали (UX-аудит 03.08). Довгі ряди решіток/аксесуарів лишаються
       стрічкою: 16–21 чип у 6–7 рядів ховає решту форми (коментар вище). */
    .preset-chips:not(:has(.chip:nth-child(9))) {
        flex-wrap: wrap; overflow-x: visible; mask-image: none; scroll-snap-type: none;
    }

    /* Згасання на 28 px на світлому тлі майже не видно, а overlay-скролбари
       macOS не видно взагалі — про решту ряду можна було не здогадатись
       (аудит UI). Для КОРОТКОГО ряду підказку не рендерить сам ChipsHint
       (WrapMax): ховати її звідси через :has не можна — style-рушій частини
       збірок Chromium матчить :has не так, як querySelector (доведено виміром
       на /ldt). Поріг тут (nth-child(9)) і WrapMax=8 — одне число у двох
       синтаксисах; збіг тримає Chips_wrap_threshold_matches_css. */
    .chips-hint { display: block; }
}
.chips-hint { display: none; font-weight: 400; color: var(--c-muted); font-size: var(--t-xs); margin-top: 2px; }

/* Довгий типоряд у нативному селекті: на 390 px мітки з ціною обрізались
   праворуч («— 8,272.7…»), тобто саме число, заради якого список і читають. */
@media (max-width: 480px) {
    .field select[size] { font-size: var(--t-xs); }
}

/* Ціни немає через помилку вводу — картка пояснює, чому, замість порожнечі. */
.price-card-blocked { color: var(--c-text-soft); }
.chip {
    min-height: 34px; padding: 0.3rem 0.7rem;
    font: inherit; font-size: var(--t-sm); font-variant-numeric: tabular-nums;
    color: var(--c-text-soft); background: var(--c-surface);
    border: 1px solid var(--c-border-strong); border-radius: var(--r-pill);
    cursor: pointer; transition: all var(--dur) var(--ease);
}
.chip:hover { border-color: var(--c-brand); color: var(--c-brand); }
.chip-active { background: var(--c-brand); border-color: var(--c-brand); color: var(--c-on-brand); font-weight: 650; }
.chip-active:hover { background: var(--c-brand-hover); color: var(--c-on-brand); }

/* === Повідомлення ======================================================== */
.issue {
    padding: var(--s-2) var(--s-3); border-radius: var(--r-sm);
    font-size: var(--t-sm); margin: var(--s-2) 0; border-left: 3px solid transparent;
}
.issue-error { background: var(--err-50); color: var(--err-600); border-left-color: var(--err-600); }
.issue-ok { background: var(--ok-50, var(--c-surface-sunk)); color: var(--ok-600, var(--c-text)); border-left-color: var(--ok-600, var(--c-brand)); }
.issue-info { background: var(--info-50); color: var(--info-600); border-left-color: var(--info-600); }
.issue-warn { background: var(--warn-50); color: var(--warn-600); border-left-color: var(--warn-600); }

/* === Каркас конфігуратора ================================================
   Точка зламу тут 720 px, а НЕ 1000, як у шела, — і це не розсинхрон. Нижче 1000
   сайдбар їде за екран, тож контенту дістається вся ширина вікна: на планшеті
   768–1000 двох колонок вистачає із запасом. Поки злам був єдиний (1000), уся ця
   смуга діставала мобільну розкладку з прев'ю на 596 px заввишки — форма опинялась
   повністю під згином. */
/* minmax(0, 1fr), а не 1fr: у грид-елемента min-width за замовчуванням auto, тож
   нескорочувана горизонтальна стрічка чипів розпирала колонку і давала сторінці
   ~1990 px прокрутки на телефоні. */
.configurator { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
.config-form, .config-result { min-width: 0; }
.page-title {
    margin-bottom: var(--s-5);
    /* Плавний кегль: на 360 px заголовок ліз у два рядки і з'їдав пів-екрана. */
    font-size: clamp(1.375rem, 1.05rem + 1.45vw, 1.75rem);
}
.config-result { display: flex; flex-direction: column; gap: var(--s-4); }

@media (min-width: 720px) {
    .configurator { grid-template-columns: minmax(300px, 1fr) minmax(320px, 1.05fr); gap: var(--s-6); align-items: start; }
    .config-result { position: sticky; top: var(--s-4); }
}

@media (min-width: 1280px) {
    .configurator { grid-template-columns: minmax(340px, 1fr) minmax(360px, 1.05fr); gap: var(--s-8); }
}

@media (max-width: 719px) {
    /* «Розчиняємо» праву колонку, щоб її діти стали елементами тієї самої сітки:
       прев'ю йде НАД формою (спершу бачиш виріб), а ціна й аеродинаміка — ПІД нею.
       Доти обидві картки стояли над формою, і до полів доводилось прокручувати екран. */
    /* Селектори — від .config-result, а не від .configurator: display:contents прибирає
       бокс, але НЕ змінює DOM, тож «.configurator > .preview» не збігається ні з чим. */
    .config-result { display: contents; }
    .config-result > .preview { order: -1; }
    .config-result > .price-card { order: 1; }
    .config-result > .aero-card { order: 2; }
    /* Без явного order ADPI і поради лишались з order:0 і випливали МІЖ формою
       та ціною (аудит UI: на мобільному ціна опинялась четвертою). Порядок той
       самий, що в десктопній колонці: ціна → аеродинаміка → ADPI → поради. */
    .config-result > .adpi-card { order: 3; }
    .config-result > .advice-card { order: 4; }
    /* Та сама пастка втретє (рев'ю зрізу 34): kf-рядок і криві без order
       випливали МІЖ формою та ціною, відірвавшись від AeroCard, чию дальність
       вони коментують. Однаковий order — DOM-порядок усередині зберігається:
       аеродинаміка → kf-рядок → криві. */
    .config-result > .kf-note { order: 2; }
    .config-result > .aero-curves { order: 2; }
    /* Пастка вчетверте (жива персона на 375px, 07.08): .fan-spec-card «Дані
       виробника» без order випливала МІЖ формою та ціною — тобто каталожні
       характеристики вентилятора стояли перед ціною самого виробу. Тепер вона
       поруч з аеродинамікою, до якої змістовно й належить.
       ПРАВИЛО НА МАЙБУТНЄ: кожен новий прямий нащадок .config-result мусить
       дістати order ТУТ ЖЕ — інакше він мовчки стає першим. */
    .config-result > .fan-spec-card { order: 2; }
}

/* === Картки ============================================================== */
.preview, .price-card, .aero-card, .variant-card, .quote-table, .saved-table,
.aero-curves, .room-section, .room-plan, .fan-spec-card {
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-sm);
}
.preview { padding: var(--s-4); }
/* Тільки ПРЯМИЙ нащадок. Нащадковий «.preview svg» діставав і ВКЛАДЕНІ <svg>
   видів DwgView, а CSS-ширина у SVG2 перекриває геометричний атрибут width:
   кожен вид ставав на всю ширину кадру (560 одиниць), центрувався вже в ній —
   і другий вид («Переріз») з'їжджав за viewBox: у решітки/дифузора/анемостата
   лишався обрізок заголовка, у LDT переріз зникав без сліду. Залежить від
   збірки браузера (Chromium пісочниці v1194 малює зсув, новіший у CI — ні),
   тож зелений CI цього не бачив; ловиться drawing-labels.spec.ts на браузері
   пісочниці («вилазить за кадр на 134px»). */
.preview > svg { width: 100%; height: auto; display: block; }

/* Підписи креслення. Правила навмисно на .drawing, а не на .preview: мініатюри
   схем у КП (.quote-thumb) лежать ПОЗА .preview, тож із «.preview .dim» вони
   лишались зовсім без стилю — розмірні написи падали в дефолт браузера
   (чорні 16 px) і в самій пропозиції, і в друку. */
.drawing { width: 100%; height: auto; display: block; }

/* --- Ролі ліній креслення -------------------------------------------------
   Доти товщина задавалась числом у кожному <line>, тож контур, ламель, вісь і
   розмірна лінія виглядали однаково важливими — око не мало за що зачепитись.
   non-scaling-stroke тримає товщину сталою: кожен вид — вкладений <svg> зі
   ВЛАСНИМ масштабом, інакше лінії в різних видах були б різної ваги. */
.drawing .dwg-contour, .drawing .dwg-thin, .drawing .dwg-hidden, .drawing .dwg-axis,
.drawing .dwg-dim, .drawing .dwg-ext, .drawing .dwg-air { vector-effect: non-scaling-stroke; }

.drawing .dwg-contour { stroke: var(--dwg-ink); stroke-width: 1.7; fill: none; }
.drawing .dwg-thin { stroke: var(--dwg-thin); stroke-width: 1; fill: none; }
.drawing .dwg-hidden { stroke: var(--dwg-thin); stroke-width: 1; fill: none; stroke-dasharray: 5 3; }
.drawing .dwg-axis { stroke: var(--dwg-mark); stroke-width: .8; fill: none; stroke-dasharray: 9 3 2 3; }
.drawing .dwg-dim, .drawing .dwg-ext { stroke: var(--dwg-line); stroke-width: .8; fill: none; }
.drawing .dwg-arrow { fill: var(--dwg-line); stroke: none; }
.drawing .dwg-body { fill: var(--dwg-fill); stroke: none; }
.drawing .dwg-void { fill: var(--dwg-void); stroke: none; }
/* Матеріал у розрізі — суцільною заливкою: штрихування на товщині 3 одиниці
   перетворюється на кашу. */
.drawing .dwg-cut { fill: var(--dwg-ink); stroke: none; }
.drawing .dwg-air { stroke: var(--dwg-air); fill: none; }
.drawing .dwg-air-head { fill: var(--dwg-air); stroke: none; }

/* Підписи. Обведення кольором тла прибирає накладання тексту на лінії.

   Кегль і колір були підібрані «щоб не заважало», і схема вийшла найдрібнішим
   і найблідішим текстом на сторінці: ~10 px проти 13 px у підписів полів поруч,
   ще й кольором розмірної ЛІНІЇ (--dwg-line), а не тексту. Читати креслення
   було важче, ніж будь-що інше в тій самій картці. Тепер кегль на рівні решти
   інтерфейсу, а колір — чорнильний: розмірний напис це текст, а не тонка лінія
   побудови. Розмір тримає e2e drawing-labels: підписи не сміють злипнутися. */
.drawing .dwg-label, .drawing .dwg-title {
    font-size: 7.5px; fill: var(--dwg-ink);
    paint-order: stroke; stroke: var(--dwg-void); stroke-width: 2.5; stroke-linejoin: round;
}
.drawing .dwg-title { font-weight: 700; fill: var(--dwg-mark); }
/* Позначки перерізу й кута — акцентом, але тим самим кеглем, що й решта
   підписів. Старий клас .section-mark задавав 14 px у координатах виду, і в
   нормалізованому виді літери «А» виходили більшими за сам виріб; його правило
   (разом із .section-title і .angle-text) прожило в файлі ще довго ПІСЛЯ того,
   як розмітка перейшла на .dwg-mark — мертвий стиль читається як чинний, тож
   прибрано (замір 20.08). */
.drawing .dwg-mark { font-weight: 700; fill: var(--dwg-mark); }
.drawing .dwg-label.air-text { fill: var(--dwg-air); }
.drawing .dim { font-size: 13px; fill: var(--dwg-line); }
/* Родина без габаритів у прайсі (вентилятори, кріплення, пластикові канали):
   замість «узагальненого виробу», якого не існує, — чесна заглушка. Прямокутник
   там, де геометрії немає, гірший за порожнє місце: він читається як креслення. */
.drawing-none {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-3);
  padding: var(--s-6) var(--s-5); text-align: center; color: var(--c-muted);
}
/* Явний розмір лишається потрібним: «.preview > svg» іконку вже не чіпає
   (вона не прямий нащадок), але без width іконка впала б у власний розмір
   спрайта — і колись уже розтягувалась на всю картку. */
.drawing-none .drawing-none-ico { width: 56px; height: 56px; opacity: .5; }
.drawing-none p { margin: 0; font-size: var(--t-sm); max-width: 34ch; }

/* Фото позиції з прайс-книги постачальника — образ виробу там, де креслення
   чесно немає. Біла підкладка НАВМИСНА і в темній темі: фото постачальника
   зняті на білому, і прозорий PNG на темному тлі перетворюється на силует. */
.drawing-photo {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-2);
  margin: 0; padding: var(--s-3) var(--s-3) 0;
}
.drawing-photo img {
  max-width: 100%; max-height: 220px; object-fit: contain;
  background: #fff; border-radius: 10px; padding: var(--s-2);
  border: 1px solid var(--c-border);
}
.drawing-photo figcaption { font-size: var(--t-xs); color: var(--c-muted); text-align: center; }
/* У мініатюрах каталогу/КП підпис зайвий — образ і так підписаний карткою. */
.catalog-thumb .drawing-photo figcaption, .quote-thumb .drawing-photo figcaption { display: none; }
.catalog-thumb .drawing-photo { padding: 0; }
.catalog-thumb .drawing-photo img { max-height: 80px; padding: 2px; }
.drawing .air-text { fill: var(--dwg-air); }
.drawing-spec { margin: var(--s-3) 0 0; font-size: var(--t-xs); color: var(--c-muted); }
.drawing-spec strong { color: var(--c-text-soft); }

.price-card { padding: var(--s-5); }
/* Пульс при ЗМІНІ суми (вузол перестворюється через @key у PriceCard):
   видимий доказ, що перерахунок живий, — замість кнопки «Розрахувати»,
   якої просив зовнішній аудит і яка суперечила б принципу №1. */
@keyframes price-tick {
    from { background: color-mix(in srgb, var(--c-brand) 16%, transparent); }
    to { background: transparent; }
}
.price-total, .price-sticky-total {
    animation: price-tick 0.6s var(--ease);
    border-radius: var(--r-sm);
}
@media (prefers-reduced-motion: reduce) {
    .price-total, .price-sticky-total { animation: none; }
}
.price-card.quote { background: var(--warn-50); border-color: color-mix(in srgb, var(--warn-600) 30%, transparent); color: var(--warn-600); }
.price-total { font-size: var(--t-price); font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; }
.price-unit { color: var(--c-muted); font-size: var(--t-sm); margin-bottom: var(--s-3); }
.price-lines { list-style: none; padding: var(--s-3) 0 0; margin: var(--s-3) 0 0; border-top: 1px solid var(--c-border); }

/* Підтвердження видалення в кабінеті — інлайном, а не діалогом. */
.confirm-delete { font-size: var(--t-sm); color: var(--c-text-soft); margin-right: var(--s-2); }

/* Код замовлення на картці ціни: те, що менеджер диктує виробнику. */
.order-code { margin: var(--s-2) 0 0; font-size: var(--t-sm); color: var(--c-muted); }
.order-code strong { color: var(--c-text-soft); font-variant-numeric: tabular-nums; }
/* Заголовок рядка може бути довгим («Фарбування RAL 5015 «Небесно-синій»
   (+15…70%, у сумі — нижня межа)») — він мусить переноситися, а не з'їжджати
   за край картки разом із сумою. */
.price-lines li {
    display: flex; justify-content: space-between; align-items: baseline;
    gap: var(--s-4); padding: 0.2rem 0; font-size: var(--t-sm); color: var(--c-text-soft);
}
.price-lines li > :first-child { min-width: 0; overflow-wrap: anywhere; }
.price-lines li > :last-child { white-space: nowrap; }
.price-card .actions { margin-top: var(--s-4); }

.aero-card { padding: var(--s-5); }
.aero-card h2 { font-size: var(--t-md); margin-bottom: var(--s-3); }
.aero-card dl { display: grid; grid-template-columns: auto auto; gap: var(--s-1) var(--s-4); margin: 0; }
.aero-card dt { color: var(--c-muted); font-size: var(--t-sm); }
.aero-card dd { margin: 0; font-weight: 650; text-align: right; font-size: var(--t-sm); }
.aero-rating { margin: var(--s-3) 0 0; font-size: var(--t-sm); padding: var(--s-2) var(--s-3); border-radius: var(--r-sm); font-weight: 600; }

/* Каталожні характеристики вентилятора (прайс-книга AirRoxy) — та сама
   типографіка, що AeroCard, але БЕЗ вердиктів/рейтингів: це максимуми
   каталогу, не робоча точка мережі. */
.fan-spec-card { padding: var(--s-5); }
.fan-spec-card h3 { font-size: var(--t-md); margin: 0 0 var(--s-3); }
.fan-spec-card h3 .aero-source { font-size: var(--t-xs); font-weight: 400; color: var(--c-muted); }
.fan-spec-grid { display: grid; grid-template-columns: auto auto; gap: var(--s-1) var(--s-4); margin: 0; }
.fan-spec-grid > div { display: contents; }
.fan-spec-grid dt { color: var(--c-muted); font-size: var(--t-sm); }
.fan-spec-grid dd { margin: 0; font-weight: 650; text-align: right; font-size: var(--t-sm); }
.fan-spec-note { margin: var(--s-3) 0 0; font-size: var(--t-xs); color: var(--c-muted); }
/* Корекція Kf (/accessory, KQ1/KQ8): рядок під AeroCard — результат, а не примітка,
   тож звичайний колір тексту; джерело позначає вкладений .aero-source. */
.kf-note { margin: var(--s-2) 0 0; font-size: var(--t-sm); }
.kf-note .aero-source { font-size: var(--t-xs); color: var(--c-muted); }
.rating-quiet { background: var(--ok-50); color: var(--ok-600); }
.rating-normal { background: var(--info-50); color: var(--info-600); }
.rating-loud { background: var(--warn-50); color: var(--warn-600); }
.rating-toofast { background: var(--err-50); color: var(--err-600); }

/* === Характеристичні криві (AeroCurves) ==================================
   Стиль лінії = джерело значення: суцільна «каталог», пунктир «оцінка» —
   та сама мова, що бейджі AeroCard. Кольори — акцент «повітря», як і решта
   аеродинаміки; жодного сирого hex (правило design-tokens). */
.aero-curves { padding: var(--s-4) var(--s-5); }
.aero-curves > summary { cursor: pointer; font-size: var(--t-sm); font-weight: 650; color: var(--c-text-soft); }
.aero-curves > summary small { font-weight: 400; color: var(--c-muted); }
.aero-curves-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s-4); margin-top: var(--s-3); }
/* Тільки ПРЯМИЙ нащадок + явні width/height в атрибутах svg: на Chromium
   v1194 CSS-ширина перекриває геометричну (див. коментар до .preview > svg) —
   тут svg не вкладені, але селектор навмисно такий самий вузький, щоб правило
   ніколи не дотяглось туди, куди не мітило. */
.ac-chart > svg { width: 100%; height: auto; display: block; }

/* Товщина стала при масштабуванні кадру — як у ролей ліній креслення. */
.ac-cat, .ac-est, .ac-floor, .ac-axis, .ac-env { vector-effect: non-scaling-stroke; fill: none; }
.ac-cat   { stroke: var(--air-700); stroke-width: 1.7; }
.ac-est   { stroke: var(--air-500); stroke-width: 1.4; stroke-dasharray: 5 4; }
/* Цензурований шум: плазом по порогу, крапками — «тут каталог не міряв». */
.ac-floor { stroke: var(--c-muted); stroke-width: 1.2; stroke-dasharray: 2 3; }
.ac-axis  { stroke: var(--c-border-strong); stroke-width: 1; }
.ac-env   { stroke: var(--c-border-strong); stroke-width: 1; stroke-dasharray: 2 3; }
.ac-floor-zone { fill: var(--c-surface-sunk); stroke: none; }
.ac-tick, .ac-unit { font-size: 9px; fill: var(--c-muted); }
.ac-unit { font-weight: 650; }
.ac-point { fill: var(--c-brand); stroke: var(--c-surface); stroke-width: 1.5; }
.ac-point-label { font-size: 10px; font-weight: 650; fill: var(--c-text); }
.ac-legend { margin: var(--s-3) 0 0; font-size: var(--t-xs); color: var(--c-muted); }

/* === Прев'ю: таби, RAL, DXF ============================================== */
.preview-tabs { display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-3); }
.tab-group { display: inline-flex; gap: 2px; padding: 2px; background: var(--c-surface-sunk); border: 1px solid var(--c-border); border-radius: var(--r-sm); }
.preview-tab {
    border: none; background: transparent; color: var(--c-muted);
    border-radius: 4px; padding: 0.28rem 0.75rem; font: inherit; font-size: var(--t-sm); cursor: pointer;
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.preview-tab:hover { color: var(--c-text); }
/* Рамка на активній пігулці — не прикраса: у темній темі --c-surface майже
   збігається з фоном треку, і стан «Схема|3D» тримався лише на вазі шрифту
   (UI/UX-прохід 18.08). Рамка видима в обох темах без theme-специфічних правил. */
.preview-tab.tab-active { background: var(--c-surface); color: var(--c-text); font-weight: 650; box-shadow: var(--sh-xs); border: 1px solid var(--c-border-strong); }
/* Зразок кольору з кодом. Сам по собі квадратик нічого не повідомляв: базовий
   RAL 9016 — це майже білий на білій картці, тобто читався як дефект верстки. */
.ral-chip {
    display: inline-flex; align-items: center; gap: var(--s-2);
    margin-left: auto; padding: 0.18rem 0.45rem 0.18rem 0.22rem;
    background: var(--c-surface-sunk);
    border: 1px solid var(--c-border); border-radius: var(--r-sm);
}
.ral-swatch { width: 16px; height: 16px; border-radius: 4px; border: 1px solid var(--c-border-strong); }
.ral-code { font-size: var(--t-xs); font-weight: 650; color: var(--c-text-soft); white-space: nowrap; }

/* У вузькій колонці слово «RAL» ховається, а КОД лишається: голий квадратик
   без підпису читався як порожній чекбокс (у темній темі — особливо), і сенс
   елемента зникав разом із тап-ціллю (UI/UX-прохід 18.08). Доти тут ховався
   весь .ral-code. */
@media (max-width: 460px) {
    .ral-word { display: none; }
}

/* ── Вибір кольору ────────────────────────────────────────────────────────
   Було голе поле на чотири цифри: щоб замовити колір, треба знати код
   напам'ять. Тепер поруч із полем — зразок обраного відтінку і палітра
   всього каталогу RAL CLASSIC, а поле лишається для тих, хто код знає. */
.ral-input-row {
    display: grid; grid-template-columns: 42px minmax(0, 1fr) auto;
    gap: var(--s-2); align-items: center;
}
.ral-current {
    width: 42px; height: 42px; border-radius: var(--r-sm);
    border: 1px solid var(--c-border-strong); box-shadow: var(--sh-xs);
}
.ral-toggle { min-height: 42px; white-space: nowrap; }

.field-hint { color: var(--c-muted); font-size: var(--t-xs); }
.field-error { color: var(--err-600); font-size: var(--t-xs); font-weight: 600; }

.ral-palette {
    margin-top: var(--s-2); padding: var(--s-3);
    background: var(--c-surface-sunk);
    border: 1px solid var(--c-border); border-radius: var(--r-md);
}
.ral-groups { display: flex; flex-wrap: wrap; gap: var(--s-1); margin: var(--s-2) 0; }
.ral-group {
    padding: 0.25rem 0.6rem; font: inherit; font-size: var(--t-xs); font-weight: 600;
    color: var(--c-text-soft); background: var(--c-surface); cursor: pointer;
    border: 1px solid var(--c-border-strong); border-radius: var(--r-pill);
}
.ral-group:hover { border-color: var(--c-muted); color: var(--c-text); }
.ral-group-on { background: var(--c-brand); border-color: var(--c-brand); color: #fff; }

/* Висота — рівно на кілька рядів плиток: палітра не має відсувати ціну за екран. */
.ral-scroll { max-height: 300px; overflow-y: auto; overscroll-behavior: contain; }
.ral-section {
    position: sticky; top: 0; z-index: 1;
    margin: var(--s-2) 0 var(--s-1); padding: 2px 0;
    background: var(--c-surface-sunk);
    font-size: var(--t-xs); font-weight: 650; color: var(--c-text-soft);
}
.ral-section:first-child { margin-top: 0; }
.ral-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(54px, 1fr)); gap: var(--s-2); }
.ral-tile {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    padding: 3px; font: inherit; cursor: pointer;
    background: transparent; border: 1px solid transparent; border-radius: var(--r-sm);
}
.ral-tile:hover { border-color: var(--c-border-strong); background: var(--c-surface); }
.ral-tile:focus-visible { outline: 2px solid var(--c-brand); outline-offset: 1px; }
.ral-tile-swatch {
    width: 100%; aspect-ratio: 1; border-radius: 4px;
    border: 1px solid var(--c-border-strong);
}
.ral-tile-code {
    font-size: 11px; font-weight: 600; color: var(--c-text-soft);
    font-variant-numeric: tabular-nums;
}
.ral-tile-on { border-color: var(--c-brand); background: var(--c-surface); box-shadow: var(--ring); }
.ral-tile-on .ral-tile-code { color: var(--c-brand); }
.ral-empty { font-size: var(--t-sm); color: var(--c-muted); margin: var(--s-3) 0; }
.dxf-link {
    font-size: var(--t-xs); font-weight: 650; color: var(--c-text-soft); text-decoration: none;
    border: 1px solid var(--c-border-strong); border-radius: var(--r-sm);
    padding: 0.3rem 0.55rem; white-space: nowrap; transition: all var(--dur) var(--ease);
}
.dxf-link:hover, .dxf-link:focus-visible { border-color: var(--c-brand); color: var(--c-brand); }

.model3d-frame { position: relative; }
.model3d-host {
    width: 100%; aspect-ratio: 4 / 3;
    /* Стеля висоти: без неї на планшеті 4:3 розганяло прев'ю до ~600 px і
       виштовхувало форму за екран. Ширина лишається 100%, кадр просто нижчий. */
    max-height: clamp(200px, 44vh, 460px);
    border-radius: var(--r-md); overflow: hidden;
    /* Тло сцени дає CSS (канвас three.js прозорий) — інакше воно лишалось би
       зашитим у model3d.js і в темній темі виріб стояв у білому боксі. */
    background: radial-gradient(120% 92% at 50% 4%, var(--viewer-1), var(--viewer-2));
}
.model3d-host canvas { display: block; width: 100% !important; height: 100% !important; }

/* Кнопки ракурсу/фулскріна — накладкою в кутку кадру. Правила для них не було
   зовсім, хоча `.model3d-frame { position: relative }` існує саме заради них:
   без нього пара іконок просто провалювалась під канвас окремим рядком. */
.model3d-toolbar {
    position: absolute;
    top: var(--s-2);
    right: var(--s-2);
    display: flex;
    gap: var(--s-1);
}

.model3d-toolbar .btn {
    width: 32px; min-height: 32px; padding: 0;
    background: color-mix(in srgb, var(--c-surface) 85%, transparent);
    backdrop-filter: blur(6px);
    border-color: var(--c-border);
    color: var(--c-text-soft);
}

.model3d-toolbar .btn:hover { background: var(--c-surface); color: var(--c-text); }
/* На весь екран іде РАМКА, а полотно лишалось зі стелею 460 px і 4:3 — виріб
   висів маленьким у верхньому куті, а 3/4 екрана були порожні (аудит UI).
   Знімаємо стелю саме у fullscreen; камеру перекадровує model3d.js. */
.model3d-frame:fullscreen { background: var(--viewer-2); display: grid; }
.model3d-frame:fullscreen .model3d-host {
    width: 100%; height: 100%; max-height: none; aspect-ratio: auto; border-radius: 0;
}

/* === Підбір за витратою ================================================== */
.select-page h1 { margin-bottom: var(--s-2); }
.select-intro { color: var(--c-muted); margin-bottom: var(--s-5); max-width: 62ch; }

/* Сторінка «Звідки числа»: текстова довідка, тож ширина рядка — читацька
   (62ch, як .select-intro), а розділи розділені лінією, а не карткою: це не
   набір дій, а документ. */
.methods-block { max-width: 74ch; margin-bottom: var(--s-6); }
.methods-block + .methods-block { border-top: 1px solid var(--c-border); padding-top: var(--s-5); }
.methods-block h2 { margin-bottom: var(--s-3); }
.methods-block p { color: var(--c-text-soft); max-width: 62ch; }
.methods-list { color: var(--c-text-soft); margin: var(--s-3) 0 0; padding-left: var(--s-5); max-width: 62ch; }
.methods-list li + li { margin-top: var(--s-2); }
.methods-dl { margin: var(--s-3) 0 0; max-width: 62ch; }
.methods-dl dt { margin-top: var(--s-3); }
.methods-dl dd { margin: var(--s-1) 0 0; color: var(--c-text-soft); }
.methods-note { font-size: .9em; }
/* Таблиці довідки ширші за текст — числа й цитати ДБН в один рядок. */
.methods-block .table-scroll { max-width: none; margin-top: var(--s-3); }
/* 900, а не 620: зріз 04.08 подвоїв кількість полів (14), і в 620 px вони
   лягали в 3 тісні колонки з наїздами підписів. */
.select-inputs { max-width: 900px; }
/* Лічильник видачі біля фільтрів: тихий рядок, а не кнопка — він ІНФОРМУЄ про
   наслідок правки, і мусить читатись як частина форми, не як заклик до дії. */
.select-count {
    display: inline-block; margin: 0 0 var(--s-3);
    font-size: var(--t-sm); font-weight: 650; color: var(--c-brand);
    text-decoration: none; padding: 0.15rem 0;
}
.select-count:hover { text-decoration: underline; }
.select-count-empty { color: var(--warn-700); }
/* На телефоні лічильник ЛИПНЕ — і рівно в межах панелі фільтрів. Замір 20.08:
   з розкритою панеллю поля займають 1055 px, тож правлячи восьме поле,
   користувач уже не бачив ні видачі, ні числа, що змінюється. Липне під
   топбаром (він сам sticky), а на видачу не заходить: обгортка закінчується
   разом із панеллю. Десктоп не чіпаємо — там поля лягають у три колонки,
   і проблеми виміряно не було. */
@media (max-width: 719px) {
    .select-count {
        position: sticky; top: var(--topbar-h); z-index: 5;
        display: block; margin: 0 0 var(--s-2);
        background: var(--c-bg); padding: var(--s-2) 0;
        border-bottom: 1px solid var(--c-border);
    }
}
/* Розкриття додаткових фільтрів (зріз 39). Кнопка навмисно НЕ .btn: це не дія,
   а перемикач видимості — вигляд посилання з трикутником читається саме так. */
.select-more-toggle {
    display: inline-flex; align-items: center; gap: var(--s-2);
    margin: 0 0 var(--s-4); padding: var(--s-1) 0;
    background: none; border: 0; cursor: pointer;
    color: var(--c-brand); font: inherit; font-size: var(--t-sm); font-weight: 600;
}
.select-more-toggle:hover { text-decoration: underline; }
.select-more-toggle:focus-visible { outline: 2px solid var(--c-brand); outline-offset: 2px; }
.select-more-caret { font-size: var(--t-xs); }
/* Згортання — КЛАСОМ під медіазапитом, а не атрибутом hidden. Два наслідки, і
   обидва потрібні: на широкому екрані правило не діє взагалі (десктоп лишається
   пікселя в піксель таким, як був — за нього платити не треба), а зміна ширини
   вікна не може лишити поля схованими без кнопки, бо і кнопка, і згортання
   живуть в одному медіазапиті. Атрибут hidden цього не дав би: display:grid із
   @supports(subgrid) його перебиває, і згортання не спрацьовувало б саме там,
   де воно й потрібне. */
/* Згортання діє на ВСІХ ширинах (зріз 47).
   Доти на десктопі додаткові фільтри стояли розкритими завжди, а перемикач
   ховався: «контрол, який нічого не міняє, — це шум». Міркування було вірне
   рівно доти, доки не заміряли, що воно коштує. Замір 08.08 на 1280×720: десять
   додаткових полів займають 466 px МІЖ полем витрати й видачею, і разом із
   розрізом кімнати відсували першу картку на y=1311 px — 1,82 екрана, 0 із 8
   карток у полі зору після вводу витрати. Тобто сторінка, чия робота «введи
   число → побач вироби», не показувала жодного виробу.
   Ціна згортання мала й зворотна: значення переживають згортання, один клік
   повертає, а сам перемикач несе «· задано N» — хто прийшов по шер-лінку з
   фільтрами, бачить це, не розкриваючи. */
.select-inputs-more.is-collapsed { display: none; }
/* Телефон: первинні входи — у ДВІ колонки, вступ ховається за наявності видачі.
   Мінімум 200 px на поле (див. вище) ставили за колізії підписів у контейнері
   620 px із чотирма полями — на 390 px він давав одну колонку й 540 px висоти
   самих входів. Замір 08.08 на 390×844: у дві колонки поле = 171 px, ЖОДНОГО
   обрізання ні підпису, ні селекта (scrollWidth = clientWidth у всіх чотирьох),
   а перша картка піднялась із 1046 на 882 px. Разом зі схованим вступом — 727. */
@media (max-width: 719px) {
    .field-row.select-inputs-main { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .select-inputs-main .field { flex-basis: auto; min-width: 0; }
    .select-intro.has-results { display: none; }
}
.select-assistant { max-width: 620px; }
.select-assistant textarea { resize: vertical; min-height: 62px; }
.assistant-notes { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: 0 0 var(--s-4); max-width: 720px; }
.assistant-note { background: var(--air-50); color: var(--air-700); border-radius: var(--r-pill); padding: 0.22rem 0.7rem; font-size: var(--t-xs); font-weight: 600; }
.assistant-miss { color: var(--c-muted); font-size: var(--t-sm); margin: 0 0 var(--s-4); }

.variant-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(236px, 1fr)); gap: var(--s-4); }
.variant-card { padding: var(--s-4); display: flex; flex-direction: column; gap: var(--s-2); transition: box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease); cursor: pointer; }
/* Обрана для розрізу картка живиться кольором «повітря», а не бренду: бренд
   уже зайнятий ярликом «Оптимальний» (variant-optimal), і два сині обводи
   зливалися б в один. */
.variant-card.variant-selected { border-color: var(--air-700); box-shadow: 0 0 0 1px var(--air-700), var(--sh-sm); }

/* ── Розріз приміщення на /select (RoomSection) ── */
/* Тільки ПРЯМИЙ нащадок — та сама пастка, що .preview > svg: нащадковий
   селектор дістав би вкладені <svg> DwgView, а CSS-ширина у SVG2 перекриває
   геометричний атрибут width на старших Chromium (v1194). */
.room-section > svg { width: 100%; height: auto; display: block; }
.room-section { padding: var(--s-4); margin-bottom: var(--s-4); max-width: 720px; }
/* План розкладки (вид зверху) — та сама картка, що розріз; коло дальності —
   акцент «повітря» з легкою заливкою (клас LindQST), штрихове = оцінка. */
.room-plan { padding: var(--s-4); margin-bottom: var(--s-4); max-width: 720px; }
.room-plan .room-throw { fill: var(--dwg-air); fill-opacity: .07; stroke-width: 1.6; }
.noise-verdict { max-width: 720px; }
/* Вентилятори на витрату (/select, зріз 27): картки-лінки з фото і
   каталожними даними; біла підкладка фото навмисна і в темній темі. */
.fan-suggest { margin-top: var(--s-6); }
.fan-suggest h2 { font-size: var(--t-lg); margin-bottom: var(--s-3); }
/* inline-block: підзаголовок переноситься ЦІЛИМ — інакше «— каталожні…»
   лишав висяче тире в кінці рядка заголовка (UI/UX-прохід 18.08). */
.fan-suggest h2 small { font-size: var(--t-xs); font-weight: 400; color: var(--c-muted); display: inline-block; }
.fan-suggest-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: var(--s-3); }
.fan-suggest-card {
  display: flex; flex-direction: column; gap: var(--s-1);
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--r-lg); box-shadow: var(--sh-sm);
  padding: var(--s-3); text-decoration: none; color: inherit;
}
.fan-suggest-card:hover { border-color: var(--c-border-strong); }
/* Ознака дії на картці-лінку: без неї свіже око не знаходило на картці жодної
   кнопки й вважало підбір вентилятора глухим кутом (UI/UX-прохід 18.08). */
.fan-suggest-open { margin-top: var(--s-2); font-size: var(--t-sm); font-weight: 600; color: var(--c-brand); }
.fan-suggest-card:hover .fan-suggest-open { text-decoration: underline; }
.fan-suggest-photo { max-height: 96px; object-fit: contain; background: #fff; border-radius: 8px; padding: 4px; align-self: center; max-width: 100%; }
.fan-suggest-title { font-weight: 700; }
.fan-suggest-series { font-size: var(--t-xs); color: var(--c-muted); }
.fan-suggest-specs { display: grid; grid-template-columns: auto auto; gap: 2px var(--s-3); margin: var(--s-1) 0 0; }
.fan-suggest-specs > div { display: contents; }
.fan-suggest-specs dt { color: var(--c-muted); font-size: var(--t-xs); }
.fan-suggest-specs dd { margin: 0; font-size: var(--t-xs); font-weight: 650; text-align: right; }
/* Нульовий запас вентилятора: попередження стоїть рівно біля числа «+0%»,
   бо саме воно й вводило в оману (у мережі з опором такий подасть менше).
   Колір — той самий, що в інших застережень картки, аби не заводити новий. */
.fan-nil-reserve { margin-left: 2px; color: var(--warn-600); cursor: help; }
.fan-suggest-price { margin-top: auto; padding-top: var(--s-2); font-weight: 700; }
.fan-suggest-note { margin-top: var(--s-3); font-size: var(--t-xs); color: var(--c-muted); max-width: 84ch; }
/* Вентилятори для об'єкта на /project — той самий ряд карток, згорнутий у details. */
.project-fans { margin: var(--s-3) 0; }
.project-fans > summary { cursor: pointer; font-weight: 650; }
.project-fans > summary small { font-weight: 400; color: var(--c-muted); }
.project-fans-side { margin-top: var(--s-3); }
.project-fans-side h4 { margin: 0 0 var(--s-2); font-size: var(--t-md); }
/* Акустика приміщення: рядок Σ-шуму специфікації (зріз 33). */
.room-noise { margin: var(--s-2) 0 0; font-size: var(--t-sm); }
.room-noise small { color: var(--c-muted); font-size: var(--t-xs); }
/* Нотатка вентиляторів — власний клас, не «пігулка асистента» (аудит). */
.fan-note { margin: var(--s-2) 0 0; font-size: var(--t-sm); color: var(--c-brand); }
.fan-added { font-size: var(--t-sm); color: var(--c-muted); }

/* Підказка комплектності в КП: текст + кнопка «дібрати» в один ряд. */
.completeness-hint { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
/* Звірка знімків цін із чинним прайсом: робоча смуга менеджера (no-print). */
.reprice-note { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
    margin: var(--s-3) 0; padding: var(--s-2) var(--s-3);
    border: 1px solid var(--c-border); border-left: 3px solid var(--c-brand);
    border-radius: var(--r-sm); font-size: var(--t-sm); }
.reprice-badge { font-size: var(--t-xs); color: var(--c-brand); white-space: nowrap; }
.reprice-badge.reprice-gone { color: var(--c-muted); }
.noise-verdict small { display: block; font-weight: 400; opacity: .8; }
.room-caption { margin: var(--s-2) 0 0; font-size: var(--t-xs); color: var(--c-muted); line-height: 1.4; }
/* Оцінна дальність — штрихом: суцільна лінія обіцяла б паспорт, якого немає
   (той самий поділ, що бейджі aero-badge-cat / aero-badge-est). */
.drawing .dwg-air-est { stroke-dasharray: 6 4; }
.variant-card:hover { box-shadow: var(--sh-md); }
.variant-optimal { border-color: var(--c-brand); box-shadow: 0 0 0 1px var(--c-brand), var(--sh-sm); }
.variant-tags { display: flex; gap: var(--s-1); flex-wrap: wrap; min-height: 1.1rem; }
/* В одноколонковій стрічці вирівнювати нема з чим — порожня смуга під бейдж
   лише подовжувала мобільну видачу і виглядала недомальованою (UI/UX 18.08);
   у сітці десктопа min-height лишається — він тримає картки в один рівень. */
@media (max-width: 719px) {
    .variant-tags:empty { display: none; }
}
.variant-tag { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; border-radius: 4px; padding: 0.1rem 0.4rem; }
.tag-optimal { background: var(--c-brand); color: var(--c-on-brand); }
.tag-quietest { background: var(--ok-50); color: var(--ok-600); }
.tag-cheapest { background: var(--warn-50); color: var(--warn-600); }
/* «Чому ця картка тут» — НЕ ярлик: без капсу, без заливки, приглушено.
   Ярлик каже «чим вона краща за решту» і є щонайбільше в трьох карток із
   восьми; підпис відповідає на інше питання й стоїть у КОЖНОЇ. Зрівняти їх
   виглядом означало б перетворити опис місця у видачі на восьму «нагороду». */
.variant-why { margin: 0; font-size: var(--t-xs); color: var(--c-muted); }
/* Заголовок картки/рядка видачі — КНОПКА (клавіатурне обрання варіанта),
   тож знімаємо все, що браузер малює кнопці, лишаючи вигляд заголовка.
   Фокус-рамку не чіпаємо: глобальний :focus-visible вище і є тим, що робить
   обрання видимим для клавіатури. */
.variant-title {
    font-weight: 650; font-size: var(--t-sm);
    display: block; width: 100%; text-align: left;
    background: none; border: 0; margin: 0;
    color: inherit; font: inherit; font-weight: 650; font-size: var(--t-sm);
    cursor: pointer;
    /* Тап-ціль, а не просто текст: рядок заголовка має 20 px, а мінімум за
       WCAG 2.2 (і за сторожем responsive.spec «жодного не-рядкового елемента
       < 24×24 px») — 24. Саме цей сторож і спіймав першу редакцію цієї правки:
       кнопка з padding: 0 давала 324×20 на десяти картках. */
    padding: 3px 0;
}
.variant-price { font-size: var(--t-md); font-weight: 700; color: var(--c-brand); }
.variant-aero { display: grid; grid-template-columns: auto auto; gap: 0.1rem var(--s-3); margin: 0; font-size: var(--t-xs); }
.variant-aero dt { color: var(--c-muted); }
.variant-aero dd { margin: 0; text-align: right; font-weight: 650; }
.variant-card .btn { justify-content: center; margin-top: var(--s-1); }
.variant-card .aero-rating { margin: var(--s-1) 0 0; font-size: var(--t-xs); }

/* === Пропозиція ========================================================== */
.quote-page h1 { margin-bottom: var(--s-5); }
.quote-empty { color: var(--c-muted); }
/* Горизонтальний скрол таблиці КП лишається для СЕРЕДНІХ ширин (640–719):
   там колонки вже вміщаються без послівних переносів. Нижче 640 діє карткова
   розкладка (див. нижче) — попередній min-width:640px на вузьких екранах
   ховав колонки «Ціна/шт» і «Сума» правіше краю БЕЗ жодного натяку на скрол:
   документ про гроші не показував грошей, а «Разом | 1» (кількість) читався
   як підсумок «1 грн» (UI/UX-прохід 18.08, HIGH; те саме бачив замовник на
   /kp). У друку скрол зайвий — там A4. */
.table-scroll { overflow-x: auto; }

/* КП на вузькому екрані — рядок таблиці стає карткою: назва з розбивкою на
   всю ширину, під нею — кількість, ціна/шт і сума, підписані словами
   (шапки таблиці тут немає). Розмітка спільна для /quote і /kp — гачки
   td-* стоять в обох. */
@media screen and (max-width: 639px) {
    .quote-table thead { display: none; }
    .quote-table, .quote-table tbody, .quote-table tfoot { display: block; }
    .quote-table tr {
        display: flex; flex-wrap: wrap; align-items: baseline;
        column-gap: var(--s-4); row-gap: var(--s-2);
        padding: var(--s-3) 0; border-bottom: 1px solid var(--c-border);
    }
    .quote-table td { display: block; border: none; padding: 0; }
    /* Номер позиції на телефоні не вартий колонки — порядок видно і так. */
    .quote-table .td-no, .quote-table td:empty { display: none; }
    .quote-table .td-item { flex: 1 1 100%; }
    .quote-table .td-qty::before { content: "К-ть "; }
    .quote-table .td-unit::before { content: "Ціна/шт "; }
    .quote-table .td-total::before { content: "Сума "; }
    .quote-table .td-qty::before, .quote-table .td-unit::before,
    .quote-table .td-total::before {
        font-size: var(--t-xs); color: var(--c-muted);
        font-weight: 400; text-transform: uppercase; letter-spacing: .04em;
    }
    .quote-table .td-actions { margin-left: auto; }
    /* Смуга приміщення: фон переїжджає на рядок — клітинки тепер без падінгів. */
    .quote-room-row { background: var(--c-surface-sunk); padding: var(--s-2) var(--s-2); }
    .quote-room-row td { background: transparent; }
    /* Підсумки: «Разом» ліворуч, сума праворуч; риска зверху — одна на блок,
       а не на кожну клітинку-блок. */
    .quote-table tfoot { border-top: 2px solid var(--c-border-strong); }
    .quote-table tfoot td { border-top: none; }
    .quote-table tfoot tr { border-bottom: none; padding: var(--s-2) 0; }
    .quote-table tfoot .td-label { flex: 1 1 auto; }
    .quote-table tfoot .td-total { margin-left: auto; }
}
/* «Назва» в таблицях кабінету: сусідні колонки (числа, дати, кнопки) не
   переносяться, тож auto-layout віддає breakable-колонці лише мінімум одного
   слова — «ЖК / Сонячний, / секція Б» стовпчиком (виміряно: 95 px при таблиці
   803). Floor на саму колонку: таблиця стає ширшою і скролиться в
   .table-scroll, а назва читається рядком. */
.saved-table td:first-child, .saved-table th:first-child { min-width: 220px; }
@media print {
    .table-scroll { overflow: visible; }
    .quote-table { min-width: 0; }
}

.quote-table { width: 100%; border-collapse: collapse; overflow: hidden; }
.quote-table th, .quote-table td { padding: var(--s-3); border-bottom: 1px solid var(--c-border); text-align: left; vertical-align: top; font-size: var(--t-sm); }
.quote-table th { background: var(--c-surface-sunk); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .04em; color: var(--c-muted); font-weight: 650; }
.quote-table th.num, .quote-table td.num { text-align: right; white-space: nowrap; }
.quote-table tfoot td { border-top: 2px solid var(--c-border-strong); border-bottom: none; font-size: var(--t-base); }
.quote-table tbody tr:last-child td { border-bottom: none; }
.quote-breakdown { font-size: var(--t-xs); color: var(--c-muted); margin-top: var(--s-1); }
.quote-item-link { font-weight: 600; }
.quote-request-cell { color: var(--warn-600); font-style: italic; }
.quote-item-caveat { margin-top: 2px; color: var(--warn-600); font-size: var(--t-xs); line-height: 1.35; }
/* Смуга приміщення в КП (зріз 2 проєкту): токен поверхні, а не свій колір, —
   темна тема успадковується сама; у PDF та сама роль — Palette.Band (--n-100). */
.quote-room-row td { background: var(--c-surface-sunk); color: var(--c-text-soft); font-weight: 650; font-size: var(--t-sm); }
.quote-note { color: var(--c-muted); font-size: var(--t-xs); margin-top: var(--s-3); }
.quote-hint { margin: var(--s-3) 0 0; font-size: var(--t-sm); color: var(--c-text-soft); }
.quote-badge { background: var(--c-brand); color: var(--c-on-brand); border-radius: var(--r-pill); font-size: var(--t-xs); font-weight: 650; padding: 0.05rem 0.45rem; margin-left: var(--s-1); }
/* Мініатюра схеми в рядку КП. 190 px на 560-одиничний viewBox — це масштаб 0.34,
   тобто розмірні написи виходили ~4 px: не підпис, а шум. Збільшувати шрифт у
   координатах SVG не можна — підписи розставлені під 12–14 одиниць і почали б
   налазити один на одного. Тому міняємо тільки кадр: трохи ширший на екрані
   (рядок лишається компактним) і помітно ширший на друк, де сторінка все одно
   віддається під документ для замовника. */
.quote-thumb { max-width: 230px; margin-top: var(--s-2); opacity: .95; }
@media print { .quote-thumb { max-width: 330px; opacity: 1; } }
.quote-print-header {
    display: flex; align-items: center; gap: var(--s-2);
    margin-bottom: var(--s-4); font-size: var(--t-sm); color: var(--c-muted);
}
/* Підпис авторства притиснутий до правого краю аркуша — навпроти назви застосунку. */
.designed-by-print { margin-left: auto; }

/* === Запит на прорахунок (зріз 38) ====================================== */
/* Секція живе під діями /quote і має власну межу зверху: це окремий крок
   воронки, а не ще один рядок кнопок. */
.quote-request {
    margin-top: var(--s-5); padding-top: var(--s-4);
    border-top: 1px solid var(--c-border);
}
.quote-request h2 { margin: 0 0 var(--s-2); font-size: var(--t-lg); }
.quote-request-lead { margin: 0 0 var(--s-3); color: var(--c-muted); max-width: 72ch; }
.quote-request-fields { margin-top: var(--s-2); }
.quote-request-problems { margin: var(--s-3) 0 0; padding-left: var(--s-5); }
.quote-request-problems li { margin: 2px 0; }
.quote-request-actions { margin-top: var(--s-3); }
.quote-request-hint { margin: var(--s-2) 0 0; color: var(--c-muted); font-size: var(--t-xs); }
.quote-request-outcome { margin-top: var(--s-3); }
.quote-request textarea { width: 100%; resize: vertical; }

/* Профіль кабінету (зріз 41): згорнутий, коли вже заповнений — це налаштування,
   а не щоденна дія; порожній розгортається сам, бо саме тоді його й треба. */
.account-profile, .account-password, .account-danger { margin-bottom: var(--s-5); padding-bottom: var(--s-4); border-bottom: 1px solid var(--c-border); }
.account-profile > summary, .account-password > summary, .account-danger > summary { cursor: pointer; font-weight: 650; }
.account-profile > summary small, .account-password > summary small, .account-danger > summary small { color: var(--c-muted); font-weight: 400; }
.account-profile .field-row, .account-password .field-row, .account-danger .field-row { margin-top: var(--s-3); }
.account-profile-hint { margin: var(--s-2) 0 0; color: var(--c-muted); font-size: var(--t-xs); max-width: 76ch; }
/* Попередження перед видаленням акаунта: звичайний текст, не error-плашка —
   помилки тут ще немає, є незворотність, і її пояснює зміст, а не колір. */
.account-danger-warn { margin: var(--s-3) 0 0; max-width: 76ch; }
.account-danger > summary { color: var(--err-600); }
/* Поля секцій кабінету на вузькому екрані — В ОДНУ колонку: базовий field-row
   (flex-basis 140px) ставив «Чинний/Новий пароль» парою по ~150 px — пароль не
   набрати й не перевірити оком, а «Телефон» лишався сиротою в другому ряду
   (UI/UX-прохід 18.08). */
@media (max-width: 480px) {
    /* Розкладку field-row насправді веде ҐРІД із блоку @supports(subgrid)
       (auto-fit minmax(140px,1fr) — дві колонки по ~171px на 390px, виміряно
       наживо), тож головний рядок тут — grid-template-columns; flex-* — фолбек
       для браузерів без subgrid, де field-row лишається флексом. */
    .account-profile .field-row, .account-password .field-row, .account-danger .field-row {
        grid-template-columns: 1fr;
        flex-direction: column; align-items: stretch;
    }
}

.save-quote { display: flex; align-items: center; gap: var(--s-2); margin-top: var(--s-5); flex-wrap: wrap; padding-top: var(--s-4); border-top: 1px solid var(--c-border); }
.save-quote-title { flex: 1 1 240px; width: auto; }
/* Той самий інпут усередині label.field (колонковий flex): flex-basis 240px там
   стає ВИСОТОЮ, і однорядкове поле «Об'єкт / замовник» розтягувалось у
   псевдо-textarea на чверть екрана (спіймано скриншотом зрізу 2). */
.field .save-quote-title { flex: none; width: 100%; }
/* Збереження проєкту: поле з підписом вище за кнопку, тож вирівнюємо по низу. */
.save-project { align-items: end; }
.save-project .object-field { flex: 1 1 260px; margin: 0; }
.save-project-hint { flex-basis: 100%; color: var(--c-muted); }
.save-quote-shared { flex-basis: 100%; margin: var(--s-3) 0 0; color: var(--c-muted); font-size: var(--t-sm); }
.save-quote-hint { margin-top: var(--s-5); padding-top: var(--s-4); border-top: 1px solid var(--c-border); color: var(--c-muted); font-size: var(--t-sm); }

/* === Кабінет і вхід ====================================================== */
.auth-page { max-width: 400px; margin: var(--s-8) auto; }
.auth-page h1 { margin-bottom: var(--s-2); }
.auth-note { color: var(--c-muted); font-size: var(--t-sm); margin-bottom: var(--s-5); }
.auth-form { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-lg); padding: var(--s-5); box-shadow: var(--sh-sm); }
.auth-form .btn { width: 100%; margin-top: var(--s-2); }
/* Лінки під формою — окремими рядками: «Немає акаунта? Зареєструватись Забули
   пароль?» упритул читалось одним реченням, а тап-цілі злипались (UI/UX 18.08). */
.auth-switch { margin-top: var(--s-4); font-size: var(--t-sm); color: var(--c-muted); text-align: center; }
.auth-switch > span { display: block; }
.auth-switch > span + span { margin-top: var(--s-2); }

.account-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-6); }
.logout-form { display: flex; align-items: center; gap: var(--s-3); }
.account-email { color: var(--c-muted); font-size: var(--t-sm); }
.saved-table { width: 100%; border-collapse: collapse; overflow: hidden; }
.saved-table th, .saved-table td { padding: var(--s-3); border-bottom: 1px solid var(--c-border); text-align: left; font-size: var(--t-sm); }
.saved-table th { background: var(--c-surface-sunk); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .04em; color: var(--c-muted); }
.saved-table tbody tr:last-child td { border-bottom: none; }
.saved-actions { display: flex; gap: var(--s-2); }
.account-hint, .account-empty { color: var(--c-muted); font-size: var(--t-sm); }
/* Числа, одиниці й дати таблиць кабінету не ламаються всередині
   («770 м³/ год», «ПОЗИЦІЙ КП») — перенос між словами тут гірший за скрол. */
.saved-table .no-wrap { white-space: nowrap; }

/* Шер-лінк збереженої КП (зріз 54): панель під рядком + бейдж чинного лінка. */
.share-panel { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
/* Моноширинний і на всю вільну ширину: токен довгий, і обрізаний хвіст має бути
   видно ЯК обрізаний, а не читатись як повний URL. */
.share-url { flex: 1 1 260px; min-width: 0; font-family: var(--font-mono, monospace);
    font-size: var(--t-xs); padding: var(--s-2); border: 1px solid var(--c-border);
    border-radius: var(--r-sm, 6px); background: var(--c-surface-sunk); color: var(--c-text); }
.share-until { color: var(--c-muted); font-size: var(--t-xs); flex-basis: 100%; }
.share-badge { color: var(--c-muted); font-size: var(--t-xs); margin-top: var(--s-1); }

/* Журнал запитів (зріз 85). Три підсумки розрізняються КОЛЬОРОМ і словом одразу:
   «Взято вручну» — не невдача (текст складено й показано), але й не «надіслано»,
   тож нейтральний muted, а не зелений і не червоний. Той самий поділ, що бейджі
   aero-badge-cat / aero-badge-est: тон каже про клас надійності твердження. */
.request-outcome { font-size: var(--t-xs); font-weight: 600; white-space: nowrap; }
.request-outcome-sent { color: var(--ok-600); }
.request-outcome-copied { color: var(--c-muted); }
.request-outcome-failed { color: var(--err-600); }
.request-comment { color: var(--c-muted); font-size: var(--t-xs); margin-top: var(--s-1);
    overflow-wrap: anywhere; }

/* Публічний перегляд КП за лінком (/kp/{token}). */
.shared-quote-meta { color: var(--c-text-soft); font-size: var(--t-sm); margin-bottom: var(--s-4); }
.shared-quote-footer { color: var(--c-muted); font-size: var(--t-xs); margin-top: var(--s-4); }

/* === Бічна навігація ======================================================
   Глобально, а не scoped: .nav-link рендерить компонент NavLink, і scoped-атрибут
   NavMenu на його <a> не потрапляє (для цього й існує ::deep). */

.nav-brand {
    display: flex; align-items: center;
    height: var(--topbar-h);
    padding: 0 var(--s-4);
    border-bottom: 1px solid var(--c-border);
}

.nav-brand-link {
    display: inline-flex; align-items: center; gap: var(--s-2);
    color: var(--c-text); text-decoration: none;
}

/* Знак несе власний градієнт і фон-плитку — обгортка-підкладка більше не потрібна. */
.brand-logo { display: block; flex: none; }

/* «Vent» — вагою, «Calc» — кольором. Доти жирними були обидві половини:
   акцент нічого не виділяв, бо виділяти було нічого. */
.nav-brand-text { font-weight: 700; font-size: var(--t-md); letter-spacing: -0.015em; }
.nav-brand-accent { color: var(--c-brand); font-weight: 400; }

/* --- підвал меню: вибір теми --- */

.nav-foot {
    margin-top: auto;
    /* Підпис авторства став третім елементом підвалу — сітка замість flex-рядка,
       щоб «designed by ТЕЗ» стояв власним рядком над вибором теми. */
    display: grid; grid-template-columns: auto 1fr; align-items: center;
    gap: var(--s-2) var(--s-3);
    padding: var(--s-3) var(--s-4) calc(var(--s-4) + env(safe-area-inset-bottom));
    border-top: 1px solid var(--c-border);
}
.designed-by-nav { grid-column: 1 / -1; }

/* === Підпис авторства ==================================================== */
.designed-by {
    display: inline-flex; align-items: center; gap: 0.45em;
    font-size: var(--t-xs); color: var(--c-muted);
}
.designed-by-label { letter-spacing: .02em; }
/* inline-flex, щоб анкер не додавав базлайнового просвіту під растром і межа
   кліку збігалася з межею знака, а не з рядком тексту.
   min-height — не косметика: сам знак 15 px заввишки, а це МЕНШЕ за палець, і
   тап-сторож responsive.spec.ts на /quote це впіймав одразу, як анкер з'явився.
   Ціль ростимо до 24 px (WCAG 2.5.8), знак лишається 15 — align-items центрує
   його в цілі, тобто видимий розмір не змінюється, змінюється зона дотику. */
.designed-by-link {
    display: inline-flex; align-items: center; min-height: 24px;
    border-radius: 2px;
}
/* Висота задає розмір, ширину рахує браузер із width/height у розмітці —
   без стрибка розкладки на завантаженні. */
.designed-by img {
    height: 15px; width: auto; display: block;
    border-radius: 2px;
    /* Плашка знака чорна: на світлому тлі це його рідний вигляд, на темному
       вона зливається з тлом і лишається саме напис — обидва стани навмисні. */
}

.nav-foot-label {
    font-size: var(--t-xs); font-weight: 650;
    text-transform: uppercase; letter-spacing: .06em;
    color: var(--c-muted);
}

.theme-picker {
    display: inline-flex; gap: 2px; padding: 2px;
    background: var(--c-surface-sunk);
    border: 1px solid var(--c-border); border-radius: var(--r-sm);
}

.theme-option {
    border: none; background: transparent; color: var(--c-muted);
    border-radius: 4px; padding: 0.25rem 0.5rem;
    font: inherit; font-size: var(--t-xs); font-weight: 600;
    cursor: pointer;
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.theme-option:hover { color: var(--c-text); }

/* Активний варіант — від атрибута на <html>, а не від класу, доданого скриптом:
   переживає перебудову піддерева на гідратації і не потребує interop. */
:root:not([data-theme]) .theme-option[data-mode="system"],
:root[data-theme="light"] .theme-option[data-mode="light"],
:root[data-theme="dark"] .theme-option[data-mode="dark"] {
    background: var(--c-surface);
    color: var(--c-text);
    box-shadow: var(--sh-xs);
}

.nav-scrollable { padding: var(--s-4) var(--s-3) var(--s-8); }

.nav-section {
    margin: var(--s-4) var(--s-2) var(--s-2);
    font-size: var(--t-xs); font-weight: 650;
    text-transform: uppercase; letter-spacing: .06em;
    color: var(--c-muted);
}
.nav-section:first-child { margin-top: 0; }

.nav-link {
    display: flex; align-items: center; gap: var(--s-3);
    padding: 0.5rem var(--s-3);
    margin-bottom: 2px;
    border-radius: var(--r-sm);
    color: var(--c-text-soft);
    font-size: var(--t-sm); font-weight: 550;
    text-decoration: none;
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav-link:hover { background: var(--c-surface-sunk); color: var(--c-text); }

/* NavLink додає .active поточному пункту */
.nav-link.active {
    background: var(--brand-50);
    color: var(--c-brand);
    font-weight: 650;
}

.nav-ico { width: 20px; height: 20px; flex: none; opacity: .85; }
.nav-link.active .nav-ico { opacity: 1; }

.nav-count {
    margin-left: auto;
    font-size: var(--t-xs); font-weight: 650;
    color: var(--c-muted);
    background: var(--c-surface-sunk);
    border: 1px solid var(--c-border);
    border-radius: var(--r-pill);
    padding: 0 0.4rem;
}
.nav-link.active .nav-count { color: var(--c-brand); border-color: color-mix(in srgb, var(--c-brand) 30%, transparent); background: var(--c-surface); }

/* === Мобільний sticky-бар ціни =========================================== */
.price-sticky { display: none; }
/* Лише одноколонкова розкладка: від 720 px картка ціни стоїть у правій колонці
   й видима без прокрутки, тож дублювати її смугою внизу нема сенсу. */
@media (max-width: 719px) {
    .price-sticky {
        display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
        position: fixed; inset: auto 0 0 0; z-index: 40;
        padding: var(--s-3) var(--s-4);
        background: var(--c-surface); border-top: 1px solid var(--c-border);
        box-shadow: var(--sh-lg);
        padding-bottom: max(var(--s-3), env(safe-area-inset-bottom));
    }
    /* Було .price-total — такого класу в sticky немає (price-sticky-total). */
    .price-sticky-total { font-size: var(--t-lg); font-weight: 700; letter-spacing: -0.02em; }
    /* Лише коли sticky реально в DOM: інакше /catalog /select /quote мали
       мертву смугу 76 px без бару (аудит UI). */
    /* 88, не 76: із двома рядками тексту бар вищий, і останній елемент форми
       розминався з ним на ~10 px (замір по /accessory, UI/UX-прохід 18.08). */
    body:has(.price-sticky) { padding-bottom: 88px; }
}

/* === Друк ================================================================ */
@media print {
    /* Селектор навмисно повторює темний (`:root:not([data-theme="light"])`, 0-1-1):
       голий `:root` (0-0-1) програвав йому за специфічністю, тож у користувача з
       темною системною темою пропозиція йшла на друк ТЕМНОЮ — світлий текст на
       білому папері. Медіазапит специфічності не додає. */
    :root, :root:not([data-theme="light"]), :root[data-theme="dark"] {
        color-scheme: light;
        --c-bg: #fff; --c-surface: #fff; --c-surface-sunk: #fff;
        --c-text: #000; --c-text-soft: #222; --c-muted: #444;
        --c-border: #bbb; --c-border-strong: #999;
        --dwg-ink: #363f4d; --dwg-line: #5b6673; --dwg-thin: #97a3b4;
        --dwg-fill: #f5f7fa; --dwg-void: #fff; --dwg-hatch: #eceff4;
        --dwg-mark: #b3543a; --dwg-air: #0a6b6a;
    }
    .app-sidebar, .app-topbar, .no-print, .price-sticky { display: none !important; }

    /* Сторінка виробу на папері — це паспорт, а не форма: кнопки, вкладки прев'ю,
       палітра, майстер приміщення й поради на аркуші не діють, а місце з'їдають
       (аудит UI: Ctrl+P на конфігураторі давав друковані контроли). Значення
       полів лишаються — саме вони й описують виріб. */
    .btn, .preview-tabs, .room-panel, .advice-card, .chips-hint,
    .ral-palette, .ral-search, .preset-chips, .size-filter { display: none !important; }

    /* Поля без рамок і підкладки читаються як рядки специфікації. */
    .field input, .field select, .field textarea {
        border: none !important; box-shadow: none !important; background: none !important;
        padding-left: 0 !important; min-height: 0 !important; font-weight: 600;
        appearance: none; -webkit-appearance: none;
    }

    /* Порожня картка ADPI (довжину не вводили) — на аркуші сам заголовок ні про що. */
    .adpi-card:not(:has(.adpi-verdict)) { display: none !important; }

    /* Каркас на друк розкладає MainLayout.razor.css: `.app` описаний там, тобто
       зі scope-атрибутом, і голий `.app` звідси програвав би за специфічністю. */
    .app-main, .app-content { padding: 0 !important; margin: 0 !important; }
    .quote-table, .saved-table { box-shadow: none; }
    a { text-decoration: none; color: #000; }
}
/* Показуємо ЛИШЕ на друк. Значення `revert`, а не block/inline: клас носять і
   <header class="quote-print-header print-only">, і <span class="print-only"> у
   комірці кількості — жорстке значення завжди ламало б один із двох. Так і було:
   правило оголошувалось ДВІЧІ (тут `block`, нижче по файлу `inline`), пізніше
   перемагало за порядком, і шапка друкованої КП ставала інлайновою — gap,
   align-items, margin-bottom і притиснутий праворуч підпис ТЕЗ не діяли.
   `.quote-print-header` перевизначається ТУТ ЖЕ, після `.print-only`: у нього
   та сама специфічність (один клас), тож виграє те, що стоїть пізніше. */
.print-only { display: none; }
@media print {
    .print-only { display: revert; }
    .quote-print-header { display: flex; }
}

/* === Сторінка помилки ==================================================== */
.error-lead { color: var(--c-text-soft); max-width: 62ch; margin: var(--s-3) 0 var(--s-5); }
.error-request-id { color: var(--c-muted); font-size: var(--t-sm); margin-bottom: var(--s-5); word-break: break-all; }
.error-request-id code {
    font-family: var(--font-mono); font-size: var(--t-xs);
    background: var(--c-surface-sunk); border: 1px solid var(--c-border);
    border-radius: var(--r-sm); padding: 0.05rem 0.35rem;
}

/* Blazor-помилка: не з дефолтного шаблону, а в мові інтерфейсу */
#blazor-error-ui {
    display: none; position: fixed; inset: auto 0 0 0; z-index: 1000;
    padding: var(--s-3) var(--s-5); background: var(--warn-50); color: var(--warn-600);
    border-top: 1px solid var(--warn-600); box-shadow: var(--sh-lg); font-size: var(--t-sm);
}
#blazor-error-ui .dismiss { cursor: pointer; float: right; }

/* === Поради в конфігураторі =============================================== */

.advice-card {
    padding: var(--s-5);
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-sm);
}

.advice-card h2 { margin-bottom: var(--s-3); font-size: var(--t-md); }
.advice-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }

.advice-item {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--s-3);
    padding-left: var(--s-3);
    border-left: 3px solid var(--c-border-strong);
}

.advice-item .btn { flex: none; }
.advice-text { font-size: var(--t-sm); color: var(--c-text-soft); line-height: var(--lh-base); }

.advice-quieter { border-left-color: var(--ok-600); }
.advice-cheaper { border-left-color: var(--c-brand); }
.advice-oversized { border-left-color: var(--warn-600); }

/* === Підбір за фото ====================================================== */

.photo-panel {
    max-width: 620px;
    margin-bottom: var(--s-6);
    padding: var(--s-5);
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-sm);
}

/* Падінги успадковуються від `.field input` — власні робили поле файлу на 7 px
   вищим за кожен інший контрол у застосунку (49 проти 42). Своє тут лише те,
   що справді відрізняє зону завантаження: пунктир і втоплене тло. */
.photo-drop input[type="file"] {
    border-style: dashed;
    background: var(--c-surface-sunk);
    cursor: pointer;
}

.photo-drop input[type="file"]:hover { border-color: var(--c-brand); }
.photo-drop small { display: block; margin-top: var(--s-1); color: var(--c-muted); font-size: var(--t-xs); }

.photo-result {
    display: flex; align-items: center; gap: var(--s-3);
    margin: var(--s-4) 0 var(--s-3);
}

.photo-swatch {
    width: 44px; height: 44px; flex: none;
    border-radius: var(--r-md);
    border: 1px solid var(--c-border-strong);
}

.photo-ral { font-weight: 650; font-size: var(--t-md); }
.photo-shape { font-size: var(--t-sm); color: var(--c-muted); }
.photo-heading { margin-bottom: var(--s-4); font-size: var(--t-md); }
.photo-why { font-size: var(--t-xs); color: var(--c-muted); }

/* === Каталог ============================================================= */
.catalog-search { max-width: 460px; margin-bottom: var(--s-4); }
/* Стрибок до розділу: чипи одним рядком зі скролом — на телефоні каталог
   ~11 екранів, і до «Автоматики»/«Монтажу» інакше десяток свайпів наосліп
   (UI/UX-прохід 18.08). */
.catalog-nav { display: flex; gap: var(--s-2); overflow-x: auto; padding-bottom: var(--s-2); margin-bottom: var(--s-4); }
.catalog-nav-chip {
    flex: 0 0 auto; font-size: var(--t-sm); padding: 0.3rem 0.7rem;
    background: var(--c-surface-sunk); color: var(--c-text-soft);
    border: 1px solid var(--c-border); border-radius: var(--r-pill); cursor: pointer;
}
.catalog-nav-chip:hover { border-color: var(--c-border-strong); color: var(--c-text); }
.catalog-nav-chip .catalog-count { font-size: var(--t-xs); }
.catalog-group { margin-bottom: var(--s-8); }
.catalog-group h2 { display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-4); font-size: var(--t-md); }
.catalog-count {

/* Уточнення групи каталогу («— 4 у конфігураторі «Решітка», 2 в аксесуарах»):
   тонше й тихіше за сам заголовок — це пояснення числа, а не другий заголовок. */
.catalog-group-split {
    margin-left: var(--s-2); color: var(--c-muted);
    font-size: var(--t-xs); font-weight: 400;
}    font-size: var(--t-xs); font-weight: 650; color: var(--c-muted);
    background: var(--c-surface-sunk); border: 1px solid var(--c-border);
    border-radius: var(--r-pill); padding: 0 0.45rem;
}
.catalog-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: var(--s-3); }
.catalog-card {
    display: grid; gap: 0.15rem;
    padding: var(--s-4);
    background: var(--c-surface); border: 1px solid var(--c-border);
    border-radius: var(--r-md); box-shadow: var(--sh-xs);
    text-decoration: none; color: var(--c-text);
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.catalog-card:hover { border-color: var(--c-brand); box-shadow: var(--sh-md); transform: translateY(-1px); color: var(--c-text); }
/* Мініатюра картки каталогу — той самий .quote-thumb, що в КП, лише вписаний
   у сітку картки: фіксована висота вирівнює ряд, схема без габаритів чесно
   лишає порожньо (компонент нічого не рендерить). Прямі нащадки svg — та
   сама пастка вкладених видів, що .preview > svg. */
.catalog-thumb { min-height: 84px; display: flex; align-items: center; justify-content: center; overflow: hidden; }
/* Текстова специфікація креслення в мініатюрі каталогу дублює картку і
   роздуває її — в КП вона доречна (документ), тут лише схема. */
.catalog-thumb .drawing-spec { display: none; }
/* Підписи схем у мініатюрі — ГЕТЬ, і це не смак: кегль 7.5 юніт при
   масштабі мініатюри дає ~2,5 px, а браузер мовчки піднімає його до свого
   мінімуму — текст росте ВІДНОСНО геометрії, розповзається за кадр виду і
   правим краєм обрізається контейнером (кліп «Переріз А-А…», борг 18.08,
   доведено елемент-знімками ART-1 до/після). Нечитабельний текст у 84-px
   мініатюрі — шум; геометрія без нього читається як іконка. КП-мініатюр
   (.quote-thumb поза каталогом) не чіпаємо — там масштаб більший.  */
.catalog-thumb .drawing text { display: none; }
.catalog-thumb .quote-thumb { max-width: 200px; margin: 0; }
.catalog-thumb .quote-thumb > * > svg, .catalog-thumb .quote-thumb > svg { width: 100%; height: auto; display: block; max-height: 84px; }
.catalog-code { font-weight: 700; font-size: var(--t-sm); color: var(--c-brand); font-variant-numeric: tabular-nums; }
.catalog-name { font-size: var(--t-sm); line-height: 1.35; }
.catalog-size { font-size: var(--t-xs); color: var(--c-muted); font-variant-numeric: tabular-nums; }
.catalog-price { font-size: var(--t-sm); margin-top: var(--s-1); font-variant-numeric: tabular-nums; }
/* Бейджем, а не сірим курсивом: «на прорахунку» — змістовна відмінність картки
   (ціну дасть виробник), а дрібний текст читався як другорядна примітка і
   губився при скануванні каталогу (зовнішній UX-аудит 03.08). Палітра — та
   сама, що в .price-card.quote: один стан — один колір усюди. */
/* Чому позиція у видачі: збіг через транслітерацію чи одруживку інакше читається
   як випадковість. Дрібним і приглушеним — це довідка, а не назва. */
.catalog-why {
    font-size: var(--t-xs); color: var(--c-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.catalog-why:empty { display: none; }

.catalog-noprice {
    display: inline-block; padding: 0.1rem 0.5rem;
    font-size: var(--t-xs); font-style: normal; font-weight: 600;
    color: var(--warn-600); background: var(--warn-50);
    border: 1px solid color-mix(in srgb, var(--warn-600) 30%, transparent);
    border-radius: var(--r-pill);
}
.series-note { margin-top: var(--s-2); }
.series-note > summary {
    cursor: pointer; color: var(--c-muted); font-size: var(--t-xs);
    list-style: none;
}
.series-note > summary::-webkit-details-marker { display: none; }
.series-note > summary::before { content: "▸ "; }
.series-note[open] > summary::before { content: "▾ "; }
.series-note .face-note { display: block; margin-top: var(--s-1); }
.preview-schematic-note {
    margin: var(--s-2) 0 0; font-size: var(--t-xs); color: var(--c-muted); line-height: 1.35;
}
.select-assistant-details { margin-bottom: var(--s-4); }
.select-assistant-details > summary {
    cursor: pointer; font-weight: 600; font-size: var(--t-sm); color: var(--c-text-soft);
    margin-bottom: var(--s-2);
}
.select-assistant-details .select-assistant { margin-top: var(--s-2); }
.assistant-note-link {
    display: inline-block; text-decoration: underline; text-underline-offset: 2px;
}
.catalog-cat { font-size: var(--t-xs); color: var(--c-muted); }
.aero-source { margin: var(--s-2) 0 0; font-size: var(--t-xs); color: var(--c-muted); line-height: 1.4; }
/* Каталожні характеристики читаються як тверде число, оцінка — як застереження. */
.aero-source-catalogue { color: var(--ok-600); }

/* Позначка походження біля КОЖНОЇ величини: паспорт TEZ і інженерна оцінка
   законно живуть в одній картці, тож розрізняти їх мусить кожен рядок. */
.aero-badge {
    display: inline-block; margin-left: var(--s-1); padding: 0 var(--s-1);
    border: 1px solid transparent;
    border-radius: var(--r-sm); font-size: 0.62rem; font-weight: 600;
    text-transform: none; letter-spacing: .02em; vertical-align: middle;
}
/* Фон бейджа — СУЦІЛЬНИЙ токен, а не домішка до прозорого.

   Доти обидва бейджі малювались як color-mix(... , transparent), тобто їхній
   фактичний фон залежав від того, ЩО ПОЗАДУ, — а з ним і контраст тексту.
   Заміряно 20.08: «каталог» на картці (--c-surface) дає 4.65, а на фоні сторінки
   (--c-bg) уже 4.36 при потрібних 4.5 — тобто той самий бейдж читабельний в
   одному місці й ні в іншому. Axe це показав аж на сторінці «Звідки числа»: на
   /select і / бейджі не потрапляли у свіп, бо картки там з'являються лише після
   введення витрати.

   Тепер контраст детермінований і не залежить від батька: «каталог» 5.15 у
   світлій темі й 9.03 у темній, «оцінка» 5.69 і 6.16. Рамка — щоб тихий бейдж
   оцінки лишався бейджем на білій картці, а не плямою фону. */
.aero-badge-cat {
    background: var(--ok-50); color: var(--ok-600);
    border-color: color-mix(in srgb, var(--ok-600) 30%, transparent);
}
.aero-badge-est {
    background: var(--c-surface-sunk); color: var(--c-muted);
    border-color: var(--c-border);
}
.aero-envelope { margin: var(--s-2) 0 0; font-size: var(--t-xs); color: var(--c-muted); font-variant-numeric: tabular-nums; }
/* Рядок «звідки саме число»: той самий рівень, що .aero-envelope — це не
   застереження (issue) і не футер, а цитата другої таблиці ТОГО САМОГО каталогу.
   Виділити його сильніше означало б натякнути, що крива менш надійна за таблицю. */
.aero-detail-note { margin: var(--s-1) 0 0; font-size: var(--t-xs); color: var(--c-muted); line-height: 1.4; }

/* ── ADPI-вердикт ── */
.adpi-card { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-lg); padding: var(--s-4); margin-top: var(--s-3); }
.adpi-card h2 { font-size: var(--t-md); margin: 0 0 var(--s-3); }
.adpi-verdict { margin: var(--s-2) 0 0; font-size: var(--t-sm); line-height: 1.45; font-variant-numeric: tabular-nums; }
.adpi-comfort { color: var(--ok-600); }
/* --warn-700 тепер справжній токен (обидві теми) — фолбек-хардкод не адаптувався. */
.adpi-tooshort, .adpi-toolong { color: var(--warn-700); }
.adpi-source { margin: var(--s-2) 0 0; font-size: var(--t-xs); color: var(--c-muted); line-height: 1.4; }

/* ── Панель «Приміщення»: розумний вхід витрати ── */
.room-panel { border: 1px dashed var(--c-border-strong); border-radius: var(--r-md); padding: var(--s-2) var(--s-3); margin-bottom: var(--s-4); background: color-mix(in srgb, var(--c-brand) 4%, transparent); }
.room-panel summary { cursor: pointer; display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: baseline; font-size: var(--t-sm); }
.room-panel summary::-webkit-details-marker { display: none; }
.room-title { font-weight: 600; color: var(--c-brand); }
.room-panel summary small { color: var(--c-muted); }
/* align-items:end — як у .field-row: поле з дворядковим підписом («Висота, м
   (порожньо = 2,7)») зсувало СВІЙ інпут нижче за сусідні, і ряд читався
   зламаним (UI/UX-огляд 03.08). */
.room-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: var(--s-3); margin-top: var(--s-3); align-items: end; }
.room-grid .field { margin-bottom: 0; }
/* Тип приміщення — на весь рядок: у третині панелі селект різав найдовшу
   опцію до «Житлова кімна…», а вибраного типу не було видно повністю. */
.room-grid .field:first-child { grid-column: 1 / -1; }
.room-result { margin-top: var(--s-2); }
.room-flow { margin: 0 0 var(--s-1); font-size: var(--t-sm); }
/* Свій маркер уже стоїть у тексті кожного рядка («✓» — вимога, що визначила
   результат; «·» — решта), тож дефолтний буліт давав подвійний: «• ✓ На людей…». */
.room-notes { margin: 0 0 var(--s-2); padding-left: 0; list-style: none; font-size: var(--t-xs); color: var(--c-muted); line-height: 1.5; }

/* ── Підбір: ADPI-бейджі, розкладка, фільтри ── */
.adpi-badge-comfort { background: color-mix(in srgb, var(--ok-600) 15%, transparent); color: var(--ok-600); }
/* 14% міксу давало фон, темніший за токен --warn-50, і бейдж не дотягував до 4.5:1
   навіть у світлій темі (4.14). Беремо суцільний токен фону. */
.adpi-badge-tooshort, .adpi-badge-toolong { background: var(--warn-50); color: var(--warn-700); }
.adpi-filter { margin: calc(-1 * var(--s-2)) 0 var(--s-3); }
.units-note { display: block; margin: 0 0 var(--s-3); }

/* ── Проєкт об'єкта (мультикімнатний) ── */
.project-room {
    background: var(--c-surface); border: 1px solid var(--c-border);
    border-radius: var(--r-lg); box-shadow: var(--sh-sm);
    padding: var(--s-4); margin-bottom: var(--s-4); max-width: 980px;
}
.project-room .field-row .field { margin-bottom: var(--s-2); }
.project-room-fields { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); column-gap: var(--s-3); }
.project-room-result { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin-top: var(--s-2); }
.project-airflow { font-size: var(--t-sm); color: var(--c-text-soft); }
.project-airflow strong { font-size: var(--t-lg); color: var(--c-text); }
.project-airflow-empty { color: var(--c-muted); }
.project-remove { margin-left: auto; color: var(--c-muted); }
.project-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4); margin-top: var(--s-4); }
.project-total { margin: 0; }
/* Що вже в Пропозиції під приміщенням (зріз 3): рядок під результатом кімнати.
   Кольори бейджів — токени станів (ok/warn), темна тема успадковується сама. */
.project-quote-load { margin: var(--s-2) 0 0; font-size: var(--t-sm); color: var(--c-text-soft); display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.project-load-badge { border-radius: var(--r-pill); font-size: var(--t-xs); font-weight: 650; padding: 0.05rem 0.5rem; }
/* Пояснення балансу об'єкта. Раніше обидва тексти жили в title, тобто на дотику
   не існували; тепер вони в потоці — і мусять читатись як примітка, а не як ще
   один рядок підсумку. */
.project-balance-note { font-size: var(--t-xs); color: var(--c-muted); margin: var(--s-2) 0 0; max-width: 68ch; }
.project-balance-warn { color: var(--warn-700); }
.project-balance-why { margin-top: var(--s-2); }
.project-balance-why summary {
    font-size: var(--t-xs); color: var(--c-muted); cursor: pointer;
    width: fit-content; padding: 0.1rem 0;
}
.project-balance-why summary:hover { color: var(--c-text-soft); }

/* «Як пораховано?» — розгортка правил норми під числом приміщення. Доти вона
   жила в title, тобто на дотику не існувала, хоч це обґрунтування числа, яке
   веде весь проєкт. */
.project-advice { margin-top: var(--s-2); }
.project-advice summary {
    font-size: var(--t-xs); color: var(--c-muted); cursor: pointer;
    width: fit-content; padding: 0.1rem 0;
}
.project-advice summary:hover { color: var(--c-text-soft); }
.project-advice-notes {
    margin: var(--s-2) 0 0; padding-left: var(--s-4);
    font-size: var(--t-xs); color: var(--c-text-soft); max-width: 68ch;
}
.project-advice-notes li + li { margin-top: var(--s-1); }
.project-load-ok { background: color-mix(in srgb, var(--ok-600) 14%, transparent); color: var(--ok-600); }
/* Текст — warn-700, НЕ warn-600: на 14%-підмісі warn-600 дає 3,87:1 (axe
   serious; зріз 14 — бейдж дефіциту припливу перший потрапив під axe, але
   битими були ВСІ badge-part). Темна тема має власний warn-700. */
.project-load-part { background: color-mix(in srgb, var(--warn-600) 14%, transparent); color: var(--warn-700); }
/* Звіт автопідбору: що саме додано (або чому ні) — на весь рядок під кнопками. */
.project-auto-note { flex-basis: 100%; color: var(--c-muted); }
/* Місток панелі приміщення в дугу об'єкта. */
.room-project-link { margin: var(--s-3) 0 0; font-size: var(--t-sm); color: var(--c-muted); }
/* Швидкий старт порожнього проєкту: підпис + кнопки шаблонів у ряд. */
.project-templates { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-bottom: var(--s-4); }
.project-templates-label { color: var(--c-muted); font-size: var(--t-sm); }

/* ── Підбір: порівняння варіантів (зріз 9; ринковий стандарт з дослідження) ── */
.compare-panel {
    background: var(--c-surface); border: 1px solid var(--c-border);
    border-radius: var(--r-lg); box-shadow: var(--sh-sm);
    padding: var(--s-4); margin-top: var(--s-5);
}
.compare-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-bottom: var(--s-2); }
.compare-head h2 { margin: 0; font-size: var(--t-lg); }
.compare-table { width: 100%; border-collapse: collapse; }
.compare-table th, .compare-table td { padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--c-border); text-align: left; font-size: var(--t-sm); vertical-align: top; }
.compare-table thead th { font-weight: 650; }
.compare-table tbody th { color: var(--c-muted); font-weight: 600; white-space: nowrap; }
.compare-table tbody tr:last-child th, .compare-table tbody tr:last-child td { border-bottom: none; }
.compare-table td small { color: var(--c-muted); margin-left: 0.25rem; }
/* Найкраще в рядку — токен ok, темна тема успадковується. */
.compare-best { color: var(--ok-600); font-weight: 650; }
.compare-remove { padding: 0 0.4rem; margin-left: var(--s-1); color: var(--c-muted); }
.compare-toggle.compare-on { color: var(--ok-600); }
.compare-note { margin: var(--s-2) 0 0; color: var(--c-muted); font-size: var(--t-xs); }
/* Транш 2: сценарні пресети (макроси фільтрів) і табличний режим видачі. */
.select-presets { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-bottom: var(--s-3); }
.select-presets-label { color: var(--c-muted); font-size: var(--t-sm); }
.select-view-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-2); margin: var(--s-3) 0 var(--s-2); }
.select-view { display: inline-flex; gap: 2px; }
.select-table th { padding: 0; }
.select-sort {
    all: unset; cursor: pointer; display: block; width: 100%; box-sizing: border-box;
    padding: 0.45rem 0.6rem; font-weight: 650; white-space: nowrap;
}
.select-sort:hover { color: var(--c-brand); }
.select-sort:focus-visible { outline: 2px solid var(--c-brand); outline-offset: -2px; }
.select-sort-on { color: var(--c-brand); }
.select-table tbody tr { cursor: pointer; }
.select-row-active td { background: color-mix(in srgb, var(--c-brand) 8%, transparent); }
.select-row-actions { display: flex; gap: var(--s-1); }
@media (max-width: 719px) { .select-table { min-width: 640px; } }
/* Вихід із «0 результатів»: кнопки зняття обмежень (транш 2). */
.relax-row { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-2); }
/* margin-top:auto — кнопки притиснуті до низу картки: у сітці картки одного
   ряду однакової висоти (stretch), але різне перенесення підписів лишало
   кнопки на різних рівнях — ряд виглядав розхристаним і заважав порівнювати
   картки очима по горизонталі (UI/UX-прохід 18.08). */
.variant-actions { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; margin-top: auto; }
/* Другий захід тієї ж пастки (борг п. 1): auto тримав рівними ЛИШЕ кнопки, а
   смуга «Тихо/Норма» лишалася плавати одразу під dl — заміряний розкид у ряді
   сягав 36 px саме від перенесення підписів. Якір переїжджає НА смугу (вона в
   картці завжди є), кнопки йдуть за нею впритул: обидва низові блоки рівні по
   всьому ряду. Обидва auto лишати не можна — вільне місце поділилося б між
   двома проміжками порівну, і смуга зависла б посередині картки. */
.variant-card .aero-rating { margin-top: auto; }
.variant-card .aero-rating + .variant-actions { margin-top: 0; }
@media (max-width: 719px) {
    .compare-table { min-width: 560px; }
}

/* ── Підбір: розумні підказки ── */
/* Блок між фільтрами й видачею: пояснює, ЧОМУ видача така, і дає готову дію.
   Максимум ДВІ підказки — правило зашите в SelectionHints.Evaluate (тут лише
   вигляд): три і більше читаються як шум, а не порада. Ніяких :has(), тим
   паче вкладених: невалідні/розбіжні у style-рушії частини збірок Chromium
   (доведено на /ldt) — видимістю керує C#, а не селектор. */
.smart-hints { display: flex; flex-direction: column; gap: var(--s-2); margin: 0 0 var(--s-4); max-width: 720px; }
.smart-hint {
    border: 1px solid var(--c-border-strong); border-left: 3px solid var(--c-brand);
    border-radius: var(--r-sm); background: var(--c-surface);
    padding: var(--s-2) var(--s-3);
}
.smart-hint-text { margin: 0; font-size: var(--t-sm); color: var(--c-text-soft); }
.smart-hint-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-2); }
/* пігулка, як .assistant-note: чип підказки — та сама мова UI, що нотатки асистента */
.smart-hint-chip { border-radius: var(--r-pill); }
.size-filter { width: 100%; margin-bottom: var(--s-2); padding: 0.45rem 0.7rem; font: inherit; color: var(--c-text); background: var(--c-surface); border: 1px solid var(--c-border-strong); border-radius: var(--r-sm); }
.qty-input { width: 4.5rem; padding: 0.25rem 0.4rem; font: inherit; text-align: right; color: var(--c-text); background: var(--c-surface); border: 1px solid var(--c-border-strong); border-radius: var(--r-sm); }
@media print { .no-print-input { display: none; } }
/* Номер документа під заголовком КП: помітний, але не кричить — його диктують
   у телефон, тож головне читабельність, а не вага. */
.quote-doc-number { margin: 0 0 var(--s-3); color: var(--c-muted); }
.quote-doc-number small { margin-left: var(--s-2); }
/* Умови (ПДВ, строк дії) і підпис автора — вигляд той самий, що в примітки про
   прайс, але КЛАС власний: «.quote-note» означає рівно одну примітку, і другий
   елемент із тим самим класом одразу поклав чотири e2e на strict mode. */
.quote-doc-terms,
.quote-doc-author { color: var(--c-muted); font-size: var(--t-xs); margin-top: var(--s-2); }
.quote-doc-terms span { display: block; }

.discount-field { max-width: 340px; margin-top: var(--s-3); }
/* Підпис джерела знижки: підставлену з профілю видно як підставлену, а не як
   набрану — мовчазна зміна суми документа неприпустима (зріз 41). */
.discount-from-profile { display: block; margin-top: var(--s-1); color: var(--c-muted); }
.quote-discount-row td { color: var(--c-muted); }

/* === Доступність ========================================================= */
/* Перший таб-стоп: без нього кожна сторінка починалася з 14 пунктів навігації
   (WCAG 2.4.1). Видимим стає лише у фокусі. */
.skip-link {
    position: absolute; left: -9999px; top: var(--s-3); z-index: 100;
    padding: var(--s-2) var(--s-4); border-radius: var(--r-sm);
    background: var(--c-brand); color: var(--c-on-brand); font-weight: 650;
    text-decoration: none;
}
.skip-link:focus-visible { left: var(--s-4); outline: 2px solid var(--c-focus); outline-offset: 2px; }

/* Текст для скрін-рідера, невидимий на екрані (той самий clip-патерн, що в
   прихованому файловому інпуті). !important — не з розпачу: клас надягають і
   на FORM-елементи (textarea-буфер листа в QuoteRequestPanel), а видові
   правила форм (.quote-request textarea { width:100% }) специфічніші за
   одинарний клас — так «прихований» буфер ставав 390px завширшки, і БУДЬ-ЯКА
   мобільна КП з позицією скролилась убік (заміряно живим DOM 31.08: документ
   405 px на 390-екрані; сторож циклу responsive цього не бачив — він ходить
   по порожній КП). Канонічні sr-only пишуться з !important рівно тому. */
.visually-hidden {
    position: absolute !important; width: 1px !important; height: 1px !important;
    padding: 0 !important; margin: -1px !important;
    overflow: hidden !important; clip: rect(0 0 0 0) !important;
    white-space: nowrap !important; border: 0 !important;
}

/* Паспортний діапазон моделі до введення витрати — головна відповідь на «що ця
   решітка витягне», яку раніше давали лише постфактум. */
.passport-hint {
    margin: 0 0 var(--s-4); padding: var(--s-3);
    font-size: var(--t-sm); color: var(--c-text-soft);
    background: var(--c-surface-sunk); border-radius: var(--r-sm);
    border-left: 3px solid var(--ok-600);
}
.passport-hint small { color: var(--c-muted); }

/* === Головна за ролями ==================================================== */
/* Дві рольові картки — головний вибір сторінки, тож вони мусять читатись як
   рівноцінні: однакова ширина, однакова висота дій унизу (тому auto-fit і
   flex-колонка з margin-top:auto на .role-actions). */
.home-lead {
    max-width: 62ch; margin: 0 0 var(--s-6);
    font-size: var(--t-md); color: var(--c-text-soft);
}

.role-grid {
    display: grid; gap: var(--s-4); margin-bottom: var(--s-8);
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.role-card {
    display: flex; flex-direction: column; gap: var(--s-3);
    padding: var(--s-5);
    background: var(--c-surface); border: 1px solid var(--c-border);
    border-radius: var(--r-lg); box-shadow: var(--sh-sm);
}
.role-title { font-size: var(--t-lg); }
/* «З чим ви прийшли» — це те, за чим людина себе впізнає, тож воно стоїть
   ПЕРЕД переліком вигод, а не після. */
.role-have { margin: 0; color: var(--c-text-soft); }
.role-gains {
    margin: 0; padding-left: var(--s-5);
    display: flex; flex-direction: column; gap: var(--s-1);
    font-size: var(--t-sm); color: var(--c-muted);
}
.role-actions {
    display: flex; flex-wrap: wrap; gap: var(--s-2);
    margin-top: auto; padding-top: var(--s-2);
}

.home-h2 { margin-bottom: var(--s-2); }
.home-quick { margin-bottom: var(--s-8); }
.home-configs { margin-bottom: var(--s-8); }
.home-quick-lead { margin-bottom: var(--s-3); color: var(--c-text-soft); }
.quick-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-3); }
.quick-chip { text-decoration: none; }
/* Число норми — саме воно причина натиснути, тож моноширинні цифри й акцент. */
.quick-flow {
    margin-left: var(--s-2); font-variant-numeric: tabular-nums;
    color: var(--c-muted); font-size: var(--t-xs);
}
.chip-active .quick-flow, .quick-chip:hover .quick-flow { color: inherit; }
.home-quick-note { margin: 0; font-size: var(--t-sm); color: var(--c-muted); }

.home-tiles {
    display: grid; gap: var(--s-3);
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
.home-tile {
    display: flex; flex-direction: column; gap: var(--s-1);
    padding: var(--s-4);
    background: var(--c-surface); border: 1px solid var(--c-border);
    border-radius: var(--r-md); text-decoration: none; color: var(--c-text);
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.home-tile:hover { border-color: var(--c-brand); box-shadow: var(--sh-md); color: var(--c-text); }
.home-tile-title { font-weight: 650; color: var(--c-text); }
.home-tile-note { font-size: var(--t-sm); color: var(--c-muted); }

/* === /import: вставлена специфікація ======================================
   Двоколонкова панель уводу (вставка | файл) і таблиця розбору. Кольорів тут
   немає — лише токени: сирий колір у цьому файлі заборонений, і одрук в імені
   токена мовчки повертає елемент до успадкування (CssTokenTests). */
.import-panel {
    display: grid; gap: var(--s-4); margin: var(--s-4) 0;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
}
@media (max-width: 720px) {
    .import-panel { grid-template-columns: minmax(0, 1fr); }
}

.import-paste textarea {
    width: 100%; font-family: var(--font-mono); font-size: var(--t-sm);
    line-height: var(--lh-base); resize: vertical;
}

/* Пунктирна зона — той самий словник, що на /photo: межа «куди тягнути» має
   читатися до наведення, а не лише під час перетягування. */
.import-drop {
    border: 2px dashed var(--c-border-strong); border-radius: var(--r-md);
    padding: var(--s-3); text-align: center; cursor: pointer;
    transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.import-drop.drop-over { border-color: var(--c-brand); background: var(--c-surface-sunk); }
.import-drop input[type="file"] { position: absolute; opacity: 0; width: 1px; height: 1px; }

.import-summary { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: var(--s-3) 0; }
.import-count {
    padding: var(--s-1) var(--s-2); border-radius: var(--r-pill);
    font-size: var(--t-xs); background: var(--c-surface-sunk); color: var(--c-text-soft);
}
.import-count-matched { background: var(--ok-50); color: var(--ok-600); }
.import-count-ambiguous { background: var(--warn-50); color: var(--warn-600); }
.import-count-unrecognized { background: var(--info-50); color: var(--info-600); }

.import-table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.import-table th, .import-table td {
    padding: var(--s-2); border-bottom: 1px solid var(--c-border); vertical-align: top;
    text-align: left;
}
.import-num { color: var(--c-muted); font-variant-numeric: tabular-nums; }
.import-raw-text { display: block; }
.import-price { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* «Чому» — токени рядка, які влучили. Дрібним, але поруч із рядком: це
   пояснення рішення, а не журнал для розробника. */
.import-why { display: block; margin-top: var(--s-1); font-size: var(--t-xs); color: var(--c-muted); }
.import-token {
    display: inline-block; padding: 0 var(--s-1); margin-right: var(--s-1);
    border-radius: var(--r-sm); background: var(--c-surface-sunk); font-family: var(--font-mono);
}
.import-note { display: block; margin-top: var(--s-1); font-size: var(--t-xs); color: var(--warn-700); }

.import-verdict {
    display: inline-block; padding: 0 var(--s-2); border-radius: var(--r-pill);
    font-size: var(--t-xs); white-space: nowrap;
}
.import-verdict-matched { background: var(--ok-50); color: var(--ok-600); }
.import-verdict-ambiguous { background: var(--warn-50); color: var(--warn-600); }
.import-verdict-unrecognized { background: var(--info-50); color: var(--info-600); }
.import-single { display: block; margin-top: var(--s-1); color: var(--c-text-soft); }
/* width:100% + min-width:0, а не max-width: селект не сміє ДИКТУВАТИ ширину
   таблиці — його природна ширина дорівнює найдовшій опції («FAN-DUCT ·
   TD-500/… — 14,082.59 ₴»), і max-width:100% у авто-лейауті циклічний
   (100% від колонки, яку розтягнув сам селект): на 390px таблиця займала
   782. Текст закритого селекта кліпає браузер, у розгорнутому списку опції
   видно повністю. Заміряно живим DOM 31.08: 782 → 376. */
.import-choice { display: block; margin-top: var(--s-1); width: 100%; min-width: 0; }

.import-qty-input { width: 5rem; }

/* Вузький екран ділить шість колонок 390 пікселями: типові падінги й поле
   кількості з'їдали ~80 зайвих, а nowrap у ціні («на прорахунку у
   виробника») і бейджі тримав ще ~50. Мета та сама, що в КП (UI/UX-прохід
   18.08, HIGH): ГРОШІ видно без бічного скролу — .table-scroll вище лише
   страховка на екстремальний вміст, не спосіб їх сховати. */
@media (max-width: 640px) {
    .import-table th, .import-table td { padding: var(--s-1); }
    .import-qty-input { width: 3.5rem; }
    .import-price, .import-verdict { white-space: normal; }
    /* Порядковий номер на телефоні — найдешевша жертва: без нього ціна
       влазить в екран ЦІЛОЮ (374 ≤ 390 замість 391,5), а порядок рядків
       видно й так. Ховається за КЛАСОМ, а не nth-child: у таблиці
       приміщень «№» — перша колонка, і позиційний селектор зняв би їй
       «Приміщення». Фіксовану розкладку з явними ширинами колонок міряно
       і ВІДКИНУТО: числа зелені, але слова рвались по літерах
       («Решітк-а», «найбли-жчі») — сторож був би зелений, око ні. */
    .import-num { display: none; }
}
.import-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin: var(--s-4) 0; }
.import-added { color: var(--ok-600); }
.import-take { width: 2rem; }
.import-take-input { margin: 0; }
