:root{
  --bg:#f6f7f9; --card:#fff; --ink:#12161c; --muted:#5b6572; --line:#e2e6ec;
  --accent:#1f6feb; --alert:#d92d20; --alert-bg:#fef3f2; --clear:#0e9f6e; --clear-bg:#f0fdf6;
  --stale:#6b7280; --stale-bg:#f3f4f6; --grid:#eceff3;
  --warn:#b54708; --warn-bg:#fffaeb; --warn-rgb:181,71,8; --water:#c7ddf5;
  --c1:#1f6feb; --c2:#d92d20; --c3:#0e9f6e; --c4:#b54708; --c5:#6941c6;
  --hm0:#eceff3; --hm1:#cfe3fb; --hm2:#8fc0f6; --hm3:#4a95ec; --hm4:#1f6feb; --hm5:#0b3f8f;
}
/* Тёмная палитра задаётся дважды намеренно:
   — по системной настройке, но ТОЛЬКО если человек не выбрал светлую руками;
   — по явному выбору, который обязан перебивать систему в обе стороны. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
  --bg:#0d1117; --card:#161b22; --ink:#e6edf3; --muted:#9198a1; --line:#262c36;
  --accent:#4493f8; --alert:#ff6b63; --alert-bg:#2a1614; --clear:#3fb950; --clear-bg:#11221a;
  --stale:#8b949e; --stale-bg:#1b1f25; --grid:#21262d;
  --warn:#e3a008; --warn-bg:#2a2011; --warn-rgb:227,160,8; --water:#16324f;
  --c1:#4493f8; --c2:#ff6b63; --c3:#3fb950; --c4:#e3a008; --c5:#a371f7;
  --hm0:#1b2027; --hm1:#0d2f5e; --hm2:#134a8e; --hm3:#1f6feb; --hm4:#4493f8; --hm5:#8fc0f6;
  }
}
:root[data-theme="dark"]{
  --bg:#0d1117; --card:#161b22; --ink:#e6edf3; --muted:#9198a1; --line:#262c36;
  --accent:#4493f8; --alert:#ff6b63; --alert-bg:#2a1614; --clear:#3fb950; --clear-bg:#11221a;
  --stale:#8b949e; --stale-bg:#1b1f25; --grid:#21262d;
  --warn:#e3a008; --warn-bg:#2a2011; --warn-rgb:227,160,8; --water:#16324f;
  --c1:#4493f8; --c2:#ff6b63; --c3:#3fb950; --c4:#e3a008; --c5:#a371f7;
  --hm0:#1b2027; --hm1:#0d2f5e; --hm2:#134a8e; --hm3:#1f6feb; --hm4:#4493f8; --hm5:#8fc0f6;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
  font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  -webkit-text-size-adjust:100%}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
.wrap{max-width:1120px;margin:0 auto;padding:0 16px}

header.top{border-bottom:1px solid var(--line);background:var(--card);position:sticky;top:0;z-index:20}
/* height:56px при переносе nav обрезал вторую строку — она вываливалась
   поверх контента. Высота по содержимому, на узких экранах nav уходит
   на свою строку. */
.top .wrap{display:flex;align-items:center;gap:16px;min-height:56px;flex-wrap:wrap;padding-top:8px;padding-bottom:8px}
.brand{font-weight:650;letter-spacing:-.01em;white-space:nowrap}
.brand span{color:var(--muted);font-weight:400}
nav.main{display:flex;gap:14px;margin-left:auto;flex-wrap:wrap}
nav.main a{color:var(--muted);padding:4px 0;border-bottom:2px solid transparent}
nav.main a.on{color:var(--ink);border-color:var(--accent)}
@media (max-width:640px){
  .top .wrap{gap:10px}
  .brand{margin-right:auto}
  nav.main{order:3;width:100%;margin-left:0;gap:16px;border-top:1px solid var(--line);padding-top:6px}
}
.clock{display:flex;align-items:baseline;gap:7px;white-space:nowrap}
.clock .city{color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.04em}
.clock time{font-size:15px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:.01em}
@media (max-width:640px){.clock .city{display:none}}
@media (max-width:420px){.clock{display:none}}
.langs{display:flex;gap:6px}
.langs a{font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);
  border:1px solid var(--line);border-radius:6px;padding:3px 7px}
