/* Стили страницы подбора. Вынесены из <style> в шаблоне без единой правки
 * правил: задача выноса — только перестать держать 530 строк CSS внутри
 * разметки. Порядок подключения важен: общий пакет идёт раньше этого
 * файла, чтобы правила страницы оставались последним словом. */



* {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        /* AIR-CURTAINS-QUICK-SELECTION-UX-01: reserve the scrollbar's own
           width whether or not the page is currently tall enough to need
           one, so navigating between a short page and a tall one doesn't
           change the available content width by a scrollbar's worth of
           pixels (visible as the whole page nudging left/right). */
        html {
            scrollbar-gutter: stable;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, sans-serif;
            background: var(--bg);
            color: var(--ink);
            line-height: 1.6;
            padding: 24px 20px;
        }

        /* AIR-CURTAINS-MANUAL-SELECTION-ACTION-PANEL-01: the bottom
           clearance used to live on `body` (a dynamic padding chasing the
           fixed panel's measured height -- see git history / EVIDENCE.md
           for the old rule). With .action-panel no longer `position:
           fixed`, main's own bottom padding is just ordinary breathing
           room before the panel row, not overlap prevention. */
        .ac-scroll-main {
            padding-bottom: 24px;
        }

        .container {
            max-width: 960px;
            margin: 0 auto;
        }

        /* Shared top nav -- this app previously had no persistent
           cross-page navigation at all, only scattered per-page action
           buttons. Same component/tokens as static/air_curtains.css'
           .app-header (kept inline here rather than linking that
           stylesheet, to avoid any cascade-order risk with this page's
           own long-established .btn-primary/.btn-secondary/.card-named
           classes on the app's busiest page). */
        .app-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            /* AIR-CURTAINS-QUICK-SELECTION-UX-01: this copy of the shared
               .app-header rule (see the comment above) had dropped the
               `gap` the original in static/air_curtains.css carries --
               with `justify-content: space-between` and eight nav items
               often leaving no slack in a 960px-capped header, the logo
               and "Проекты" ended up flush against each other with zero
               visible space. `gap` guarantees a floor regardless of how
               much slack space-between actually has to distribute. */
            gap: 16px;
            padding: 14px 24px;
            background: var(--card);
            border: 1px solid var(--line);
            border-radius: 10px;
            margin: 0 auto 24px;
            max-width: 960px;
        }

        .app-header .app-logo {
            font-size: 1.3rem;
            font-weight: 700;
            color: var(--accent);
            text-decoration: none;
            /* Логотип не сжимается и не переносится. Шапка ограничена 960px,
               пунктов меню восемь — без этого на ноутбучных 1440px им не
               хватало места, и «Air Curtains GUI» вставало в две строки. */
            flex: 0 0 auto;
            white-space: nowrap;
        }

        .app-header .app-nav {
            display: flex;
            gap: 8px 24px;
            flex-wrap: wrap;
            justify-content: flex-end;
        }

        .app-header .app-nav a {
            color: var(--muted);
            text-decoration: none;
            font-weight: 500;
            font-size: 0.9rem;
            /* Пункт переносится целиком, а не разрывается по словам. */
            white-space: nowrap;
        }

        .app-header .app-nav a:hover,
        .app-header .app-nav a.active {
            color: var(--accent);
        }

        /* Same dropdown-group fix as static/air_curtains.css -- kept as a
           separate copy here for the same cascade-order reason the rest of
           this page's .app-header/.app-nav rules are duplicated rather than
           shared. */
        .app-header .app-nav details {
            position: relative;
        }

        .app-header .app-nav summary {
            display: flex;
            align-items: center;
            gap: 4px;
            color: var(--muted);
            font-weight: 500;
            font-size: 0.9rem;
            white-space: nowrap;
            cursor: pointer;
            list-style: none;
            user-select: none;
        }

        .app-header .app-nav summary::-webkit-details-marker {
            display: none;
        }

        .app-header .app-nav summary::after {
            content: "\25be";
            font-size: 0.7em;
        }

        .app-header .app-nav details[open] summary,
        .app-header .app-nav details.has-active summary {
            color: var(--accent);
        }

        .app-header .app-nav .nav-group-panel {
            position: absolute;
            top: calc(100% + 10px);
            right: 0;
            z-index: 30;
            display: flex;
            flex-direction: column;
            gap: 2px;
            min-width: 200px;
            padding: 8px;
            background: var(--card);
            border: 1px solid var(--line);
            border-radius: 10px;
            box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
        }

        .app-header .app-nav .nav-group-panel a {
            padding: 6px 10px;
            border-radius: 6px;
            white-space: nowrap;
        }

        .app-header .app-nav .nav-group-panel a:hover {
            background: var(--bg);
        }

        /* На узком экране шапка складывается в колонку: логотип сверху,
           меню под ним слева. */
        @media (max-width: 820px) {
            .app-header {
                flex-direction: column;
                align-items: stretch;
            }

            .app-header .app-nav {
                justify-content: flex-start;
            }
        }

        .page-header {
            text-align: center;
            margin-bottom: 28px;
        }

        .page-header h1 {
            color: var(--ink-title);
            font-size: 2rem;
            margin-bottom: 6px;
        }

        .page-header .subtitle {
            color: var(--muted);
            font-size: 1rem;
        }

        #status-message .status {
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 20px;
            text-align: center;
            font-weight: 600;
        }

        .status.ready {
            background: var(--ok-bg);
            color: var(--ok);
            border: 1px solid var(--ok-line);
        }

        .status.partial {
            background: var(--warn-bg);
            color: var(--warn);
            border: 1px solid var(--warn-line);
        }

        .status.failed {
            background: var(--bad-bg);
            color: var(--bad);
            border: 1px solid var(--bad-line);
        }

        /* Section cards */
        .section {
            background: var(--card);
            border: 1px solid var(--line);
            border-radius: 12px;
            margin-bottom: 16px;
            overflow: hidden;
        }

        .section.hidden-water-contour {
            display: none;
        }

        .section > summary {
            list-style: none;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 18px 22px;
            font-size: 1.05rem;
            font-weight: 600;
            color: var(--ink-heading);
            user-select: none;
        }

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

        .section > summary .chevron {
            color: var(--accent);
            font-size: 0.9rem;
            transition: transform 0.15s ease;
        }

        .section[open] > summary .chevron {
            transform: rotate(180deg);
        }

        .section > summary:hover {
            background: var(--card-hover);
        }

        .section-body {
            padding: 4px 22px 22px;
        }

        /* AIR-CURTAINS-UI-MOTION-LAYER-01 Шаг 6: opening only, same reason
           as .field-error above -- native <details> closing is instant and
           unanimatable without a JS controller (owner's call: not worth it
           here, "для спокойного инженерного интерфейса достаточно
           CSS-анимации при открытии"). No max-height involved at all, so
           arbitrarily long section content is unaffected -- height is
           whatever it naturally is the instant [open] is set; only opacity/
           position animate on top of that already-final layout. Content
           stays fully interactive throughout (no visibility/pointer-events
           gating, unlike .motion-panel in Шаг 2/3 -- an accordion body isn't
           a floating overlay that needs to stay out of the way when
           "closed", it's either rendered or it isn't). Replays correctly on
           every open because native <details> content is a fresh element
           each time it mounts, not something with retained mid-transition
           state. Scoped to .section only -- templates/air_curtains_project.html,
           NOT air_curtains_project_result.html (different template, no
           .section class there; excluded per SPEC.md "Known conflict risk"
           regardless). */
        .section[open] > .section-body {
            animation: ac-accordion-in var(--motion-normal) var(--ease-motion-out);
        }

        @keyframes ac-accordion-in {
            from {
                opacity: 0;
                transform: translateY(var(--motion-distance-xs, 4px));
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        /* Boxed fields with label inset in the border */
        .field-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 16px;
            /* start, а не stretch по умолчанию: иначе ячейка тянется под высоту
               соседа, и пустое поле рядом с полем, у которого три строки
               подсказки, вырастает вдвое. Пустая коробка двойной высоты
               читается как большое текстовое поле, хотя это строка ввода. */
            align-items: start;
        }

        .field-grid + .field-grid {
            margin-top: 16px;
        }

        .field {
            position: relative;
            border: 1.5px solid var(--line-2);
            border-radius: 8px;
            padding: 9px 14px 8px;
            background: var(--card);
            /* AIR-CURTAINS-UI-MOTION-LAYER-01 Шаг 5: was border-color 0.15s
               ease, box-shadow 0.15s ease -- routed through the shared
               motion tokens (static/air_curtains_motion.css) instead of a
               locally hardcoded duration/easing, and background-color added
               so a future background change on this element (there isn't
               one today) doesn't silently ship untransitioned. */
            transition:
                border-color var(--motion-fast, 0.15s) var(--ease-motion-soft, ease),
                box-shadow var(--motion-fast, 0.15s) var(--ease-motion-soft, ease),
                background-color var(--motion-fast, 0.15s) var(--ease-motion-soft, ease);
        }

        .field.full {
            grid-column: 1 / -1;
        }

        .field label {
            display: block;
            font-size: 12px;
            font-weight: 500;
            color: var(--ink-label);
            margin-bottom: 2px;
        }

        .field input,
        .field textarea,
        .field select {
            /* tasks/CDW-UXFORM-03: scrollIntoView({block:'center'}) alone
               centers a field against the FULL viewport height, which does
               not account for the fixed .action-panel eating into the
               bottom -- on a short-enough viewport the field still lands
               partially behind it (measured directly via
               getBoundingClientRect during this task's own visual QA: a
               983px-tall viewport left the field ~43px behind the panel).
               scroll-margin-bottom is honored by scrollIntoView natively,
               keeps this much clearance from the viewport's bottom edge
               regardless of centering math, and tracks the panel's REAL
               height via the same --action-panel-height variable the body
               padding uses. */
            scroll-margin-bottom: calc(var(--action-panel-height, 150px) + 16px);
            display: block;
            width: 100%;
            border: none;
            border-bottom: 1px solid var(--line-2);
            outline: none;
            padding: 4px 0 3px;
            font-size: 16px;
            font-family: inherit;
            color: var(--ink);
            background: transparent;
        }

        .field select {
            cursor: pointer;
        }

        /* AIR-CURTAINS-QUICK-SELECTION-UX-01: same specificity bug already
           documented and fixed for .app-breadcrumbs[hidden] elsewhere in
           this file (see that comment, ~line 450) -- `.field select` above
           is an author rule with `display: block`, which has the same
           specificity as the UA default `[hidden] { display: none }` and
           wins by being an author rule, so plain `hidden` on
           #object_requirement_profile/#requirement_block_id never actually
           hid them (confirmed live: the picker dialog's own trigger button
           rendered with the raw <select> still fully visible right below
           it). `.field select[hidden]` has higher specificity (0,2,0 vs
           0,1,0) so this wins without `!important`. */
        .field select[hidden] {
            display: none;
        }

        .field:focus-within {
            border-color: var(--accent);
            box-shadow: 0 0 0 3px rgba(46, 125, 50, 0.12);
        }

        .field.has-value {
            border-color: var(--accent);
        }

        .field .field-error {
            display: none;
            font-size: 12px;
            color: var(--invalid);
            margin-top: 4px;
        }

        .field.invalid {
            border-color: var(--invalid);
        }

        /* AIR-CURTAINS-UI-MOTION-LAYER-01 Шаг 5: this element goes straight
           from `display: none` to `display: block` in JS (see the
           form.checkValidity() handler) -- `transition` cannot animate a
           display change at all (nothing to interpolate from), so the
           entrance uses `animation` instead, which runs the instant the
           element is painted in its `block` state. Deliberately NOT
           animating `display`/height here: the block appears at its final
           height in one step, same as before this task, and only its own
           opacity/position fade in on top of that -- SPEC.md's "анимация
           не должна менять высоту формы" is about not adding a SECOND,
           height-based animation on top of the existing instant reflow, not
           about removing that reflow (removing it would mean permanently
           reserving empty space under every valid field, which changes the
           form's height in a different, worse way). Fixing the value goes
           back to `display: none` instantly -- no fade-out: a correction
           should read as immediate, not as something to wait out
           (Constraints: "без задержек, мешающих быстрому вводу"). */
        .field.invalid .field-error {
            display: block;
            animation: ac-field-error-in var(--motion-normal, 220ms) var(--ease-motion-out, ease-out);
        }

        /* Positive Y (settles UPWARD into place) to match the direction
           static/air_curtains_motion.css already established for
           .motion-panel (a panel appearing below its trigger starts
           slightly lower and settles up) -- one motion vocabulary, not a
           second convention invented here. */
        @keyframes ac-field-error-in {
            from {
                opacity: 0;
                transform: translateY(var(--motion-distance-xs, 4px));
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .field .field-hint {
            font-size: 12px;
            color: var(--muted);
            margin-top: 4px;
        }

        .field-actions {
            margin-top: 8px;
            font-size: 12px;
        }

        .field-actions a {
            color: var(--link);
            text-decoration: none;
        }

        .field-actions button {
            margin-left: 10px;
            font-size: 12px;
            padding: 3px 8px;
            border: 1px solid var(--line-2);
            border-radius: 4px;
            background: var(--surface);
            cursor: pointer;
        }

        #profile-status-indicator {
            margin-top: 6px;
            font-size: 12px;
            display: none;
        }

        .buttons {
            display: flex;
            gap: 12px;
            margin-top: 8px;
            flex-wrap: wrap;
        }

        .action-panel {
            /* AIR-CURTAINS-MANUAL-SELECTION-ACTION-PANEL-01: was
               `position: fixed` + a body padding-bottom chasing its
               measured height -- that only ever protected the ONE field
               you scrollIntoView'd to, not everything a user scrolls PAST
               on the way there (real, measured overlap -- see
               tests_browser/test_action_panel_real_overlap.py and this
               task's CURRENT_STATE.md section J). Now a normal flex row,
               the last child of body.ac-standalone-shell (or a plain
               static block when embedded in the заявка/opening workspace
               fragment, where body.ac-standalone-shell is absent by
               construction -- see the HTML comment above <main
               class="ac-scroll-main"> in the template). main's own bounded
               height + overflow-y:auto means nothing can render under
               this panel any more; no overlap-prevention padding math
               needed. */
            width: 100%;
            max-width: 960px;
            margin: 0 auto;
            flex: 0 0 auto;
            padding: 12px;
            padding-bottom: calc(12px + env(safe-area-inset-bottom));
            align-items: center;
            background: var(--card);
            border: 1px solid var(--line);
            border-radius: 12px;
            box-shadow: var(--shadow);
        }

        /* Only meaningful inside body.ac-standalone-shell (the panel is a
           flex sibling of <main> there, see below) -- harmless no-op when
           embedded in the workspace fragment, where .action-panel is a
           plain block and this selector simply doesn't match anything
           relevant. */
        body.ac-standalone-shell {
            display: flex;
            flex-direction: column;
            height: 100dvh;
            overflow: hidden;
        }

        body.ac-standalone-shell > .app-header,
        body.ac-standalone-shell > .app-breadcrumbs,
        body.ac-standalone-shell > .container {
            flex: 0 0 auto;
        }

        /* Pre-existing bug, exposed (not caused) by the bounded-flex layout.
           CORRECTED 2026-09-04 (independent review caught the original
           version of this comment misattributing the conflicting rule to
           the SHARED static/air_curtains.css -- wrong: this page doesn't
           even link that file, see the comment at line ~864 above
           explaining why. The real conflicting rule is `.app-breadcrumbs`
           right in THIS file, duplicated here for that same reason).
           This file's own `.app-breadcrumbs { display: flex; }` (below)
           has the same specificity as the UA `[hidden] { display: none }`
           default and, being an author rule, always wins regardless of
           source order -- so the `hidden` attribute never actually hid
           this element (same bug class as the one documented in
           tests_browser/test_workspace_tabs.py's docstring, 2026-08-07,
           found in a different component). Harmless when body scrolled
           normally (just an empty ~22px gap nobody noticed); now that
           body.ac-standalone-shell bounds the header region, that phantom
           space measurably eats into main's flex budget on short
           viewports. `.app-breadcrumbs[hidden]` has higher specificity
           (0,2,0 vs 0,1,0) so this wins without `!important`, and it's a
           self-contained fix within this same file. */
        .app-breadcrumbs[hidden] {
            display: none;
        }

        body.ac-standalone-shell > .ac-scroll-main {
            flex: 1 1 auto;
            min-height: 0;
            overflow-y: auto;
        }

        body.ac-standalone-shell > .action-panel {
            flex: 0 0 auto;
        }

        .action-panel #submit-btn {
            flex: 0 1 auto;
        }

        .action-panel #status-message {
            flex: 1 1 240px;
            min-width: 0;
            max-height: 64px;
            color: var(--muted);
            text-align: center;
            overflow-y: auto;
            overflow-wrap: anywhere;
        }

        .action-panel #status-message .status,
        .action-panel #status-message .profile-warning {
            margin: 0;
            padding: 8px 10px;
        }

        .action-panel .spinner {
            flex: 1 1 240px;
            padding: 0;
        }

        .action-panel .spinner.show {
            display: flex;
            align-items: center;
            justify-content: center;
            flex-wrap: wrap;
            gap: 4px;
        }

        .action-panel .spinner p {
            margin-inline-start: 4px;
        }

        button, .btn-link, .btn-tertiary {
            padding: 13px 22px;
            border: none;
            border-radius: 8px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            /* AIR-CURTAINS-UI-MOTION-LAYER-01 Шаг 5: was background 0.15s
               ease, transform 0.1s ease -- routed through the shared motion
               tokens for the same reason as .field above. */
            transition:
                background var(--motion-fast, 0.15s) var(--ease-motion-soft, ease),
                transform var(--motion-fast, 0.1s) var(--ease-motion-soft, ease);
        }

        /* Hover lift, ≤1px per SPEC.md Constraints ("смещения не более
           4-10px" -- a hover affordance on a button is deliberately at the
           small end of that, not the same size as an entrance motion).
           Declared before :active/:disabled below so those win the cascade
           (equal specificity, source order) when hover overlaps with
           pressing or with a disabled button under the pointer. */
        button:hover, .btn-link:hover, a.btn-tertiary:hover {
            transform: translateY(-1px);
        }

        button:active, .btn-link:active, a.btn-tertiary:active {
            transform: scale(0.98);
        }

        /* Пункт constraint "disabled-состояние без движения": override
           the hover lift above -- a disabled button still visually
           registers :hover when the pointer sits over it, and per the
           source-order rule this needs to come after that rule to win. */
        button:disabled, .btn-link:disabled, a.btn-tertiary[aria-disabled="true"] {
            transform: none;
        }

        /* Пункт constraint "focus-visible должен быть заметнее hover":
           hover here is a background tint (+ the 1px lift above); a 2px
           outline ring reads as clearly stronger, and reuses the same
           outline treatment .technical-reset:focus-visible already uses
           elsewhere on this page rather than inventing a second style. */
        button:focus-visible, .btn-link:focus-visible, a.btn-tertiary:focus-visible {
            outline: 2px solid var(--blue-600, #2563eb);
            outline-offset: 2px;
        }

        .btn-primary {
            background: var(--accent);
            color: white;
            flex: 1;
        }

        .btn-primary:hover {
            background: var(--accent-hover);
        }

        .btn-primary:disabled {
            background: var(--accent-disabled);
            cursor: not-allowed;
        }

        .btn-secondary {
            background: var(--line);
            color: var(--ink-2);
        }

        .btn-secondary:hover {
            background: var(--line-2);
        }

        .btn-tertiary {
            background: var(--info-bg);
            color: var(--info);
        }

        .btn-tertiary:hover {
            background: var(--info-bg-hover);
        }

        a.btn-tertiary {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            text-decoration: none;
        }

        .spinner {
            display: none;
            text-align: center;
            padding: 20px;
        }

        .spinner.show {
            display: block;
        }

        .spinner div {
            width: 20px;
            height: 20px;
            background: var(--accent);
            border-radius: 50%;
            display: inline-block;
            margin: 0 5px;
        }

        /* The loop must still say "working" under reduced motion, so it
           isn't simply removed -- a bounce (repeated scale) is exactly the
           kind of motion that preference exists to opt out of, so it's
           gated behind no-preference and replaced with a plain opacity
           pulse otherwise (still communicates progress, no repeated
           scaling). */
        @media (prefers-reduced-motion: no-preference) {
            .spinner div {
                animation: bounce 1.4s infinite ease-in-out both;
            }

            .spinner div:nth-child(1) {
                animation-delay: -0.32s;
            }

            .spinner div:nth-child(2) {
                animation-delay: -0.16s;
            }
        }

        @media (prefers-reduced-motion: reduce) {
            .spinner div {
                animation: pulse 1.4s infinite ease-in-out both;
            }

            .spinner div:nth-child(1) {
                animation-delay: -0.32s;
            }

            .spinner div:nth-child(2) {
                animation-delay: -0.16s;
            }
        }

        @keyframes bounce {
            0%, 80%, 100% {
                transform: scale(0);
            }
            40% {
                transform: scale(1);
            }
        }

        @keyframes pulse {
            0%, 80%, 100% {
                opacity: 0.25;
            }
            40% {
                opacity: 1;
            }
        }

        /* Шаг 5/6 entrance animations (.field-error, .section-body) --
           separate block from the bounce/pulse one above on purpose: that
           one is a deliberate no-preference/reduce SWAP between two
           different animations (see its own comment), not a collapse, and
           static/air_curtains_motion.css's shared reduced-motion rule
           (Шаг 2) only covers ITS OWN classes (.motion-panel etc.), not
           these page-local @keyframes -- each new animation added here has
           to opt itself in. */
        @media (prefers-reduced-motion: reduce) {
            .field.invalid .field-error,
            .section[open] > .section-body {
                animation-duration: 1ms;
            }
        }

        /* 820, а не 600: при 700px в двух колонках на поле остаётся около
           300px, и «RAL 7004 — сигнальный серый (стандарт)» в селекте
           обрезается на половине. Порог поднят до той же ширины, на которой
           складывается шапка. */
        @media (max-width: 820px) {
            .field-grid {
                grid-template-columns: 1fr;
            }

            .buttons {
                flex-direction: column;
            }

            button, .btn-link, .btn-tertiary {
                width: 100%;
            }

            .action-panel {
                gap: 8px;
                padding: 10px;
                padding-bottom: calc(10px + env(safe-area-inset-bottom));
            }

            .action-panel #status-message,
            .action-panel .spinner {
                flex-basis: auto;
                width: 100%;
            }

            .action-panel .spinner div {
                width: 14px;
                height: 14px;
            }
        }

        /* Profile status indicators */
        .profile-status-draft {
            color: var(--bad);
            background-color: var(--bad-bg);
            border: 1px solid var(--bad-line);
            padding: 4px 8px;
            border-radius: 4px;
            font-weight: 600;
        }

        .profile-status-needs_review {
            color: var(--warn);
            background-color: var(--warn-bg);
            border: 1px solid var(--warn-line);
            padding: 4px 8px;
            border-radius: 4px;
            font-weight: 600;
        }

        .profile-status-approved {
            color: var(--ok);
            background-color: var(--ok-bg);
            border: 1px solid var(--ok-line);
            padding: 4px 8px;
            border-radius: 4px;
            font-weight: 600;
        }

        .profile-status-rejected {
            color: var(--bad);
            background-color: var(--bad-bg);
            border: 1px solid var(--bad-line);
            padding: 4px 8px;
            border-radius: 4px;
            font-weight: 600;
        }

        .profile-status-archived {
            color: var(--archived);
            background-color: var(--archived-bg);
            border: 1px solid var(--archived-line);
            padding: 4px 8px;
            border-radius: 4px;
            font-weight: 600;
        }

        .profile-warning {
            color: var(--bad);
            background-color: var(--bad-bg);
            border: 1px solid var(--bad-line);
            padding: 8px;
            border-radius: 4px;
            margin: 10px 0;
            font-weight: 600;
        }

        /* Requirements preview (v1.2) */
        #requirements-preview-block {
            margin-top: 16px;
            padding: 16px;
            border: 1px solid var(--line);
            border-radius: 8px;
            background: var(--surface);
        }
        /* h2, not h3: matches the requirements-preview heading's actual tag
           in templates/air_curtains_project.html (kept at h2 to not skip a
           level after the page's single h1 -- an a11y fix on this branch).
           Visual size unchanged (still 1rem). */
        #requirements-preview-block h2 {
            font-size: 1rem;
            margin-bottom: 10px;
        }
        .req-block-actions {
            display: flex;
            align-items: center;
            gap: 14px;
            margin: 4px 0 10px;
        }
        .req-block-actions button {
            font-size: 0.85rem;
            padding: 5px 10px;
            border: 1px solid var(--line-2);
            border-radius: 6px;
            background: white;
            cursor: pointer;
        }
        .req-block-actions button:hover {
            background: var(--surface-2);
        }
        .req-block-actions a {
            font-size: 0.85rem;
        }
        .req-block-sections {
            margin: 4px 0 10px;
            padding: 8px 10px;
            background: white;
            border: 1px solid var(--line);
            border-radius: 6px;
        }
        .req-section-line {
            font-size: 0.85rem;
            color: var(--ink-2);
            margin: 3px 0;
        }
        .req-status-badge {
            display: inline-block;
            padding: 4px 10px;
            border-radius: 999px;
            font-weight: 600;
            font-size: 0.85rem;
            margin-bottom: 10px;
        }
        .req-status-ok {
            color: var(--ok);
            background-color: var(--ok-bg);
            border: 1px solid var(--ok-line);
        }
        .req-status-warning {
            color: var(--warn);
            background-color: var(--warn-bg);
            border: 1px solid var(--warn-line);
        }
        .req-status-blocked {
            color: var(--bad);
            background-color: var(--bad-bg);
            border: 1px solid var(--bad-line);
        }
        .req-source-line {
            font-size: 0.85rem;
            color: var(--ink-3);
            margin-bottom: 4px;
        }
        .req-merged-list {
            margin: 8px 0 8px 20px;
            font-size: 0.9rem;
        }
        .req-duplicates-note {
            font-size: 0.82rem;
            color: var(--muted);
            margin-bottom: 8px;
        }
        .req-conflict-item {
            border-radius: 6px;
            padding: 10px 12px;
            margin-bottom: 8px;
            font-size: 0.88rem;
        }
        .req-conflict-item.blocked {
            background: var(--bad-bg);
            border: 1px solid var(--bad-line);
        }
        .req-conflict-item.warning {
            background: var(--warn-bg);
            border: 1px solid var(--warn-line);
        }
        .req-conflict-item .req-conflict-title {
            font-weight: 600;
            margin-bottom: 4px;
        }
        .req-conflict-item .req-conflict-action {
            color: var(--ink-3);
            margin-top: 4px;
        }
        .req-preview-loading, .req-preview-error {
            font-size: 0.9rem;
            color: var(--muted);
        }
        .req-preview-error {
            color: var(--bad);
        }
        .selection-context-banner {
            margin-bottom: 16px;
            padding: 12px 14px;
            border-radius: 8px;
            background: var(--slate-50);
            color: var(--slate-700);
            font-size: 0.9rem;
        }

        .selection-context-banner a { color: var(--blue-600); }

        .technical-source {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
            gap: 6px 10px;
            margin-top: 5px;
            color: var(--slate-500);
            font-size: 0.78rem;
        }

        .technical-reset {
            min-height: 24px;
            padding: 1px 5px;
            border: 0;
            background: transparent;
            color: var(--blue-600);
            text-decoration: underline;
            cursor: pointer;
        }

        .technical-reset:focus-visible {
            outline: 2px solid var(--blue-600);
            outline-offset: 2px;
        }

        /* Хлебные крошки. Дублируются здесь по той же причине, что и
           .app-header выше: эта страница намеренно не линкует
           static/air_curtains.css (полный пакет ломает ей кнопки). */
        .app-breadcrumbs {
            display: flex;
            flex-wrap: wrap;
            align-items: baseline;
            gap: 8px;
            margin: 0 auto 20px;
            max-width: 1200px;
            padding: 10px 16px;
            background: var(--card, #fff);
            border: 1px solid var(--line, #e2e8f0);
            border-radius: 10px;
            font-size: 13.5px;
        }

        .app-breadcrumbs .crumb {
            color: var(--accent, #2e7d32);
            text-decoration: none;
            font-weight: 600;
        }

        .app-breadcrumbs .crumb:hover { text-decoration: underline; }

        .app-breadcrumbs .crumb-current {
            color: var(--text, #0f172a);
            font-weight: 600;
        }

        .app-breadcrumbs .crumb-kind {
            color: var(--muted, #64748b);
            font-weight: 400;
            margin-right: 4px;
        }

        .app-breadcrumbs .crumb-sep { color: var(--muted, #64748b); }

        /* AIR-CURTAINS-QUICK-SELECTION-UX-01 (SPEC п.3): compact current-value
           readout replacing the visible native <select> for
           object_requirement_profile/requirement_block_id -- the select
           itself is `hidden` and stays the real form control. */
        .picker-trigger {
            display: flex;
            align-items: center;
            gap: 12px;
            flex-wrap: wrap;
            padding: 10px 12px;
            border: 1px solid var(--line, #e2e8f0);
            border-radius: 8px;
            background: var(--bg, #f8fafc);
        }

        .picker-current-value {
            font-weight: 600;
            color: var(--text, #0f172a);
        }

        /* Wide picker dialog -- was previously "the native <select>'s own
           dropdown", cramped and offering no room for a status badge or
           description per item. min(880px, 92vw) fills most of the
           viewport on the narrower screens from the task's own screenshots
           without becoming unreadably wide on desktop. */
        .picker-dialog {
            width: min(880px, 92vw);
            max-height: min(720px, 88vh);
            padding: 0;
            border: 1px solid var(--line, #e2e8f0);
            border-radius: 14px;
            box-shadow: 0 24px 60px rgba(0, 0, 0, 0.22);
        }

        .picker-dialog::backdrop {
            background: rgba(15, 23, 42, 0.45);
        }

        .picker-dialog-inner {
            display: flex;
            flex-direction: column;
            max-height: min(720px, 88vh);
        }

        .picker-dialog header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 12px;
            padding: 18px 22px;
            border-bottom: 1px solid var(--line, #e2e8f0);
        }

        .picker-dialog header h2 {
            font-size: 1.15rem;
            margin: 0;
        }

        .picker-close {
            background: none;
            border: none;
            font-size: 1.4rem;
            line-height: 1;
            cursor: pointer;
            color: var(--muted, #64748b);
            padding: 4px 8px;
        }

        .picker-current {
            margin: 0;
            padding: 12px 22px 0;
            color: var(--muted, #64748b);
            font-size: 0.92rem;
        }

        .picker-list {
            flex: 1;
            overflow-y: auto;
            padding: 14px 22px;
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .picker-item {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 16px;
            padding: 12px 14px;
            border: 1px solid var(--line, #e2e8f0);
            border-radius: 10px;
            text-align: left;
            background: var(--card, #fff);
            cursor: pointer;
            font: inherit;
            width: 100%;
        }

        .picker-item:hover,
        .picker-item:focus-visible {
            border-color: var(--accent, #2e7d32);
            background: var(--bg, #f8fafc);
        }

        .picker-item.is-selected {
            border-color: var(--accent, #2e7d32);
            box-shadow: inset 0 0 0 1px var(--accent, #2e7d32);
        }

        .picker-item-label {
            font-weight: 600;
        }

        .picker-item-meta {
            color: var(--muted, #64748b);
            font-size: 0.85rem;
            margin-top: 2px;
        }

        .picker-empty {
            color: var(--muted, #64748b);
            padding: 20px;
            text-align: center;
        }

        .picker-dialog footer {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 12px;
            padding: 16px 22px;
            border-top: 1px solid var(--line, #e2e8f0);
        }

        @media (max-width: 640px) {
            .picker-dialog footer {
                flex-direction: column;
                align-items: stretch;
            }
        }


/* ===================================================================
   AIR-CURTAINS-QUICK-SELECTION-UX-01 (второе задание, §6-§7):
   визуальный каркас «параметры слева — сводка "Ваш подбор" справа»,
   переключатель «Простой подбор / Экспертный» и динамическая схема.
   Характер согласованного макета: светлый серый фон, белые карточки,
   тонкие нейтральные границы, синие акценты, компактные заголовки.
   Только слой представления: не меняет ни один id/name поля формы и
   не трогает серверные контракты.
   =================================================================== */

.qs-page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 14px;
}

.qs-page-header h1 {
    font-size: 1.6rem;
    letter-spacing: -0.02em;
    line-height: 1.25;
}

.qs-mode-row {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}

.qs-mode-switch {
    display: inline-flex;
    gap: 3px;
    padding: 4px;
    background: #e8edf4;
    border: 1px solid #e3e9f1;
    border-radius: 10px;
}

.qs-mode-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 0;
    border-radius: 7px;
    background: transparent;
    padding: 8px 16px;
    font-size: 0.85rem;
    font-weight: 500;
    color: #708096;
}

.qs-mode-btn[aria-pressed="true"] {
    background: #fff;
    color: var(--accent);
    font-weight: 650;
    box-shadow: 0 1px 4px rgba(34, 60, 96, 0.14);
}

.qs-mode-help {
    font-size: 0.78rem;
    color: var(--muted);
}

/* Screen-reader-only live region for the mode toggle's status text
   (#qs-expert-indicator-text role="status"). Kept in this file because the
   toggle is part of this page's presentation layer; visually hidden but
   announced to assistive tech. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.qs-expert-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 3px #e9f2fe;
    vertical-align: 2px;
}

/* Two-column workspace: parameters left, sticky summary right. */
.qs-workspace {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: 22px;
    align-items: start;
}

.qs-form-stack {
    display: grid;
    gap: 16px;
    /* Without min-width: 0 a grid item refuses to shrink below its
       min-content width, so on narrower viewports the form column would
       overflow the grid (and the page) horizontally -- the exact class of
       defect tests_browser/test_quick_selection_ux_fixes.py's
       test_narrow_viewport_has_no_horizontal_overflow guards. The form's
       own .field-grid/.field children already use 1fr columns and can
       compress; this just lets the column itself compress too. */
    min-width: 0;
}

.qs-form-stack details.section,
.qs-form-stack .section-body {
    min-width: 0;
}

.qs-aside {
    position: sticky;
    top: 16px;
    display: grid;
    gap: 16px;
}

.qs-summary-card,
.qs-import-card {
    background: var(--card, #fff);
    border: 1px solid var(--line, #dfe6ef);
    border-radius: 12px;
    overflow: hidden;
}

.qs-summary-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 18px;
    border-bottom: 1px solid var(--line, #dfe6ef);
}

.qs-summary-header h2 {
    font-size: 0.95rem;
}

.qs-summary-status {
    font-size: 0.68rem;
    color: #78879a;
    background: #f0f3f7;
    border-radius: 5px;
    padding: 3px 7px;
    white-space: nowrap;
}

.qs-scheme {
    padding: 12px 14px 4px;
    background: linear-gradient(180deg, #f9fbfe, #fff);
}

.qs-scheme svg {
    width: 100%;
    height: 150px;
}

.qs-scheme-caption {
    text-align: center;
    font-size: 0.65rem;
    color: #93a0b1;
    padding-bottom: 10px;
}

.qs-summary-body {
    padding: 0 18px 18px;
}

.qs-summary-list {
    margin: 0 0 14px;
}

.qs-summary-list > div {
    display: flex;
    justify-content: space-between;
    gap: 14px;
    padding: 8px 0;
    border-bottom: 1px solid #edf1f6;
    font-size: 0.8rem;
}

.qs-summary-list dt {
    color: var(--muted);
}

.qs-summary-list dd {
    margin: 0;
    text-align: right;
    font-weight: 500;
    max-width: 170px;
}

.qs-summary-constraints {
    font-size: 0.72rem;
    color: var(--muted);
    line-height: 1.6;
    padding: 10px 12px;
    border-radius: 7px;
    background: #f6f8fb;
    margin-bottom: 14px;
}

.qs-import-card {
    padding: 16px 18px;
}

.qs-import-card h3 {
    font-size: 0.82rem;
    font-weight: 600;
    margin-bottom: 8px;
}

.qs-import-card p {
    font-size: 0.75rem;
    color: var(--muted);
    line-height: 1.6;
    margin-bottom: 8px;
}

.qs-import-card a {
    font-size: 0.8rem;
    color: var(--accent);
}

@media (max-width: 900px) {
    .qs-workspace {
        grid-template-columns: minmax(0, 1fr) 260px;
        gap: 16px;
    }
}

@media (max-width: 760px) {
    .qs-workspace {
        grid-template-columns: 1fr;
    }
    .qs-aside {
        position: static;
    }
    .qs-page-header {
        flex-direction: column;
    }
    .qs-mode-switch {
        width: 100%;
    }
    .qs-mode-btn {
        flex: 1;
        justify-content: center;
    }
}


/* Focused UX correction: real options, simple mode and embedded fragment. */
:root { --accent:#2563b5; --accent-hover:#1c4f92; --accent-soft:#eaf2fe; --bg:#f3f5f8; --line:#dce3ed; }
[hidden] { display:none !important; }
.container,.app-header { max-width:1200px; }
.app-header { border-radius:12px; }
.qs-page-header { margin:8px 0 18px; text-align:left; }
.qs-page-header h1 { font-size:26px; }
.qs-page-header .subtitle { font-size:14px; line-height:1.55; margin:4px 0 0; }
.qs-workspace { grid-template-columns:minmax(0,1fr) 310px; gap:24px; }
.qs-form-stack { gap:16px; align-content:start; }
.qs-mode-row { margin:0 0 20px; flex-wrap:wrap; }
.qs-catalog-line { display:flex; align-items:center; }
.qs-catalog-line .field { width:100%; display:flex; align-items:center; gap:18px; border:0; padding:0; background:none; box-shadow:none; }
.qs-catalog-line .field > label { margin:0; font-size:13px; }
.qs-catalog-line .field-hint,.qs-catalog-line .technical-source { display:none; }
.qs-catalog-line .qs-options { display:inline-flex; width:auto; padding:0; border:0; gap:6px; background:none; }
.qs-native-control { position:absolute !important; width:1px !important; height:1px !important; margin:-1px !important; opacity:0; pointer-events:none; }
.qs-options { display:grid; width:100%; gap:8px; }
.qs-options button { min-height:48px; border:1px solid #d7e0ec; border-radius:9px; padding:12px 15px; background:#f8fafc; color:#42516a; font-size:14px; font-weight:600; line-height:1.35; cursor:pointer; }
.qs-options button:hover { border-color:#91b5e7; background:#f1f6ff; }
.qs-options button[aria-pressed=true] { color:#1858a9; background:#eaf2fe; border-color:#73a4e1; box-shadow:0 0 0 1px #73a4e1 inset; }
.qs-options button:focus-visible,.qs-mode-btn:focus-visible { outline:3px solid #a9cef7; outline-offset:3px; }
.qs-options-heating_type { grid-template-columns:repeat(3,minmax(0,1fr)); }
.qs-options-layout_mode { grid-template-columns:repeat(4,minmax(0,1fr)); }
.qs-catalog-line .qs-options button { min-height:34px; padding:6px 20px; }
.qs-form-stack .section { margin:0; border-radius:12px; border:1px solid var(--line); box-shadow:none; overflow:visible; }
.qs-form-stack .section > summary { font-size:15px; font-weight:650; padding:17px 20px; }
.qs-form-stack .section-body { padding:0 20px 20px; }
.qs-form-stack .field-grid { gap:14px; }
.qs-form-stack .field { border:0; padding:0; box-shadow:none; min-width:0; background:none; }
.qs-form-stack .field.has-value,.qs-form-stack .field:focus-within { border:0; box-shadow:none; }
.qs-form-stack .field > label { display:block; color:#526079; margin:0 0 7px; font-size:12px; font-weight:500; }
.qs-form-stack input,.qs-form-stack select,.qs-form-stack textarea { border:1px solid #d7e0ec; border-radius:8px; padding:10px 12px; background:#fff; font-size:15px; width:100%; }
.qs-form-stack input:focus,.qs-form-stack select:focus,.qs-form-stack textarea:focus { border-color:#73a4e1; outline:3px solid #eaf2fe; }
.qs-form-stack .field:has(> #heating_type),.qs-form-stack .field:has(> #layout_mode) { grid-column:1/-1; }
#qs-water-schedule { margin-top:16px; padding-top:16px; border-top:1px solid #edf1f6; }
#qs-water-schedule select { max-width:200px; }
.qs-voltage-note { margin-top:15px; padding:10px 12px; border-radius:8px; background:#f4f7fb; color:#62718a; font-size:13px; }
.qs-form-stack .field-hint { font-size:11px; line-height:1.5; margin-top:6px; color:#8190a5; }
.qs-form-stack .technical-source:empty { display:none; }
#tag_number-hint { display:none; }
.qs-form-stack input[aria-invalid="true"],.qs-form-stack .field.invalid input { border-color:#cf4654; }
.qs-summary-card { border-radius:12px; box-shadow:none; }
.qs-summary-header { padding:18px; }
.qs-summary-body { padding:0 18px 18px; }
.qs-summary-list > div { font-size:12px; gap:16px; }
.action-panel { border-color:var(--line); box-shadow:none; padding:12px 18px; gap:12px; }
.action-panel .btn-primary { background:#2563b5; border-color:#2563b5; border-radius:9px; }
.action-panel .btn-secondary { border-radius:9px; }
.qs-form-stack [data-qs-expert] .section { margin-top:16px; }
.qs-form-stack [data-qs-expert] .section > summary { padding-left:0; }
.qs-form-stack [data-qs-expert] .section .section-body { padding-left:0; padding-right:0; }
@media(max-width:800px) { .qs-workspace { grid-template-columns:1fr; } .qs-aside { position:static; } }
@media(max-width:480px) { body { padding:12px; } .app-header { padding:12px; flex-wrap:wrap; gap:10px; margin-bottom:16px; } .app-header .app-logo { font-size:17px; } .app-header .app-nav { justify-content:flex-start; gap:8px 16px; } .qs-form-stack .section > summary { padding:14px; } .qs-form-stack .section-body { padding:0 14px 14px; } .qs-options button { padding:10px 6px; font-size:12px; min-width:0; } .qs-options-layout_mode { grid-template-columns:repeat(2,minmax(0,1fr)); } .qs-mode-help { display:none; } .action-panel { flex-wrap:wrap; padding:12px; } .action-panel button { flex:1 1 auto; } .qs-page-header h1 { font-size:22px; } }

/* One page scroll: the header no longer reserves a fixed viewport region. */
body.ac-standalone-shell { display:block; height:auto; min-height:100dvh; overflow:visible; padding:0; }
body.ac-standalone-shell > .ac-scroll-main { overflow:visible; min-height:auto; padding:0 0 20px; }
body.ac-standalone-shell > .app-header { position:static; width:100%; max-width:none; margin:0 0 20px; padding:12px max(24px,calc((100vw - 1200px)/2)); border:0; border-bottom:1px solid var(--line); border-radius:0; min-height:58px; box-shadow:none; gap:28px; }
body.ac-standalone-shell > .app-header .app-logo { font-size:18px; line-height:1.4; }
body.ac-standalone-shell > .app-header .app-nav { gap:8px 22px; }
body.ac-standalone-shell > .app-header .app-nav a { font-size:13px; }
body.ac-standalone-shell > .container,body.ac-standalone-shell > .app-breadcrumbs { width:100%; max-width:1248px; padding:0 24px; }
body.ac-standalone-shell .ac-scroll-main > .container { width:100%; max-width:1248px; padding:0 24px; }
body.ac-standalone-shell .qs-page-header { margin:0 0 16px; padding:0; text-align:left; }
body.ac-standalone-shell .qs-page-header h1 { font-size:22px; line-height:1.3; }
body.ac-standalone-shell .qs-page-header .subtitle { font-size:12px; margin-top:5px; }
body.ac-standalone-shell > .action-panel { position:static; width:calc(100% - 48px); max-width:1200px; margin:0 auto 20px; }
body.ac-standalone-shell > .preview-note { margin:0 auto 20px; }
@media(max-width:480px) {
 body.ac-standalone-shell > .app-header { padding:10px 12px; gap:7px; margin-bottom:16px; }
 body.ac-standalone-shell > .app-header .app-nav { justify-content:flex-start; gap:8px 15px; }
 body.ac-standalone-shell > .container,body.ac-standalone-shell > .app-breadcrumbs,body.ac-standalone-shell .ac-scroll-main > .container { padding:0 12px; }
 body.ac-standalone-shell > .action-panel { width:calc(100% - 24px); }
 body.ac-standalone-shell .qs-page-header h1 { font-size:20px; }
}

@media(prefers-reduced-motion:reduce) { *,*::before,*::after { scroll-behavior:auto !important; transition:none !important; } }
