/* ===== Табло МЦ (JMB) — ЖИВЫЕ надстройки над статичным дизайном MC =====
   theme.css + board.css скопированы из MC 1:1 (пиксель-парити). Здесь ТОЛЬКО то, чего нет у статичного
   MC: экран логина, цветные статус-точки объектов (online/alarm/offline/unlinked) и индикатор связи табло.
   Дизайн-токены (--panel/--ink/--accent/...) берём из theme.css — не переопределяем. */

/* ---------- Телефон поста — аккуратный эллипсис вместо тихого горизонтального клипа ----------
   board.css: `.pp-tel .tel{white-space:nowrap}` внутри `.pp-head{display:flex}` — у flex-элемента ДЕФОЛТНЫЙ
   min-width:auto (НЕ 0) не даёт ему сжаться ниже natural nowrap-ширины текста; на длинных реальных номерах
   («+7 (909) 195-10-37» — длиннее плейсхолдеров «+7 (000) 000-00-0N», на которых верстался MC) это раздувает
   .pp-head за пределы карточки, а `.pcard{overflow:hidden}` молча СРЕЗАЕТ хвост номера без «…» (владелец
   этого не просил чинить явно, но обнаружено при доводке анти-клип коррекции постов — см. wallboard.js
   correctOverflowingZones()/headroomOk(): БЕЗ этого фикса headroomOm видел «шапка и так впритык» на КАЖДОЙ
   карточке с длинным номером и блокировал докрутку колонок, нужную для ФИО/объектов). min-width:0 разрешает
   flex-элементу сжаться, text-overflow:ellipsis показывает «…» вместо обрубленных цифр — читаемо и предсказуемо. */
.pp-tel{ min-width:0; overflow:hidden; }
.pp-tel .tel{ display:inline-block; max-width:100%; overflow:hidden; text-overflow:ellipsis; vertical-align:bottom; }

/* ---------- Экран логина ---------- */
.login-screen{
  position:fixed; inset:0; z-index:50; display:grid; place-items:center;
  background:
    radial-gradient(1000px 620px at 82% -10%, var(--accent-soft), transparent 58%),
    linear-gradient(180deg, var(--bg-grad-1), var(--bg-grad-2));
}
/* 🔴 КРИТИЧНО: без этого правила `display:grid` выше ПЕРЕБИВАЕТ дефолтный UA-стиль браузера [hidden]{display:
   none} — авторские CSS-правила ВСЕГДА выигрывают у UA-стилей в каскаде, независимо от специфичности. wallboard.js
   ставит el.hidden=true после успешного логина (hideLogin()), атрибут выставляется корректно, но экран логина
   визуально остаётся на весь экран (position:fixed;inset:0;z-index:50) НАВСЕГДА, маскируя уже отрендеренную под
   собой доску — «жмёт Войти → тихо ничего не происходит», хотя логин/рендер отрабатывают штатно. Подтверждено
   вживую (headless Edge, CDP): getComputedStyle().display оставался "grid" при hidden-атрибуте true. */