.langs a.on{background:var(--accent);border-color:var(--accent);color:#fff}

/* ---- карточка статуса ---- */
.status{margin:20px 0;border-radius:14px;border:1px solid var(--line);background:var(--card);
  padding:20px 22px;display:flex;align-items:flex-start;gap:20px;flex-wrap:wrap}
.status.on{border-color:var(--alert);background:var(--alert-bg)}
.status.off{border-color:var(--clear);background:var(--clear-bg)}
.status.stale{border-color:var(--line);background:var(--stale-bg)}
.dot{width:14px;height:14px;border-radius:50%;flex:none}
.status.on .dot{background:var(--alert);box-shadow:0 0 0 0 rgba(217,45,32,.6);animation:pulse 2s infinite}
.status.off .dot{background:var(--clear)}
.status.stale .dot{background:var(--stale)}
@keyframes pulse{70%{box-shadow:0 0 0 12px rgba(217,45,32,0)}100%{box-shadow:0 0 0 0 rgba(217,45,32,0)}}
@media (prefers-reduced-motion:reduce){.status.on .dot{animation:none}}
/* Жёлтый уровень: alerts.in.ua отличает «жовта» тривогу от «червоної», и на
   карточке это должно читаться цветом, а не только меткой. Пульс своим
   ключевым кадром — rgba() красного в него зашита числами. */
.status.on.lvl-yellow{border-color:var(--warn);background:var(--warn-bg)}
.status.on.lvl-yellow .status-h{color:var(--warn)}
.status.on.lvl-yellow .dot{background:var(--warn);
  box-shadow:0 0 0 0 rgba(var(--warn-rgb),.6);animation:pulse-warn 2s infinite}
@keyframes pulse-warn{70%{box-shadow:0 0 0 12px rgba(var(--warn-rgb),0)}
  100%{box-shadow:0 0 0 0 rgba(var(--warn-rgb),0)}}
@media (prefers-reduced-motion:reduce){.status.on.lvl-yellow .dot{animation:none}}
.status .status-h{margin:0;font-size:24px;font-weight:650;letter-spacing:-.02em}
.status.on .status-h{color:var(--alert)} .status.off .status-h{color:var(--clear)}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
.status .meta{color:var(--muted);font-size:14px;margin-top:3px}
/* Текст карточки делит строку с точкой, а не уходит под неё: длинная строка
   «сколько ещё» иначе выталкивала блок на новую строку и точка оставалась одна.
   Точка — вровень с заголовком, а не посередине выросшего блока. */
.status .dot{margin-top:8px}
.status-body{flex:1 1 240px;min-width:0}
.status .threats{align-self:center}
/* «Сколько ещё»: сама оценка читается основным цветом, оговорка — приглушённо.
   Ширина ограничена, иначе на десктопе фраза тянется в одну строку на всю карточку. */
.status .remain{margin:10px 0 0;max-width:62ch;color:var(--ink);font-size:15px;font-weight:550}
.status .remain-note{margin:3px 0 0;max-width:62ch;color:var(--muted);font-size:12.5px}
.threats{display:flex;gap:8px;margin-left:auto;flex-wrap:wrap}
.chip{border:1px solid var(--alert);color:var(--alert);border-radius:999px;
  padding:5px 12px;font-size:13px;font-weight:550;background:var(--card)}
.chip.lvl-red{border-color:var(--alert);color:var(--alert)}
.chip.lvl-yellow{border-color:var(--c4);color:var(--c4)}
.status.lvl-yellow .chip{border-color:var(--warn);color:var(--warn)}
.status.lvl-yellow .chip.lvl-red{border-color:var(--alert);color:var(--alert)}

/* ---- сетки и карточки ---- */
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));gap:12px;margin:16px 0 28px}
.tile{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px}
.tile .v{font-size:24px;font-weight:650;letter-spacing:-.02em}
.tile .k{color:var(--muted);font-size:13px;margin-top:2px;overflow-wrap:anywhere}
.tile .s{color:var(--muted);font-size:12px;margin-top:4px}

section.card{background:var(--card);border:1px solid var(--line);border-radius:14px;
  padding:18px 20px;margin:0 0 18px}
section.card h2{margin:0 0 4px;font-size:18px;letter-spacing:-.01em}
section.card .hint{color:var(--muted);font-size:13px;margin:0 0 14px}
section.card h4.subhead{margin:26px 0 6px;font-size:14px;color:var(--muted);font-weight:550;
  text-transform:uppercase;letter-spacing:.03em;border-top:1px solid var(--line);padding-top:18px}
.note{border-left:3px solid var(--accent);background:var(--bg);padding:10px 14px;
  border-radius:0 8px 8px 0;color:var(--muted);font-size:13px;margin:0 0 14px}
.split{display:grid;grid-template-columns:1.15fr .85fr;gap:22px;align-items:start}
@media (max-width:820px){.split{grid-template-columns:1fr}}
.chartbox{position:relative;height:280px}
.scroll{overflow-x:auto}

table{border-collapse:collapse;width:100%;font-size:14px}
th,td{text-align:right;padding:7px 10px;border-bottom:1px solid var(--line);white-space:nowrap}
th:first-child,td:first-child{text-align:left}
thead th{color:var(--muted);font-weight:550;font-size:12px;text-transform:uppercase;letter-spacing:.03em}
tbody tr:hover{background:var(--bg)}
tfoot td{font-weight:600;border-top:2px solid var(--line);border-bottom:none}
td.dim{color:var(--muted)}

