  :root {
    /* Поверхности идут ступенями, а не полупрозрачным стеклом. Стекло с
       размытием на фото-фоне давало грязный серый оттенок и «мыло» на
       тексте — отсюда и ощущение дешевизны. Плотные ступени читаются
       чётко и одинаково на любом фоне. */
    /* Холодный нейтральный ряд вместо тёплого: тёплый серый на фото-фоне
       давал грязный коричневый отлив, особенно на телефонах с тёплой
       цветопередачей. */
    /* ===== СВЕТЛАЯ ТЕМА (по умолчанию) =====
       Кремовая бумага и глубокая зелень — как в присланном макете.
       Тёмная тема сохранена ниже, в [data-theme="dark"]: переключается
       кнопкой в шапке, менять файлы для этого не нужно. */
    --bg: #f6f4ee;
    --surface-1: #fbfaf6;
    --surface-2: #ffffff;
    --surface-3: #f1efe7;
    --surface: var(--surface-2);
    --card: var(--surface-2);

    --line: rgba(45,58,42,.1);
    --line-strong: rgba(45,58,42,.18);

    /* Зелень вместо кораллового: акцент теперь спокойный, потому что на
       светлом фоне яркий красный на каждой кнопке быстро утомляет. */
    --accent: #3c5a3a;
    --accent-soft: rgba(60,90,58,.1);
    --accent-2: #8a6d3b;
    --accent-grad: linear-gradient(135deg, #4a6d47, #3c5a3a 60%, #2f4a2e);
    --text: #1e2a1d;
    /* Вторичный текст: контраст к белой карточке около 6:1 — читается
       и при ярком свете на кухне. */
    --text-muted: #5c6b58;
    --text-faint: #8b968a;
    /* Числа набираются моноширинным: вес, нормы и остатки должны
       выстраиваться по разрядам, иначе взгляд не поймает расхождение. */
    --font-mono: ui-monospace, 'SF Mono', 'Cascadia Mono', 'Roboto Mono', Menlo, monospace;
    --success: #2f7d4f;
    --warning: #b07d2b;
    --danger: #b6453a;

    --font-display: 'Fraunces', 'Georgia', serif;
    --font-body: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

    /* Старые имена оставлены: на них завязана половина файла, и менять
       их разом значило бы переписывать всё сразу. Значения новые. */
    --glass-bg: var(--surface-2);
    --glass-bg-strong: var(--surface-3);
    --glass-border: var(--line);
    --glass-blur: 0px;

    /* Единая шкала скруглений вместо разнобоя 12/16/20/22. */
    --r-sm: 10px;
    --r-md: 14px;
    --r-lg: 18px;
    --radius-lg: var(--r-lg);
    --radius-md: var(--r-md);

    /* Тени мягче и ниже: раньше карточки «висели» над фоном на 30px. */
    /* На светлом фоне тень мягкая и еле заметная: тяжёлая тень делает
       из карточек наклейки. */
    --shadow-soft: 0 2px 10px rgba(45,58,42,.06);
    --shadow-accent: none;
  }

  /* ===== ТЁМНАЯ ТЕМА =====
     Прежнее оформление целиком. Включается кнопкой в шапке, выбор
     сохраняется на устройстве. */
  [data-theme="dark"] {
    --bg: #0b0c0e;
    --surface-1: #101114;
    --surface-2: #16181c;
    --surface-3: #1d2025;
    --line: rgba(255,255,255,.07);
    --line-strong: rgba(255,255,255,.13);
    --accent: #e2564b;
    --accent-soft: rgba(226,86,75,.14);
    --accent-2: #d9a54e;
    --accent-grad: linear-gradient(135deg, #ef6f5f, #e2564b 60%, #cc4438);
    --text: #f2f2f4;
    --text-muted: #a8adb6;
    --text-faint: #7c828c;
    --success: #3fb579;
    --warning: #d9a54e;
    --danger: #e2564b;
    --shadow-soft: 0 6px 20px rgba(0,0,0,.35);
  }

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

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

  body {
    font-family: var(--font-body);
    background: var(--bg); /* фон-картинка теперь на отдельном фиксированном слое body::after, см. ниже */
    color: var(--text);
    min-height: 100vh;
    min-height: 100dvh; /* учитывает адресную строку/панели браузера на iOS и Android */
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    -webkit-tap-highlight-color: transparent;
    overscroll-behavior-y: contain;
  }
  ::selection { background: var(--accent); color: #fff; }
  /* Фоновая картинка вынесена из background-attachment:fixed на body в
     отдельный неподвижный слой (position:fixed + z-index ниже body::before).
     background-attachment:fixed прямо на прокручиваемом body — известный
     баг Mobile Safari: при появлении клавиатуры (фокус на поле поиска и
     т.п.) страница резко сбрасывает скролл в самый верх, потому что
     WebKit пересчитывает fixed-фон при каждом изменении высоты видимой
     области. На отдельном фиксированном слое, не являющемся фоном
     самого скроллящегося body, этого пересчёта/сброса не происходит. */
  body::after {
    content: '';
    position: fixed; inset: 0;
    pointer-events: none;
    z-index: -2;
    /* На светлой теме фона-фотографии нет: кремовый лист должен
       оставаться чистым, иначе текст ложится на грязное пятно. */
    background: var(--bg);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
  /* В тёмной теме фото возвращается — там оно и задумывалось. */
  [data-theme="dark"] body::after {
    background:
      linear-gradient(rgba(9,9,11,.965), rgba(7,7,9,.985)),
      url(images/site-background.jpg);
    background-size: cover;
    background-position: center;
  }
  body::before {
    content: '';
    position: fixed; inset: 0;
    pointer-events: none;
    z-index: -1;
    opacity: .035;
    mix-blend-mode: multiply;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='90'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  }
  ::-webkit-scrollbar { width: 10px; height: 10px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); border-radius: 10px; }
  ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.26); }
  /* Засечный шрифт оставлен только заголовку сайта в шапке. В интерфейсе
     он давал разнобой: подписи внутри карточек и панелей должны быть той
     же гарнитурой, что и остальной текст. */
  h1, h2, h3, h4 { font-family: var(--font-body); font-weight: 700; letter-spacing: -.2px; }
  /* Засечный шрифт остаётся только у заголовка сайта — задан в блоке
     .header h1 ниже, вместе с его размером. */

  /* Кнопка смены темы — в углу шапки, рядом со входом. */
  .theme-toggle {
    position: absolute;
    top: calc(12px + env(safe-area-inset-top, 0px));
    right: 66px;
    width: 44px; height: 44px;
    border-radius: var(--r-sm);
    border: 1px solid rgba(255,255,255,.18);
    background: rgba(12,12,14,.45);
    color: #fff;
    font-size: 17px;
    cursor: pointer;
    z-index: 101;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
  }
  .theme-toggle:hover { background: rgba(12,12,14,.65); }

  /* ===== HEADER ===== */
  .header {
    background:
      linear-gradient(180deg, rgba(10,8,10,.35) 0%, rgba(10,8,10,.55) 55%, rgba(15,12,18,.88) 100%),
      url(images/site-header.jpg);
    background-size: cover;
    background-position: center 35%;
    /* Было 420px — почти экран телефона уходил на картинку, и до первого
       рецепта приходилось прокручивать. Стало компактной шапкой. */
    /* Ещё компактнее: шапка — это подпись, а не витрина. Экран нужен
       списку, за которым сюда и приходят. */
    min-height: 120px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding: 18px 20px 14px;
    padding-top: calc(18px + env(safe-area-inset-top, 0px));
    text-align: center;
    position: relative;
    z-index: 100;
    max-width: none;
    margin: 0;
  }
  @media (min-width: 900px) { .header { min-height: 96px; } }
  .header h1 {
    font-family: var(--font-display);
    font-size: 22px;
    margin-bottom: 2px;
    letter-spacing: -.2px;
    font-weight: 700;
    color: #fff;
    text-shadow: 0 2px 12px rgba(0,0,0,.6);
  }
  @media (min-width: 900px) { .header h1 { font-size: 20px; } }
  .header p {
    opacity: .78; font-size: 11px; letter-spacing: 2.5px; text-transform: uppercase;
    font-weight: 600;
    color: #fff;
    text-shadow: 0 1px 6px rgba(0,0,0,.7);
    position: relative;
    display: inline-flex; align-items: center; gap: 10px;
  }
  .header p::before, .header p::after {
    content: ''; width: 22px; height: 1px;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.5));
  }
  .header p::after { transform: rotate(180deg); }
  /* Кнопки в углах шапки — стиль "тёмный квадрат, иконка + подпись" как в макете */
  .header-corner-btn {
    position: absolute;
    top: calc(12px + env(safe-area-inset-top, 0px));
    width: 44px; height: 44px;
    border-radius: var(--r-sm);
    background: rgba(12,12,14,.6);
    border: 1px solid rgba(255,255,255,.16);
    color: #fff;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    transition: transform .15s ease, background .2s ease;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
  }
  .header-corner-btn:active { transform: scale(.94); background: rgba(15,15,20,.6); }
  .header-corner-btn span.label {
    font-size: 10px; line-height: 1; opacity: .9; letter-spacing: .3px;
  }
  .header-corner-btn span.icon { line-height: 1; display: flex; }
  .header-corner-btn span.icon svg { width: 20px; height: 20px; stroke: #fff; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  .admin-toggle { right: 14px; }
  .export-toggle { left: 14px; }

  /* Поисковая строка "прилипает" сразу под липкой панелью вкладок
     (.nav-tabs), а не просто лежит в потоке страницы. Раньше при каждом
     нажатии клавиши список результатов ниже перерисовывался и менял
     высоту — из-за этого позиция самого поля на экране скакала, и
     Mobile Safari (у которого при открытой клавиатуре и так включена
     автоматическая подстройка скролла к сфокусированному полю) на
     каждое такое изменение реагировал резким прыжком скролла в начало
     страницы. Если поле зафиксировано на экране и не двигается от
     перерисовки списка под ним, скроллу незачем "подстраиваться" — и
     прыжки прекращаются. Отступ top берётся из --nav-h (высота
     .nav-tabs, вычисляется в JS, см. initStickySearchOffset в app.js). */
  .search-wrap {
    position: sticky; top: var(--nav-h, 56px); z-index: 80;
    margin: 0 -16px 12px; scroll-margin-top: 60px;
    background: var(--bg);
    border-bottom: 1px solid var(--line);
    padding: 10px 16px;
  }
  @media (min-width: 900px) { .search-wrap { margin: 0 -24px 14px; padding: 12px 24px; } }
  .search-wrap .search-icon {
    position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
    width: 18px; height: 18px;
    stroke: var(--text-muted); fill: none; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
    pointer-events: none;
  }
  .search-wrap .search-input { margin-bottom: 0; padding-left: 42px; padding-right: 38px; }
  .search-clear {
    position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    width: 28px; height: 28px; border-radius: 50%;
    border: none; background: rgba(255,255,255,.06);
    color: var(--text-muted); font-size: 13px;
    display: flex; align-items: center; justify-content: center;
    cursor: pointer; touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background .15s ease, color .15s ease;
  }
  .search-clear:hover { background: var(--accent); color: #fff; }
  .search-clear:active { transform: translateY(-50%) scale(.9); }

  /* Чипы категорий (вкладка "Категории") */
  /* Категории раскладываются в несколько рядов, без прокрутки вбок.
     Горизонтальная лента прятала половину категорий за краем экрана, а
     их редко больше восьми — все помещаются в два ряда. */
  .chip-row {
    display: flex; gap: 6px;
    flex-wrap: wrap;
  }
  .chip {
    flex-shrink: 0;
    padding: 7px 13px;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    border: 1px solid var(--glass-border);
    touch-action: manipulation;
    -webkit-user-select: none; user-select: none;
    -webkit-tap-highlight-color: transparent;
    transition: transform .15s ease, background .15s ease, color .15s ease, border-color .15s ease;
  }
  .chip:hover { color: var(--text); border-color: var(--line-strong); background: var(--surface-3); }
  .chip:active { transform: scale(.96); }
  .chip.active { background: var(--accent); color: #fff; border-color: transparent; box-shadow: none; }

  /* Панель управления внутри раздела (видна только админу): добавить
     рецепт и открыть список категорий ЭТОГО раздела. Раньше добавление
     рецепта было отдельной вкладкой в верхней навигации, общей на весь
     сайт; теперь оно живёт внутри того раздела, куда рецепт и попадёт. */
  .section-toolbar {
    display: flex; gap: 8px; flex-wrap: wrap;
    margin: 12px 0 4px;
  }
  .section-toolbar.tool-bar { gap: 6px; width: fit-content; max-width: 100%; }

  /* Раскрывающийся список категорий раздела — тут они и создаются. */
  .section-cats {
    margin-top: 10px;
    padding: 12px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-md);
  }
  .section-cats-title {
    font-size: 12px; font-weight: 700; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: .04em;
    margin-bottom: 10px;
  }

  /* Админ-панель */
  .admin-panel-card {
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: 16px 18px;
    margin-bottom: 12px;
  }
  .admin-panel-card > .admin-panel-row > span:first-child {
    font-size: 15px; font-weight: 700; letter-spacing: -.1px;
  }
  .admin-panel-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; flex-wrap: wrap;
  }
  /* На узком экране кнопка/значение в правой части строки переносится
     под подпись и растягивается на всю ширину — иначе на телефоне
     длинная подпись ("👥 Участники (доступ по ключу)") и кнопка
     ("🔑 Сгенерировать ключ") сжимались бы в одну тесную строку. */
  @media (max-width: 480px) {
    .admin-panel-row > .btn { width: 100%; justify-content: center; }
  }
  /* Пояснения в админ-панели длинные, и раньше карточка «Заведения»
     занимала пол-экрана текстом. Показываем три строки, остальное — по
     нажатию: инструкция нужна один раз, а карточкой пользуются каждый день. */
  .admin-panel-hint {
    color: var(--text-faint); font-size: 12.5px; line-height: 1.6; margin-top: 8px;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden;
    cursor: pointer;
  }
  .admin-panel-hint.expanded { -webkit-line-clamp: unset; }

  /* Строка "поле ввода + кнопка" в карточках админ-панели (юзернейм
     Telegram, ссылка на группу и т.п.) — на телефоне становится
     колонкой, чтобы поле не сжималось до нечитаемой ширины рядом с
     кнопкой. */
  .admin-input-row { display: flex; gap: 8px; margin-top: 10px; }
  @media (max-width: 480px) {
    .admin-input-row { flex-direction: column; }
    .admin-input-row .btn { width: 100%; justify-content: center; }
  }

  /* Карточка участника в списке — раньше это была просто строка с
     нижней границей и всё сливалось в один серый список; теперь у
     каждой записи свой фон + скруглённая обводка, чтобы участники
     визуально отделялись друг от друга и от фона панели. Админы и
     заблокированные дополнительно подсвечены своим цветом. */
  .participant-item {
    display: flex; align-items: center; justify-content: space-between;
    gap: 8px; flex-wrap: wrap;
    padding: 12px 14px;
    margin-bottom: 8px;
    background: var(--surface);
    border: 1.5px solid var(--glass-border);
    border-radius: var(--radius-md);
    transition: border-color .15s ease, background .15s ease;
  }
  .participant-item:last-child { margin-bottom: 0; }
  .participant-item.is-admin {
    border-color: rgba(217, 165, 78, .55);
    background: rgba(217, 165, 78, .08);
  }
  .participant-item.is-purchase {
    border-color: rgba(110, 168, 214, .55);
    background: rgba(110, 168, 214, .08);
  }
  .participant-item.is-blocked {
    border-color: rgba(226, 86, 75, .55);
    background: rgba(226, 86, 75, .08);
  }

  /* "Закупка" как отдельная РОЛЬ участника (в отличие от админа) — видит
     вкладку "Закупка" (и ничего больше сверх обычного гостя). Как и
     .admin-only/.developer-only, скрыт по умолчанию и раскрывается через
     класс на <body>: is-admin (админ/разработчик и так видят всё) ИЛИ
     is-purchase-role (именно этот участник назначен на закупку). */
  .purchase-access-only { display: none !important; }
  body.is-admin .purchase-access-only { display: block !important; }
  body.is-purchase-role .purchase-access-only { display: block !important; }
  body.is-admin .nav-tab.purchase-access-only,
  body.is-purchase-role .nav-tab.purchase-access-only { display: block !important; }

  /* Главная страница вкладки "Закупка" — список цехов и поставщиков
     карточками (вместо строки чипов), с иконками правки/удаления
     напротив каждого. Клик по самой карточке открывает список позиций
     этого цеха/поставщика; иконки справа — отдельные действия и клик
     по ним не должен "провалиться" в открытие карточки (event.stopPropagation
     в разметке). */
  .purchase-home-group-title {
    font-size: 13px; font-weight: 700; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: .04em;
    margin: 18px 0 8px;
  }
  .purchase-home-group-title:first-child { margin-top: 0; }
  .purchase-home-card-main {
    flex: 1; min-width: 0;
    display: flex; align-items: center; gap: 10px;
    padding: 10px 0; cursor: pointer;
    touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  }
  .purchase-home-card-icon { font-size: 20px; flex-shrink: 0; }
  .purchase-home-card-meta { font-size: 12px; color: var(--text-muted); font-weight: 500; }

  /* Баннер незавершённой рассылки ("📤 Отправить всё") — появляется на
     главной странице "Закупки", если сайт был случайно закрыт посреди
     рассылки (прогресс хранится в localStorage). Позволяет продолжить
     с того же места или начать заново. */
  .purchase-send-progress-banner {
    background: rgba(217, 165, 78, .12);
    border: 1.5px solid rgba(217, 165, 78, .4);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    margin-bottom: 18px;
  }
  .purchase-send-progress-banner-title { font-size: 14px; font-weight: 600; color: var(--text); }
  .purchase-send-progress-banner-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
  .purchase-send-progress-banner-actions { display: flex; gap: 8px; margin-top: 10px; }
  .purchase-home-card-actions { display: flex; gap: 4px; flex-shrink: 0; }
  .purchase-home-icon-btn {
    width: 32px; height: 32px; border-radius: 8px; border: none;
    background: transparent; color: var(--text-faint);
    font-size: 14px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    touch-action: manipulation; -webkit-tap-highlight-color: transparent;
    transition: transform .15s ease, background .15s ease;
  }
  .purchase-home-icon-btn:active { transform: scale(.92); }
  .purchase-home-icon-btn-danger { color: #e2564b; background: rgba(226,86,75,.14); }
  .purchase-home-icon-btn-danger:hover { background: rgba(226,86,75,.22); }
  .purchase-home-empty { color: var(--text-muted); font-size: 13px; padding: 10px 2px 4px; }

  /* Карточка "живого" посетителя в блоке "Онлайн" — тот же стиль,
     что у участников, плюс цветной индикатор (зелёный — онлайн
     прямо сейчас, серый — просто был(а) недавно). */
  .online-item.is-live {
    border-color: rgba(63, 181, 121, .55);
    background: rgba(63, 181, 121, .08);
  }
  .online-dot {
    display: inline-block; width: 8px; height: 8px; border-radius: 50%;
    background: var(--text-muted); margin-right: 6px; flex-shrink: 0;
  }
  .online-item.is-live .online-dot { background: var(--success); box-shadow: 0 0 0 3px rgba(63,181,121,.25); }
  .online-dot.is-live { background: var(--success); box-shadow: 0 0 0 3px rgba(63,181,121,.25); }
  .online-member-badge {
    display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: .3px;
    padding: 2px 7px; border-radius: 20px;
    background: rgba(217, 165, 78, .18); color: var(--accent-2);
    margin-left: 6px; vertical-align: middle;
  }

  /* Цветные бейджи типа/категории на карточках */
  .type-badge { color: #fff; font-weight: 600; }

  /* Подвал */
  .app-footer {
    text-align: center;
    padding: 18px 12px calc(18px + env(safe-area-inset-bottom, 0px));
    color: var(--text-muted);
    font-size: 12px;
    letter-spacing: .5px;
  }

  /* По умолчанию (обычный посетитель) все элементы редактирования скрыты */
  .admin-only { display: none !important; }
  body.is-admin .admin-only { display: revert !important; }
  body.is-admin .nav-tab.admin-only { display: block !important; }
  body.is-admin button.admin-only,
  body.is-admin .btn.admin-only { display: inline-flex !important; }
  body.is-admin .guest-only { display: none !important; }
  /* "developer-only" — как и .admin-only, но видно ТОЛЬКО настоящему
     разработчику (вход по GitHub-ключу), а не обычному админу,
     которому права выданы через список участников. Обычному админу
     доступно ТОЛЬКО редактирование карточек рецептов (вкладки
     "Категории"/"Добавить") — вкладка "⚙️ Админ-панель" (включая
     раздел "Участники"), управление шаблоном закупки (цеха/поставщики
     во вкладке "Закупка") и кнопка экспорта в шапке скрыты этим
     классом и видны только настоящему разработчику. */
  .developer-only { display: none !important; }
  body.is-developer .developer-only { display: revert !important; }
  body.is-developer .nav-tab.developer-only { display: block !important; }
  body.is-developer button.developer-only,
  body.is-developer .btn.developer-only { display: inline-flex !important; }
  .admin-login-hint {
    margin: 0 16px 14px; padding: 12px 14px;
    background: var(--glass-bg); border: 1px solid var(--glass-border);
    border-radius: var(--radius-md); font-size: 13px; color: var(--text-muted);
    line-height: 1.5; cursor: pointer;
  }
  .admin-login-hint:active { opacity: .8; }

  /* ===== NAV TABS ===== */
  /* ===== БОКОВОЕ МЕНЮ (только широкий экран) =====
     На мониторе список разделов держится слева постоянно: прятать его
     за кнопкой там незачем — место есть, а лишнее нажатие на каждый
     переход раздражает. На телефоне всё остаётся как было: кнопка и
     выпадающий список.

     Важно: это те же элементы .nav-tab, что и в выпадающем списке.
     Логика доступа, подсветки и переключения не меняется — меняется
     только расположение. */
  .sidebar-brand { display: none; }

  /* ===== ВЫБОР РАЗДЕЛА =====
     Кнопка с названием текущего раздела, под ней — выпадающий список.
     Лента с прокруткой не годилась: при шести разделах нужный уезжал за
     край и найти его можно было только вслепую листая. */
  .nav-picker {
    position: sticky; top: 0; z-index: 91;
    /* Точка отсчёта для выпадающего меню — оно позиционируется от
       кнопки, а не от края экрана. */
    background: var(--surface-1);
    border-bottom: 1px solid var(--line);
    padding: 8px 12px;
  }
  @media (min-width: 900px) {
    .nav-picker { padding: 8px 24px; }
    /* Здесь было `.nav-tabs { left: 24px; }` — правило стояло ВЫШЕ
       базового объявления .nav-tabs и перекрывалось им при равной
       силе, то есть не работало никогда. Ширины от 760px и так
       обслуживает блок боковой панели ниже по файлу. */
  }

  .nav-picker-btn {
    display: inline-flex; align-items: center; gap: 9px;
    min-width: 200px; max-width: 100%;
    padding: 9px 13px;
    border-radius: var(--r-sm);
    background: var(--surface-3);
    border: 1px solid var(--line-strong);
    color: var(--text);
    font-family: inherit; font-size: 14.5px; font-weight: 650;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background .15s ease;
  }
  .nav-picker-btn:hover { background: #24272d; }
  .nav-picker-icon { font-size: 17px; line-height: 1; }
  .nav-picker-label { flex: 1; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .nav-picker-caret { color: var(--text-faint); font-size: 11px; transition: transform .18s ease; }
  .nav-tabs.open ~ * .nav-picker-caret { transform: rotate(180deg); }

  /* Сам список. Скрыт, пока не нажали кнопку; открывается панелью
     поверх содержимого, а не раздвигает его. */
  .nav-tabs {
    display: none;
    /* Меню привязано к экрану, а не к странице. Раньше оно позиционировалось
       от блока с кнопкой, и стоило пролистать список — уезжало вверх
       вместе со страницей: первых разделов было не видно, а нижние
       наезжали на содержимое. */
    /* Отступ сверху считает JS при открытии (toggleNavPicker) и кладёт
       в --nav-menu-top. Жёсткие 58px были верны только для прокрученной
       страницы, где кнопка уже прилипла к верхнему краю; на непрокрученной
       кнопка стоит ниже шапки, а меню всё равно рисовалось на 58px и
       пряталось под неё — первых двух разделов не было видно.
       Слой выше шапки (100) и переключателя темы (101) по той же
       причине: меню всплывает НАД ними, а не под. */
    position: fixed; top: var(--nav-menu-top, 58px); left: 12px; z-index: 102;
    width: 260px; max-width: calc(100vw - 24px);
    flex-direction: column;
    gap: 2px;
    padding: 6px;
    margin: 0;
    background: var(--surface-2);
    border: 1px solid var(--line-strong);
    border-radius: var(--r-md);
    box-shadow: 0 18px 44px rgba(0,0,0,.28);
    /* Ограничение по высоте с запасом на нижнюю панель: список
       прокручивается внутри себя, а не уходит под неё. */
    max-height: calc(100vh - 150px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .nav-tabs.open { display: flex; animation: pickerIn .16s ease; }
  @keyframes pickerIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
  /* Здесь было `@media (min-width: 900px) { .nav-tabs { margin: 0 24px;
     max-width: 340px; } }` — полностью перекрыто блоком боковой панели
     @media (min-width: 760px) ниже по файлу, который задаёт margin: 0
     и max-width: none. Остаток прежней раскладки. */
  .nav-tab {
    flex: 0 0 auto;
    display: flex; flex-direction: row; align-items: center; justify-content: flex-start;
    gap: 10px;
    padding: 10px 12px;
    border-radius: var(--r-sm);
    white-space: nowrap;
    cursor: pointer;
    color: var(--text-muted);
    transition: color .15s ease, background .15s ease;
    border-bottom: none;
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
    touch-action: manipulation;
  }
  .nav-tab .nav-icon {
    line-height: 1; display: flex; align-items: center; justify-content: center;
    width: 20px; height: 20px; font-size: 16px;
  }
  .nav-tab .nav-icon svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  .nav-tab .nav-label {
    font-size: 14px; line-height: 1; letter-spacing: -.1px; font-weight: 600;
    border-bottom: none; padding-bottom: 0;
  }
  .nav-tab:hover { color: var(--text); background: rgba(255,255,255,.05); }
  .nav-tab:active { transform: scale(.97); }
  /* Активная вкладка — заливкой, а не подчёркиванием: подчёркивание в
     прокручиваемой ленте теряется, а заливка видна с любого места. */
  .nav-tab.active { color: #fff; background: var(--accent); }
  .nav-tab.active .nav-icon { background: transparent; }
  .nav-tab[data-tab="admin"] { color: var(--text-muted); }
  .nav-tab[data-tab="admin"].active { color: #fff; }

  /* Раскладка бокового меню описана ПОСЛЕ обычных правил .nav-tabs:
     при равной силе побеждает то, что ниже в файле. Пока эти
     медиазапросы стояли выше, обычные правила перекрывали их, и на
     мониторе панель оставалась выпадающей и ложилась поверх списка. */

  @media (min-width: 760px) {
    body { padding-left: 212px; }

    .nav-picker { display: none; }

    .nav-tabs {
      display: flex !important;
      /* Панель — компактная карточка по высоте содержимого, а не
         колонка во весь экран: разделов немного, и растянутый на всю
         высоту блок с пустотой внизу выглядит тяжело. Остаётся
         закреплённой — при прокрутке списка не уезжает. */
      position: fixed; left: 12px; top: 12px; bottom: auto;
      width: 188px; max-width: none;
      flex-direction: column;
      gap: 2px;
      margin: 0;
      padding: 12px 10px;
      background: var(--surface-2);
      border: 1px solid var(--line);
      border-radius: var(--r-lg);
      box-shadow: var(--shadow-soft);
      /* Если разделов станет много, панель начнёт прокручиваться внутри
         себя, но не вырастет выше экрана. */
      max-height: calc(100vh - 24px);
      overflow-y: auto;
      animation: none;
      z-index: 92;
    }

    .sidebar-brand {
      display: flex; align-items: center; gap: 11px;
      padding: 4px 8px 18px;
      margin-bottom: 8px;
      border-bottom: 1px solid var(--line);
    }
    .sidebar-brand-mark {
      width: 38px; height: 38px; flex-shrink: 0;
      display: flex; align-items: center; justify-content: center;
      border-radius: 50%;
      background: var(--accent); color: #fff;
      font-size: 12.5px; font-weight: 800; letter-spacing: -.3px;
    }
    .sidebar-brand-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
    .sidebar-brand-text strong { font-size: 14px; letter-spacing: -.1px; }
    .sidebar-brand-text small { font-size: 11.5px; color: var(--text-faint); }

    /* Пункт меню — во всю ширину колонки, чтобы попадать не целясь. */
    .nav-tab { width: 100%; padding: 10px 11px; border-radius: var(--r-sm); }
    .nav-tab .nav-label { white-space: normal; font-size: 13.5px; }
    .sidebar-brand { padding: 2px 6px 12px; margin-bottom: 6px; }
    .sidebar-brand-mark { width: 32px; height: 32px; font-size: 11px; }

    /* Шапка и содержимое живут в оставшейся части экрана. */
    .header { max-width: none; }
    .theme-toggle { right: 66px; }
  }

  /* На большом мониторе колонка шире: место есть, а длинные названия
     разделов («Админ-панель / Настройки») читаются в одну строку. */
  @media (min-width: 1100px) {
    body { padding-left: 252px; }
    .nav-tabs { width: 224px; padding: 16px 12px; }
    .nav-tab { padding: 11px 12px; }
    .nav-tab .nav-label { font-size: 14.5px; }
    .sidebar-brand { padding: 4px 8px 18px; }
    .sidebar-brand-mark { width: 38px; height: 38px; font-size: 12.5px; }
  }

  /* ===== CONTENT AREA ===== */
  .tab-content { display: none; padding: 16px; max-width: 820px; margin: 0 auto; }

  /* На большом экране список идёт в несколько колонок: раньше контент
     держался узкой полосой по центру, и на мониторе помещалось пять
     рецептов из сорока двух — остальное приходилось прокручивать. */
  @media (min-width: 900px) {
    .tab-content { max-width: 1180px; padding: 20px 24px; }
    /* Вид «строки»: на широком экране идут в две колонки, как раньше. */
    .cards-grid.view-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; align-items: start; }
    .cards-grid.view-list .recipe-card { margin-bottom: 0; }
  }
  @media (min-width: 1320px) {
    .tab-content { max-width: 1480px; }
    .cards-grid.view-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  }
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

  /* ===== СТРОКА ПОСТАВЩИКА =====
     Была карточка в несколько строк: название, цех, статус ссылки,
     отметка правки, контакты — всё столбиком, по 120 пикселей высоты на
     каждого из пятнадцати. Стало плотной строкой: значок, название с
     подписью, признаки и кнопки в одну линию. */
  .purchase-home-card {
    display: flex; align-items: center; gap: 11px;
    padding: 9px 11px;
    border-radius: var(--r-sm);
    background: var(--surface-2);
    border: 1px solid var(--line);
    margin-bottom: 6px;
    transition: background .15s ease, border-color .15s ease;
  }
  .purchase-home-card:hover {
    border-color: var(--line-strong);
    background: var(--surface-3);
  }
  .purchase-home-card-text {
    min-width: 0;
    flex: 1;
  }
  .purchase-home-card-text strong {
    display: block;
    font-size: 14.5px;
    font-weight: 650;
    letter-spacing: -.1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Служебные подписи — одной приглушённой строкой под названием.
     Раньше каждая занимала свою строку и растягивала карточку. */
  .purchase-home-card-sub,
  .purchase-home-card-link-badge,
  .purchase-home-card .edit-stamp-inline {
    display: inline; margin: 0;
    font-size: 12px; color: var(--text-faint);
  }
  .purchase-home-card-link-badge {
    font-size: 12px;
    color: #5cbd88;
    font-weight: 500;
  }
  .purchase-home-card .purchase-contacts { margin-top: 5px; }
  .purchase-home-card .purchase-contact { padding: 3px 8px; font-size: 11.5px; }

  /* Список цехов и поставщиков на широком экране — в две колонки:
     их бывает полтора десятка, и одной колонкой это длинная простыня.

     ВАЖНО про раскладку. В этом же контейнере лежат заголовки групп
     («🏭 Цеха», «🚚 Поставщики») и кнопка «Отправить всё» — они не
     карточки. Пока сетка применялась ко всему подряд, каждый такой
     элемент занимал ячейку и сдвигал карточки: стоило появиться кнопке
     отправки, как первый поставщик уезжал во вторую колонку.
     Поэтому заголовки и кнопка растянуты на всю ширину строки. */
  @media (min-width: 900px) {
    #purchase-home-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; align-items: start; }
    #purchase-home-list .purchase-home-card { margin-bottom: 0; }
    #purchase-home-list > .purchase-home-group-title,
    #purchase-home-list > button,
    #purchase-home-list > p,
    #purchase-home-list > .purchase-home-empty { grid-column: 1 / -1; }
  }

  /* ===== ПАНЕЛЬ УПРАВЛЕНИЯ РАЗДЕЛОМ =====
     Поиск, фильтр актуальности и кнопки. На широком экране — одной
     строкой: раньше это были три отдельных ряда, и до первого рецепта
     оставалась треть экрана. */
  .section-controls { margin-bottom: 12px; }
  .section-controls .search-wrap { position: static; margin: 0 0 8px; padding: 0; border: none; background: transparent; }
  .section-controls .section-toolbar { margin: 0 0 8px; }
  .section-controls .status-filter-row { margin: 0 0 8px; }

  @media (min-width: 900px) {
    .section-controls {
      display: flex; align-items: center; gap: 10px;
      margin-bottom: 14px;
    }
    .section-controls .search-wrap { flex: 1; min-width: 240px; margin: 0; }
    .section-controls .section-toolbar,
    .section-controls .status-filter-row { margin: 0; flex-shrink: 0; }
  }

  /* Счётчик рецептов у категории. Приглушён и через точку — это
     подпись к названию, а не отдельная величина. */
  .chip-count { opacity: .6; margin-left: 5px; font-variant-numeric: tabular-nums; }
  .chip-count::before { content: '· '; }

  /* Строка категорий и переключатель вида — в одну линию. */
  .chips-line { display: flex; align-items: center; gap: 10px; }
  .chips-line .chip-row { flex: 1; min-width: 0; }
  .view-toggle {
    flex-shrink: 0;
    width: 34px; height: 34px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--r-sm);
    border: 1px solid var(--line);
    background: var(--surface-2);
    color: var(--text-muted);
    font-size: 15px;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
  }
  .view-toggle:hover { background: var(--surface-3); color: var(--text); }

  /* ===== ВИД «КАРТОЧКИ» =====
     Фото сверху, под ним название и приметы блюда. Так список читается
     как витрина — ради этого вид и делался. */
  .cards-grid.view-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; align-items: start; }
  .cards-grid.view-grid .recipe-card {
    flex-direction: column;
    align-items: stretch;
    margin-bottom: 0;
    border-radius: var(--r-md);
    box-shadow: var(--shadow-soft);
  }
  /* Полоска категории в карточке ни к чему: цвет уже несёт обложка. */
  .cards-grid.view-grid .recipe-card::before { display: none; }
  .cards-grid.view-grid .card-cover {
    width: auto; height: 148px;
    margin: 0;
    border-radius: 0;
    align-self: stretch;
  }
  .cards-grid.view-grid .card-cover-icon { font-size: 34px; }
  .cards-grid.view-grid .card-info { padding: 11px 13px 13px; }
  .cards-grid.view-grid .card-composition { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
  .cards-grid.view-grid .card-video-btn { right: 8px; bottom: 8px; width: 30px; height: 30px; }
  .cards-grid.view-grid .card-video-btn svg { width: 14px; height: 14px; }
  /* В карточке место есть — возвращаем подпись статуса словом. */
  .cards-grid.view-grid .badge-status { font-size: 12px; gap: 5px; }

  /* Сердечко избранного. Пустое — тонким контуром, отмеченное —
     залитое акцентом: состояние видно, не наводя курсор. */
  .card-fav-btn svg { fill: none; stroke: currentColor; stroke-width: 1.8; }
  .card-fav-btn.is-fav { color: var(--accent); }
  .card-fav-btn.is-fav svg { fill: currentColor; }

  /* Кнопка «только избранные» — та же форма, что у переключателя вида. */
  /* Значки рисуем контуром, а не символами из шрифта: типографские
     «★» и «▦» на части устройств показывались пустыми квадратами. */
  .view-toggle svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  .view-toggle.fav-filter.active { background: var(--accent); color: #fff; border-color: transparent; }
  .view-toggle.fav-filter.active svg { fill: currentColor; }

  /* Счётчик рецептов у раздела в меню. */
  .nav-count {
    margin-left: auto;
    padding: 1px 7px;
    border-radius: 999px;
    background: rgba(127,127,127,.16);
    color: var(--text-faint);
    font-size: 11.5px; font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .nav-tab.active .nav-count { background: rgba(255,255,255,.22); color: #fff; }
  /* В выпадающем списке на телефоне счётчик тоже полезен, а вот в
     свёрнутой кнопке раздела он был бы лишним шумом. */
  .nav-picker-btn .nav-count { display: none; }

  .badge-time { background: rgba(127,127,127,.14); color: var(--text-muted); font-weight: 600; }

  /* Экран избранного. */
  .favorites-title { font-size: 20px; margin-bottom: 4px; letter-spacing: -.3px; }
  .favorites-count {
    display: inline-block;
    padding: 1px 9px; margin-left: 4px;
    border-radius: 999px;
    background: var(--accent-soft); color: var(--accent);
    font-size: 13px; font-weight: 700;
    font-variant-numeric: tabular-nums;
    vertical-align: middle;
  }
  .favorites-hint { font-size: 12.5px; line-height: 1.5; color: var(--text-faint); margin-bottom: 14px; }

  /* ===== CARD LIST ===== */
  /* Строка списка: цветная метка категории, миниатюра, название с
     составом, статус точкой и вес в колонку. Обложка во всю ширину
     красива, но на экран влезало четыре рецепта из сорока двух — а
     список открывают, чтобы быстро найти нужный, а не любоваться. */
  .recipe-card {
    animation-duration: .28s;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: 0;
    margin-bottom: 6px;
    overflow: hidden;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease;
    display: flex;
    flex-direction: row;
    align-items: stretch;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    animation: cardIn .3s ease backwards;
  }

  /* Полоска цвета категории. Главный инструмент беглого поиска: глаз
     цепляется за цвет раньше, чем читает название. */
  .recipe-card::before {
    content: '';
    width: 3px; flex-shrink: 0;
    background: var(--card-accent, var(--line-strong));
  }

  .card-cover {
    position: relative;
    width: 52px; flex-shrink: 0;
    align-self: center;
    height: 52px;
    margin: 8px 0 8px 10px;
    border-radius: 9px;
    background: var(--surface-1);
    display: flex; align-items: center; justify-content: center;
    border: none;
    overflow: hidden;
  }
  .card-cover .card-thumb { width: 100%; height: 100%; border: none; border-radius: 0; }
  /* Без фото — значок категории. Список остаётся ровным, даже если
     снимки есть не у всех. */
  .card-cover-icon { font-size: 21px; opacity: .8; }
  .card-video-btn {
    position: absolute; right: 2px; bottom: 2px;
    width: 20px; height: 20px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    border: none;
    background: rgba(10,10,12,.72);
    color: #fff;
    cursor: pointer;
  }
  .card-video-btn svg { width: 16px; height: 16px; fill: currentColor; }
  .card-video-btn:hover { background: var(--accent); }
  /* Наведение — только подсветка рамки и фона. Подпрыгивание карточки
     на 3px в многоколоночной сетке дёргало соседей. */
  .recipe-card:hover { background: var(--surface-3); border-color: var(--line-strong); }
  .recipe-card:active { transform: scale(.995); }

  @keyframes cardIn {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
  }

  .card-thumb { object-fit: cover; }
  .card-info { flex: 1; min-width: 0; padding: 9px 12px; display: flex; flex-direction: column; justify-content: center; }
  .card-title-row { display: flex; align-items: baseline; gap: 10px; }
  /* Вес — моноширинным и по правому краю: у сорока строк цифры должны
     стоять колонкой, иначе взгляд их не сравнивает. */
  .card-weight {
    margin-left: auto; flex-shrink: 0;
    font-family: var(--font-mono);
    font-size: 12.5px; color: var(--text-muted);
    font-variant-numeric: tabular-nums;
  }
  /* Состав — ради него и переделана карточка: рецепты с похожими
     названиями иначе не различить, не открывая каждый. */
  .card-composition {
    margin: 2px 0 0;
    font-size: 12.5px; line-height: 1.4;
    color: var(--text-faint);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* Название — основной ориентир в списке, поэтому крупнее и без
     курсивной подачи: важнее скорость чтения, чем стиль. */
  .card-info h3 {
    font-family: var(--font-body);
    font-weight: 700; font-size: 16px; line-height: 1.3;
    letter-spacing: -.1px;
    margin-bottom: 3px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .card-meta { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-top: 6px; }
  .card-actions { display: flex; gap: 2px; margin-left: auto; flex-shrink: 0; }
  .card-icon-btn {
    width: 30px; height: 30px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 8px; border: none;
    background: transparent;
    color: var(--text-faint);
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
  }
  .card-icon-btn:hover { background: rgba(255,255,255,.08); color: var(--text); }
  .card-icon-btn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; }
  .card-icon-btn svg circle { fill: currentColor; stroke: none; }

  /* Актуальность — точкой с подписью. Плашка на каждой из сорока строк
     превращала список в пёстрое полотно; точка отмечает состояние, не
     перетягивая внимание, а слово остаётся для тех, кто не различает
     цвета. */
  .badge-status {
    background: none !important;
    padding: 0; font-weight: 600; font-size: 12px;
    display: inline-flex; align-items: center; gap: 5px;
  }
  .badge-status::before {
    content: ''; width: 7px; height: 7px; border-radius: 50%;
    background: currentColor; flex-shrink: 0;
  }
  .badge-status.status-active { color: #5cbd88; }
  .badge-status.status-off-menu { color: #ff8d80; }
  .badge-status.status-seasonal { color: #7ba7e8; }
  /* В плотной строке слово прячем — остаётся точка с подсказкой. */
  .recipe-card .badge-status { font-size: 0; gap: 0; }
  .recipe-card .badge-status::before { width: 8px; height: 8px; }

  /* Переключатель актуальности спрятан в меню карточки (кнопка «⋮»):
     три кнопки на каждой из сорока карточек превращали список в стену. */
  .recipe-card .card-status-switch { display: none; }
  .badge {
    font-size: 11.5px; padding: 3px 9px;
    border-radius: 20px; background: rgba(255,255,255,.1);
    color: var(--text);
    font-weight: 600;
  }
  .badge-cal { background: transparent; color: var(--text-muted); padding: 3px 4px; }

  /* Бейдж категории/стиля рецепта — единый нейтральный цвет на всех карточках */
  /* Плашка со стилем блюда. Светлый текст на светлом фоне не читался —
     в светлой теме берём тёмно-коричневый, в тёмной остаётся песочный. */
  .badge-style { background: rgba(176,125,43,.16); color: #7a5518; }
  [data-theme="dark"] .badge-style { background: rgba(217,165,78,.2); color: #ecd4a0; }

  /* ===== FORMS ===== */
  .form-group { margin-bottom: 13px; }
  /* Подписи полей — мелкой капителью, как в закупке: единый язык у всех
     форм сайта, и подпись не спорит с самим значением. */
  .form-group label {
    display: block;
    font-size: 10.5px; letter-spacing: .05em; text-transform: uppercase;
    color: var(--text-faint);
    margin-bottom: 5px;
    font-weight: 700;
  }

  /* Парные поля формы. На телефоне идут друг под другом, на широком
     экране — в один ряд: два коротких поля во всю ширину оставляли
     половину экрана пустой. */
  .form-row-2 { display: grid; grid-template-columns: 1fr; gap: 0 14px; }
  @media (min-width: 620px) { .form-row-2 { grid-template-columns: 1fr 1fr; } }

  /* Пояснение под подписью поля — обычным текстом, а не в самой подписи:
     подпись должна оставаться коротким ярлыком. */
  .form-hint { margin: -2px 0 6px; font-size: 12px; line-height: 1.5; color: var(--text-faint); }
  input[type="text"], input[type="number"], input[type="search"], textarea, select {
    width: 100%;
    padding: 11px 14px;
    background: var(--surface-1);
    border: 1px solid var(--line-strong);
    border-radius: var(--r-sm);
    color: var(--text);
    font-size: 16px; /* >=16px обязательно: иначе Safari iOS зумит страницу при фокусе */
    outline: none;
    transition: border-color .2s ease, box-shadow .2s ease;
    -webkit-appearance: none;
    appearance: none;
  }
  /* type="search" на iOS/Android иначе рисует свой крестик очистки и системную
     "рамку для поиска" — у нас уже есть свой .search-clear, поэтому системные
     декорации прячем, чтобы не дублировались и не съезжала верстка. */
  input[type="search"]::-webkit-search-decoration,
  input[type="search"]::-webkit-search-cancel-button,
  input[type="search"]::-webkit-search-results-button,
  input[type="search"]::-webkit-search-results-decoration {
    -webkit-appearance: none;
    display: none;
  }
  input::placeholder, textarea::placeholder { color: var(--text-faint); }
  input:hover, textarea:hover, select:hover { border-color: rgba(255,255,255,.2); }
  input:focus, textarea:focus, select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
  /* Без этого правила выпадающий список <option> отрисовывается системным
     (обычно белым) фоном, а цвет текста наследуется от select (светлый) —
     в итоге буквы читаются только на подсвеченном при наведении пункте.
     Задаём фон/цвет самим <option>, чтобы весь список был читаем сразу. */
  select option { background: var(--card); color: var(--text); }

  /* Photo upload */
  .photo-upload {
    position: relative; /* якорь для кнопки удаления фото */
    width: 100%; height: 160px;
    background: var(--surface);
    border: 2px dashed rgba(255,255,255,.18);
    border-radius: 14px;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    cursor: pointer; transition: all .2s;
    position: relative;
  }
  .photo-upload:hover { border-color: var(--accent); background: rgba(226,86,75,.05); }
  .photo-upload img { max-width: 100%; max-height: 100%; object-fit: contain; }
  .photo-upload .placeholder-icon { font-size: 40px; margin-bottom: 6px; opacity: .5; }
  .photo-upload .placeholder-text { font-size: 13px; color: var(--text-muted); }

  /* Buttons */
  .btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    padding: 11px 18px;
    border-radius: var(--r-sm);
    font-size: 14.5px;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    border: none;
    /* Часть кнопок — это ссылки (<a class="btn">), например «Написать
       администратору в Telegram» на экране ввода ключа. Без этого они
       рисовались с подчёркиванием и читались как обычный текст, а не
       как кнопка. */
    text-decoration: none;
    transition: transform .15s ease, filter .2s ease, box-shadow .2s ease;
    color: #fff;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    -webkit-appearance: none;
    appearance: none;
  }
  .btn:active { transform: scale(.97); }
  /* Единственная кнопка с заливкой-акцентом — главное действие экрана.
     Раньше градиент стоял и на второстепенных, и глаз не понимал, куда
     нажимать в первую очередь. */
  .btn-primary { background: var(--accent); box-shadow: none; }
  .btn-primary:hover { background: #ec6355; }
  /* Успех и опасность — плоской заливкой без градиента: два градиента
     рядом на одной панели выглядели как набор наклеек. */
  .btn-success { background: #2f9e63; color: #fff; box-shadow: none; }
  .btn-success:hover { background: #35b070; }
  .btn-danger { background: rgba(226,86,75,.16); color: #ff9d92; }
  .btn-danger:hover { background: rgba(226,86,75,.26); color: #ffb3a8; }
  .btn-ghost {
    background: var(--surface-3);
    border: 1px solid var(--line);
    color: var(--text-muted);
  }
  .btn-ghost:hover { background: #26262b; color: var(--text); border-color: var(--line-strong); }
  .btn-sm { padding: 8px 14px; font-size: 13.5px; }

  /* Ingredients list in form */
  .ingredient-row {
    display: flex; gap: 6px; align-items: center;
    margin-bottom: 6px;
  }
  /* Кнопка удаления строки — приглушённая: их в форме десяток, и
     красные плашки в столбик выглядели как список ошибок. */
  .ingredient-row .btn-danger {
    flex-shrink: 0;
    width: 34px; height: 34px; padding: 0;
    display: flex; align-items: center; justify-content: center;
  }
  .ingredient-row input { flex: 1; }
  .ingredient-row button { flex-shrink: 0; }

  /* ===== ЗАКУПКА (калькулятор недельной закупки) ===== */
  .purchase-row {
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: 10px 12px;
    margin-bottom: 6px;
  }
  /* Все числа закупки — моноширинными и по правому краю. На складе
     сравнивают норму с остатком глазами, и «25» под «6» должно стоять
     колонкой, иначе расхождение приходится вычитать в уме. */
  .purchase-row input[type="number"],
  .purchase-result,
  .purchase-field > span {
    font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase;
    color: var(--text-faint); font-weight: 600;
  }
  /* Стили итога «Докупить» и названия позиции — ниже по файлу, рядом с
     остальной разметкой строки закупки. Держим их в одном месте: пока
     они были и здесь, и там, работала только нижняя половина. */
  .purchase-row-top { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
  .purchase-row-top .purchase-name { flex: 1; }
  .purchase-remove {
    flex-shrink: 0; width: 34px; height: 34px; border-radius: 9px; border: none;
    background: rgba(231,76,60,.14); color: #e2564b; font-size: 14px; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  }
  .purchase-remove:active { transform: scale(.92); }
  .purchase-row-fields {
    display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px;
    margin-top: 8px;
  }
  .purchase-field-reorder input { border-color: rgba(217,165,78,.5); }
  .purchase-reorder-group { display: flex; gap: 4px; align-items: center; }
  .purchase-field { display: flex; flex-direction: column; gap: 3px; flex: 0 0 auto; }
  .purchase-field span {
    font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase;
    color: var(--text-faint); font-weight: 600; white-space: nowrap;
  }
  .purchase-field select, .purchase-field input {
    padding: 8px 8px; font-size: 14px;
  }
  /* Поля "Норма"/"Остаток"/"Дозаказ" — короткие числа, поэтому ширина
     считается по факту введённого текста в JS (см. autosizePurchaseInput
     в app.js) через свойство ch — так поле остаётся компактным для
     "12", но само расширяется, если ввести более длинное число.
     field-sizing: content — нативный аналог для браузеров, которые его
     поддерживают, на случай если JS почему-то не отработал. */
  .purchase-field input[type="number"] {
    -moz-appearance: textfield;
    width: 4ch; min-width: 44px; max-width: 100%;
    field-sizing: content;
    /* Числа по правому краю и моноширинным: остаток и норму сравнивают
       взглядом, и разряды должны стоять друг под другом. */
    text-align: right;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
  }
  /* Стрелки-степпер у числовых полей занимают ~20px внутри поля, но
     ширина считается в JS (autosizePurchaseInput) только по длине текста
     — поэтому при вводе 2-3 цифр они упирались в стрелки и оказывались
     под ними, невидимые (см. скриншот с "4" под стрелками). Убираем
     стрелки — вводить дозаказ на телефоне всё равно удобнее просто
     цифрами, чем щёлкать по крошечным стрелкам, — и теперь вся ширина
     поля уходит на сами цифры. */
  .purchase-field input[type="number"]::-webkit-outer-spin-button,
  .purchase-field input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }
  .purchase-field select {
    width: auto; min-width: 56px;
  }
  /* Итог строки — то, ради чего её и читают, поэтому крупнее полей. */
  .purchase-result {
    padding: 8px 10px; border-radius: 10px; font-weight: 700; font-size: 14.5px;
    text-align: center; min-height: 34px; min-width: 64px; white-space: nowrap;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255,255,255,.05); color: var(--text-muted);
  }
  /* Нужное к заказу — акцентом, «хватает» — спокойным зелёным: перед
     отправкой взгляд должен цепляться за то, что надо купить. */
  .purchase-result-need { background: rgba(226,86,75,.14); color: #ff8d80; }
  .purchase-result-ok {
    color: #5cbd88;
    font-weight: 600;
    background: rgba(92,189,136,.12);
  }
  /* Кратковременная подсветка только что добавленной строки — чтобы
     после нажатия "+ Добавить ингредиент" было явно видно, что строка
     появилась (особенно когда она пустая и визуально мало отличается
     от остальных). */
  .purchase-row.is-new {
    border-color: var(--accent);
    animation: purchaseRowNew 1.6s ease;
  }
  @keyframes purchaseRowNew {
    0% { background: rgba(226,86,75,.22); }
    100% { background: var(--card); }
  }
  /* Общий список закупки (все поставщики сразу, для обхода цеха) —
     та же карточка .purchase-row, только имя/ед./норма — просто текст
     (шаблон правится в разделе поставщика выше), редактируется только
     остаток. Список сплошной, без разбивки по поставщику. */
  .purchase-row-readonly-name { font-weight: 650; font-size: 14.5px; letter-spacing: -.1px; margin-bottom: 8px; }
  .purchase-row-readonly-name .purchase-row-meta { font-weight: 500; font-size: 12px; color: var(--text-muted); margin-left: 6px; }

  /* Кнопка "ОК" в результатах поиска на главной "Закупка" (см.
     renderPurchaseHomeSearchResults / togglePurchaseHomeConfirmBtn) —
     появляется только после того, как в найденной позиции вписан
     остаток или дозаказ, и по нажатию сохраняет и сбрасывает поиск. */
  .purchase-home-confirm-btn { width: 100%; margin-top: 10px; }

  /* Videos list in form */
  .video-row { margin-bottom: 10px; }
  .video-row-inputs { display: flex; gap: 8px; align-items: center; }
  .video-row-inputs input { flex: 1; }
  .video-row-inputs button { flex-shrink: 0; }
  .video-row-hint { color: var(--text-muted); font-size: 12px; margin-top: 4px; }

  .row-move-btns { display: flex; flex-direction: column; gap: 3px; flex-shrink: 0; }
  .row-move-btns button {
    width: 28px; height: 19px; padding: 0;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255,255,255,.06); border: none; border-radius: 5px;
    color: var(--text-muted); cursor: pointer;
    touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  }
  .row-move-btns button svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
  .row-move-btns button:active:not(:disabled) { background: var(--accent); color: #fff; }
  .row-move-btns button:disabled { opacity: .3; cursor: default; }

  /* Steps list in form */
  .step-row {
    display: flex; gap: 8px; align-items: flex-start;
    margin-bottom: 8px;
  }
  .step-num {
    min-width: 28px; height: 28px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; font-weight: 700;
    margin-top: 4px;
  }

  /* ===== ДВЕ КОЛОНКИ: КАТАЛОГ И ОТКРЫТЫЙ РЕЦЕПТ =====
     На мониторе рецепт открывается рядом со списком, а не вместо него:
     видно, что рядом, и можно перебирать блюда, не возвращаясь назад.
     На узком экране всё остаётся как было — рецепт занимает экран
     целиком, потому что двум колонкам там просто нет места. */
  .tab-content.split-left { display: none; }

  @media (min-width: 1280px) {
    body.has-detail .tab-content.split-left {
      display: block;
      position: fixed;
      top: 0; bottom: 0;
      left: 252px;              /* сразу за боковой панелью с её отступом */
      width: 340px;
      overflow-y: auto;
      padding: 18px 16px;
      margin: 0;
      max-width: none;
      background: var(--surface-1);
      border-right: 1px solid var(--line);
      z-index: 80;
    }
    /* В узкой колонке карточкам тесно — показываем их строками. */
    body.has-detail .split-left .cards-grid,
    body.has-detail .split-left .cards-grid.view-grid,
    body.has-detail .split-left .cards-grid.view-list { display: block; }
    body.has-detail .split-left .recipe-card { flex-direction: row; margin-bottom: 6px; }
    body.has-detail .split-left .card-cover { width: 46px; height: 46px; margin: 6px 0 6px 8px; border-radius: 8px; }
    body.has-detail .split-left .card-cover-icon { font-size: 18px; }
    body.has-detail .split-left .card-composition { display: none; }
    body.has-detail .split-left .section-controls,
    body.has-detail .split-left .chips-line,
    body.has-detail .split-left .card-actions,
    body.has-detail .split-left .section-toolbar { display: none; }

    /* Сам рецепт — в оставшейся части экрана. */
    body.has-detail #tab-detail { margin-left: 356px; }
    /* Шапка и меню при открытом рецепте не нужны — экран отдаётся делу. */
    body.has-detail .header { display: none; }
  }

  /* ===== DETAIL VIEW ===== */
  .detail-back { margin-bottom: 16px; }
  /* Название рецепта — самый крупный текст на экране, ингредиенты и шаги
     набраны с увеличенным межстрочным: их читают, стоя у плиты. */
  #detail-body h2 { font-size: 25px; line-height: 1.2; margin-bottom: 6px; letter-spacing: -.4px; }
  /* Заголовки блоков рецепта — мелкой капителью: они разделяют, но не
     спорят с названием и не съедают место, нужное самому рецепту. */
  #detail-body .section-title {
    font-size: 11.5px; letter-spacing: .1em; text-transform: uppercase;
    color: var(--text-faint); font-weight: 700;
    margin: 22px 0 10px;
  }

  /* Ингредиенты и шаги читают стоя у плиты, часто с метра — поэтому
     крупнее основного интерфейса и с большим межстрочным. */
  #detail-body .ingr-name, #detail-body .steps-list li { font-size: 15.5px; line-height: 1.55; }

  /* Стили самих шагов — ниже по файлу, в разделе steps-list. */

  /* Характеристики рецепта — строками с разделителями, значение справа.
     Раньше это была россыпь бейджей, и найти в ней нужную цифру (вес,
     время) удавалось не с первого взгляда. */
  .detail-meta {
    display: flex; flex-direction: column; gap: 0;
    margin: 14px 0 18px;
  }
  /* Характеристика — строкой: название слева, значение справа. Прежние
     «таблетки» глаз не сканировал: нужную цифру приходилось искать. */
  .detail-badge {
    display: flex; align-items: center; gap: 10px;
    padding: 11px 2px;
    border-top: 1px solid var(--line);
    background: none;
    font-size: 14.5px;
    color: var(--text);
    font-variant-numeric: tabular-nums;
  }
  .detail-badge:last-child { border-bottom: 1px solid var(--line); }

  /* Плавающая компактная кнопка "Назад" — дублирует обычную "← Назад"
     наверху рецепта, но остаётся доступной при прокрутке вниз. Не
     занимает место в потоке (position:fixed), маленькая и полупрозрачная,
     чтобы не перекрывать контент; появляется плавно через opacity/transform,
     а не display:none/block, чтобы переход был мягким. pointer-events
     выключены, пока кнопка скрыта, — иначе invisible-кнопка мешала бы
     кликам по контенту под ней. */
  .detail-back-floating {
    position: fixed;
    left: 14px;
    bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    z-index: 80;
    width: 42px; height: 42px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    background: var(--surface-3);
    border: 1px solid var(--line-strong);
    color: var(--text);
    box-shadow: 0 8px 24px rgba(0,0,0,.5);
    cursor: pointer;
    opacity: 0;
    transform: translateY(10px) scale(.9);
    pointer-events: none;
    transition: opacity .2s ease, transform .2s ease;
  }
  .detail-back-floating svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
  .detail-back-floating.show {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
  }
  .detail-back-floating:active { transform: scale(.92); }

  .detail-photo {
    width: 100%; max-height: 320px; object-fit: cover;
    border-radius: var(--radius-lg); margin-bottom: 18px;
    box-shadow: 0 12px 32px rgba(0,0,0,.22);
    border: 1px solid rgba(255,255,255,.08);
  }
  .video-embed-wrap {
    position: relative;
    border-radius: 14px; overflow: hidden; margin: 0 auto 20px;
    background: #000;
  }
  /* Горизонтальное видео (обычный YouTube, Vimeo, Facebook, Google Диск) */
  .video-embed-wrap.ratio-16-9 { width: 100%; aspect-ratio: 16 / 9; }
  /* Вертикальное видео (Shorts, TikTok, Reels) — ограничиваем по высоте,
     чтобы видео целиком помещалось на экране без прокрутки вверх/вниз */
  .video-embed-wrap.ratio-9-16 { aspect-ratio: 9 / 16; max-height: 68vh; width: auto; max-width: 100%; }
  /* Обычный пост Instagram (часто квадратный/почти квадратный) */
  .video-embed-wrap.ratio-4-5 { aspect-ratio: 4 / 5; max-height: 68vh; width: auto; max-width: 100%; }
  .video-embed-wrap iframe,
  .video-embed-wrap video {
    position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;
  }
  .video-fallback-link {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 12px 18px; margin-bottom: 20px;
    background: var(--card); border: 1px solid var(--glass-border); border-radius: 12px;
    color: var(--accent); text-decoration: none; font-size: 14px;
  }
  .qr-share-card {
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: 20px; margin-top: 24px;
    text-align: center;
  }
  .qr-share-card h3 { font-size: 16px; margin-bottom: 4px; }
  .qr-share-card p { color: var(--text-muted); font-size: 13px; margin-bottom: 14px; }
  .qr-canvas-holder {
    display: inline-block; border-radius: 10px; background: #fff; padding: 10px;
    min-width: 180px; min-height: 180px;
    box-shadow: 0 4px 16px rgba(0,0,0,.14);
  }
  .qr-canvas-holder img, .qr-canvas-holder canvas { display: block; }
  .qr-share-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 14px; }
  .btn-telegram {
    background: #29a9eb; color: #fff;
    display: inline-flex; align-items: center; gap: 8px;
  }
  .btn-telegram svg { width: 16px; height: 16px; fill: #fff; }
  .detail-title { font-family: var(--font-display); font-style: italic; font-weight: 700; font-size: 29px; margin-bottom: 10px; }
  /* Вид характеристик задан выше, в блоке .detail-meta / .detail-badge:
     строками с разделителями, значение справа. Прежний вариант с
     «таблетками» убран — он стоял ниже и перекрывал новый. */

  .section-title {
    font-weight: 700;
    font-size: 11.5px; letter-spacing: .1em; text-transform: uppercase;
    color: var(--text-faint);
    margin: 24px 0 10px;
    padding-bottom: 0; display: flex; align-items: center; gap: 8px;
    border-bottom: none;
  }
  .section-title::before {
    content: none;
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--accent-grad);
    flex-shrink: 0;
    box-shadow: 0 0 0 4px rgba(226,86,75,.14);
  }

  .ingredients-list li,
  /* Отступы и размер шагов заданы ниже, в блоке .steps-list li. */

  /* ===== ИНГРЕДИЕНТЫ-КАЛЬКУЛЯТОР ===== */
  /* Вид подсказки задан ниже: одна строка с многоточием, раскрывается
     нажатием. */
  .ingr-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; padding: 10px 10px;
    border-bottom: 1px solid rgba(255,255,255,.08);
    border-radius: 10px;
    font-size: 15px;
    transition: background .15s ease;
  }
  .ingr-row:hover { background: rgba(255,255,255,.03); }
  .ingr-row:last-of-type { border-bottom: none; }
  .ingr-row-plain {
    color: var(--text-muted); font-size: 13.5px;
    padding: 6px 0; border-bottom: none;
  }
  /* Имени отдаём всю оставшуюся ширину. word-break: break-word рвал
     слова посреди буквы («Моцаре / лла») — переносим по словам, а рвём
     только то, что не влезает целиком. */
  .ingr-name { flex: 1; min-width: 0; overflow-wrap: break-word; word-break: normal; }
  .ingr-suffix { color: var(--text-muted); font-size: 12.5px; }
  .ingr-controls { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
  .ingr-row .ingr-input {
    width: 64px; flex-shrink: 0;
    padding: 7px 8px; border-radius: 9px;
    border: 1px solid var(--line-strong); background: var(--surface-1);
    color: var(--text); font-size: 14.5px; text-align: right;
    font-family: var(--font-mono); font-variant-numeric: tabular-nums;
    -moz-appearance: textfield;
  }
  .ingr-row .ingr-input:focus { outline: none; border-color: var(--accent); }
  /* Тот же селектор, что и у остальных правил поля — чтобы при правке
     не искать его в двух написаниях. */
  .ingr-row .ingr-input::-webkit-outer-spin-button,
  .ingr-row .ingr-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
  .ingr-unit-static {
    font-size: 13.5px; color: var(--text-muted);
    min-width: 26px; flex-shrink: 0;
  }
  /* Единица измерения — это <select>, а общее правило форм растягивает
     любой select на всю ширину. Из-за этого колонка «г» занимала
     пол-строки пустотой, а названию не оставалось места и оно рвалось
     переносом. Ширину задаём по содержимому. */
  .ingr-row .ingr-unit {
    width: auto; min-width: 56px; max-width: 74px; flex-shrink: 0;
    background: var(--surface-1); color: var(--text);
    border: 1px solid var(--line-strong); border-radius: 8px;
    padding: 7px 6px;
    font-size: 13.5px;
  }
  .ingr-calc-reset { margin-top: 14px; }
  .steps-list { padding-left: 0; counter-reset: step; }
  .steps-list li {
    counter-increment: step;
    cursor: pointer; -webkit-user-select: none; user-select: none;
    touch-action: manipulation; -webkit-tap-highlight-color: transparent;
    transition: opacity .2s ease;
    position: relative;
    padding: 10px 0 10px 40px;
    margin-bottom: 0;
    /* Разделитель вместо отступа: по нему видно границу шага, и список
       не расползается на два экрана. */
    border-bottom: 1px solid var(--line);
    font-size: 15.5px; line-height: 1.55;
    min-height: 28px;
  }
  .steps-list li:last-child { border-bottom: none; }
  .steps-list li::before {
    content: counter(step);
    position: absolute; left: 0; top: 9px;
    width: 26px; height: 26px;
    border-radius: 50%;
    /* Номер — приглушённой заливкой вместо градиента с тенью: двадцать
       ярких кружков подряд перетягивали внимание с самого текста. */
    background: var(--accent-soft);
    color: #ff8d80;
    font-family: var(--font-mono);
    display: flex; align-items: center; justify-content: center;
    color: #fff;
    font-family: var(--font-body);
    font-size: 13px; font-weight: 700;
  }
  /* Соединительная линия между шагами убрана: с разделителями строк она
     стала лишним украшением и мешала при коротких шагах в одну строку. */
  /* Поведение шага (нажатие, выделение) задано в основном блоке выше. */
  .steps-list li.step-done { opacity: .5; text-decoration: line-through; text-decoration-color: var(--text-muted); }
  .steps-list li.step-done::before { content: '✓'; background: rgba(92,189,136,.18); color: #5cbd88; box-shadow: none; }

  /* ===== SKELETON LOADING ===== */
  .skeleton {
    background: linear-gradient(100deg, rgba(255,255,255,.06) 30%, rgba(255,255,255,.13) 50%, rgba(255,255,255,.06) 70%);
    background-size: 200% 100%;
    animation: shimmer 1.4s ease-in-out infinite;
    border-radius: 10px;
  }
  @keyframes shimmer { from { background-position: 150% 0; } to { background-position: -50% 0; } }
  .skeleton-photo { width: 100%; height: 240px; border-radius: var(--radius-lg); margin-bottom: 18px; }
  .skeleton-title { width: 65%; height: 30px; margin-bottom: 14px; border-radius: 8px; }
  .skeleton-badges { display: flex; gap: 8px; background: none; animation: none; margin-bottom: 22px; }
  .skeleton-chip { width: 90px; height: 26px; border-radius: 20px; }
  .skeleton-line { height: 15px; margin-bottom: 10px; width: 100%; }

  /* ===== EMPTY STATE ===== */
  .empty-state {
    text-align: center;
    padding: 60px 20px;
    color: var(--text-muted);
  }
  .empty-icon { font-size: 60px; margin-bottom: 14px; opacity: .55; filter: drop-shadow(0 6px 14px rgba(0,0,0,.2)); }
  .empty-state h3 { font-family: var(--font-display); font-style: italic; }

  /* ===== TOAST ===== */
  .toast {
    position: fixed; bottom: 30px; left: 50%;
    transform: translateX(-50%) translateY(8px);
    padding: 14px 28px;
    background: var(--surface-3);
    border: 1px solid var(--line-strong);
    color: var(--text);
    border-radius: var(--r-sm);
    font-size: 14px;
    box-shadow: 0 12px 32px rgba(0,0,0,.5);
    opacity: 0; transition: opacity .3s ease, transform .3s ease;
    z-index: 999;
    pointer-events: none;
  }
  .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

  /* ===== MODAL (замена prompt()/confirm() — на iOS в режиме "На экран Домой" они не работают) ===== */
  .photo-lightbox {
    position: fixed; inset: 0;
    background: rgba(0,0,0,.92);
    display: none;
    align-items: center; justify-content: center;
    z-index: 2100;
    padding: 20px;
    padding-top: calc(20px + env(safe-area-inset-top, 0px));
    padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  }
  .photo-lightbox.show { display: flex; }
  .photo-lightbox img {
    max-width: 100%; max-height: 100%;
    border-radius: 8px;
    object-fit: contain;
  }

  .modal-overlay {
    position: fixed; inset: 0;
    background: rgba(0,0,0,.6);
    display: none;
    align-items: center; justify-content: center;
    z-index: 100000;
    padding: 20px;
    padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  }
  .modal-overlay.show { display: flex; }
  .modal-box {
    display: flex; flex-direction: column; max-height: 88vh;
    background: var(--surface-2);
    border: 1px solid var(--line-strong);
    border-radius: var(--r-lg);
    padding: 22px;
    max-width: 420px;
    width: 100%;
    box-shadow: 0 24px 60px rgba(0,0,0,.55);
    animation: modalIn .2s ease;
  }
  @keyframes modalIn { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: translateY(0) scale(1); } }

  /* Раньше использовалось для плашки-предупреждения на шаге "вступите
     в группу" в многошаговом сценарии отправки заявки — сейчас этот
     шаг убран (см. requireTelegramSend), класс оставлен неиспользуемым
     на случай, если понадобится похожая плашка где-то ещё. */
  @keyframes pulseWarn {
    0%, 100% { box-shadow: 0 0 0 0 rgba(217, 165, 78, .38); }
    50% { box-shadow: 0 0 0 7px rgba(217, 165, 78, 0); }
  }
  .modal-box h3 { margin-bottom: 8px; font-size: 18px; }
  .modal-box p { color: var(--text-muted); font-size: 14px; margin-bottom: 14px; white-space: pre-line; line-height: 1.5; }
  .modal-box input {
    width: 100%; padding: 12px 14px; font-size: 16px;
    background: var(--surface); border: 1px solid rgba(255,255,255,.14);
    border-radius: 10px; color: var(--text); margin-bottom: 4px;
  }
  .modal-box select {
    width: 100%; padding: 12px 14px; font-size: 16px;
    background: var(--surface); border: 1px solid rgba(255,255,255,.14);
    border-radius: 10px; color: var(--text); margin-bottom: 4px;
  }
  /* Заголовок и текст окна. Раньше и то и другое было одного размера и
     цвета, из-за чего окно читалось как сплошной абзац. */
  #app-modal-title { font-size: 17px; font-weight: 700; letter-spacing: -.2px; margin-bottom: 6px; }
  #app-modal-message {
    font-size: 13.5px; line-height: 1.55; color: var(--text-muted);
    white-space: pre-line; /* переносы строк в сообщениях остаются переносами */
    margin-bottom: 2px;
  }
  /* Длинные объяснения (про бота, про отзыв ключа) прокручиваются
     внутри окна, а кнопки остаются на виду. */
  /* Раскладка окна задана в основном блоке .modal-box. */
  #app-modal-message, .modal-fields, #app-modal-checklist { overflow-y: auto; }

  .modal-actions {
    display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap;
    margin-top: 18px;
  }
  /* Подтверждение — справа и акцентом, отмена — рядом и приглушённой.
     На телефоне кнопки растягиваются на всю ширину: попасть пальцем в
     узкую кнопку у края экрана неудобно. */
  @media (max-width: 480px) {
    .modal-actions { flex-direction: column-reverse; }
    .modal-actions .btn { width: 100%; }
  }

  /* Подвал модалки (footerHtml в showModal) — второстепенное действие
     под основными кнопками, отделённое линией. Сюда попадает, например,
     кнопка «Написать администратору» на экране ввода ключа: она нужна,
     но не должна оказаться выше поля ввода и перебивать кнопку «Войти». */
  .modal-footer-slot {
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--glass-border);
  }

  /* Список галочек в модалке (showModal({withChecklist})) — используется
     окном «🎭 Роли» участника, где несколько ролей отмечаются и
     сохраняются за один раз. Ограничиваем по высоте со скроллом: ролей
     столько же, сколько дополнительных вкладок на сайте, и их может
     стать много — окно не должно выйти за пределы экрана телефона. */
  .modal-checklist {
    max-height: 46vh; overflow-y: auto;
    margin: 4px 0 2px;
    display: flex; flex-direction: column; gap: 6px;
  }
  .modal-check-row {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 10px 12px;
    background: var(--surface);
    border: 1.5px solid var(--glass-border);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
  }
  .modal-check-row:hover { border-color: rgba(255,255,255,.25); }
  .modal-check-row input[type="checkbox"] {
    width: 18px; height: 18px; margin: 1px 0 0;
    accent-color: var(--accent);
    flex-shrink: 0; cursor: pointer;
  }
  .modal-check-row.is-disabled { opacity: .55; cursor: not-allowed; }
  .modal-check-row.is-disabled input[type="checkbox"] { cursor: not-allowed; }
  .modal-check-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .modal-check-label { font-size: 14px; font-weight: 600; color: var(--text); line-height: 1.3; }
  .modal-check-hint { font-size: 12px; color: var(--text-muted); line-height: 1.35; }

  /* Бейдж выданной роли в списке участников. Для ролей дополнительных
     вкладок подпись берётся из самой вкладки, поэтому переименование
     вкладки сразу отражается и здесь. */
  .role-badge {
    display: inline-block;
    font-size: 11px; font-weight: 600; line-height: 1.4;
    padding: 2px 9px; margin: 4px 4px 0 0;
    border-radius: 20px;
    background: rgba(255,255,255,.08);
    color: var(--text);
    border: 1px solid var(--glass-border);
  }

  /* Модалка пошаговой рассылки отчётов поставщикам (см. "📤 Отправить
     всё" в "Закупке") — текст сообщения только для чтения (его копируют
     в буфер и вставляют в открывшемся чате), поэтому оформлен как
     textarea, а не input. */
  .send-purchase-text {
    width: 100%; min-height: 160px; resize: vertical;
    padding: 12px 14px; font-size: 13px; line-height: 1.5;
    background: var(--surface); border: 1px solid rgba(255,255,255,.14);
    border-radius: 10px; color: var(--text); font-family: inherit;
    margin-bottom: 4px;
  }

  /* Переключатель заведений под заголовком шапки. */
  .venue-switch {
    display: inline-flex; align-items: center; gap: 7px;
    margin: 10px auto 0;
    padding: 7px 14px;
    border-radius: 999px;
    background: rgba(12,12,14,.66);
    border: 1px solid rgba(255,255,255,.18);
    color: #fff;
    font-family: inherit;
    font-size: 13.5px; font-weight: 600;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background .2s ease, transform .15s ease;
  }
  .venue-switch:hover { background: rgba(15,15,20,.62); }
  .venue-switch:active { transform: scale(.96); }
  .venue-switch .icon { font-size: 15px; line-height: 1; }
  .venue-switch-caret { opacity: .7; font-size: 11px; }

  /* Заведение, открытое прямо сейчас, в списке админ-панели. */
  .venue-current { border-color: rgba(226,86,75,.45) !important; }

  /* Вкладка, спрятанная по данным (а не по роли): у выбранного
     заведения может не быть закупки. Правила роли выше объявлены с
     !important, поэтому перекрываем их более точным селектором. */
  body .nav-tab.tab-hidden,
  body.is-admin .nav-tab.purchase-access-only.tab-hidden,
  body.is-purchase-role .nav-tab.purchase-access-only.tab-hidden { display: none !important; }

  /* ===== ВЫБОР ЗНАЧКА (эмодзи) =====
     Список открывается в обычном модальном окне: сверху рекомендованные
     под конкретный случай, ниже общий набор группами. */
  .modal-emoji {
    max-height: 46vh;
    overflow-y: auto;
    margin: 12px 0 4px;
    padding-right: 4px;
    -webkit-overflow-scrolling: touch;
  }
  .emoji-group-title {
    margin: 10px 0 6px;
    font-size: 11.5px; font-weight: 700;
    letter-spacing: .04em; text-transform: uppercase;
    color: var(--text-muted);
  }
  .emoji-group-title:first-child { margin-top: 0; }
  .emoji-grid {
    display: grid;
    /* Кнопки крупные: значок выбирают пальцем с телефона, мелкая сетка
       приводила бы к промахам. */
    grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
    gap: 6px;
  }
  .emoji-btn {
    aspect-ratio: 1;
    display: flex; align-items: center; justify-content: center;
    border-radius: 12px;
    border: 1px solid var(--glass-border);
    background: rgba(255,255,255,.05);
    font-size: 22px; line-height: 1;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background .15s ease, transform .1s ease, border-color .15s ease;
  }
  .emoji-btn:hover { background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.25); }
  .emoji-btn:active { transform: scale(.9); }
  /* Значок, стоящий сейчас, — чтобы при переименовании было видно, что
     менять не обязательно. */
  .emoji-btn.is-current { border-color: var(--accent); background: rgba(226,86,75,.2); }

  /* ===== КОМПАКТНАЯ ПАНЕЛЬ ИНСТРУМЕНТОВ =====
     Раньше такие ряды собирались из полноразмерных кнопок: шесть штук
     с длинными подписями занимали на телефоне почти целый экран и
     читались как список ссылок. Теперь это одна панель со значками —
     подписи прячутся на узких экранах, название остаётся в title. */
  .tool-bar {
    display: flex; flex-wrap: wrap; gap: 6px;
    padding: 5px;
    border-radius: var(--r-md);
    background: var(--surface-1);
    border: 1px solid var(--line);
  }
  .tool-btn {
    position: relative;
    display: inline-flex; align-items: center; gap: 6px;
    padding: 8px 12px;
    border-radius: 10px;
    border: 1px solid transparent;
    background: rgba(255,255,255,.05);
    color: var(--text-muted);
    font-family: inherit;
    font-size: 12.5px; font-weight: 600;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    -webkit-appearance: none; appearance: none;
    transition: background .15s ease, color .15s ease, transform .1s ease;
  }
  .tool-btn:hover { background: rgba(255,255,255,.12); color: var(--text); }
  .tool-btn:active { transform: scale(.94); }
  .tool-btn:disabled { opacity: .5; cursor: default; }
  .tool-btn-icon { font-size: 15px; line-height: 1; }
  .tool-btn-primary { background: var(--accent); color: #fff; box-shadow: none; }
  .tool-btn-primary:hover { background: #ec6355; color: #fff; }
  .tool-btn-danger { background: rgba(231,76,60,.16); color: #ff9b90; }
  .tool-btn-danger:hover { background: rgba(231,76,60,.28); color: #ffb3aa; }

  /* Признак «уже заполнено» (ссылка для отправки, контакты). Точкой, а
     не словом: она видна и когда подпись скрыта. */
  .tool-btn.is-done::after {
    content: '';
    position: absolute; top: 5px; right: 5px;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--success, #2ecc71);
  }

  /* Ширина панели раздела задана выше, вместе с отступом между кнопками. */

  /* Неактивная кнопка подтверждения — пока обязательное поле пустое.
     Видно, что нажимать пока нечего, и понятно, что дело в поле. */
  .modal-actions .btn.is-disabled {
    opacity: .45;
    cursor: not-allowed;
    filter: grayscale(.4);
  }
  .modal-actions .btn.is-disabled:active { transform: none; }

  /* Метка заведения у ключа — чтобы не путать её с ролями. */
  .venue-badge { background: rgba(255,255,255,.1); border-color: rgba(255,255,255,.2); }

  /* Кнопка удаления фото — поверх снимка в форме рецепта. */
  .photo-remove-btn {
    position: absolute; top: 8px; right: 8px;
    width: 34px; height: 34px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    border: 1px solid rgba(255,255,255,.2);
    /* Тёмная подложка: фото бывает любым, и без неё значок терялся бы
       на светлых снимках. */
    background: rgba(20,20,24,.72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    color: #fff;
    font-size: 15px;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background .15s ease, transform .1s ease;
  }
  .photo-remove-btn:hover { background: rgba(231,76,60,.85); }
  .photo-remove-btn:active { transform: scale(.9); }

  /* Группа карточек в админ-панели. Их четырнадцать, и подряд они
     читались как стена; редкие настройки убраны под заголовок. */
  .admin-group { margin-bottom: 12px; }
  .admin-group-head {
    display: flex; align-items: center; gap: 10px;
    width: 100%;
    padding: 13px 16px;
    border-radius: var(--r-md);
    background: var(--surface-2);
    border: 1px solid var(--line);
    color: var(--text);
    font-family: inherit; font-size: 15px; font-weight: 700;
    letter-spacing: -.1px;
    text-align: left;
    cursor: pointer;
    transition: background .15s ease;
  }
  .admin-group-head:hover { background: var(--surface-3); }
  .admin-group-caret { margin-left: auto; color: var(--text-faint); font-size: 12px; transition: transform .18s ease; }
  .admin-group.open .admin-group-caret { transform: rotate(180deg); }
  .admin-group-body { display: none; padding-top: 10px; }
  .admin-group.open .admin-group-body { display: block; }

  /* Сворачиваемая карточка админ-панели. */
  .admin-panel-toggle {
    display: flex; align-items: center; gap: 10px;
    width: 100%;
    background: none; border: none; padding: 0;
    color: var(--text);
    font-family: inherit; font-size: 15px; font-weight: 700;
    letter-spacing: -.1px;
    cursor: pointer;
    text-align: left;
  }
  .admin-panel-toggle-count {
    margin-left: auto;
    padding: 2px 8px;
    border-radius: 999px;
    background: rgba(255,255,255,.08);
    color: var(--text-muted);
    font-size: 12px; font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .admin-panel-toggle-caret { color: var(--text-faint); font-size: 12px; transition: transform .18s ease; }
  .admin-panel-card.open .admin-panel-toggle-caret { transform: rotate(180deg); }
  .admin-panel-collapsible { display: none; }
  .admin-panel-card.open .admin-panel-collapsible { display: block; }

  /* Значки действий проявляются при наведении, а на телефоне видны
     всегда: на компьютере ряд кнопок у каждой из пятнадцати строк
     создавал визуальный шум, а пальцем по невидимому не попасть. */
  @media (hover: hover) {
    .purchase-home-card .purchase-home-icon-btn { opacity: 0; transition: opacity .15s ease; }
    .purchase-home-card:hover .purchase-home-icon-btn { opacity: 1; }
  }
  .purchase-home-icon-btn:hover {
    background: rgba(255,255,255,.1);
    color: var(--text);
  }

  /* Подсказка-инструкция над списком позиций: нужна первые пару раз,
     дальше только занимает верх экрана. Свёрнута до одной строки,
     раскрывается нажатием. */
  .ingr-calc-hint {
    font-size: 12.5px; line-height: 1.5;
    color: var(--text-faint);
    background: var(--surface-1);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: 9px 12px;
    cursor: pointer;
    /* Одна строка целиком с многоточием. Через -webkit-line-clamp вторая
       строка обрезалась посреди буквы и выглядела как сбой вёрстки. */
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .ingr-calc-hint.expanded { white-space: normal; overflow: visible; }
  /* Плавающая кнопка «назад» стоит поверх содержимого, поэтому снизу
     нужен запас — иначе она накрывала последние строки шагов. */
  #tab-detail { padding-bottom: 76px; }

  /* Телефон в заявке и в списке участников: сразу ссылка для звонка —
     администратору чаще всего нужно именно позвонить, а не прочитать. */
  .request-phone {
    display: inline-block;
    margin: 3px 0;
    font-size: 13px;
    color: var(--accent);
    text-decoration: none;
    font-variant-numeric: tabular-nums;
  }
  .request-phone:hover { text-decoration: underline; }

  /* ===== ЖУРНАЛ ДЕЙСТВИЙ ===== */
  .activity-item {
    padding: 10px 12px;
    margin-bottom: 8px;
    border-radius: var(--r-sm);
    background: var(--surface-1);
    border: 1px solid var(--line);
  }
  .activity-head {
    display: flex; align-items: baseline; gap: 8px;
    margin-bottom: 3px;
  }
  .activity-head strong { font-size: 13.5px; }
  /* Время справа и приглушённое: в журнале сначала читают «кто и что»,
     а время нужно уже вторым шагом. */
  .activity-time { margin-left: auto; flex-shrink: 0; font-size: 11.5px; color: var(--text-muted); }
  .activity-what { font-size: 13px; color: var(--text); }
  .activity-where { font-size: 12px; color: var(--text-muted); font-weight: 600; }
  .activity-details {
    margin-top: 4px;
    font-size: 12px; line-height: 1.5;
    color: var(--text-muted);
    word-break: break-word;
  }
  .activity-modal-list { max-height: 48vh; overflow-y: auto; margin-top: 10px; text-align: left; }

  /* ===== ОТМЕТКА «КТО И КОГДА ИЗМЕНИЛ» ===== */
  .edit-stamp {
    margin: -6px 0 16px;
    font-size: 12.5px;
    color: var(--text-muted);
  }
  .edit-stamp-inline {
    display: block;
    margin-top: 3px;
    font-size: 11.5px;
    color: var(--text-muted);
  }

  /* ===== ПРАВА =====
     Сводка набора роли и пометка о личных настройках участника. */
  .perm-summary { font-size: 12.5px; line-height: 1.55; color: var(--text-muted); }
  .perm-summary-line { margin-bottom: 6px; }
  .perm-summary-line strong { color: var(--text); }
  .perm-badge { background: rgba(226,86,75,.18); border-color: rgba(226,86,75,.35); }

  /* ===== УЧАСТНИКИ ПО ЗАВЕДЕНИЯМ =====
     Список собирается сворачиваемыми группами — по одной на точку. */
  .participants-group { margin-bottom: 10px; }
  .participants-group-head {
    display: flex; align-items: center; gap: 9px;
    width: 100%;
    padding: 11px 13px;
    border-radius: var(--r-sm);
    background: var(--surface-3);
    border: 1px solid var(--line);
    color: var(--text);
    font-family: inherit; font-size: 14px; font-weight: 700;
    text-align: left;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background .15s ease;
  }
  .participants-group-head:hover { background: rgba(255,255,255,.09); }
  .participants-group-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  /* Счётчик виден и у свёрнутой группы: сколько там людей, полезно
     знать, не разворачивая. */
  .participants-group-count {
    flex-shrink: 0;
    min-width: 24px; padding: 2px 8px;
    border-radius: 10px;
    background: rgba(255,255,255,.1);
    color: var(--text-muted);
    font-size: 12px; font-weight: 700; text-align: center;
  }
  .participants-group-caret {
    flex-shrink: 0;
    color: var(--text-muted); font-size: 12px;
    transition: transform .18s ease;
  }
  .participants-group.is-open .participants-group-caret { transform: rotate(90deg); }
  .participants-group-body { padding-top: 8px; }

  /* ===== НЕСКОЛЬКО ПОЛЕЙ В ОДНОМ ОКНЕ =====
     Так заполняют контакты поставщика: все поля сразу, а не по одному
     окну на каждое. */
  .modal-fields { display: flex; flex-direction: column; gap: 12px; margin: 14px 0 4px; }
  .modal-field { display: block; }
  .modal-field-label {
    display: block; margin-bottom: 5px;
    font-size: 12.5px; font-weight: 600;
    color: var(--text-muted);
  }
  .modal-field input { margin-bottom: 0; }
  .modal-field-hint { display: block; margin-top: 4px; font-size: 11.5px; color: var(--text-muted); }

  /* ===== КОНТАКТЫ ПОСТАВЩИКА =====
     Телефон, почта и сайт. Показываются только заполненные — сам блок
     не рисуется, если не заполнено ничего. */
  .purchase-contacts {
    display: flex; flex-wrap: wrap; gap: 6px;
    margin-top: 6px;
  }
  .purchase-contact {
    display: inline-flex; align-items: center; gap: 5px;
    font-family: inherit;
    cursor: pointer;
    -webkit-appearance: none; appearance: none;
    -webkit-tap-highlight-color: transparent;
    max-width: 100%;
    padding: 4px 10px;
    border-radius: 14px;
    background: rgba(255,255,255,.06);
    border: 1px solid var(--glass-border);
    color: var(--text-muted);
    font-size: 12px; font-weight: 600;
    text-decoration: none;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    transition: background .15s ease, color .15s ease;
  }
  .purchase-contact:hover { background: rgba(255,255,255,.12); color: var(--text); }
  .purchase-contact-icon { font-size: 12.5px; }

  /* На экране открытого поставщика контакты крупнее: там их читают и
     нажимают, а не просматривают мельком в списке. */
  .purchase-contacts-detail { margin: -6px 0 14px; }
  .purchase-contacts-detail .purchase-contact { font-size: 13px; padding: 7px 13px; }

  /* ===== АКТУАЛЬНОСТЬ РЕЦЕПТА =====
     Статус («Актуально» / «Убрано с меню» / «Не актуально (сезонное)»)
     виден только админу и разработчику: сотруднику неактуальные
     карточки вообще не показываются. */

  /* Строка фильтра по актуальности. Слегка отделена от чипов категорий
     сверху, чтобы две строки отбора не читались как одна длинная. */
  /* Фильтр актуальности собран в один блок с разделителями — так видно,
     что это один переключатель на четыре положения, а не четыре
     самостоятельные кнопки рядом с чипами категорий. */
  .status-filter-row {
    margin: 8px 0 2px;
    display: inline-flex; gap: 0;
    padding: 3px;
    border-radius: 12px;
    background: rgba(255,255,255,.05);
    border: 1px solid var(--glass-border);
    max-width: 100%;
  }
  .status-chip {
    font-size: 12.5px; padding: 7px 12px;
    border-radius: 9px;
    background: transparent; border-color: transparent;
    box-shadow: none;
  }
  .status-chip.active { background: var(--accent-grad); box-shadow: none; }

  /* ===== ПЛАВНОСТЬ =====
     Появление и переходы делаем короткими: интерфейсом пользуются на
     кухне, между делом, и медленная анимация там раздражает. */
  /* Длительность появления карточки задана в самом блоке .recipe-card. */
  .tab-content.active {
    display: block;
    animation: fadeIn .2s ease;
  }
  .chip, .status-opt, .tool-btn, .nav-tab { transition-duration: .15s; }
  /* Уважаем системную настройку «меньше движения». */
  @media (prefers-reduced-motion: reduce) {
    .recipe-card, .tab-content.active { animation: none; }
    * { transition-duration: .01ms !important; }
  }

  /* Приглушённая карточка: рецепт на месте, но в работе его сейчас нет.
     Пунктирная рамка слева отличает такие карточки от актуальных даже
     при беглом взгляде на список. */
  .recipe-card-inactive { opacity: .62; border-left: 3px dashed rgba(255,255,255,.28); }
  .recipe-card-inactive:hover { opacity: .85; }
  .recipe-card-inactive .card-thumb { filter: grayscale(.65); }

  /* Цвета статусов заданы в блоке .badge-status выше. */
  .detail-badge-status {
    background: rgba(255,255,255,.1);
    color: var(--accent);
    font-weight: 700;
  }

  /* Переключатель статуса. Кнопки узкие и в один ряд — их жмут с
     телефона прямо в списке, не открывая рецепт. */
  .card-status-switch {
    display: flex; gap: 4px; flex-wrap: wrap;
    margin-top: 7px;
  }
  /* В списке у переключателя остаются одни значки: три подписи на каждой
     из сорока карточек превращали список в стену кнопок. Полные названия
     видны в самой карточке рецепта и во всплывающей подсказке. */
  .recipe-card .status-opt-label { display: none; }
  .recipe-card .status-opt { padding: 5px 9px; font-size: 13px; }
  .status-opt {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 5px 10px;
    border-radius: 16px;
    border: 1px solid var(--glass-border);
    background: rgba(255,255,255,.05);
    color: var(--text-muted);
    font-size: 11.5px; font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background .15s ease, color .15s ease, border-color .15s ease, transform .1s ease;
  }
  .status-opt:hover { color: var(--text); border-color: rgba(255,255,255,.22); }
  .status-opt:active { transform: scale(.95); }
  .status-opt.is-current {
    background: var(--accent); color: #fff;
    border-color: transparent; box-shadow: none;
  }

  /* Тот же переключатель внутри открытого рецепта */
  .detail-status {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    margin: -10px 0 22px;
  }
  .detail-status-title { font-size: 13px; color: var(--text-muted); font-weight: 600; }
  .detail-status .card-status-switch { margin-top: 0; }
  .detail-status .status-opt { font-size: 12.5px; padding: 7px 13px; }

  /* ===== НИЖНЯЯ ПАНЕЛЬ (только телефон) ===== */
  .mobile-bar { display: none; }
  @media (max-width: 640px) {
    .mobile-bar {
      display: flex;
      position: fixed; left: 0; right: 0; bottom: 0;
      z-index: 95;
      background: var(--surface-1);
      border-top: 1px solid var(--line);
      padding: 6px 6px calc(6px + env(safe-area-inset-bottom, 0px));
    }
    .mobile-bar-btn {
      flex: 1;
      min-width: 0; /* иначе четвёртая кнопка выдавливает панель за экран */
      display: flex; flex-direction: column; align-items: center; gap: 3px;
      padding: 7px 2px;
      border: none; background: transparent;
      color: var(--text-faint);
      font-family: inherit; font-size: 10.5px; font-weight: 600;
      white-space: nowrap;
      cursor: pointer;
      border-radius: var(--r-sm);
      -webkit-tap-highlight-color: transparent;
      touch-action: manipulation;
      transition: color .15s ease, background .15s ease;
    }
    .mobile-bar-btn svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
    .mobile-bar-btn.active { color: var(--accent); background: var(--accent-soft); }
    .mobile-bar-btn:active { transform: scale(.95); }
    /* Место под панель, иначе она перекрывала последний рецепт. */
    body { padding-bottom: 68px; }
    /* Плавающая кнопка «назад» поднимается над панелью. */
    .detail-back-floating { bottom: calc(78px + env(safe-area-inset-bottom, 0px)); }
  }

  /* ===== СПИСОК НА ТЕЛЕФОНЕ =====
     Обложка во всю ширину хороша на мониторе, но на телефоне из-за неё
     в экран помещалось два рецепта. Поэтому там карточка сворачивается
     в строку: миниатюра слева, название и состав справа. */
  @media (max-width: 640px) {
    .recipe-card { flex-direction: row; align-items: stretch; }
    .card-cover {
      width: 76px; height: auto; min-height: 76px;
      flex-shrink: 0;
      border-bottom: none; border-right: 1px solid var(--line);
    }
    .card-cover-icon { font-size: 26px; }
    .card-video-btn { right: 4px; bottom: 4px; width: 26px; height: 26px; }
    .card-video-btn svg { width: 13px; height: 13px; }
    .card-info { padding: 10px 12px; }
    .card-composition { -webkit-line-clamp: 1; }
    .card-meta { margin-top: 8px; }
  }

  /* ===== RESPONSIVE ===== */
  @media (max-width: 500px) {
    /* На маленьком экране в карточке остаются только значки статуса —
       три подписи туда не помещаются и ломают вёрстку карточки. */
    .recipe-card .status-opt-label { display: none; }
    .recipe-card .status-opt { padding: 5px 9px; font-size: 13px; }

    /* То же самое с панелью инструментов: значки остаются, подписи
       уходят — кроме главной кнопки, по которой должно быть сразу
       понятно, что вообще делает эта панель. */
    .tool-btn:not(.tool-btn-primary) .tool-btn-label { display: none; }
    .tool-btn:not(.tool-btn-primary) { padding: 9px 11px; }
    .tool-btn-primary { font-size: 12px; }
  }
  @media (min-width: 901px) {
    .header { border-radius: 0 0 var(--radius-lg) var(--radius-lg); }
  }


  /* Полоса всплеска просмотров в админ-панели. Видна даже если
     сообщение в Телеграм не дошло — сеть подводит, панель нет. */
  .views-burst {
    margin-top: 10px;
    padding: 9px 12px;
    border-radius: var(--r-sm);
    background: var(--warn-soft, rgba(200, 120, 0, .12));
    border: 1px solid var(--warn, rgba(200, 120, 0, .35));
    font-size: 13px;
    line-height: 1.4;
  }
  .views-burst + .views-burst { margin-top: 6px; }

  /* ===== ФОТОГРАФИИ ШАГОВ ПРИГОТОВЛЕНИЯ ===== */
  /* В редакторе: полоса миниатюр под строкой шага. Строка шага —
     сетка, поэтому полоса занимает всю ширину новой строкой. */
  .step-photos {
    grid-column: 1 / -1;
    display: flex; flex-wrap: wrap; gap: 6px;
    margin-top: 6px;
  }
  .step-photos:empty { display: none; margin: 0; }
  .step-photo-chip {
    position: relative;
    width: 58px; height: 58px;
    border-radius: var(--r-sm);
    overflow: hidden;
    border: 1px solid var(--line);
  }
  .step-photo-chip img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .step-photo-chip button {
    position: absolute; top: 2px; right: 2px;
    width: 18px; height: 18px; padding: 0;
    border: none; border-radius: 50%;
    background: rgba(0,0,0,.62); color: #fff;
    font-size: 11px; line-height: 18px; cursor: pointer;
  }
  .step-photo-btn { padding: 5px 9px; }

  /* В карточке рецепта: снимки под текстом шага. Нажатие открывает во
     весь экран, поэтому курсор — лупа. */
  .steps-list .step-shots {
    display: flex; flex-wrap: wrap; gap: 8px;
    margin: 8px 0 2px;
  }
  .steps-list .step-shots img {
    width: 104px; height: 104px;
    object-fit: cover;
    border-radius: var(--r-sm);
    border: 1px solid var(--line);
    cursor: zoom-in;
  }
  /* Вычеркнутый шаг гасит и свои снимки — иначе непонятно, сделан он
     или нет. */
  .steps-list li.step-done .step-shots { opacity: .4; }

  /* Заведение в карточке заявки: по нему управляющий сразу видит,
     что кандидат указал свою точку, а не чужую. */
  .request-venue {
    display: inline-block;
    margin-top: 3px;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 12px;
    font-weight: 600;
  }
