  /* Цвета тем, шкалы и все переменные — app/static/ds/tokens.css (подключён
     раньше этого файла), правила — app/static/ds/DESIGN.md. Здесь только
     шрифты, раскладки и виджеты страниц — на токенах. */
  /* Шрифты свои, из app/static/fonts (лицензия OFL рядом): страница не ходит
     к Google — сервер в России, а в дашборде ФИО клиентов. Inter — текст,
     таблицы, кнопки и подписи графиков; Manrope — заголовки и крупные цифры
     карточек, как на сайте кейсов. Файлы вариативные (одна гарнитура на все
     начертания) и порезаны по алфавитам: браузер качает только те, чьи
     символы есть на странице. Если файл не загрузился — системный шрифт. */
  @font-face{font-family:'Inter'; font-style:normal; font-weight:400 700; font-display:swap;
    src:url(fonts/inter-cyrillic.woff2) format('woff2'); unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;}
  @font-face{font-family:'Inter'; font-style:normal; font-weight:400 700; font-display:swap;
    src:url(fonts/inter-latin-ext.woff2) format('woff2'); unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
  @font-face{font-family:'Inter'; font-style:normal; font-weight:400 700; font-display:swap;
    src:url(fonts/inter-latin.woff2) format('woff2'); unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}
  @font-face{font-family:'Manrope'; font-style:normal; font-weight:400 800; font-display:swap;
    src:url(fonts/manrope-cyrillic.woff2) format('woff2'); unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;}
  @font-face{font-family:'Manrope'; font-style:normal; font-weight:400 800; font-display:swap;
    src:url(fonts/manrope-latin-ext.woff2) format('woff2'); unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
  @font-face{font-family:'Manrope'; font-style:normal; font-weight:400 800; font-display:swap;
    src:url(fonts/manrope-latin.woff2) format('woff2'); unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}
  *{box-sizing:border-box;}
  body{background:var(--bg); color:var(--fg); font-family:var(--font-body); margin:0; padding:0; line-height:var(--leading-normal); -webkit-font-smoothing:antialiased;}
  /* Кнопки и поля по умолчанию берут системный шрифт браузера, а не шрифт страницы. */
  button,input,select,textarea{font-family:inherit;}
  h1,h2,h3,.ig-brand,.ig-subheading{font-family:var(--font-head);}
  a{color:inherit;}

  .ig-shell{display:flex; min-height:100vh;}

  .ig-sidebar{width:240px; flex-shrink:0; border-right:1px solid var(--border); padding:var(--space-20) var(--space-14); display:flex; flex-direction:column; gap:var(--space-24); position:sticky; top:0; height:100vh; transition:width var(--dur-base) ease, padding var(--dur-base) ease; overflow-x:hidden;}
  /* Полоса прокрутки сайдбара скрыта, но сам он прокручивается колесом.
     Появляется она из-за overflow-x:hidden выше: браузер в этом случае
     считает overflow-y равным auto, и стоит раскрыть два-три списка
     разделов, как справа возникает полоса, режущая колонку. Скрываем
     двумя способами сразу: scrollbar-width понимают Firefox и свежие
     Chromium, ::-webkit-scrollbar — те, что постарше, и Safari. */
  .ig-sidebar{scrollbar-width:none;}
  .ig-sidebar::-webkit-scrollbar{width:0; height:0;}
  /* Кто вошёл и выход. margin-top:auto прижимает блок к низу колонки —
     сайдбар это flex-колонка, и отдельной распорки для этого не нужно. */
  .ig-account{margin-top:auto; padding-top:var(--space-14); border-top:1px solid var(--border); display:flex; flex-direction:column; gap:var(--space-2);}
  .ig-account-name{font-size:var(--text-base); font-weight:var(--weight-semibold); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  .ig-account-role{font-size:var(--text-xs); color:var(--muted-fg); white-space:nowrap;}
  /* Переключатель темы (theme.js): .seg из трёх иконок, как в Bot funnel. */
  .ig-theme-seg{align-self:flex-start; margin:var(--space-8) 0 0;}
  .ig-shell.collapsed .ig-theme-seg{display:none;}
  /* Цвет акцента (app/accent.py): ряд кружков, выбранный обведён; «+»
     открывает палитру своего цвета прямо под рядом, без всплывающего окна —
     сайдбар узкий, и на телефоне окно поверх меню было бы негде держать. */
  .ig-accent-pick{display:flex; flex-wrap:wrap; gap:var(--space-6); margin:var(--space-8) 0 var(--space-2);}
  .ig-accent-dot{width:16px; height:16px; border-radius:var(--radius-circle); border:0; padding:0; cursor:pointer;
                 box-shadow:0 0 0 1px var(--border);}
  .ig-accent-dot:hover{box-shadow:0 0 0 2px var(--input);}
  .ig-accent-dot.on{box-shadow:var(--ring);}
  .ig-accent-dot:focus-visible{outline:2px solid var(--fg); outline-offset:2px;}
  .ig-accent-add{background:transparent; color:var(--muted-fg); font-size:var(--text-base); line-height:var(--leading-none);
                 box-shadow:0 0 0 1px var(--input) inset; display:flex; align-items:center; justify-content:center;}
  .ig-accent-add:hover, .ig-accent-add[aria-expanded="true"]{color:var(--fg); box-shadow:0 0 0 1px var(--fg) inset;}
  .ig-accent-edit{display:flex; flex-direction:column; gap:var(--space-8); margin:var(--space-8) 0 var(--space-2); padding:var(--space-10);
                  background:var(--card); border:1px solid var(--border); border-radius:var(--radius-md);}
  .ig-accent-edit[hidden], .ig-accent-warn[hidden]{display:none;}
  .ig-accent-field{display:flex; align-items:center; gap:var(--space-10); cursor:pointer;}
  .ig-accent-input{width:44px; height:28px; padding:0; border:1px solid var(--input); border-radius:var(--radius-sm);
                   background:none; cursor:pointer;}
  .ig-accent-input::-webkit-color-swatch-wrapper{padding:var(--space-2);}
  .ig-accent-input::-webkit-color-swatch{border:0; border-radius:var(--radius-xs);}
  .ig-accent-hex{font-size:var(--text-sm); font-family:ui-monospace,Consolas,monospace; color:var(--muted-fg);}
  .ig-accent-warn{font-size:var(--text-xs); line-height:var(--leading-tight); color:var(--destructive);}
  .ig-accent-actions{display:flex; gap:var(--space-6);}
  /* Сохранить / Отмена / Удалить — .btn (ds/components.css); здесь только ширина */
  .ig-accent-save, .ig-accent-cancel, .ig-accent-confirm-no, .ig-accent-confirm-yes{flex:1;}
  /* Крестик своего цвета — в углу кружка. При наведении на десктопе и
     всегда там, где наведения нет (телефон): иначе удалить было бы нечем. */
  .ig-accent-item{position:relative; display:flex;}
  .ig-accent-x{position:absolute; top:-6px; right:-6px; width:13px; height:13px; padding:0; border:0;
               border-radius:var(--radius-circle); background:var(--popover); color:var(--fg); font:inherit; font-size:var(--text-xs);
               line-height:var(--leading-none); cursor:pointer; box-shadow:0 0 0 1px var(--input); display:none;
               align-items:center; justify-content:center;}
  .ig-accent-x:hover{background:var(--destructive); color:var(--bg);}
  .ig-accent-item:hover .ig-accent-x, .ig-accent-x:focus-visible{display:flex;}
  @media (hover:none){ .ig-accent-x{display:flex;} }
  /* Окно «Удалить цвет?» — .dialog (ds/components.css); здесь только точка цвета */
  .ig-accent-confirm-dot{width:20px; height:20px; border-radius:var(--radius-circle); flex-shrink:0; box-shadow:0 0 0 1px var(--border);}
  .ig-accent-confirm-hex{font-family:var(--font-mono); color:var(--fg);}
  /* Режим показа (privacy.js): «Выкл / Размыть / Подменить» — сегмент,
     как у темы; включённый режим залит акцентом, чтобы на встрече было
     видно, что цифры сейчас не настоящие. */
  .ig-privacy{display:flex; flex-direction:column; gap:var(--space-4); margin:var(--space-10) 0 0;}
  .ig-privacy-label{font-size:var(--text-xs); color:var(--muted-fg);}
  .ig-privacy-seg{align-self:flex-start;}
  /* Включённый режим — заливка акцентом, а не карточка .seg: видно, что
     цифры сейчас не настоящие. */
  .ig-privacy-btn[aria-pressed="true"]:not([data-privacy-btn="off"]){background:var(--accent); color:var(--accent-fg);}
  .ig-shell.collapsed .ig-privacy{display:none;}
  .ig-logout-row{display:flex; gap:var(--space-12); margin:var(--space-6) 0 0;}
  .ig-logout-form{margin:0;}
  .ig-logout{font:inherit; font-size:var(--text-sm); color:var(--muted-fg); background:none; border:0; padding:0; cursor:pointer; text-align:left; text-decoration:none; white-space:nowrap;}
  .ig-logout:hover{color:var(--fg); text-decoration:underline;}
  /* В свёрнутом сайдбаре остаётся только «Выйти» — имя, роль и цвета там не помещаются. */
  .ig-shell.collapsed .ig-account-name,
  .ig-shell.collapsed .ig-account-role,
  .ig-shell.collapsed .ig-accent,
  .ig-shell.collapsed .ig-logout-all{display:none;}
  .ig-shell.collapsed .ig-account{align-items:center;}
  .ig-sidebar-top{display:flex; align-items:center; justify-content:space-between; gap:var(--space-8);}
  .ig-brand{display:flex; align-items:center; gap:var(--space-8); font-size:var(--text-md); font-weight:var(--weight-semibold); padding:var(--space-4) var(--space-8); white-space:nowrap;}
  .ig-collapse-ic{transition:transform var(--dur-base) ease;}
  .ig-acc-switch{display:flex; flex-direction:column; gap:var(--space-2);}
  /* Пункты сайдбара — вкладки (ig-source-btn, .nav-item.primary), разделы
     (ig-nav-item) и аккаунты (ig-acc-btn) — .nav-item (ds/components.css);
     здесь только отступы в свёрнутом меню, в списке разделов и на телефоне. */
  .ig-nav{display:flex; flex-direction:column; gap:var(--space-px);}
  .ig-nav-ic{display:flex; flex-shrink:0;}

  /* группы верхнего уровня в сайдбаре (Контент / Продажи / ... — по образцу референса) */
  .ig-nav-group{display:flex; flex-direction:column; gap:var(--space-6);}
  .ig-group-label{color:var(--muted-fg); font-size:var(--text-xs); font-weight:var(--weight-bold); text-transform:uppercase; letter-spacing:var(--tracking-caps); padding:0 var(--space-10); white-space:nowrap;}

  /* свёрнутое меню — узкая иконочная панель, разворачивается кнопкой в ig-sidebar-top */
  .ig-shell.collapsed .ig-sidebar{width:60px; padding:var(--space-20) var(--space-10);}
  .ig-shell.collapsed .ig-sidebar-top{flex-direction:column; gap:var(--space-10);}
  .ig-shell.collapsed .ig-brand-text,
  .ig-shell.collapsed .ig-acc-switch,
  .ig-shell.collapsed .ig-source-caret,
  .ig-shell.collapsed .ig-group-label,
  .ig-shell.collapsed .ig-nav-text{display:none;}
  .ig-shell.collapsed .ig-nav-item{justify-content:center; padding:var(--space-8);}
  .ig-shell.collapsed .ig-collapse-ic{transform:rotate(180deg);}

  .ig-main-wrap{flex:1; min-width:0;}
  .ig-topbar{padding:var(--space-24) var(--space-32) var(--space-16); border-bottom:1px solid var(--border);}
  .ig-topbar h1{margin:0 0 var(--space-6); font-size:var(--text-xl);}
  .ig-topbar .sub{color:var(--muted-fg); font-size:var(--text-base);}
  .ig-topbar-source{display:none;}
  .ig-topbar-source.active{display:flex; flex-wrap:wrap; align-items:flex-start; gap:var(--space-12);}
  .ig-topbar-main{flex:1; min-width:0;}
  .ig-topbar-actions{display:flex; align-items:center; gap:var(--space-8);}

  /* Кнопка «Обновить». Спрятана до тех пор, пока JS не убедится, что за
     страницей стоит живой backend (GET /api/status) — в автономном HTML,
     открытом файлом, обслуживать её некому. */
  .ig-refresh-btn{display:none;}
  .ig-live .ig-refresh-btn{display:inline-flex;}
  /* Выгрузка данных Threads. Спрятана там же, где «Обновить», и по той
     же причине: в автономном HTML файл отдавать некому. */
  .ig-export-btn{display:none;}
  .ig-live .ig-export-btn{display:inline-flex;}
  /* Атрибут hidden прячет элемент правилом display:none из стилей браузера,
     а любое авторское display его перебивает — поэтому вкладке без скрипта
     сбора (ChatPlace) кнопка оставалась видимой. Поймано тестом. */
  .ig-refresh-btn[hidden]{display:none;}
  .ig-refresh-btn .ig-refresh-ic{display:flex;}
  .ig-refresh-btn.busy .ig-refresh-ic{animation:ig-spin var(--dur-spin) linear infinite;}
  @keyframes ig-spin{to{transform:rotate(360deg);}}

  .ig-updated{color:var(--muted-fg); font-size:var(--text-base);}
  .ig-updated:not(:empty)::before{content:" · ";}

  /* Статус задачи обновления — на всю ширину под заголовком */
  .ig-job{flex-basis:100%; background:var(--muted); border:1px solid var(--border);
          border-radius:var(--radius-md); padding:var(--space-10) var(--space-14); font-size:var(--text-base);}
  .ig-job[hidden]{display:none;}
  .ig-job-line{display:flex; align-items:center; gap:var(--space-8); flex-wrap:wrap;}
  .ig-job-steps{margin:var(--space-8) 0 0; padding:0; list-style:none; display:flex; flex-direction:column; gap:var(--space-4);}
  .ig-job-steps li{color:var(--muted-fg); font-size:var(--text-sm); display:flex; gap:var(--space-8); align-items:baseline;}
  .ig-job-steps li.running{color:var(--fg);}
  .ig-job-steps li.ok .ig-job-mark{color:var(--accent-text);}
  .ig-job-steps li.error .ig-job-mark, .ig-job-steps li.timeout .ig-job-mark{color:var(--destructive);}
  .ig-job-mark{width:1em; flex-shrink:0;}
  .ig-job-dur{color:var(--muted-fg); font-size:var(--text-xs);}

  /* --- Конкуренты: список аккаунтов слева, профиль справа --- */
  /* Список аккаунтов и профиль — в одной строке, контент под ними во всю
     ширину: в узкой правой колонке карточки занимали быполовину экрана, а слева
     под никами зияла пустота. */
  .ig-comp-layout{display:grid; grid-template-columns:250px minmax(0,1fr);
                  gap:var(--space-20); margin-top:var(--space-14); align-items:start;}
  .ig-comp-sidebar{grid-column:1; grid-row:1;}
  .ig-comp-tops{grid-column:2; grid-row:1; min-width:0;}
  .ig-comp-bodies{grid-column:1 / -1; grid-row:2; min-width:0;}
  @media (max-width:900px){
    .ig-comp-layout{grid-template-columns:1fr;}
    .ig-comp-sidebar, .ig-comp-tops, .ig-comp-bodies{grid-column:1; grid-row:auto;}
  }

  .ig-comp-sidebar{padding:var(--space-14); align-self:start;}  /* вид — .card.flat */
  .ig-comp-sidebar-title{color:var(--muted-fg); font-size:var(--text-xs); font-weight:var(--weight-bold);
                         text-transform:uppercase; letter-spacing:var(--tracking-caps); margin-bottom:var(--space-10);}

  /* Форма показывается только под живым backend'ом — в автономном HTML
     отправлять её некуда (класс .ig-live ставит JS после /api/status). */
  .ig-comp-add{display:none; gap:var(--space-6); margin-bottom:var(--space-12); flex-wrap:wrap;}
  .ig-live .ig-comp-add{display:flex;}
  /* поле — .field (ds/components.css) */
  .ig-comp-add input{flex:1;}
  .ig-comp-err{flex-basis:100%; color:var(--destructive); font-size:var(--text-sm);}
  .ig-comp-err:empty{display:none;}
  /* Заявка принята — это не ошибка, красный тут вводил бы в
     заблуждение: ничего не сломалось, просто собирает другая машина. */
  .ig-comp-err.ig-comp-note{color:var(--muted-fg);}

  /* Список скроллится: аккаунтов может быть до двадцати */
  .ig-comp-list{display:flex; flex-direction:column; gap:var(--space-2); max-height:420px; overflow-y:auto;}
  /* Системная полоса прокрутки на тёмной теме — светлая, со стрелками.
     Прятать нельзя (без полосы не видно, что блок листается), поэтому та же
     тонкая тёмная полоса, что в Директе (direct.css). На всё содержимое
     сразу, а не списком классов: список пополнялся по одной жалобе на
     вкладку (конкуренты, лента рилсов, сторис...). Сайдбар вне .ig-main-wrap
     и свою полосу прячет сам. */
  .ig-main-wrap *{scrollbar-width:thin; scrollbar-color:var(--border) transparent;}
  .ig-main-wrap *::-webkit-scrollbar{width:10px; height:10px;}
  .ig-main-wrap *::-webkit-scrollbar-track{background:transparent;}
  .ig-main-wrap *::-webkit-scrollbar-thumb{
    background:var(--border); border-radius:var(--radius-full); border:3px solid transparent; background-clip:content-box;}
  .ig-main-wrap *:hover::-webkit-scrollbar-thumb{background:var(--muted-fg); background-clip:content-box;}
  /* Пункт списка — .nav-item (ds/components.css), как в сайдбаре; счётчик
     выбранного не жирный — жирным выделен только ник. */
  .ig-comp-item{width:100%; padding:var(--space-8);}
  .ig-comp-item-nick{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  .ig-comp-item-count{color:var(--muted-fg); font-size:var(--text-xs); font-weight:var(--weight-regular); flex-shrink:0;}

  .ig-comp-avatar{border-radius:var(--radius-circle); object-fit:cover; flex-shrink:0; background:var(--muted);}
  .ig-comp-avatar-sm{width:26px; height:26px;}
  .ig-comp-avatar-lg{width:76px; height:76px;}
  .ig-comp-avatar-empty{display:flex; align-items:center; justify-content:center;
                        color:var(--muted-fg); font-weight:var(--weight-bold);}
  .ig-comp-avatar-empty.ig-comp-avatar-sm{font-size:var(--text-sm);}
  .ig-comp-avatar-empty.ig-comp-avatar-lg{font-size:var(--text-3xl);}

  .ig-comp-panel{display:none;}
  .ig-comp-bodies .ig-comp-panel.active{display:block;}
  .ig-comp-panel.active{display:block;}

  /* Вкладка «Подписчики»: переключатель разреза и две таблицы под ним. */
  .ig-fw{display:flex; flex-direction:column; gap:var(--space-12);}
  /* Два ряда кнопок в одну строку: разрез слева, период справа — они
     независимы, и ставить их друг под другом значило бы намекать на связь. */
  .ig-fw-bar{display:flex; gap:var(--space-14); flex-wrap:wrap; align-items:center;
            justify-content:space-between;}
  .ig-fw-chart{width:100%; height:260px;}
  /* Неактивный разрез убираем из потока целиком: 366 строк в display:none
     всё равно остаются в DOM, но не участвуют в раскладке. */
  .ig-fw-table{display:none;}
  .ig-fw-table.active{display:block;}
  .ig-fw .ig-table td{white-space:nowrap;}

  .ig-comp-header{display:flex; gap:var(--space-20); align-items:flex-start; margin-bottom:var(--space-16);}
  /* Шапка ещё не собранного аккаунта: без аватара и счётчиков, но с теми же
     кнопками — иначе ошибочно добавленный ник нельзя было бы удалить. */
  .ig-comp-header-pending{margin-bottom:var(--space-10);}
  .ig-comp-header-pending .ig-comp-nick{word-break:break-all;}
  .ig-comp-ident{min-width:0;}
  .ig-comp-name{font-size:var(--text-lg); font-weight:var(--weight-bold); line-height:var(--leading-tight);}
  .ig-comp-nick{color:var(--muted-fg); font-size:var(--text-base); text-decoration:none;}
  .ig-comp-nick:hover{color:var(--accent-text);}
  .ig-comp-counts{display:flex; flex-wrap:wrap; gap:var(--space-14); margin-top:var(--space-8); font-size:var(--text-base); color:var(--muted-fg);}
  .ig-comp-counts b{color:var(--fg);}
  .ig-comp-bio{margin-top:var(--space-8); font-size:var(--text-base); line-height:var(--leading-normal); max-width:70ch;}
  .ig-comp-link{display:inline-block; margin-top:var(--space-8); font-size:var(--text-base);
                color:var(--accent-text); text-decoration:none; word-break:break-all;}
  .ig-comp-link:hover{text-decoration:underline;}
  .ig-comp-fetched{margin-top:var(--space-8); color:var(--muted-fg); font-size:var(--text-xs);}
  /* Действия над конкурентом. Скрыты вне веб-приложения (класс .ig-live
     ставит JS после успешного /api/status): в автономном HTML их некому
     обслуживать. */
  .ig-comp-actions{margin-left:auto; display:none; gap:var(--space-8); flex-shrink:0;}
  .ig-live .ig-comp-actions{display:flex;}
  /* «Обновить», «Экспорт» (ссылка: файл отдаёт сервер) и «Удалить» — .btn
     (ds/components.css). */

  /* Четыре плитки в ряд, как в референсе. Селектор из двух классов —
     иначе .grid.ig-summary перебивает по специфичности и четвёртая
     плитка уезжает на вторую строку. */
  .ig-summary.ig-comp-kpi{margin-bottom:var(--space-8); grid-template-columns:repeat(4, minmax(0, 1fr));}
  @media (max-width:1200px){ .ig-summary.ig-comp-kpi{grid-template-columns:repeat(2, minmax(0, 1fr));} }
  @media (max-width:600px){ .ig-summary.ig-comp-kpi{grid-template-columns:1fr;} }
  .ig-mix-bar{height:5px; border-radius:var(--radius-xs); background:var(--muted); overflow:hidden; margin-top:var(--space-6);}
  .ig-mix-reels{display:block; height:100%; background:var(--accent);}
  .ig-comp-content-label{color:var(--muted-fg); font-size:var(--text-xs); font-weight:var(--weight-bold);
                         text-transform:uppercase; letter-spacing:var(--tracking-caps); margin:var(--space-20) 0 var(--space-10);}
  .ig-job.error{border-color:var(--destructive);}
  .ig-job-err{color:var(--destructive); margin-top:var(--space-6);}
  .ig-job-log{margin-top:var(--space-8);}
  .ig-job-log summary{cursor:pointer; color:var(--muted-fg); font-size:var(--text-sm);}
  .ig-job-log pre{margin:var(--space-6) 0 0; padding:var(--space-10); background:var(--bg); border:1px solid var(--border);
                  border-radius:var(--radius-sm); font-size:var(--text-xs); line-height:var(--leading-normal); max-height:260px;
                  overflow:auto; white-space:pre-wrap; word-break:break-word;}
  .ig-job-close{margin-left:auto;}

  /* переключатель источника (Instagram / Telegram-канал) в сайдбаре */
  .ig-source-switch{display:flex; flex-direction:column; gap:var(--space-2); padding-bottom:var(--space-6); border-bottom:1px solid var(--border);}
  /* Разделы вкладки — выпадающим списком прямо под ней, а не общим блоком
     внизу сайдбара: там было не видно, к какой вкладке они относятся.

     Раскрытие анимируется через grid-template-rows 0fr -> 1fr, а НЕ через
     display или max-height. display вообще не анимируется, а max-height
     требует выдуманного числа: возьмёшь мало — длинный список обрежется,
     возьмёшь много — короткий раскрывается с паузой, потому что скорость
     считается от выдуманной высоты, а не от настоящей. Приём с grid едет
     ровно до высоты содержимого, какой бы она ни была.

     Раскрытие НЕ привязано к активной вкладке (класс .open, а не .active):
     списков может быть открыто сколько угодно, и заглянуть в разделы
     соседнего источника можно, не уходя со своего. */
  .ig-source-nav{display:grid; grid-template-rows:0fr;
                 transition:grid-template-rows var(--dur-slow) cubic-bezier(.4,0,.2,1);}
  .ig-source-nav.open{grid-template-rows:1fr;}
  /* overflow:hidden обязан висеть здесь, на вложенном узле: на самом
     grid-контейнере он приёма не работает. min-height:0 — иначе grid не
     даёт строке сжаться ниже высоты содержимого, и список не закроется.

     Отступы сверху и снизу — padding, и он анимируется вместе со строкой.
     margin лежал бы СНАРУЖИ анимируемой строки и появлялся мгновенно:
     раскрытие начиналось скачком на 8 px и только потом ехало плавно.
     А статический padding, наоборот, в нулевой строке никуда не девается
     (min-height:0 обнуляет только content-box) — и под каждым свёрнутым
     пунктом оставалось бы по 8 px пустоты. Поэтому он в переходе. */
  .ig-source-nav-in{overflow:hidden; min-height:0;
                    margin-left:var(--space-12); padding:0 0 0 var(--space-8);
                    border-left:1px solid var(--border);
                    transition:padding-top var(--dur-slow) cubic-bezier(.4,0,.2,1),
                               padding-bottom var(--dur-slow) cubic-bezier(.4,0,.2,1);}
  .ig-source-nav.open > .ig-source-nav-in{padding-top:var(--space-2); padding-bottom:var(--space-6);}
  .ig-source-nav .ig-nav-item{font-size:var(--text-sm); padding:var(--space-6) var(--space-10);}
  .ig-source-nav .ig-acc-switch{margin-bottom:var(--space-6);}
  /* Кликабельная зона «птички» шире самой стрелки — в неё надо попадать. */
  .ig-source-caret{margin-left:auto; display:flex; opacity:.55; border-radius:var(--radius-xs);
                   padding:var(--space-4); margin-right:calc(var(--space-4) * -1);
                   transition:transform var(--dur-slow) cubic-bezier(.4,0,.2,1), opacity var(--dur-fast) ease,
                              background-color var(--dur-fast) ease;}
  .ig-source-caret:hover{opacity:1; background:var(--hover-strong);}
  .ig-source-btn:not(.expanded) .ig-source-caret{transform:rotate(-90deg);}
  /* В свёрнутом меню отступа нет — колонка узкая, остаются одни иконки. */
  .ig-shell.collapsed .ig-source-nav-in{margin-left:0; padding-left:0; border-left:none;}
  /* Уважаем системную настройку «меньше движения»: там анимация не нужна. */
  @media (prefers-reduced-motion: reduce){
    .ig-source-nav, .ig-source-nav-in, .ig-source-caret{transition:none;}
  }
  .ig-source-content{display:none;}
  .ig-source-content.active{display:block;}
  .ig-shell.collapsed .ig-source-switch{align-items:center;}
  .ig-shell.collapsed .ig-source-btn{justify-content:center; padding:var(--space-8);}

  /* карточки постов Telegram-канала — превью как у Instagram, если есть фото/видео */
  .tg-card{gap:0;}
  .tg-card > .tg-kind-badge{margin:var(--space-14) var(--space-16) 0;}
  .tg-kind-badge{align-self:flex-start;}  /* вид — .badge */
  /* Обложка кружка круглая, а вот ИГРАЮЩЕЕ видео — нет. Скругление
     обрезало собственные контролы браузера: полоса перемотки уходила за
     край круга, и промотать кружок было нечем. Форма важна как признак
     формата на обложке, а во время просмотра важнее управление. */
  .tg-note-wrap .ig-thumb{border-radius:var(--radius-circle);}
  .tg-note-wrap .ig-card-video{background:transparent;}
  .tg-note-len{position:absolute; left:8px; bottom:8px; background:var(--media-badge);
            color:var(--on-media); font-size:var(--text-xs); padding:var(--space-2) var(--space-8); border-radius:var(--radius-full);
            font-variant-numeric:tabular-nums;}
  .tg-note-wrap.ig-playing .tg-note-len{display:none;}

  /* Плеер голосовых — компонент .audio.filled (ds/components.css); здесь
     только место в карточке. */
  .tg-audio{margin:var(--space-10) 0 var(--space-4);}
  .tg-multi-badge{position:absolute; top:8px; right:8px;}  /* вид — .badge.on-media */
  main{padding:var(--space-24) var(--space-32) var(--space-64); max-width:1200px;}

  .ig-section{display:none;}
  .ig-section.active{display:block;}
  .ig-subsection{display:none;}
  .ig-subsection.active{display:block;}

  .ig-summary{margin-bottom:var(--space-20);}
  .ig-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); gap:var(--space-16);}
  .ig-card{overflow:hidden; display:flex; flex-direction:column;}  /* вид — .card */
  .ig-thumb-wrap{position:relative; width:100%; aspect-ratio:1/1; background:var(--muted);}
  /* Карточки за пределами экрана браузер не раскладывает и не рисует:
     в разделе их бывают тысячи, и показ раздела стоил ~250 мс раскладки.
     Высота-заглушка нужна, чтобы полоса прокрутки не прыгала; «auto»
     запоминает настоящую высоту после первого показа. */
  .ig-grid > .ig-card{content-visibility:auto; contain-intrinsic-size:auto 240px auto 420px;}
  .ig-thumb{width:100%; height:100%; object-fit:cover; display:block;}
  /* Сторис, медиа которой не сохранилось: подпись вместо пустой рамки —
     битая картинка выглядит как поломка интерфейса. */
  .ig-thumb-gone{display:flex; align-items:center; justify-content:center; text-align:center; padding:var(--space-8); box-sizing:border-box; color:var(--muted-fg); font-size:var(--text-sm); line-height:var(--leading-tight); background:var(--muted); border:1px dashed var(--border);}
  /* Плашки поверх фото и видео — тёмная подложка и белый текст в ЛЮБОЙ теме:
     под ними картинка, а не фон страницы (цвет темы тут давал тёмное на тёмном). */
  .ig-type-badge{position:absolute; top:8px; left:8px;}  /* вид — .badge.on-media */
  .ig-card-body{padding:var(--space-14) var(--space-16) var(--space-16); display:flex; flex-direction:column; gap:var(--space-10); flex:1;}
  .ig-date{color:var(--muted-fg); font-size:var(--text-sm);}
  .ig-caption{font-size:var(--text-base); color:var(--card-fg); flex:1;}
  .ig-stats-row{display:flex; flex-wrap:wrap; gap:var(--space-10); font-size:var(--text-sm); color:var(--card-fg);}
  .ig-stat{display:flex; align-items:center; gap:var(--space-4);}
  .ig-stat-ic{font-size:var(--text-sm);}
  .ig-nostat{color:var(--muted-fg); font-size:var(--text-xs); font-style:italic;}
  .ig-detail{font-size:var(--text-sm);}
  .ig-detail summary{cursor:pointer; color:var(--muted-fg); font-weight:var(--weight-medium); list-style:none; display:flex; align-items:center; gap:var(--space-4);}
  .ig-detail summary::-webkit-details-marker{display:none;}
  .ig-detail summary::before{content:"›"; display:inline-block; transition:transform var(--dur-fast);}
  .ig-detail[open] summary::before{transform:rotate(90deg);}
  .ig-detail table{margin-top:var(--space-8);}
  /* Расшифровка рилса (кнопка «Получить текст», app/transcripts.py) */
  .ig-tr-btn{align-self:flex-start;}
  .ig-tr-btn.done{color:var(--accent-text);}
  .ig-tr-btn:disabled{cursor:wait;}
  .ig-tr-box{font-size:var(--text-sm); line-height:var(--leading-normal); color:var(--muted-fg); background:var(--muted);
             border-radius:var(--radius-md); padding:var(--space-8) var(--space-10); display:flex; flex-direction:column; gap:var(--space-8);}
  .ig-tr-box[hidden]{display:none;}
  .ig-tr-box.error{color:var(--destructive);}
  .ig-tr-text{color:var(--card-fg); white-space:pre-wrap; max-height:220px; overflow-y:auto;
              user-select:text; overflow-wrap:anywhere;}
  .ig-tr-copy{align-self:flex-start;}  /* вид — .btn.primary.sm */
  .ig-detail td{padding:var(--space-4) 0; font-size:var(--text-sm); border-bottom:1px solid var(--border);}
  .ig-detail td:last-child{text-align:right; font-weight:var(--weight-semibold);}
  .ig-detail-note{color:var(--muted-fg); font-size:var(--text-xs); font-style:italic;}
  .ig-detail-text{margin-top:var(--space-8); font-size:var(--text-sm); color:var(--card-fg); white-space:pre-wrap; line-height:var(--leading-normal);}
  .ig-link{display:inline-block; text-align:center; background:var(--accent); color:var(--accent-fg); font-size:var(--text-sm); font-weight:var(--weight-semibold); text-decoration:none; margin-top:var(--space-4); padding:var(--space-8) var(--space-14); border-radius:var(--radius-lg);}
  .ig-link:hover{filter:brightness(1.08);}
  .ig-note{margin-top:var(--space-14);}  /* вид — .note */
  .ig-h3{font-size:var(--text-base); margin:var(--space-20) 0 var(--space-8); color:var(--muted-fg); font-weight:var(--weight-semibold);}
  .ig-h3:first-child{margin-top:0;}
  .ig-raw{background:var(--card); border:1px solid var(--border); border-radius:var(--radius-md); padding:var(--space-12); font-size:var(--text-xs); color:var(--muted-fg); overflow:auto; max-height:300px;}
  /* Каждая разбивка — ОДНА карточка. Раньше заголовок и его таблица были
     двумя отдельными элементами сетки, и подпись уезжала в соседнюю
     колонку от своих же цифр (пользователь сообщил это как «нечитаемо»). */
  .ig-demo-grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(340px,1fr));
                gap:var(--space-20); align-items:start;}
  .ig-demo-block{padding:var(--space-14) var(--space-16) var(--space-10);}  /* вид — .card */
  .ig-demo-block .ig-h3{margin:0 0 var(--space-6);}
  .ig-demo-table tr:last-child td{border-bottom:none;}
  .ig-demo-name{padding-left:0 !important;}
  /* Числа не переносятся и стоят в столбик: «12 351» разрывалось на две
     строки, и колонка прыгала. */
  .ig-demo-val{text-align:right; white-space:nowrap; font-variant-numeric:tabular-nums;
               font-weight:var(--weight-semibold);}
  .ig-demo-pct{text-align:right; white-space:nowrap; color:var(--muted-fg);
               font-size:var(--text-sm); font-variant-numeric:tabular-nums; width:48px;}
  /* Полосе хватает четверти ширины: она показывает соотношение, а не
     значение. Отдать ей больше — значит переносить подписи на две строки. */
  .ig-demo-bar-cell{width:26%; padding-right:0 !important;}
  /* Своё имя, а не .ig-bar: тот класс занят столбиками трендов ниже по
     файлу, и его правила перебивали бы эти. */
  .ig-demo-bar{height:7px; background:var(--accent); border-radius:var(--radius-xs); min-width:2px;}
  .ig-demo-total{color:var(--muted-fg); font-size:var(--text-sm); padding:var(--space-8) 0 var(--space-2);}
  .ig-bar-cell{width:40%;}
  .ig-bar{height:6px; background:var(--accent); border-radius:var(--radius-xs); min-width:2px;}
  .ig-chart{width:100%; height:260px; padding:var(--space-12); margin-bottom:var(--space-8);}  /* вид — .card */

  .ig-stream-switch{margin-bottom:var(--space-20); position:sticky; top:0; z-index:var(--z-sticky); background:var(--bg); padding:var(--space-14) 0; border-bottom:1px solid var(--border);}
  /* «Сравнение запусков» — все потоки сразу: переключатель потока там лишний */
  .ig-section:has(.ig-subsection.ig-no-streams.active) .ig-stream-switch{display:none;}
  .ig-stream-section{display:none;}
  .ig-stream-section.active{display:block;}

  .ig-top-row{display:flex; gap:var(--space-12); overflow-x:auto; padding-bottom:var(--space-6); margin-bottom:var(--space-4);}
  .ig-top-card{flex:0 0 120px; display:block; text-decoration:none; color:inherit;}
  .ig-top-card .ig-thumb-wrap{width:120px; height:120px; aspect-ratio:auto; border-radius:var(--radius-md); box-shadow:var(--shadow);}
  .ig-top-badge{position:absolute; bottom:8px; left:8px;}  /* вид — .badge.accent.strong */
  .ig-top-date{color:var(--muted-fg); font-size:var(--text-xs); margin-top:var(--space-6); text-align:center;}

  .ig-viral-badge{position:absolute; top:8px; right:8px;}  /* вид — .badge.on-media.strong (.hot) */
  .ig-competitor-block{padding-bottom:var(--space-24); margin-bottom:var(--space-24); border-bottom:1px solid var(--border);}
  .ig-competitor-block:last-child{border-bottom:none;}

  /* Ветка Threads: продолжение мысли под корневой публикацией. Свёрнуто
     по умолчанию — у активного автора ветки на четыре-пять частей, и
     раскрытые они вытеснили бы из ленты всё остальное. */
  .th-thread{margin:0;}
  .th-thread > summary{cursor:pointer; color:var(--muted-fg); font-size:var(--text-sm);}
  .th-part{display:flex; gap:var(--space-8); margin-top:var(--space-10); padding-top:var(--space-10); border-top:1px solid var(--border);}
  .th-part-no{flex:0 0 auto; width:18px; height:18px; border-radius:var(--radius-circle); background:var(--muted); color:var(--muted-fg); font-size:var(--text-xs); line-height:var(--leading-none);
             display:flex; align-items:center; justify-content:center;}
  .th-part-body{min-width:0; flex:1; display:flex; flex-direction:column; gap:var(--space-6);}
  /* overflow-wrap обязателен: в Threads попадаются длинные ссылки без
     пробелов, и без него часть ветки растягивает карточку вширь. */
  .th-part-text{font-size:var(--text-base); color:var(--card-fg); overflow-wrap:anywhere;}
  .th-part-nums{display:flex; gap:var(--space-10); color:var(--muted-fg); font-size:var(--text-sm);}
  /* Картинка внутри части ниже обычной обложки: часть — это дополнение,
     а не самостоятельная карточка. */
  .th-part .ig-thumb-wrap{aspect-ratio:4/3; border-radius:var(--radius-sm); overflow:hidden;}

  .ig-sort-bar{margin:var(--space-10) 0 var(--space-14);}

  /* Ряд дат — свой flex, а не .chips: кроме чипов в нём поля дат и «Применить». */
  .ig-date-filter{display:flex; align-items:center; gap:var(--space-6); flex-wrap:wrap; margin:var(--space-20) 0 var(--space-10);}
  .ig-date-custom{display:flex; align-items:center; gap:var(--space-6); margin-left:var(--space-4);}
  /* Поля дат — .field.sm. На телефоне поле 16px (иначе iPhone приближает
     страницу), и «Применить» за двумя датами уезжал за край экрана (до
     этапа 8 — тоже, на 5px): даты делят свою строку поровну, кнопка — под
     ними во всю ширину. */
  @media (max-width:900px){
    .ig-date-custom{flex-basis:100%; margin-left:0; display:grid;
                    grid-template-columns:minmax(0, 1fr) auto minmax(0, 1fr); gap:var(--space-6);}
    .ig-date-custom .field{width:100%;}
    .ig-date-apply{grid-column:1 / -1;}
  }
  .ig-date-sep{color:var(--muted-fg); font-size:var(--text-sm);}

  .ig-type-filter{margin:0 0 var(--space-10);}

  .ig-subheading{margin:var(--space-24) 0 var(--space-4); font-size:var(--text-md); font-weight:var(--weight-semibold);}

  .ig-funnel-controls{display:flex; align-items:center; gap:var(--space-10); flex-wrap:wrap; margin:var(--space-10) 0 var(--space-14);}
  .ig-funnel-controls .ig-sort-bar, .ig-funnel-controls .ig-trend-switch{margin:0;}
  .ig-bar-label{color:var(--muted-fg); font-size:var(--text-sm); white-space:nowrap;}
  .ig-bar-sep{width:1px; height:18px; background:var(--border);}
  /* Платформа рассылки ChatPlace: фирменный цвет платформы, чтобы строки
     Telegram и Instagram различались с первого взгляда, а не по тексту. */
  .ig-plat{display:inline-flex; align-items:center; gap:var(--space-6); white-space:nowrap;
           padding:var(--space-4) var(--space-10) var(--space-4) var(--space-8); border-radius:var(--radius-full); font-size:var(--text-sm); font-weight:var(--weight-semibold);
           color:var(--plat); background:color-mix(in oklch, var(--plat) 14%, transparent);
           border:1px solid color-mix(in oklch, var(--plat) 35%, transparent);}
  .ig-plat svg{width:15px; height:15px; flex-shrink:0;}
  .ig-plat-tg{--plat:var(--brand-tg);}
  .ig-plat-ig{--plat:var(--brand-ig);}
  .ig-plat-other{--plat:var(--muted-fg);}
  .ig-fn-note{margin-top:var(--space-10); font-size:var(--text-sm);}
  /* Общий график всех автоматизаций над таблицами по ботам. */
  .ig-fn-total{margin-bottom:var(--space-24);}
  .ig-fn-total .ig-summary{margin-bottom:var(--space-14);}
  /* Пять плиток в ряд: при 220 px из .grid пятая уезжала на вторую строку. */
  @media (min-width:901px){ .ig-summary.ig-fn-total-kpi{grid-template-columns:repeat(auto-fit, minmax(160px, 1fr));} }
  /* На телефоне по две, итог за период — во всю ширину: пять плиток
     столбиком занимали полтора экрана до самого графика. */
  @media (max-width:900px){
    .ig-summary.ig-fn-total-kpi{grid-template-columns:1fr 1fr;}
    .ig-fn-total-kpi > :first-child{grid-column:1 / -1;}
  }
  .ig-fn-total-chart{height:300px;}
  /* Каналы общего графика: метка платформы, как у рассылок, с галочкой.
     Снятый канал бледнеет — состояние видно и без самой галочки. */
  .ig-fn-chans{display:flex; gap:var(--space-6); flex-wrap:wrap;}
  .ig-fn-chan{cursor:pointer; user-select:none; padding-left:var(--space-8);}
  .ig-fn-chan input{margin:0; accent-color:var(--plat); cursor:pointer;}
  .ig-fn-chan:not(:has(input:checked)){opacity:.45;}
  .ig-fn-total-note{margin-top:var(--space-4); font-size:var(--text-sm);}
  .ig-dlg-note{margin-top:var(--space-10); font-size:var(--text-sm);}
  .ig-fn-created{white-space:nowrap;}
  .ig-fn-conv{font-variant-numeric:tabular-nums;}
  .ig-fn-clients{font-variant-numeric:tabular-nums;}

  .ig-bar-trend{display:flex; align-items:flex-end; gap:var(--space-2); height:32px; width:170px;}
  /* Больше 30 точек (обычно дни за месяц и дольше) в 170 px сжались бы в
     нитку. Колонка тренда в таблице воронок свободна по ширине. */
  .ig-bar-trend.dense{width:clamp(170px, 28vw, 420px);}
  .ig-bar-slot{flex:1; height:100%; display:flex; align-items:flex-end; cursor:default;}
  .ig-bar{width:100%; background:var(--accent); opacity:.55; border-radius:var(--radius-xs) var(--radius-xs) 0 0; min-height:2px; pointer-events:none;}
  .ig-bar-slot:hover .ig-bar{opacity:1;}
  .ig-trend-numbers{display:none; flex-wrap:wrap; gap:var(--space-6) var(--space-10); max-width:420px;}
  .ig-trend-cell{min-width:38px; text-align:center;}
  .ig-trend-date{color:var(--muted-fg); font-size:var(--text-xs);}
  .ig-trend-count{font-size:var(--text-sm); font-weight:var(--weight-semibold);}
  .ig-trend-wrap.mode-numbers .ig-bar-trend{display:none;}
  .ig-trend-wrap.mode-numbers .ig-trend-numbers{display:flex;}
  .ig-bar-tooltip{position:fixed; display:none; background:var(--c-tip-bg); border:1px solid var(--border); color:var(--c-tip-fg); font-size:var(--text-xs); padding:var(--space-4) var(--space-8); border-radius:var(--radius-sm); pointer-events:none; z-index:var(--z-tooltip); white-space:nowrap;}


  .ig-view-container{display:none; margin-top:var(--space-14);}
  .ig-view-container.active{display:block;}
  /* контейнер карточек/скролла совмещён (data-view="cards scroll") — его
     layout задаёт .ig-grid/.ig-scroll-row, а не общий display:block выше;
     нужна специфичность в 3 класса, чтобы выиграть у правила над этой строкой */
  .ig-view-container.active.ig-grid{display:grid;}
  .ig-view-container.active.ig-scroll-row{display:flex;}
  /* страховка: если ig-grid/ig-scroll-row всё же остался на неактивном
     контейнере (JS должен снимать его сам при переключении на "таблица"),
     не даём двум ниже стоящим правилам .ig-grid{display:grid}/.ig-scroll-row
     {display:flex} перебить display:none только за счёт порядка в файле */
  .ig-view-container:not(.active).ig-grid,
  .ig-view-container:not(.active).ig-scroll-row{display:none;}

  .ig-table-wrap{overflow-x:auto;}
  .ig-table tr.ig-group-row td{font-weight:var(--weight-semibold); background:var(--muted);}
  .ig-table .ig-indent{padding-left:var(--space-20); display:inline-block;}
  .ig-table-thumb{width:44px; height:44px; object-fit:cover; border-radius:var(--radius-sm); display:block;}
  .ig-link-sm{padding:var(--space-4) var(--space-10); font-size:var(--text-xs); margin-top:0;}

  .ig-chart-controls{margin-bottom:var(--space-6);}

  .ig-scoped-switch{margin:var(--space-6) 0 var(--space-14);}
  .ig-scoped-variant{display:none;}
  .ig-scoped-variant.active{display:block;}

  .ig-search-row{display:flex; align-items:center; gap:var(--space-10); margin-bottom:var(--space-10);}
  .ig-search-filter{flex:0 1 320px;}  /* поле — .field */
  .ig-search-count{color:var(--muted-fg); font-size:var(--text-sm); white-space:nowrap;}

  .ig-scroll-row{display:flex; gap:var(--space-16); overflow-x:auto; padding-bottom:var(--space-10);}
  .ig-scroll-row .ig-card{flex:0 0 240px;}

  .ig-day-table{margin-bottom:var(--space-24); border:1px solid var(--border); border-radius:var(--radius); overflow:hidden; overflow-x:auto; box-shadow:var(--shadow);}
  .ig-day-row{display:grid; grid-template-columns:100px 100px 70px 90px 100px 100px 80px 90px 100px 90px 90px 90px; gap:var(--space-4); align-items:center; padding:var(--space-10) var(--space-14); font-size:var(--text-sm);}
  .ig-day-header{background:var(--muted); color:var(--muted-fg); font-weight:var(--weight-semibold); font-size:var(--text-xs); text-transform:uppercase; letter-spacing:var(--tracking-caps);}
  .ig-day-block{border-top:1px solid var(--border);}
  .ig-day-block:first-of-type{border-top:none;}
  .ig-day-block summary{cursor:pointer; list-style:none;}
  .ig-day-block summary::-webkit-details-marker{display:none;}
  .ig-day-block[open] summary{background:var(--muted);}
  .ig-day-block summary:hover{background:var(--muted);}
  .ig-day-detail-table{background:var(--card);}
  .ig-day-detail-table th{padding:var(--space-6) var(--space-14); font-size:var(--text-xs);}
  .ig-day-detail-table td{padding:var(--space-6) var(--space-14);}

  @media (max-width:900px){
    .ig-day-row{grid-template-columns:repeat(5, minmax(70px,1fr)); font-size:var(--text-xs);}
  }

  .grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:var(--space-14);}
  /* Плитка KPI — .card.kpi, тренд — .trend (ds/components.css) */

  /* Все таблицы — table.table (ds/components.css), с этапа 11 без исключений */

  /* --- Узкий экран: сайдбар становится выдвижным меню --- */
  /* Раньше он просто разворачивался в горизонтальную полосу
     (flex-direction:row + overflow-x:auto). Вкладок семь, у каждой свой
     список разделов — в строку они не помещались, а раскрыть аккордеон
     было физически некуда: список открывается ВНИЗ, а полоса высотой в
     одну кнопку. Пользователь сообщил это как «папки не открываются».
     Разметка при этом остаётся ОДНА: второго меню для телефона нет,
     значит нечему и разойтись с десктопным. */
  .ig-nav-toggle{display:none;}
  .ig-nav-backdrop{display:none;}

  @media (max-width:900px){
    .ig-nav-toggle{display:flex; align-items:center; gap:var(--space-8); width:100%;
                   position:sticky; top:0; z-index:var(--z-header);
                   background:var(--bg); border:none; border-bottom:1px solid var(--border);
                   color:var(--fg); font-size:var(--text-base); font-weight:var(--weight-semibold);
                   padding:var(--space-12) var(--space-16); cursor:pointer; text-align:left;}
    .ig-nav-toggle-ic{display:flex; flex-shrink:0;}

    /* Меню уезжает из потока целиком (position:fixed), поэтому основная
       колонка получает всю ширину и ничего не надо перестраивать. */
    .ig-sidebar{position:fixed; left:0; top:0; z-index:var(--z-drawer);
                width:min(84vw, 300px); height:100dvh; padding:var(--space-20) var(--space-14);
                background:var(--bg); border-right:1px solid var(--border);
                overflow-y:auto; -webkit-overflow-scrolling:touch;
                transform:translateX(-100%); transition:transform var(--dur-base) ease;}
    .ig-shell.nav-open .ig-sidebar{transform:none; box-shadow:0 0 40px var(--scrim);}

    .ig-nav-backdrop{display:block; position:fixed; inset:0; z-index:var(--z-overlay);
                     background:var(--scrim); opacity:0; pointer-events:none;
                     transition:opacity var(--dur-base) ease;}
    .ig-shell.nav-open .ig-nav-backdrop{opacity:1; pointer-events:auto;}

    /* Пока меню открыто, страница под ним не должна прокручиваться:
       иначе палец «проскальзывает» на содержимое за краем меню. */
    body.ig-nav-locked{overflow:hidden;}

    /* Пункты меню — под палец: 8px по вертикали давали кнопку в 36 px,
       а комфортная цель касания начинается с 44. */
    .ig-source-btn, .ig-nav-item, .ig-acc-btn{padding:var(--space-12) var(--space-10);}
    .icon-btn.ig-collapse-btn{width:var(--control-lg); height:var(--control-lg);}

    main{padding:var(--space-20) var(--space-16) var(--space-64);}
    .ig-topbar{padding:var(--space-20) var(--space-16) var(--space-14);}
    .ig-topbar h1{font-size:var(--text-lg);}
  }

  /* На совсем узких экранах у плиток пропадает минимальная ширина в 220px —
     иначе строка KPI начинает горизонтально прокручивать всю страницу. */
  @media (max-width:600px){
    .grid{grid-template-columns:1fr; gap:var(--space-12);}
  }

  /* --- Динамика Reels --- */
  .ig-rd{display:flex; flex-direction:column; gap:var(--space-14);}
  .ig-rd-label{color:var(--muted-fg); font-size:var(--text-xs); font-weight:var(--weight-bold);
               text-transform:uppercase; letter-spacing:var(--tracking-caps);}
  .ig-rd-viewer{padding:var(--space-20);}  /* вид — .card.flat */
  .ig-rd-panel{display:none; gap:var(--space-20);}
  /* flex-wrap + flex-basis:100% у таблицы по дням: обложка и текст стоят в
     ряд, а разбивка по дням уходит на свою строку во всю ширину. */
  .ig-rd-panel.active{display:flex; flex-wrap:wrap;}
  .ig-rd-daily-wrap{flex:1 0 100%; min-width:0;}
  .ig-rd-cover{flex:0 0 240px;}
  .ig-rd-cover .ig-thumb-wrap{aspect-ratio:9/16; border-radius:var(--radius-md); overflow:hidden;}
  .ig-rd-cover-badge{position:absolute; left:8px; bottom:8px;}  /* вид — .badge.on-media */
  .ig-rd-body{flex:1; min-width:0; display:flex; flex-direction:column; gap:var(--space-12);}
  .ig-rd-head{display:flex; align-items:center; gap:var(--space-12); flex-wrap:wrap;}
  .ig-rd-date{font-size:var(--text-md); font-weight:var(--weight-semibold);}
  .ig-rd-er{display:inline-flex; align-items:center; gap:var(--space-4); font-size:var(--text-sm); font-weight:var(--weight-semibold);
            color:var(--accent-text); border:1px solid color-mix(in oklch, var(--accent-text) 40%, transparent);
            border-radius:var(--radius-full); padding:var(--space-4) var(--space-10);}
  .ig-rd-caption{color:var(--muted-fg); font-size:var(--text-base); line-height:var(--leading-normal);
                 max-height:150px; overflow-y:auto;}
  .ig-rd-stats{display:grid; grid-template-columns:repeat(auto-fit, minmax(96px, 1fr)); gap:var(--space-10);}
  .ig-rd-stat-v{font-size:var(--text-md); font-weight:var(--weight-semibold); font-variant-numeric:tabular-nums;
                white-space:nowrap;}
  .ig-rd-stat-l{color:var(--muted-fg); font-size:var(--text-xs);}
  .ig-rd-chart-head{display:flex; gap:var(--space-10); flex-wrap:wrap; justify-content:space-between; align-items:center;}
  /* подписки с публикации — самая частая причина смотреть статистику
     вообще, поэтому выделена цветом. У Reels API её не отдаёт, но число
     то же самое, только источник другой (веб-статистика) — карточка
     этого не показывает, различие только в примечании к коду. */
  .ig-stat-follows{color:var(--accent-text); font-weight:var(--weight-semibold);}
  /* доля просмотров от подписчиков (ПДП) и остальных */
  .ig-split{margin-top:var(--space-10); display:flex; flex-direction:column; gap:var(--space-4);}
  .ig-split-bar{height:6px; border-radius:var(--radius-full); overflow:hidden; background:var(--c-pink);}
  .ig-split-bar i{display:block; height:100%; background:var(--c-sky);}
  .ig-split-l{display:flex; justify-content:space-between; font-size:var(--text-xs); font-weight:var(--weight-semibold);
              font-variant-numeric:tabular-nums;}
  .ig-split-l span:first-child{color:var(--c-sky);}
  .ig-split-l span:last-child{color:var(--c-pink);}
  /* колонки "Подписки" / "ПДП/нет" в таблице публикаций — узкие проценты
     и знак "+" не должны переноситься на вторую строку */
  .ig-follows-col{white-space:nowrap;}
  /* переключатель «Всего / Прирост» не действует на суточные метрики */
  .ig-rd-modes-off{opacity:.35; pointer-events:none;}
  .ig-rd-chart{width:100%; height:230px;}
  /* .ig-link в колонке-флексе растягивался во всю ширину и выглядел как
     главная кнопка раздела — прижимаем к левому краю. */
  .ig-rd-body .ig-link{align-self:flex-start;}

  /* разбивка по дням для выбранной публикации — сворачиваемый <details>,
     а не div: таблица растёт с глубиной сбора (сейчас до 90+ строк на
     старых роликах), и без сворачивания панель ролика превращается в
     длинную простыню. Маркер браузера прячем и рисуем свой, тем же
     приёмом, что у .ig-detail — но крупнее, потому что здесь заголовок
     раздела, а не мелкая ссылка «показать подробнее». */
  .ig-rd-daily{display:flex; flex-direction:column; gap:var(--space-8);}
  .ig-rd-daily > summary{cursor:pointer; list-style:none; display:flex;
                          align-items:center; gap:var(--space-4); user-select:none;}
  .ig-rd-daily > summary::-webkit-details-marker{display:none;}
  .ig-rd-daily > summary::before{content:"›"; display:inline-block;
                                  font-size:var(--text-base); transition:transform var(--dur-fast);}
  .ig-rd-daily[open] > summary::before{transform:rotate(90deg);}
  .ig-rd-day-table td{white-space:nowrap;}
  .ig-rd-day-date{font-weight:var(--weight-semibold);}
  .ig-rd-day-first{display:block; color:var(--muted-fg); font-size:var(--text-xs); font-weight:var(--weight-regular);}
  .ig-rd-plus{color:var(--accent-text); font-weight:var(--weight-semibold); font-variant-numeric:tabular-nums;}
  .ig-rd-minus{color:var(--destructive); font-weight:var(--weight-semibold); font-variant-numeric:tabular-nums;}
  .ig-rd-zero{color:var(--muted-fg); font-variant-numeric:tabular-nums;}
  .ig-rd-day-total{display:block; color:var(--muted-fg); font-size:var(--text-xs);
                   font-variant-numeric:tabular-nums;}
  .ig-rd-day-table thead th{text-align:left; white-space:nowrap;}
  .ig-rd-day-none{color:var(--muted-fg); font-size:var(--text-xs);}
  /* столбец подписок с рилса отделён линией: он про ту же публикацию, но
     приезжает из веб-статистики со своим расписанием сбора, и прирост в
     нём считается по своим срезам, а не по срезам через API */
  .ig-rd-day-table .ig-rd-web-col{border-left:1px solid var(--border);}

  /* лента выбора публикации */
  .ig-rd-strip{display:flex; gap:var(--space-8); overflow-x:auto; padding-bottom:var(--space-6);}
  .ig-rd-thumb{position:relative; flex:0 0 84px; height:130px; padding:0; cursor:pointer;
               background:transparent; border:2px solid transparent;
               border-radius:var(--radius-md); overflow:hidden;}
  .ig-rd-thumb img{width:100%; height:100%; object-fit:cover; display:block;}
  .ig-rd-thumb.active{border-color:var(--accent-text);}
  .ig-rd-thumb-badge{position:absolute; left:4px; bottom:4px;}  /* вид — .badge.on-media */

  /* список всех Reels */
  .ig-rd-list-head{display:flex; align-items:center; gap:var(--space-14); flex-wrap:wrap;}
  .ig-rd-list-title{color:var(--muted-fg); font-size:var(--text-xs); font-weight:var(--weight-bold);
                    text-transform:uppercase; letter-spacing:var(--tracking-caps);}
  .ig-rd-list{overflow:hidden;}  /* вид — .card.flat */
  .ig-rd-row{display:flex; align-items:center; gap:var(--space-16); padding:var(--space-14) var(--space-20);
             border-bottom:1px solid var(--border); cursor:pointer;}
  .ig-rd-row:last-child{border-bottom:none;}
  .ig-rd-row:hover{background:var(--muted);}
  /* превью вместо даты: ролик узнают по обложке, дата переехала в мини-статы */
  .ig-rd-row-thumb{flex:0 0 54px; height:72px; border-radius:var(--radius-sm);
                   overflow:hidden; background:var(--muted);}
  .ig-rd-row-thumb img{width:100%; height:100%; object-fit:cover; display:block;}
  .ig-rd-row-when{color:var(--fg); font-weight:var(--weight-semibold); font-variant-numeric:tabular-nums;}
  .ig-rd-row-bars{flex:1; min-width:0; display:flex; flex-direction:column; gap:var(--space-4);}
  .ig-rd-bar{display:flex; align-items:center; gap:var(--space-8);}
  .ig-rd-bar-ic{color:var(--muted-fg); font-size:var(--text-xs); width:14px; flex-shrink:0;}
  .ig-rd-bar-track{flex:1; height:6px; background:var(--muted); border-radius:var(--radius-full); overflow:hidden;}
  .ig-rd-bar-track i{display:block; height:100%; border-radius:var(--radius-full);
                     background:linear-gradient(90deg, var(--c-sky), var(--c-mint));}
  .ig-rd-bar-track.alt i{background:linear-gradient(90deg, var(--c-amber), var(--c-pink));}
  .ig-rd-row-mini{display:flex; gap:var(--space-12); flex-wrap:wrap; color:var(--muted-fg); font-size:var(--text-xs);}
  .ig-rd-growth{color:var(--accent-text); font-weight:var(--weight-semibold);}
  /* тот же оттенок, что у web_follows на графике панели (IG_RD_COLORS в
     dashboard.js) -- один цвет на "подписки с рилса" везде, где они есть */
  .ig-rd-row-follows{color:var(--c-orange); font-weight:var(--weight-semibold);}
  .ig-rd-row-nums{flex:0 0 120px; text-align:right;}
  .ig-rd-row-views{font-size:var(--text-md); font-weight:var(--weight-semibold); font-variant-numeric:tabular-nums;}
  .ig-rd-row-views span, .ig-rd-row-er span{color:var(--muted-fg); font-size:var(--text-xs); font-weight:var(--weight-regular);}
  .ig-rd-row-er{color:var(--accent-text); font-size:var(--text-sm); font-weight:var(--weight-semibold);}

  @media (max-width:900px){
    /* Панель рассчитана на строку: обложка и текст рядом, а таблица по дням
       уходит под них (flex-wrap:wrap + flex-basis:100%). При повороте в
       колонку тот же wrap начинает переносить элементы в СОСЕДНИЕ КОЛОНКИ, и
       панель раздувается вширь — на телефоне страница становилась вдвое шире
       экрана (741 px при окне 390). В колонке переносить нечего, поэтому
       nowrap, а flex-basis сбрасывается: 100% в колонке считается от высоты. */
    .ig-rd-panel.active{flex-direction:column; flex-wrap:nowrap;}
    .ig-rd-cover{flex:0 0 auto; max-width:220px;}
    .ig-rd-body, .ig-rd-daily-wrap{flex:0 0 auto; width:100%; min-width:0;}
  }

  /* --- Просмотрщик: карусели и видео конкурентов --- */
  .ig-thumb-wrap{position:relative;}
  .ig-thumb-clickable{cursor:zoom-in;}
  .ig-carousel-badge{position:absolute; top:8px; right:8px;}  /* вид — .badge.on-media */
  .ig-play-btn{position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
               width:48px; height:48px; border-radius:var(--radius-circle); border:none; cursor:pointer;
               background:var(--scrim); color:var(--on-media); padding:0;
               display:flex; align-items:center; justify-content:center;}
  .ig-play-btn:hover{background:var(--accent); color:var(--accent-fg);}
  /* Видео-сторис без файла: иначе карточка неотличима от фото. */
  .ig-video-na{position:absolute; left:50%; bottom:10px; transform:translateX(-50%);
               display:flex; align-items:center; gap:var(--space-6); white-space:nowrap;
               padding:var(--space-4) var(--space-12); border-radius:var(--radius-full); border:1px solid var(--border);
               background:var(--media-badge); color:var(--on-media); font-size:var(--text-sm); text-decoration:none;}
  .ig-video-na-ic{opacity:.7;}
  a.ig-video-na:hover{background:var(--accent); color:var(--accent-fg);}

  /* Стрелки карусели прямо на обложке карточки. Видны всегда (не только
     на hover): иначе о том, что карточку можно листать, узнаёшь случайно. */
  /* Вид — .icon-btn.on-media (ds/components.css), здесь только место. */
  .ig-slide-btn{position:absolute; top:50%; transform:translateY(-50%); opacity:.85;}
  .ig-thumb-wrap:hover .ig-slide-btn{opacity:1;}
  .ig-slide-prev{left:var(--space-6);}
  .ig-slide-next{right:var(--space-6);}

  /* Плеер прямо в карточке. Окно поверх ленты перекрывало соседние
     публикации, поэтому Reels играет на месте обложки: карточка на время
     воспроизведения принимает пропорции ролика (их ставит JS по
     videoWidth/videoHeight — обложка квадратная, а Reels вертикальные). */
  /* z-index 3 и 4 — слои внутри обложки (плеер над значками, крестик над
     плеером), не страницы: шкала --z-* к ним не относится, в храповике.
     Крестик плеера — .icon-btn.sm.on-media, здесь только место. */
  .ig-card-player{position:absolute; inset:0; z-index:3; background:var(--media-bg);
                  display:flex; align-items:center; justify-content:center;}
  .ig-card-video{width:100%; height:100%; object-fit:contain; display:block; background:var(--media-bg);}
  .ig-video-close{position:absolute; top:6px; right:6px; z-index:4;}
  .ig-card-loading{position:absolute; inset:0; display:flex; align-items:center;
                   justify-content:center; color:var(--muted-fg); font-size:var(--text-sm);}
  .ig-card-msg{color:var(--on-media); font-size:var(--text-sm); text-align:center; padding:var(--space-16); line-height:var(--leading-normal);}
  .ig-card-msg a{color:var(--accent);}
  /* Пока играет ролик, всё, что лежало на обложке, только мешает. */
  .ig-thumb-wrap.ig-playing .ig-play-btn,
  .ig-thumb-wrap.ig-playing .ig-slide-btn,
  .ig-thumb-wrap.ig-playing .ig-carousel-badge,
  .ig-thumb-wrap.ig-playing .ig-type-badge,
  .ig-thumb-wrap.ig-playing .ig-viral-badge{display:none;}

  /* Просмотрщик — компонент .lightbox (ds/components.css); здесь только
     обёртка кадра: проценты картинки от контейнера без высоты не работают. */
  .ig-lightbox-body{display:flex; align-items:center; justify-content:center;
                    max-width:100%; max-height:100%; min-width:0; min-height:0;}