/* ---- журнал ---- */
form.filters{display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end;margin-bottom:16px}
form.filters label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--muted)}
input,select,button{font:inherit;color:var(--ink);background:var(--card);
  border:1px solid var(--line);border-radius:8px;padding:7px 10px}
button{background:var(--accent);border-color:var(--accent);color:#fff;cursor:pointer;font-weight:550}
button.ghost{background:var(--card);color:var(--ink);border-color:var(--line)}
.pager{display:flex;gap:10px;align-items:center;justify-content:center;margin-top:16px;color:var(--muted)}
.badge{font-size:12px;border:1px solid var(--line);border-radius:6px;padding:1px 6px;color:var(--muted)}

.prose{max-width:76ch}
.prose h2{font-size:18px;margin:24px 0 8px}
.prose p,.prose li{color:var(--ink)}
.prose code{background:var(--bg);border:1px solid var(--line);border-radius:5px;padding:1px 5px;font-size:13px}
footer{color:var(--muted);font-size:13px;border-top:1px solid var(--line);margin-top:32px;padding:18px 0 40px}
.anchors{position:sticky;top:72px;align-self:start;display:flex;flex-direction:column;gap:6px;font-size:13px}
.anchors a{color:var(--muted)}

/* ---- кликабельные строки и модалка месяца ---- */
tr.clickable{cursor:pointer}
tr.clickable:hover{background:var(--bg)}
tr.clickable:focus{outline:2px solid var(--accent);outline-offset:-2px}
tr.clickable td:first-child::after{content:'›';color:var(--muted);margin-left:8px}

/* display:flex в классе перебивает UA-правило [hidden]{display:none}, поэтому
   скрытая модалка накрывала страницу и перехватывала все клики. Скрыта по
   умолчанию, показывается только без атрибута hidden. */
.modal{position:fixed;inset:0;z-index:50;display:none;align-items:center;justify-content:center;padding:16px}
.modal:not([hidden]){display:flex}
.modal-back{position:absolute;inset:0;background:rgba(0,0,0,.55)}
.modal-card{position:relative;background:var(--card);border:1px solid var(--line);border-radius:14px;
  width:min(860px,100%);max-height:86vh;overflow-y:auto;padding:22px 24px;
  box-shadow:0 24px 60px rgba(0,0,0,.35)}
.modal-x{position:absolute;top:10px;right:12px;background:transparent;border:none;color:var(--muted);
  font-size:26px;line-height:1;padding:4px 8px;cursor:pointer}
.modal-x:hover{color:var(--ink)}
.modal-card h3{margin:0 0 2px;font-size:19px;letter-spacing:-.01em}
.modal-card h4{margin:20px 0 8px;font-size:14px;color:var(--muted);font-weight:550;
  text-transform:uppercase;letter-spacing:.03em}
.modal-card .hint{color:var(--muted);font-size:13px;margin:0 0 8px}
.modal-card tr.quiet td{color:var(--muted)}

.daybars{display:flex;align-items:flex-end;gap:3px;height:110px;margin:4px 0 14px;
  padding-bottom:16px;position:relative}
.daybar{flex:1;height:100%;display:flex;flex-direction:column;justify-content:flex-end;
  align-items:center;position:relative}
.daybar i{width:100%;background:var(--c1);border-radius:2px 2px 0 0;display:block;min-height:2px}
.daybar span{position:absolute;bottom:-16px;font-size:10px;color:var(--muted)}
.daybar:nth-child(even) span{visibility:hidden}
@media (max-width:560px){
  /* Совсем без подписей столбики теряют смысл — оставляем каждую пятую. */
  .daybar span{display:none}
  .daybar:nth-child(5n+1) span{display:block;visibility:visible}
}

/* ================= телефон ================= */
@media (max-width:640px){
  /* Шапка съедала 90 px из 844: ужимаем, не теряя навигацию. */
  .top .wrap{padding-top:6px;padding-bottom:4px;min-height:0;gap:8px}
  .brand{font-size:15px}
  .brand a{display:inline-block;padding:8px 2px}   /* было 17 px высоты — мало для пальца */
  nav.main{gap:18px;padding-top:4px;font-size:15px}
  nav.main a{padding:6px 0}
  .langs a{padding:8px 10px}          /* было 26 px по высоте — мало для пальца */

  .wrap{padding:0 12px}
  section.card{padding:16px 14px;border-radius:12px}
  /* Карта Києва на телефоні: viewBox 1000 одиниць лягає в ~350 CSS-пікселів,
     тобто кегль 10 стає 3.5 px. Дрібні підписи там не зменшені, а нечитані —
     у первинному масштабі лишаємо тільки великі міста. На наближенні кегль
     росте разом із картою, тож імена повертаються ступенями. */
  .kyivzoom.z-1 .km-dlabel,
  .kyivzoom.z-1 .km-place.area text,
  .kyivzoom.z-1 .km-place.t2 text,
  .kyivzoom.z-2 .km-place.area text{display:none}
  .kyivzoom.z-1 .km-place.t1 text{font-size:calc(21px / var(--zt))}
  .kyivzoom.z-1 .km-place.t1 circle{r:calc(4.5px / var(--z))}
  .kyivzoom.z-1 .km-place.t2 circle{r:calc(2.6px / var(--z))}
  .kyivmap .km-rings text{font-size:calc(16px / var(--z))}
  .kyivmap .km-rings-unit{font-size:calc(14px / var(--z))}
  .kyivmap .km-scale text{font-size:calc(18px / var(--z))}
  .kyivmap text{stroke-width:calc(4px / var(--z))}
  .zoomctl button{width:40px;height:40px}

  .status{padding:16px 16px;gap:14px}
  .status .status-h{font-size:21px}
  .status .dot{margin-top:7px}
  .threats{margin-left:0;width:100%}

  /* Плитки итогов — одна колонка читается лучше двух зажатых. */
  .tiles{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}
  .tile{padding:12px 14px}
  .tile .v{font-size:21px}

  /* Графики ниже: на телефоне высокий график вытесняет всё остальное. */
  .chartbox{height:220px}
  .chartbox#tall,section.card .chartbox[style]{height:220px !important}

  /* Ссылки и кнопки — не меньше 40 px по высоте. */
  .pager a,.badge.reset,.hint a{display:inline-block;padding:10px 12px;min-height:40px;line-height:20px}
  footer a{display:inline-block;padding:6px 2px}
  button,input,select{min-height:44px}

  /* Журнал: 4 колонки не влезают, и «Тривалість» с типом угрозы уезжали за
     край без единого намёка на прокрутку. Раскладываем строку карточкой. */
  table.cardable thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  table.cardable,table.cardable tbody,table.cardable tr,table.cardable td{display:block;width:auto}
  table.cardable tr{border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:8px;
    background:var(--card)}
  table.cardable tr:hover{background:var(--card)}
  table.cardable td{border:none;padding:2px 0;text-align:left;white-space:normal;
    display:flex;justify-content:space-between;gap:12px}
  table.cardable td::before{content:attr(data-l);color:var(--muted);font-size:12px;
    text-transform:uppercase;letter-spacing:.03em;flex:none}
  table.cardable td:first-child{font-weight:600;font-size:15px}

  /* Фильтры под раскрывашкой: половину экрана они занимать не должны. */
  .filterbox summary{cursor:pointer;padding:10px 0;font-weight:550;list-style:none}
  .filterbox summary::-webkit-details-marker{display:none}
  .filterbox summary::before{content:'⚙ ';color:var(--muted)}
  form.filters{gap:10px}
  form.filters label{width:100%}
  form.filters input,form.filters select{width:100%}

  /* У части таблиц задан inline max-height — на десктопе это аккуратный
     внутренний скролл, на телефоне получается прокрутка внутри прокрутки:
     раскрытые строки уезжают в окошко высотой 320 px. Снимаем, длину
     контролирует свёртка «показать все». */
  section.card .scroll[style]{max-height:none !important}

  /* Модалка на телефоне — во весь экран, без «окна в окне». */
  .modal{padding:0}
  .modal-card{width:100%;max-height:100vh;height:100vh;border-radius:0;padding:18px 14px 28px}
  .daybars{height:90px}
}

/* Фильтры раскрыты всегда, кроме телефона. */
@media (min-width:641px){
  .filterbox summary{display:none}
  .filterbox{display:block}
}

/* Свёрнутые длинные таблицы (включает JS только на узких экранах). */
.tblwrap.collapsed{max-height:none}
/* !important не для красоты: на телефоне у журнала строки становятся блоками
   правилом карточной раскладки (table.cardable tr), которое иначе её перебивает. */
.tblwrap.collapsed tbody tr:nth-child(n+13){display:none !important}
.tblmore{display:block;width:100%;margin-top:10px;background:var(--card);color:var(--accent);
  border:1px solid var(--line);border-radius:10px;padding:11px;font-weight:550;cursor:pointer}

/* Лента переходов по разделам. Только телефон: на десктопе вся страница
   обозрима колесом, а тут это восемь экранов прокрутки. */
.secnav{display:none}
@media (max-width:640px){
  .secnav{display:flex;gap:8px;overflow-x:auto;padding:2px 0 12px;margin:0 -12px 4px;
    padding-left:12px;padding-right:12px;-webkit-overflow-scrolling:touch;
    scrollbar-width:none}
  .secnav::-webkit-scrollbar{display:none}
  .secnav a{flex:none;background:var(--card);border:1px solid var(--line);border-radius:999px;
    padding:9px 14px;font-size:13px;color:var(--muted);white-space:nowrap}
  .secnav a:active{background:var(--bg)}
  /* Якорь не должен уезжать под липкую шапку. */
  section.card{scroll-margin-top:96px}
}

/* ---- переключатель темы ---- */
.themebtn{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;
  padding:0;background:var(--card);color:var(--muted);border:1px solid var(--line);
  border-radius:9px;cursor:pointer}
.themebtn:hover{color:var(--ink)}
.themebtn svg{width:18px;height:18px;display:block}
/* В тёмной теме предлагаем светлую (солнце), в светлой — тёмную (луна). */
.themebtn .sun{display:none}
:root[data-theme="dark"] .themebtn .sun{display:block}
:root[data-theme="dark"] .themebtn .moon{display:none}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .themebtn .sun{display:block}
  :root:not([data-theme="light"]) .themebtn .moon{display:none}
}
@media (max-width:640px){.themebtn{width:40px;height:40px}}