.login-screen[hidden]{ display:none !important; }
.login-card{
  width:min(92vw, 360px); background:var(--panel); border:1px solid var(--border);
  border-radius:var(--radius); box-shadow:var(--shadow-hover); padding:26px 24px 22px;
}
.login-card .lc-brand{ display:flex; align-items:center; gap:12px; margin-bottom:18px; }
.login-card .lc-logo-img{ width:44px; height:44px; border-radius:12px; flex:0 0 auto; box-shadow:var(--shadow); object-fit:cover; }
.login-card h1{ font-size:17px; margin:0; line-height:1.2; letter-spacing:-.01em; }
.login-card .lc-sub{ font-size:12.5px; color:var(--ink-2); margin-top:2px; }
.login-card label{ display:block; font-size:12.5px; color:var(--ink-2); margin:12px 0 5px; }
.login-card input{
  width:100%; font:inherit; font-size:14px; color:var(--ink); background:var(--panel-2);
  border:1px solid var(--border); border-radius:10px; padding:10px 12px; outline:none;
}
.login-card input:focus{ border-color:var(--accent); }
.login-card button{
  width:100%; margin-top:18px; font:inherit; font-size:14px; font-weight:650; cursor:pointer;
  color:#fff; background:var(--accent); border:0; border-radius:10px; padding:11px 12px;
}
.login-card button:disabled{ opacity:.6; cursor:default; }
.login-card .lc-err{ margin-top:12px; font-size:12.5px; color:#dc2626; min-height:16px; }
:root[data-theme="dark"] .login-card .lc-err{ color:#f87171; }

/* ---------- Бренд JARWITS в сайдбаре (замена плейсхолдера «МЦ» на настоящий логотип) ----------
   board.css рисовал `.side .brand .logo` градиентным квадратом с текстом-инициалами — тут переиспользуем
   РЕАЛЬНЫЙ логотип JARWITS (тот же PNG, что в шапке JMC/JAD: frontend/monitoring/public/icons/
   jarwits_server.png, скопирован байт-в-байт в public/wallboard/icons/) вместо плейсхолдера. Разметка
   сменилась на <img class="logo-img">, поэтому board.css-правило `.logo` больше не матчится (мёртвое,
   но board.css не трогаем — вербатим-порт). h1 теперь пара «JARWITS» (бренд) + «Мониторинговый центр»
   (модуль/продукт) — тот же паттерн, что в шапке JMC/JAD (<strong>JARWITS</strong><span>Monitoring</span>). */
.side .brand .logo-img{
  width:4.6cqh; height:4.6cqh; border-radius:1.1cqh; flex:0 0 auto; box-shadow:var(--shadow); object-fit:cover;
}
.side .brand h1{ display:flex; align-items:baseline; gap:.4cqw; flex-wrap:wrap; }
.side .brand .brand-name{ font-weight:800; }
.side .brand .brand-sub{ font-weight:600; color:var(--ink-2); font-size:.92em; }

/* ---------- Статус-точка объекта (ЖИВОЕ, чего нет у MC) ----------
   Ненавязчиво в начале строки .obj: маленький кружок online/alarm/offline/unlinked. */
.obj{ position:relative; }
.obj .ost{
  width:.85cqh; height:.85cqh; border-radius:50%; flex:0 0 auto; align-self:center;
  box-shadow:0 0 0 .15cqh rgba(255,255,255,.06);
}
.ost.st-online{ background:#22c55e; }
.ost.st-offline{ background:var(--muted); }
.ost.st-unlinked{ background:#f59e0b; }
.ost.st-alarm{ background:#ef4444; animation:ost-pulse 1.4s ease-in-out infinite; }
@keyframes ost-pulse{ 0%,100%{ box-shadow:0 0 0 0 rgba(239,68,68,.55); } 50%{ box-shadow:0 0 0 .6cqh rgba(239,68,68,0); } }
/* Аларм-строка чуть подсвечена, чтобы ловилась взглядом на настенном табло */
.obj.obj-alarm{ background:color-mix(in srgb, #ef4444 12%, var(--panel-2)); }

/* ---------- Бейджи объекта: кол-во алармов / «не сопоставлен» (ЖИВОЕ, чего нет у MC) ----------
   Владелец: аларм-строка должна нести не только цвет точки, но и СКОЛЬКО алармов + название самого
   серьёзного (title = tooltip на всю строку, полный текст может быть длиннее бейджа). */
.obj .alarm-badge{
  flex:0 0 auto; font-size:1.05cqh; font-weight:800; color:#fff; letter-spacing:.01em;
  background:#dc2626; border-radius:.5cqh; padding:.12cqh .5cqw; white-space:nowrap;
  box-shadow:0 0 0 .12cqh rgba(220,38,38,.35);
}
.obj .unlinked-badge{
  flex:0 0 auto; font-size:1cqh; font-weight:650; color:var(--ink-2); font-style:italic;
  background:var(--panel); border:.1cqh dashed var(--border-strong); border-radius:.5cqh;
  padding:.1cqh .5cqw; white-space:nowrap;
}

/* ---------- Вспышка на ПЕРЕХОДЕ в alarm (JS: .obj-flash/.pcard-flash, авто-снимается ~3с) ----------
   Владелец: «кратковременная вспышка на переходе» — отдельно от постоянной пульсации точки/карточки,
   чтобы момент срабатывания был заметен даже боковым зрением через весь зал. */
@keyframes wb-flash{ 0%,100%{ filter:brightness(1); } 15%,45%{ filter:brightness(1.9); } 30%,60%{ filter:brightness(1); } }
.obj.obj-flash{ animation:wb-flash .5s ease-in-out 3; }
.pcard.pcard-flash{ animation:wb-flash .5s ease-in-out 3; }

/* ---------- Карточка поста с активным алармом ----------
   Владелец: «родительская карточка поста получает красный акцент/рамку — чтобы пост с алармящей скважиной
   сразу выделялся среди всех». Рамка+свечение+мягкая непрерывная пульсация, пока аларм активен. */
.pcard.pcard-has-alarm{
  border-color:#ef4444;
  box-shadow:0 0 0 .18cqh rgba(239,68,68,.45), 0 1px 8px rgba(0,0,0,.3);
  animation:pcard-alarm-pulse 1.8s ease-in-out infinite;
}
.pcard.pcard-has-alarm.city-a, .pcard.pcard-has-alarm.city-b{ border-top-color:#ef4444; }
@keyframes pcard-alarm-pulse{
  0%,100%{ box-shadow:0 0 0 .18cqh rgba(239,68,68,.45), 0 1px 8px rgba(0,0,0,.3); }
  50%{ box-shadow:0 0 0 .4cqh rgba(239,68,68,.15), 0 1px 8px rgba(0,0,0,.3); }
}

/* ---------- Агрегаты статуса: шапка поста (.pp-agg-wrap) + шапка города (.zh-agg-wrap) ----------
   Владелец: «агрегат на уровне поста: сколько online/alarm/offline» + то же на уровне города, не только
   в общей легенде-сводке сайдбара. Счётчики строит wallboard.js (statusAggCounts/aggPartsHtml). */
.pp-agg-wrap, .zh-agg-wrap{ display:inline-flex; align-items:center; gap:.4cqw; }
.pp-agg{ font-size:1.1cqh; font-weight:750; font-variant-numeric:tabular-nums; }
.zh-agg{ font-size:1.3cqh; font-weight:750; font-variant-numeric:tabular-nums; }

/* ---------- Индикатор связи + версия сборки (в подвале сайдбара) ----------
   side-foot по умолчанию (board.css) — простой блок в одну строку (только .conn). Кладём версию туда же
   вторым элементом, flex разводит по краям — ненавязчиво, не конкурирует за внимание с алармами/статусами. */
.side-foot{ display:flex; align-items:center; justify-content:space-between; gap:8px; flex-wrap:wrap; }
.conn{ display:inline-flex; align-items:center; gap:.6cqw; }
.conn .cdot{ width:1cqh; height:1cqh; border-radius:50%; flex:0 0 auto; background:var(--muted); }
.conn.live .cdot{ background:#22c55e; }
.conn.stale .cdot{ background:#f59e0b; }
.conn.down .cdot{ background:#ef4444; }
.wb-version{ color:var(--muted); font-size:.95cqh; white-space:nowrap; }

/* Фото-контейнеры: пока грузится blob — показываем инициалы (в noimg), после fetch добавим <img>. */
.pm-ph img, .hc-ph img, .cc-ph img{ opacity:0; transition:opacity .3s ease; }
.pm-ph img.loaded, .hc-ph img.loaded, .cc-ph img.loaded{ opacity:1; }

/* ---------- Подсветка активной рабочей смены (07:00-19:00 день/19:00-07:00 ночь) ----------
   ОТДЕЛЬНО от солнечной темы выше — трудовой график фиксированный, солнце его не касается. .on-shift —
   оператор СЕЙЧАС на смене (акцентная рамка+бейдж), .off-shift — сменившийся (приглушён). .pmini.unassigned
   (смена не назначена) НИКОГДА не получает ни один класс — см. applyShiftHighlight() в wallboard.js
   (владелец: «не подсвечивай» пустые слоты). tick() живьём переключает классы на границе 07:00/19:00 —
   transition ниже даёт плавную (не резкую) смену, а не мгновенный скачок контраста раз в 12 часов. */
.pmini{ transition: opacity .6s ease, filter .6s ease; }
.pmini.on-shift .pm-ph{ box-shadow: 0 0 0 .18cqh var(--accent), var(--shadow); transition: box-shadow .6s ease; }
.pmini.on-shift .pm-name{ font-weight: 800; color: var(--ink); }
.pmini.off-shift{ opacity: .6; }
.pmini.off-shift .pm-ph{ filter: saturate(.55); }
.on-shift-badge{
  display:inline-block; margin-top:.15cqh; font-size:.85cqh; font-weight:700; letter-spacing:.03em;
  color:#fff; background:var(--accent); padding:.15cqh .5cqw; border-radius:.5cqh; text-transform:uppercase;
}

/* ---------- Пустой ростер (ничего не заведено в JMC → «Табло МЦ») ----------
   Без этого блока пустые данные дают ПУСТУЮ main-зону — визуально неотличимо от «ничего не произошло
   после логина». Явно говорим: вход выполнен, просто нет данных. */
.main-empty{ display:flex; align-items:center; justify-content:center; }
.empty-state{
  text-align:center; max-width:70cqw; padding:3cqh 3cqw;
  background:var(--panel-2); border:.15cqh dashed var(--border-strong); border-radius:1.2cqh;
}
.empty-state .es-ico{ font-size:5cqh; line-height:1; margin-bottom:1cqh; opacity:.7; }
.empty-state .es-title{ font-size:2.1cqh; font-weight:750; color:var(--ink); margin-bottom:.8cqh; }
.empty-state .es-sub{ font-size:1.4cqh; color:var(--ink-2); line-height:1.4; }

/* ---------- Шкала суток по РЕАЛЬНОМУ восходу/закату (не фикс. 07-19 из board.css) ----------
   board.css рисует градиент .dayprog с ЗАХАРДКОЖЕННЫМИ границами 29.17%/79.17% (=07:00/19:00) — вербатим,
   не трогаем; переопределяем background тем же селектором (wallboard.css грузится ПОСЛЕ board.css —
   выигрывает по порядку каскада, доп. специфичность не нужна). Границы — CSS-переменные --wb-sunrise-pct/
   --wb-sunset-pct, которые wallboard.js синхронизирует с солнечным расчётом (SUN_LAT/SUN_LON) раз в
   календарный день. Fallback 29.17%/79.17% — тот же 07-19, если JS ещё не отработал (первый кадр). */
.dayprog{
  background: linear-gradient(90deg,
    var(--night-line) 0 var(--wb-sunrise-pct, 29.17%),
    var(--day-line) var(--wb-sunrise-pct, 29.17%) var(--wb-sunset-pct, 79.17%),
    var(--night-line) var(--wb-sunset-pct, 79.17%) 100%);
}

/* ---------- ZOOM-FIT: равномерный масштаб ВСЕЙ доски (откат от растяжения, владелец 2026-08-17) ----------
   🔴 Прошлый подход (резолюшн-автоподгон + fit-to-viewport) РЕФЛОУИЛ контент под экран — auto-fit колонки
   растягивали карточки при малом числе постов, разная высота, пустой «Смена не назначена» на пол-экрана,
   посты не выровнены (владелец на скрине горизонта: «доска разреженная/битая»). ПРАВИЛЬНЫЙ подход: board.css
   ОСТАЁТСЯ ВЕРБАТИМ (карточки/сетка/пропорции 1:1 MC, НИЧЕГО не тянем/не рефлоуим — .zone-grid снова на
   board.css'ных repeat(var(--cols),1fr) колонках и repeat(3,1fr) рядах, БЕЗ переопределения). .stage вместо
   letterbox-формулы board.css держит ФИКСИРОВАННЫЙ дизайнерский размер DESIGN_W×DESIGN_H=1920×1080 (16:9,
   тот же формат, под который рисовал MC — «настенный 16:9 = отполированный оригинал»), cqw/cqh считаются
   от него КАК ОБЫЧНО (card-пропорции пиксель-в-пиксель как в оригинале). computeZoomFit() (wallboard.js)
   вычисляет РАВНОМЕРНЫЙ scale=min(vw/1920, vh/1080) и пишет --wb-zoom → CSS применяет transform:scale()
   К ЦЕЛОЙ .stage — как зум, НЕ растяжение отдельных элементов. На точном 16:9 — заполняет экран впритык
   (zoom≈1). На иных пропорциях — небольшой леттербокс по ОДНОЙ оси (владелец: «сохранение пропорций важнее
   заполнения пикселей» — это НЕ потеря места, честный zoom). Портрет — ТА ЖЕ доска, просто уменьшенная (не
   отдельная раскладка). #app — flex-центрирование, чтобы .stage (её LAYOUT-бокс остаётся 1920×1080 ДО
   transform, сам transform не меняет layout-занимаемое место) визуально центрировался: центр flex-центрирования
   и transform-origin:center совпадают, поэтому scale «схлопывается» к центру симметрично — стандартный CSS-
   паттерн «зум с центрированием» (overflow:hidden на #app/body — двойная страховка от layout-переполнения). */
#app{ width:100%; height:100%; display:flex; align-items:center; justify-content:center; overflow:hidden; }
.stage{
  width: 1920px; height: 1080px;
  flex: 0 0 auto;
  transform: scale(var(--wb-zoom, 1));
  transform-origin: center center;
}