/* ---- карта Украины ---- */
.mapwrap{margin:4px 0 12px}
.uamap{width:100%;height:auto;display:block;max-height:min(66vh,560px);margin:0 auto}
.uamap .rg{fill:var(--hm0);stroke:var(--line);stroke-width:1.1;stroke-linejoin:round;
  transition:fill .15s}
.uamap .rg:hover{stroke:var(--accent);stroke-width:1.6}
/* Сплошная заливка — тревога на всю область, штриховка — только часть районов.
   Узор задаётся ИМЕННО в CSS: правило .rg{fill:...} перебивает атрибут fill
   в разметке (в SVG CSS сильнее презентационных атрибутов), и штриховка
   молча пропадала. */
.uamap .lv-red.sc-full{fill:var(--alert)}
.uamap .lv-yellow.sc-full{fill:var(--c4)}
.uamap .lv-red.sc-partial{fill:url(#hatch-red)}
.uamap .lv-yellow.sc-partial{fill:url(#hatch-yellow)}
.uamap .hatch-bg{fill:var(--hm0)}
.uamap .hatch-red{fill:var(--alert)}
.uamap .hatch-yellow{fill:var(--c4)}

.maplegend{display:flex;flex-wrap:wrap;gap:14px;color:var(--muted);font-size:13px;margin-bottom:12px}
.maplegend span{display:inline-flex;align-items:center;gap:6px}
.maplegend .sw{width:14px;height:14px;border-radius:3px;border:1px solid var(--line);display:block}
.maplegend .sw.none{background:var(--hm0)}
.maplegend .sw.red{background:var(--alert);border-color:var(--alert)}
.maplegend .sw.yellow{background:var(--c4);border-color:var(--c4)}
.maplegend .sw.part{background:repeating-linear-gradient(45deg,var(--alert) 0 3px,var(--hm0) 3px 7px)}
.lvl{white-space:nowrap}
.lvdot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:7px;vertical-align:1px}
.lvdot.lv-red{background:var(--alert)} .lvdot.lv-yellow{background:var(--c4)}
/* Киев — метка: сам город на карте страны занимает несколько пикселей. */
.uamap .kyivmark{fill:none;stroke:var(--muted);stroke-width:2}
.uamap .kyivmark.lv-red{stroke:var(--alert);stroke-width:3}
.uamap .kyivmark.lv-yellow{stroke:var(--c4);stroke-width:3}
.srcline{border-top:1px solid var(--line);padding-top:12px;margin-top:14px}

/* ---- карта Києва зблизька ---- */
/* viewBox майже квадратний (60 км на всі боки від центру), а картка широка:
   з height:auto + max-height малюнок лишався в межах viewBox, але все, що
   геометрія має ЗА його межами, лізло в поля і карта здавалась удвічі ширшою
   за свою рамку. Явна пропорція + overflow тримають кадр таким, як задумано. */
/* viewBox майже квадратний (60 км на всі боки від центру), а картка широка:
   з height:auto + max-height малюнок лишався в межах viewBox, але все, що
   геометрія має ЗА його межами, лізло в поля і карта здавалась удвічі ширшою
   за свою рамку. Явна пропорція + overflow тримають кадр таким, як задумано. */
.kyivmap{display:block;width:100%;max-width:min(100%,620px);aspect-ratio:1000/1004;
  height:auto;margin:0 auto;overflow:hidden;--z:1;--zt:1;touch-action:pan-y;cursor:grab}
.kyivmap:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.kyivmap.dragging{cursor:grabbing}
/* ЩО МАСШТАБУЄТЬСЯ, А ЩО НІ. Дві змінні, обидві ставить JS на самому svg.
   --z  — власне масштаб. На нього ДІЛЯТЬСЯ лінії, крапки і службові написи
          (кілометри кілець, масштабна лінійка): дороги мусять лишатись
          волосяними, а не перетворюватись на смуги.
   --zt — пом'якшений масштаб, z^0.65. На нього діляться підписи карти. Ділити
          їх на --z не можна: на телефоні назва місцевості назавжди лишилась
          би 3.5 CSS-пікселя і наближення не давало б нічого. Не ділити зовсім
          теж не можна: на z=4 підпис «ВИШГОРОД» накривав пів міста. */
/* Заливка області потрібна саме для того, щоб fill-rule="evenodd" на
   двоколовому контурі (зовнішня межа + дірка під містом) було видно: без
   заливки дірка нічим не відрізнялася б від тла і не мала б сенсу.
   Без цього правила path успадковує початкове fill=black і область стає
   чорною — саме так і сталося при перекладанні блоку під масштабування. */
.kyivmap .km-oblast{fill:var(--hm0);stroke:var(--line);opacity:1;
  stroke-width:calc(1.5px / var(--z));
  stroke-dasharray:calc(6px / var(--z)) calc(5px / var(--z))}
.kyivmap .km-raion{fill:none;stroke:var(--muted);opacity:.3;
  stroke-width:calc(.7px / var(--z));
  stroke-dasharray:calc(5px / var(--z)) calc(4px / var(--z))}
.kyivmap .km-urban{fill:var(--hm1);stroke:none;opacity:.55}
.kyivmap .km-water{fill:var(--water);stroke:none}
.kyivmap .km-district{fill:none;stroke:var(--line);opacity:.9;
  stroke-width:calc(1px / var(--z))}
.kyivmap .km-road{fill:none;stroke:var(--muted);opacity:.6;
  stroke-width:calc(1px / var(--z));stroke-linecap:round;stroke-linejoin:round}
.kyivmap .km-road2{fill:none;stroke:var(--muted);opacity:.42;
  stroke-width:calc(.7px / var(--z));stroke-linecap:round;stroke-linejoin:round}
/* Кільця відстані: тонкі, щоб не сперечатися з мітками, але видимі —
   без них не зрозуміти, ціль над містом чи за 40 км від нього. */
.kyivmap .km-rings circle{fill:none;stroke:var(--muted);opacity:.45;
  stroke-width:calc(.7px / var(--z));
  stroke-dasharray:calc(3px / var(--z)) calc(6px / var(--z))}
.kyivmap .km-rings text{fill:var(--muted);opacity:.75;
  font-size:calc(9px / var(--z));font-variant-numeric:tabular-nums}
.kyivmap .km-rings-unit{font-size:calc(8px / var(--z))}
/* Підписи: обведення кольором тла робить їх читаними поверх будь-якого шару.
   paint-order обов'язковий, інакше обведення ляже ПОВЕРХ літер і з'їсть їх. */
.kyivmap text{paint-order:stroke fill;stroke:var(--card);
  stroke-width:calc(2.4px / var(--z));stroke-linejoin:round;font-family:inherit}
.kyivmap .km-place text{font-size:calc(10px / var(--zt));fill:var(--muted)}
.kyivmap .km-place.town text{font-size:calc(11.5px / var(--zt));fill:var(--ink);font-weight:550}
.kyivmap .km-place circle{fill:var(--muted);stroke:none;r:calc(1.6px / var(--z))}
.kyivmap .km-place.town circle{fill:var(--ink);r:calc(2.6px / var(--z))}
.kyivmap .km-place.area text{opacity:.85}
.kyivmap .km-dlabel{font-size:calc(10px / var(--zt));fill:var(--muted);font-weight:550;
  text-anchor:middle;text-transform:uppercase;letter-spacing:.04em;opacity:.7}
.kyivmap .km-scale line{stroke:var(--ink);stroke-width:calc(1.4px / var(--z))}
.kyivmap .km-scale text{font-size:calc(10px / var(--z));fill:var(--ink)}
/* Підписи, яким у первинному масштабі місця не лишилось: генератор їх усе
   одно віддає, а показуємо на близькому масштабі, де місце вже є. */
.kyivzoom .km-place.crowded text{display:none}
.kyivzoom.z-3 .km-place.crowded text{display:block}

/* Кнопки масштабу. Жести необовʼязкові: будь-яку дію можна зробити тут або
   з клавіатури, тому кнопки видно завжди, а не лише при наведенні. */
/* Без JS масштабувати нічим, тому кнопки й підказка зʼявляються лише коли
   скрипт справді підхопив карту: кнопка, яка нічого не робить, гірша за
   відсутню. Клас ready ставить той самий JS. */
.kyivzoom{position:relative;width:100%;max-width:min(100%,620px);margin:0 auto}
.zoomctl,.zoomhelp,.zoomhint{display:none}
.kyivzoom.ready .zoomhelp{display:block}
.kyivzoom.ready .zoomhint:not([hidden]){display:block}
.kyivzoom.ready .zoomctl{position:absolute;top:8px;right:8px;display:flex;
  flex-direction:column;gap:4px;z-index:2}
.zoomctl button{width:34px;height:34px;border:1px solid var(--line);border-radius:8px;
  background:var(--card);color:var(--ink);font-size:17px;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:0}
.zoomctl button:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}
.zoomctl button:disabled{opacity:.4;cursor:default}
.zoomhint{position:absolute;left:50%;top:12px;transform:translateX(-50%);margin:0;
  background:var(--card);border:1px solid var(--line);border-radius:8px;
  padding:6px 12px;font-size:13px;color:var(--muted);z-index:2;pointer-events:none}
.zoomhelp{margin:6px 0 0;text-align:center}
.zoomctl button svg{width:18px;height:18px;display:block}
.zfull .ic-out{display:none}
.kyivzoom.fs .zfull .ic-in{display:none}
.kyivzoom.fs .zfull .ic-out{display:block}

/* НА ВЕСЬ ЕКРАН. Два шляхи, один вигляд. Де є Fullscreen API — просимо його
   для всієї обгортки (не для svg: інакше кнопки лишились би за кадром).
   Де його немає — а iOS Safari дозволяє повний екран лише для відео —
   кладемо ту саму обгортку накладкою position:fixed. Обидва шляхи вмикають
   клас .fs, і решта правил не знає, яким з них ми сюди потрапили. */
.kyivzoom.fs{max-width:none;width:100%;height:100%;margin:0;padding:8px;
  box-sizing:border-box;background:var(--bg);display:flex;
  align-items:center;justify-content:center}
.kyivzoom.fs-overlay{position:fixed;inset:0;z-index:60}
/* Пропорцію знімаємо свідомо: svg сам вписує карту в кадр (preserveAspectRatio),
   а JS рахує поля тим самим Math.min(r.width/vw, r.height/vh). */
.kyivzoom.fs .kyivmap{max-width:none;width:100%;height:100%;aspect-ratio:auto}
.kyivzoom.fs .zoomctl{top:12px;right:12px}
/* Рядок підказки під картою в повному екрані лише забирає місце; у розмітці
   він лишається — на нього посилається aria-describedby самої карти. */
.kyivzoom.ready.fs .zoomhelp{display:none}

.kyivmap .km-track{stroke:var(--muted);stroke-width:calc(1.5px / var(--z));
  stroke-dasharray:calc(4px / var(--z)) calc(4px / var(--z))}
.kyivmap .km-obj{stroke:var(--card);stroke-width:calc(1.5px / var(--z));
  r:calc(7px / var(--z))}
.kyivmap .t-jet_drone,.kyivmap .t-drone{fill:var(--c4)}
.kyivmap .t-kab,.kyivmap .t-ballistic,.kyivmap .t-cruise,.kyivmap .t-fast{fill:var(--alert)}
/* Ціль, у якої канал не назвав типу: порожнє кільце, а не залита точка.
   Фарбувати її як дрон означало б домалювати те, чого в повідомленні немає,
   а просто сіра заливка на світлій карті губилась серед точок населених
   пунктів — кільце видно і воно саме по собі читається як «невідомо». */
.kyivmap .t-unknown{fill:var(--card);stroke:var(--muted);stroke-width:calc(3px / var(--z))}
.badge.tag-parsed{border-color:var(--clear);color:var(--clear)}
.badge.tag-unparsed{border-color:var(--c4);color:var(--c4)}
.badge.tag-foreign{border-color:var(--muted);color:var(--muted)}
/* «Без місця» — не провина розбору: в оглядовому зведенні по областях немає
   точки, яку можна покласти на карту. Мітка приглушена, як і «інший регіон». */
.badge.tag-noplace{border-color:var(--muted);color:var(--muted)}
/* Стрічка повідомлень: текст переноситься, а не тягне таблицю за край картки.
   Правило сильніше за th,td{nowrap} і за карткове table.cardable td на
   телефоні — там перенос і так є, тут він потрібен на десктопі. anywhere —
   на випадок довгого посилання чи хештегу без пробілів. Час і канал вузькі
   й лишаються в один рядок, тож усю зайву ширину забирає текст. */
table.feed th.msg,table.feed td.msg{white-space:normal;text-align:left;
  overflow-wrap:anywhere;width:100%}
/* Попередження «не оповіщення, не реальний час» — навмисно на кольорах
   тривоги (--alert/--alert-bg), а не приглушений .note: мітки на карті
   легко сплутати зі справжнім оповіщенням, застереження мусить впадати в
   очі раніше за саму карту, а не губитися дрібним шрифтом унизу сторінки. */
.kyiv-warn{border:1px solid var(--alert);background:var(--alert-bg);color:var(--alert);
  border-radius:8px;padding:10px 14px;font-size:13px;font-weight:600;margin:0 0 14px}

/* Полоса суток под сводкой «Сегодня»: 24 часовых блока, заливка снизу —
   доля наступившей части часа под тревогой. Сетка фиксирована в 24 колонки,
   поэтому ось подписей выравнивается по тем же колонкам, а не по «на глаз». */
.hours24{display:grid;grid-template-columns:repeat(24,1fr);gap:3px;margin:14px 0 0}
.h24{position:relative;height:34px;border-radius:4px;background:var(--hm0);
  border:1px solid transparent;overflow:hidden}
.h24 i{position:absolute;left:0;right:0;bottom:0;display:block;background:var(--alert);
  border-radius:0 0 3px 3px}
.h24.hit{background:var(--alert-bg)}
/* Ещё не наступивший час — не «нулевая тревога», а отсутствие данных.
   Красить его как тихий было бы враньём, поэтому он приглушён и заштрихован. */
.h24.future{background:repeating-linear-gradient(135deg,var(--hm0),var(--hm0) 3px,
  transparent 3px,transparent 6px);opacity:.75}
.h24.skip{background:transparent;border:1px dashed var(--line)}
.h24.now{border-color:var(--accent)}
.hours24.ax{gap:3px;margin-top:4px}
.hours24.ax span{color:var(--muted);font-size:11px;line-height:1;text-align:left;
  white-space:nowrap;font-variant-numeric:tabular-nums}
/* Полоса ночей — те же блоки, колонок столько, сколько ночей. Спокойная ночь
   окрашена цветом «отбоя»: это не пропуск данных, а выспавшийся город. Подписи —
   только крайние ночи: 30 дат в ширину телефона не влезут. */
.hours24.nights30{grid-template-columns:repeat(var(--cols,30),1fr)}
.nights30 .h24.calm{background:var(--clear-bg)}
.nights-ax{display:flex;justify-content:space-between;gap:8px;margin-top:4px;
  color:var(--muted);font-size:11px;line-height:1;font-variant-numeric:tabular-nums}
@media (max-width:640px){
  /* 24 блока в ширину экрана: зазор режем, высоту тоже — полоса остаётся
     читаемой, а карточка не растёт. */
  .hours24{gap:2px}
  .hours24.ax{gap:2px}
  .h24{height:28px;border-radius:3px}
  .hours24.ax span{font-size:10px}
  .nights-ax{font-size:10px}
}

/* ---------- сторінка «Згадки» ---------- */
/* Карта згадок не масштабується, але стилі шарів рахують товщину ліній
   через calc(… / var(--z)) — без явної одиниці calc стає недійсним і лінії
   зникають зовсім. */
.nebomap{--z:1;margin:8px 0 4px}
/* Карта згадок кадрується по даних, тож її співвідношення сторін НЕ
   1000/1004, і обмеження .kyivmap (max-width 620px + aspect-ratio) тут
   перетворювало б її на маленький квадрат із полями. Даємо повну ширину
   картки і стелю по висоті екрана. */
.nebomap .kyivmap{width:100%;max-width:100%;height:auto;aspect-ratio:auto;
  max-height:min(72vh,720px);display:block;margin:0 auto}
.kyivmap .nebo-dot{fill:var(--c1);opacity:.62;stroke:var(--card);stroke-width:.8}
/* Перемикач періоду видимий на всіх екранах (на відміну від .secnav, який
   існує лише на телефоні), і це звичайні посилання: працює без JS. */
.periods{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 18px}
.periods a{background:var(--card);border:1px solid var(--line);border-radius:999px;
  padding:6px 14px;font-size:14px;text-decoration:none;color:var(--ink)}
.periods a.on{background:var(--c1);border-color:var(--c1);color:#fff}
@media (max-width:640px){
  .periods{display:flex}
  .periods a{padding:8px 14px}
}

/* ---------- админка: вход и выход ---------- */
.adm-login{max-width:420px;margin:48px auto}
.adm-login form{display:flex;flex-direction:column;gap:10px;margin-top:12px}
.adm-login input,.adm-logout button,.adm-login button{font:inherit;padding:9px 12px;border-radius:8px;
  border:1px solid var(--line);background:var(--card);color:var(--ink)}
.adm-login button,.adm-logout button{cursor:pointer}
.adm-bad{color:var(--c2)}
.adm-logout{margin-left:auto}
/* Таблицы админки. Длинные User-Agent и названия AS на десктопе режутся
   многоточием (полный текст — в title), иначе колонки с nowrap съедали
   ширину, UA ломался по букве в узкий столбец и таблица вылезала за
   карточку. На телефоне — карточки, там перенос и так есть. */
table.adm th,table.adm td{text-align:left}
table.adm th.n,table.adm td.n{text-align:right}
@media (min-width:641px){
  table.adm td.adm-ua,table.adm td.adm-as{max-width:210px;overflow:hidden;text-overflow:ellipsis}
}
@media (max-width:640px){
  table.adm td.adm-ua{overflow-wrap:anywhere}
  table.adm td.adm-as{text-align:right}
}

/* ---------- кнопка звука тревоги ---------- */
.soundbtn .slash{display:inline}
.soundbtn.on .slash{display:none}
.soundbtn.on{color:var(--c1);border-color:var(--c1)}
.soundbtn.blocked{color:var(--alert);border-color:var(--alert)}
.soundbtn.blocked .slash{display:none}
