  :root {
    --bg:#0e1116; --panel:#151a21; --line:#242c37; --ink:#dfe6ef; --dim:#8b97a7;
    --pos:#3fb950; --neg:#f85149; --warn:#d29922; --accent:#4f8cc9;
    color-scheme:dark;
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  }
  /* Світла тема. Зелений/червоний навмисно ТЕМНІШІ за свої dark-відповідники:
     на білому тлі яскраві #3fb950/#f85149 дають замалий контраст, а це цифри
     прибутку і збитку — їх не можна робити менш читабельними за решту тексту. */
  :root[data-theme="light"] {
    --bg:#ffffff; --panel:#f6f8fa; --line:#d7dde4; --ink:#1c2128; --dim:#5b6672;
    --pos:#1a7f37; --neg:#cf222e; --warn:#9a6700; --accent:#0969da;
    color-scheme:light;
  }
  /* Плавний перехід лише на кольорах — не на layout, щоб нічого не «стрибало». */
  body,.panel,.tile,input,select,textarea,button,header,td,th{transition:background-color .15s,color .15s,border-color .15s}
  *{box-sizing:border-box}
  body{margin:0;background:var(--bg);color:var(--ink);font-size:14px;line-height:1.5}
  .num{font-variant-numeric:tabular-nums;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
  .pos{color:var(--pos)} .neg{color:var(--neg)} .dim{color:var(--dim)} .warn{color:var(--warn)}
  header{display:flex;align-items:center;gap:16px;padding:12px 20px;border-bottom:1px solid var(--line);background:var(--panel);position:sticky;top:0;z-index:10}
  header h1{font-size:15px;margin:0;font-weight:600;letter-spacing:.02em}
  /* Знак і назва їдуть разом як одне ціле — див. коментар у розмітці.
     24px при 48px вихідного файла: на екранах подвійної щільності це піксель у
     піксель, більшого джерела в нас немає, і розтягувати його вгору не можна. */
  .brand{display:flex;align-items:center;gap:9px;min-width:0}
  /* Візуально сховане, але доступне читалці екрана. ⚠️ Не `display:none` і не
     `visibility:hidden`: обидва ховають вузол і від допоміжних технологій теж, тобто
     заголовок сторінки просто зник би. Виведене з потоку, тож `gap` сусіда не чіпає. */
  .vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
      clip-path:inset(50%);white-space:nowrap;border:0}
  .brand img{width:24px;height:24px;border-radius:6px;display:block;flex:none}
  header .spacer{flex:1}
  nav{display:flex;gap:4px}
  nav button{background:none;border:1px solid transparent;color:var(--dim);padding:6px 12px;border-radius:6px;cursor:pointer;font-size:13px}
  nav button.on{color:var(--ink);border-color:var(--line);background:var(--bg)}
  main{padding:20px;max-width:1240px;margin:0 auto}
  .panel{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:16px;margin-bottom:16px}
  .panel h2{margin:0 0 12px;font-size:13px;text-transform:uppercase;letter-spacing:.06em;color:var(--dim);font-weight:600}
  /* Згортання секцій. Перемикач — ЛИШЕ перший заголовок панелі: панель із двома
     h2 (такі бувають) інакше мала б два перемикачі на один вміст, і другий
     ховав би те, чого не називає.
     Ховаємо ВСЕ, крім заголовка, класом на самій панелі —
     без обгортання вмісту в контейнер: половина панелей будується з даних
     (клієнти, статистика, тарифи), і обгортка мусила б відтворюватись у кожному
     рендері, тобто ламатись у тому, який про неї забудуть. */
  .panel > h2:first-child{cursor:pointer;user-select:none;display:flex;align-items:center;gap:8px}
  .panel > h2:first-child:hover{color:var(--ink)}
  .panel > h2:first-child::before{content:"";width:0;height:0;flex:none;
    border-left:5px solid currentColor;border-top:4px solid transparent;border-bottom:4px solid transparent;
    transform:rotate(90deg);transition:transform .15s}
  .panel.collapsed > h2:first-child::before{transform:rotate(0deg)}
  .panel.collapsed > h2:first-child{margin-bottom:0}
  .panel.collapsed > *:not(h2):not(.card-head){display:none}
  /* Картка клієнта не має h2: її «заголовок» — рядок з назвою, поштою і
     бейджами стану. Він і стає перемикачем, і саме він лишається видимим у
     згорнутому вигляді — тобто згорнута картка все одно каже, ХТО це і в
     якому він стані. Згортати картку, яка потім не називає клієнта, було б
     економією місця ціною сенсу. */
  .panel > .card-head{cursor:pointer;user-select:none}
  .panel > .card-head:hover .cardname{color:var(--accent)}
  .panel > .card-head::before{content:"";width:0;height:0;flex:none;align-self:center;
    border-left:5px solid var(--dim);border-top:4px solid transparent;border-bottom:4px solid transparent;
    transform:rotate(90deg);transition:transform .15s;margin-right:2px}
  .panel.collapsed > .card-head::before{transform:rotate(0deg)}
  .panel.collapsed > .card-head{margin-bottom:0}
  .panel > .card-head:focus-visible{outline:1px solid var(--accent);outline-offset:2px;border-radius:4px}
  .panel > h2:first-child:focus-visible{outline:1px solid var(--accent);outline-offset:2px;border-radius:4px}
  table{width:100%;border-collapse:collapse;font-size:13px}
  th{text-align:left;color:var(--dim);font-weight:500;padding:6px 8px;border-bottom:1px solid var(--line);white-space:nowrap}
  td{padding:7px 8px;border-bottom:1px solid var(--line);vertical-align:top}
  tr:last-child td{border-bottom:none}
  td.r,th.r{text-align:right}
  input,select,textarea{background:var(--bg);border:1px solid var(--line);color:var(--ink);padding:7px 10px;border-radius:6px;font-size:13px;width:100%;font-family:inherit}
  label{display:block;margin-bottom:10px}
  label span{display:block;color:var(--dim);font-size:12px;margin-bottom:4px}
  button.act{background:none;border:1px solid var(--line);color:var(--ink);padding:6px 12px;border-radius:6px;cursor:pointer;font-size:13px;white-space:nowrap}
  button.act:hover{border-color:var(--accent)}
  button.act[disabled]{opacity:.4;cursor:not-allowed}
  button.danger:hover{border-color:var(--neg);color:var(--neg)}
  /* Вкладений блок усередині картки клієнта. Свідомо НЕ `.panel`: правило
     `.panel.collapsed > *` ховає прямих нащадків, і вкладена панель почала б
     реагувати на згортання власними правилами замість батьківських. */
  .subpanel{border:1px solid var(--line);border-radius:6px;padding:12px;background:var(--bg)}
  .subpanel > .cardname{margin-bottom:8px}
  .row{display:flex;gap:8px;flex-wrap:wrap;align-items:flex-end}
  /* Рядок стану машини. Свідомо НЕ `.row`: там `align-items:flex-end` під поля з
     підписами, і бейдж поруч із текстом висів би на різних базових лініях. */
  .minfo{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:10px 0 2px}
  /* Кнопку НЕ можна загортати у <label>: label перенаправляє клік на свій контрол,
     і справжній клік миші по кнопці всередині нього не спрацьовує (програмний .click()
     при цьому працює — тому баг не видно нізвідки, крім реального браузера). */
  .btnwrap{margin-bottom:10px;display:flex;align-items:flex-end}
  /* Поле разом із кнопкою, яка його заповнює: один елемент ряду, тож перенос
     ніколи їх не розлучає. */
  .pair{display:flex;gap:8px;align-items:flex-end;min-width:230px}
  .pair > label{flex:1;min-width:0}
  .grow{flex:1;min-width:150px}
  .tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px}
  .tile{background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:12px}
  .tile .k{color:var(--dim);font-size:11px;text-transform:uppercase;letter-spacing:.05em}
  .tile .v{font-size:20px;margin-top:4px}
  .badge{display:inline-block;padding:2px 8px;border-radius:99px;font-size:11px;border:1px solid var(--line)}
  .b-ACTIVE{color:var(--pos);border-color:var(--pos)}
  .b-SUSPENDED,.b-CLOSE_ONLY,.b-BANNER,.b-DESTROYING{color:var(--warn);border-color:var(--warn)}
  .b-CLOSED,.b-TERMINATED,.b-STOPPED,.b-ERROR{color:var(--neg);border-color:var(--neg)}
  /* `DECOMMISSIONED` сірим, не червоним: машину знесли СВІДОМО, це не поломка. */
  .b-PROVISIONING,.b-NONE,.b-DECOMMISSIONED{color:var(--dim)}
  .b-PAID{color:var(--pos);border-color:var(--pos)}
  .b-OPEN{color:var(--warn);border-color:var(--warn)}
  /* Обробка звернень. `NEW` акцентом, а не попередженням: нове звернення — це
     робота, яка чекає, а не поломка. `FROZEN` сірим із тієї ж причини, що й
     `DECOMMISSIONED` вище: відклали СВІДОМО. */
  .b-NEW,.b-IN_WORK{color:var(--accent);border-color:var(--accent)}
  .b-IN_WORK{border-style:dashed}
  .b-FROZEN{color:var(--dim)}
  .b-RESOLVED{color:var(--pos);border-color:var(--pos)}

  /* Значення, яке міняється кліком просто по ньому (роль і стан у «Персоналі»).
     Без позначки такий стовпець виглядає як звичайний текст — олівець і пунктир
     кажуть «сюди можна натиснути» ще до наведення миші. */
  .editv{cursor:pointer}
  .editv::after{content:"\270E";margin-left:5px;font-size:10px;color:var(--dim)}
  .editv:hover,.editv:hover::after{color:var(--accent)}
  span.editv:not(.badge){border-bottom:1px dashed var(--dim)}
  span.editv:not(.badge):hover{border-bottom-color:var(--accent)}
  .badge.editv{border-style:dashed}
  .badge.editv:hover{border-color:var(--accent)}
  /* Ширина обмежена і зверху, і знизу: на телефоні `calc` лишає поля обабіч,
     інакше форма впирається в самі краї екрана. */
  #login{max-width:340px;width:calc(100% - 24px);margin:14vh auto}
  /* Розділювач «або» між паролем і зовнішнім входом. */
  .orline{display:flex;align-items:center;gap:10px;margin:14px 0;color:var(--dim);font-size:12px}
  .orline::before,.orline::after{content:"";flex:1;height:1px;background:var(--line)}
  /* Поле пароля з кнопкою-оком. Кнопка НЕ всередині <label>-контролу як самостійний
     control — вона в тому ж span, що й input, тож label адресує саме input. */
  .pwwrap{position:relative;display:block}
  .pwwrap input{padding-right:38px}
  .pwwrap button{position:absolute;right:1px;top:1px;bottom:1px;width:34px;display:flex;
    align-items:center;justify-content:center;background:none;border:0;border-radius:0 6px 6px 0;
    color:var(--dim);cursor:pointer;padding:0}
  .pwwrap button:hover{color:var(--ink)}
  .pwwrap button:active,.pwwrap button.on{color:var(--accent)}
  .pwwrap button:focus-visible{outline:1px solid var(--accent);outline-offset:-1px}
  /* Показаний пароль — перекреслення зникає. */
  .pwwrap button.on .slash,.pwwrap button.on #peekSlash{display:none}
  .msg{position:relative;padding:10px 12px;border-radius:6px;margin-bottom:12px;font-size:13px;border:1px solid}
  .msg.err{color:var(--neg);border-color:var(--neg);background:rgba(248,81,73,.08)}
  .msg.warn{color:var(--warn);border-color:var(--warn);background:rgba(210,153,34,.08)}
  .msg.ok{color:var(--pos);border-color:var(--pos);background:rgba(63,185,80,.08)}
  /* Місце під хрестик — лише в тих повідомлень, що його мають (`.closeable`,
     `say()`). Статичні картки стану (бот вимкнений, підйом упав) той самий клас
     `.msg` теж носять, але хрестика не мають і відступ їм не потрібен. */
  .msg.closeable{padding-right:34px}
  /* Своє місце під кутові дії картки бота: їх дві і вони більші за хрестик,
     тож 34px під них не вистачає. Число покриває найширший випадок (обидві
     кнопки) — на картці з однією лишається трохи повітря, і це дешевше за
     другий клас із власним числом, яке розійшлося б із першим. */
  .msg.hasacts{padding-right:76px}
  /* Три дії — картка вихідної пошти: перевірити, змінити, знести. Ще одна кнопка
     28px плюс проміжок 4 до попередньої, решта арифметики та сама, що вище.
     Окремий клас, а не одне число на всіх: 108px на картці бота віддавали б 32px
     порожнечі рівно там, де на телефоні кожен піксель рядка на рахунку. Числа
     звіряє `webui.test.ts` — обидва, і кожне проти власної кількості кнопок. */
  .msg.hasacts3{padding-right:108px}
  /* Хрестик центрується по ПЕРШОМУ рядку, а не по всьому блоку: `top:50%` кинув
     би його в середину багаторядкової помилки, за кілометр від тексту, який він
     закриває.
       top = падінг + піврядка − пів-кнопки = 10 + (13 × 1.5)/2 − 22/2 = 8.75
     (line-height 1.5 успадкований від `body`). Звідси 9px — і на однорядковому
     повідомленні, а це майже всі, хрестик стоїть по центру: заміряно в браузері,
     відхилення 0.24px. Було 6px при 20px кнопці, тобто на 3.75px вище центру, —
     саме це й читалось як «збився». Числа звіряє тест у `webui.test.ts`, тож
     зміна падінга чи розміру кнопки без перерахунку `top` впаде на гейті. */
  .msg .msgx{position:absolute;top:9px;right:8px;width:22px;height:22px;
    display:flex;align-items:center;justify-content:center;padding:0;
    border:0;border-radius:6px;background:none;color:inherit;
    font-size:17px;font-weight:600;line-height:1;cursor:pointer;
    opacity:.6;transition:opacity .15s}
  /* Колір НЕ підміняємо на `--ink`, як у `.pwwrap button`: хрестик успадковує
     колір severity і має лишатись у тон банера — сірий хрестик на зеленому
     виглядав би чужим. Тому підсвічуємо непрозорістю і тлом того ж
     `currentColor`: воно само підлаштовується і під severity, і під світлу тему,
     чого фіксований rgba не вміє. */
  .msg .msgx:hover{opacity:1;background:color-mix(in srgb,currentColor 14%,transparent)}
  .msg .msgx:active{background:color-mix(in srgb,currentColor 22%,transparent)}
  .msg .msgx:focus-visible{opacity:1;outline:1px solid currentColor;outline-offset:1px}
  /* Кутові дії картки бота — перевірити звʼязок і знести бота.
     Дві відмінності від хрестика, і обидві по суті:
       ① По центру КАРТКИ, а не першого рядка. Хрестику центр рядка обовʼязковий —
          він закриває саме текст, і на багаторядковій помилці мусить лишатись
          біля її початку. Ці ж стосуються картки цілком, тож центр рахує сама
          обгортка, а кнопки в ній стають звичайними в потоці (`position:static`) —
          інакше друга кнопка лягла б на першу.
       ② БІЛЬШІ (28 проти 22): це не «прибрати з очей повідомлення», а дії над
          самим ботом, і промазати по них не має бути легко.
     Обгортка, а не два власні `right` на кнопках: інакше додавання третьої дії
     означало б перерахувати відступи всім, а порядок задавався б числами замість
     порядку в розмітці. */
  .msg .msgacts{position:absolute;top:50%;right:8px;transform:translateY(-50%);
    display:flex;align-items:center;gap:4px}
  .msg .msgacts .msgx{position:static;transform:none;width:28px;height:28px}
  /* Смітник у спокої тримається в тон банера (як і решта), а на наведенні
     червоніє — після цього кліку бот зникає, і колір мусить це сказати. */
  .msg .msgacts .msgx.kill:hover,.msg .msgacts .msgx.kill:focus-visible{
    color:var(--neg);background:color-mix(in srgb,var(--neg) 14%,transparent)}
  /* Стос сповіщень у кутку. `pointer-events:none` на контейнері обовʼязковий:
     інакше невидима колонка над сторінкою перехоплювала б кліки по тому, що під
     нею, — а вона висить поверх кнопок дій у таблицях. Тло НЕПРОЗОРЕ (var(--panel)),
     бо тонування `.msg.*` напівпрозоре і крізь нього просвічував би текст сторінки. */
  .toasts{position:fixed;right:16px;bottom:16px;z-index:50;display:flex;flex-direction:column;gap:8px;pointer-events:none}
  .toasts .msg{margin:0;max-width:min(360px,calc(100vw - 32px));background:var(--panel);border-color:var(--accent);color:var(--ink);box-shadow:0 4px 16px rgba(0,0,0,.35)}
  .hide{display:none}
  details.det{border:1px solid var(--line);border-radius:6px;padding:10px 12px;margin-top:8px;background:var(--bg)}
  details.det summary{cursor:pointer;color:var(--dim);font-size:12px;user-select:none}
  details.det[open] summary{margin-bottom:10px;color:var(--ink)}
  code{font-family:ui-monospace,Menlo,monospace;font-size:12px;color:var(--dim)}
  .mini{font-size:12px;color:var(--dim)}
  #bell,#profile,#supportBtn{display:flex;align-items:center;justify-content:center;gap:8px;padding:6px 9px}
  /* Профіль може стискатись: усередині нього пошта, а вона нерозривна. Без
     min-width:0 flex-елемент не звужується менше за вміст, і кнопка розпирала б
     перший ряд шапки замість того, щоб пошта в ній обрізалась трьома крапками. */
  #profile{min-width:0}
  #bell.on,#profile.on,#supportBtn.on{border-color:var(--accent);color:var(--accent)}
  /* Кнопка — система координат для позначки; без цього вона причепилася б до
     шапки і поїхала б у її кут. */
  #bell,#supportBtn{position:relative}
  /* Коло з числом, а не проста крапка: «є щось» і «є троє» — різні новини, а місця
     воно займає стільки ж. Червоний із палітри (`--neg`), а не акцент: акцентом тут
     підсвічені рамки активних кнопок, і позначка серед них не читалась як «увага».
     Білий текст на ньому контрастний в обох темах (#f85149 темна, #cf222e світла). */
  .belldot{position:absolute;top:-5px;right:-5px;min-width:15px;height:15px;padding:0 3px;
           border-radius:8px;background:var(--neg);color:#fff;
           font-size:10px;line-height:15px;font-weight:600;text-align:center}
  /* Віконце під дзвоником. `right` рівний правому відступу шапки, а не позиції
     самого дзвоника: до нього довелось би домірюватись у JS на кожному відкритті
     й перерахунку розкладки, а виграш — кілька пікселів вирівнювання. */
  /* Спільна «випадайка» шапки: позиціювання, тінь і поведінка в дзвоника й профілю
     однакові, різна лише ширина. `right` рівний правому відступу шапки, а не позиції
     самої кнопки: до неї довелось би домірюватись у JS на кожному відкритті й
     перерахунку розкладки, а виграш — кілька пікселів вирівнювання. */
  .hpanel{position:absolute;top:100%;right:20px;margin-top:6px;
          background:var(--panel);border:1px solid var(--line);border-radius:8px;
          box-shadow:0 8px 24px rgba(0,0,0,.4);z-index:20;overflow:hidden}
  .hpanel:focus-visible{outline:1px solid var(--accent);outline-offset:-2px}
  .bellpanel{width:min(340px,calc(100vw - 32px))}
  #profileMenu{width:min(340px,calc(100vw - 32px))}
  .bellhead{padding:9px 12px;border-bottom:1px solid var(--line);font-size:12px;color:var(--dim)}
  /* Стеля висоти, а не «скільки прийшло»: без неї десяток записів виганяє віконце
     за нижній край екрана, і останні рядки недосяжні — прокрутки в ньому немає. */
  .belllist{max-height:min(50vh,320px);overflow-y:auto}
  .bellitem{padding:9px 12px;border-bottom:1px solid var(--line);font-size:13px}
  .bellitem:last-child{border-bottom:0}
  .bellempty{padding:14px 12px;font-size:13px;color:var(--dim)}

  /* ── меню профілю ─────────────────────────────────────────────────────────── */
  .pmhead{padding:10px 12px;border-bottom:1px solid var(--line)}
  .pmname{font-size:13px;overflow-wrap:anywhere}
  .pmrow{display:flex;align-items:center;gap:10px;padding:8px 12px}
  .pmlbl{color:var(--dim);font-size:12px;flex:1;min-width:0}
  .pmsep{height:1px;background:var(--line)}
  /* Перемикач із двох станів. Обидва підписані словами, а не «увімк/вимк»: у теми
     і мови немає природного «вимкненого» стану, є два рівноправні. */
  .seg{display:inline-flex;border:1px solid var(--line);border-radius:6px;overflow:hidden;flex:none}
  .seg button.act{border:0;border-radius:0;padding:5px 10px;font-size:12px;color:var(--dim)}
  .seg button.act + button.act{border-left:1px solid var(--line)}
  .seg button.act:hover{color:var(--ink)}
  .seg button.act.on{color:var(--accent);background:var(--bg)}
  .seg button.act:focus-visible{outline:1px solid var(--accent);outline-offset:-2px}
  /* Пункт меню — саме <button>, а не div з обробником: він кудись ВЕДЕ, і клавіатура
     з читалкою мусять бачити це з розмітки, а не здогадуватись по курсору. */
  .pmitem{display:flex;align-items:center;gap:8px;width:100%;text-align:left;
          background:none;border:0;color:var(--ink);cursor:pointer;
          font:inherit;font-size:13px;padding:9px 12px}
  .pmitem > span:first-child{flex:1;min-width:0}
  .pmitem:hover{background:var(--bg)}
  .pmitem:focus-visible{outline:1px solid var(--accent);outline-offset:-2px}
  .setrow{padding:10px 0;border-bottom:1px solid var(--line)}
  /* Біржі на дроті. Ширина таблетки НЕ залежить від стану: підпис не міняється,
     міняються лише крапка й рамка. Інакше ряд «стрибав» би на кожному кліку —
     а клікають тут швидко і по кількох поспіль. */
  .venues{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:4px}
  .venue{display:inline-flex;align-items:center;gap:8px;min-width:118px;
    padding:7px 12px;border:1px solid var(--line);border-radius:99px;background:var(--bg);
    color:var(--ink);font-size:13px;cursor:pointer;user-select:none;
    transition:border-color .15s,color .15s,opacity .15s}
  .venue:hover{border-color:var(--accent)}
  .venue .dot{width:8px;height:8px;border-radius:50%;background:var(--pos);flex:none}
  .venue.off{color:var(--dim);border-color:var(--neg);border-style:dashed;opacity:.75}
  .venue.off .dot{background:var(--neg)}
  .venue[disabled]{cursor:default;opacity:.5}
  .venue[disabled]:hover{border-color:var(--line)}
  /* Незбережена правка: тонке кільце навколо крапки — видно, що це ще не факт. */
  .venue.dirty .dot{box-shadow:0 0 0 3px color-mix(in srgb, var(--warn) 45%, transparent)}
  .setlbl{color:var(--dim);font-size:12px;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px}

/* ── утиліти замість інлайнових style="".
   CSP без 'unsafe-inline' блокує стиль, записаний АТРИБУТОМ у розмітці
   (стилі, виставлені з JS через el.style, не блокуються — це CSSOM).
   Тому верстка переїхала сюди, а політика лишилась строгою. */
.u1{width:100%}
.u2{width:100%;display:flex;align-items:center;justify-content:center;gap:8px}
.u3{margin-top:6px;text-align:center}
.u4{text-align:center;margin-top:10px}
.u5{color:var(--accent)}
.u6{color:var(--dim)}
.u7{padding-top:0}
.u13{border-top:1px solid var(--line);border-bottom:none;margin-top:14px;padding-bottom:0}
.u14{align-items:center;gap:10px}
.u15{min-width:130px}
.u16{margin-bottom:8px;align-items:center}
.u17{padding-bottom:16px}
.u18{min-width:150px}
.u19{min-width:120px}
.u20{min-width:100px}
.u21{min-width:160px}
.u22{margin:-6px 0 10px}
.u23{margin:8px 0 12px}
.u24{margin-top:6px}
.u25{margin-bottom:8px}
.u26{margin-bottom:4px}
.u27{margin-bottom:10px}
.u28{min-width:170px}
.u29{margin-top:4px}
.u30{margin-top:8px}
.u31{min-width:190px}
.u32{margin-top:10px;align-items:center}
.u34{margin-top:10px}
.u35{margin-top:6px;opacity:.85}
.u36{align-self:center;margin-bottom:10px}
/* Пояснення, що стоїть у ряду поруч із кнопками. Щоб його текст лежав на тій самій
   лінії, що й написи на кнопках, коробка повторює їхню геометрію знизу: той самий
   нижній відступ, що й у підписів полів (10px), плюс внутрішній відступ кнопки
   (6px). Інакше воно або провисає під кнопками, або задирається до підписів. */
.hintcol{margin-bottom:10px;padding-bottom:6px}
/* Риска відділяє «що зі строком» від «що шукаємо»: без неї селект читається
   як ще один фільтр, а він не фільтр, а незворотна дія. */
.u37{border-bottom:1px solid var(--line);margin-bottom:12px}

/* ── Статистика фіда і модалка рестарту ──────────────────────────────────── */

.u33{margin:-4px 0 10px}
/* Значок «є нотатка» біля бейджа стану — притиснутий до нього, не до краю комірки. */
.u38{margin-left:6px}

/* Позначка «кнопка ще не працює». Приглушено, але поруч із самою кнопкою:
   виноска в кінці сторінки не рятує того, хто вже натиснув. */
.todo{margin-top:8px;color:var(--dim);font-style:italic}

/* Плитка з поганим значенням (паніки, скидання кадрів) — колір несе сенс,
   тому дублюємо його текстом у підписі, а не лише фарбою. */
.tile.bad{border-color:var(--neg)}
.tile.bad .v{color:var(--neg)}

/* Вимкнена біржа — НЕ дефект, а рішення оператора, тому попереджальний, а не
   червоний: червоне тут щоразу читалось би як «щось зламалось». Але й не сіре —
   мовчки вимкнена біржа це найчастіша відповідь на «чому не видно спредів». */
.tile.warn{border-color:var(--warn)}
.tile.warn .v{color:var(--warn)}
.tile .sub{color:var(--dim);font-size:11px;margin-top:2px}

/* Рядок «фід не відповідає» має читатись без вдивляння в цифри. */
.fstat-dead{border-color:var(--neg)}

dialog{border:1px solid var(--line);border-radius:8px;background:var(--panel);color:var(--ink);
  padding:20px;max-width:440px;width:calc(100% - 32px);box-shadow:0 18px 50px rgba(0,0,0,.35)}
/* Позиційний контекст для хрестика в кутку — без цього `.x` рахувався б від viewport. */
dialog{position:relative}
/* Хрестик замість тексту «Закрити» — та сама іконкова геометрія, що й у шапки
   (`#theme`/`#gear`/`#bell`), у кутку, де його й шукають руки, привчені до вебу. */
dialog .x{position:absolute;top:12px;right:12px;display:flex;align-items:center;justify-content:center;
  width:28px;height:28px;padding:0;border-radius:6px}
dialog::backdrop{background:rgba(0,0,0,.55)}
dialog h3{margin:0 0 8px;font-size:16px}
dialog .msg{margin:12px 0}
dialog label{display:block;margin-top:12px}
dialog input{width:100%}
/* Мініатюри знімків у картці звернення. Квадрат і `object-fit:cover` — щоб ряд не
   стрибав по висоті залежно від пропорцій кожного окремого скріншота. */
dialog .thumb{display:block;width:64px;height:64px;border:1px solid var(--line);border-radius:6px;overflow:hidden}
dialog .thumb img{width:100%;height:100%;object-fit:cover;display:block}

/* Картка звернення несе повний текст, JSON-контекст і мініатюри — базові 440px
   (розраховані на однорядкові підтвердження на кшталт рестарту фіда) стискали
   і текст, і JSON у вузьку колонку. Власна ширина лише для цього діалогу, решта
   лишається компактною — там стискати нічого. */
#supportDlg{max-width:640px}
#supportDlg #supportDlgBody{white-space:pre-wrap;line-height:1.5;margin:12px 0}
/* Стеля висоти на весь діалог, а не лише на JSON: довге звернення з великим
   контекстом і купою фото інакше вилазило б за нижній край екрана без прокрутки
   — нативний `<dialog>` сам по собі не скролиться. */
#supportDlg{max-height:min(80vh,720px);overflow-y:auto}
/* Клас ставиться лише поки видно фото (`supportPhotoShow`/`supportPhotoClose`).
   Картка звернення лишається компактною (640px, шапка вище) — 80% екрана потрібні
   рівно тоді, коли на ній сам знімок, не картка з текстом і JSON. */
#supportDlg.photo-open{max-width:90vw;width:90vw;max-height:90vh}
#supportDlgContext{max-height:220px;overflow:auto;white-space:pre-wrap;word-break:break-word}
#supportDlgPhotos{flex-wrap:wrap;gap:8px;margin-top:12px}
/* Мініатюра — тепер КНОПКА, не посилання: клік лишається на сторінці й перемикає
   вміст того самого діалогу, а не відкриває нову вкладку браузера. */
#supportDlg .thumb{width:96px;height:96px;border:1px solid var(--line);border-radius:6px;overflow:hidden;padding:0;background:none;cursor:pointer}
#supportDlg .thumb:hover{border-color:var(--accent)}
#supportDlg .thumb img{width:100%;height:100%;object-fit:cover;display:block}

/* Повнорозмірний перегляд усередині ТОГО САМОГО діалогу (не другий `<dialog>` —
   стек top layer у різних рушіях ненадійний, а перемикання вмісту працює скрізь). */
/* :not(.hide), а не голий ID: без цього `#supportPhotoView{display:flex}`
   (специфічність ID) перебиває `.hide{display:none}` (специфічність класу),
   і перемикання класу перестає ховати блок узагалі — саме так і сталось. */
#supportPhotoView:not(.hide){display:flex;flex-direction:column;gap:10px;max-height:90vh;align-items:flex-start;width:100%;
  /* Трохи відступу над фото — інакше воно впритул під хрестиком закриття. */
  padding-top:16px}
#supportPhotoView img{max-width:100%;max-height:90vh;width:auto;height:auto;object-fit:contain;
  border-radius:6px;background:var(--bg);align-self:center;cursor:zoom-out}

/* ── Огляд флоту ─────────────────────────────────────────────────────────── */

/* Стан рядка: колір + СЛОВО. Тільки колір не читається тим, хто бачить екран
   уперше (і тим, хто розрізняє кольори гірше за автора), тільки слово — не
   видно краєм ока при прокрутці сотні рядків. */
.sev{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;
  text-transform:uppercase;letter-spacing:.04em;border:1px solid var(--line)}
.sev-down{border-color:var(--neg);color:var(--neg)}
.sev-warn{border-color:var(--warn);color:var(--warn)}
.sev-ok{border-color:var(--pos);color:var(--pos)}

label.inline{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--dim);cursor:pointer}
label.inline input{width:auto;margin:0}
/* Селект у такому рядку тягнеться на 100% ширини (правило вище на input,select) і
   виштовхує сусідні кнопки — тут він мусить бути завширшки зі свій найдовший пункт. */
label.inline select{width:auto;margin:0}

#fleetTable td{vertical-align:top}
#fleetTable td .mini{margin-top:2px}

/* ── Планшет і телефон ────────────────────────────────────────────────────
 *
 * Панель писалась під монітор, і на вужчому екрані вона не «виглядала тісно» —
 * вона роз'їжджалась. Шапка з сімома вкладками, поштою і трьома кнопками це
 * ~1200px: вона не влазила вже в ноутбук 1180px і тягла за собою горизонтальну
 * прокрутку ВСІЄЇ сторінки, а sticky-шапка при цьому лишалась на місці по
 * горизонталі — тобто «з'їжджала» відносно змісту під нею.
 *
 * Три осі, навмисно окремі одна від одної:
 *   • ширина  — що з чим переносити;
 *   • дотик   — розмір цілей; ширина тут ні до чого, планшет буває широкий, а
 *               пальцем по ньому влучати треба так само;
 *   • таблиці — окремо, бо їхня ширина задається даними, а не екраном.
 */

/* Пошта в шапці стискається до трьох крапок замість того, щоб розпирати ряд.
   Без min-width:0 flex-елемент не можна звузити менше за вміст, а пошта — це
   якраз «довгий рядок без пробілів», який нікуди не переноситься. Правило поза
   media-запитом: воно потрібне саме на проміжних ширинах, де все ще один ряд. */
header #who{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

@media (max-width:1180px){
  /* Вкладки — власним рядом на всю ширину. `overflow-x:auto` заразом робить ряд
     стисним (у скрол-контейнера автоматичний мінімум ширини = 0), тож жодна
     довжина підписів більше не може розпирати шапку. */
  header{flex-wrap:wrap;gap:10px 14px}
  header .brand{order:1}
  header .spacer{order:2}
  /* Пошта поступається місцем першою — тепер разом зі своєю кнопкою, бо вона в
     ній і живе: 34vw вистачає, щоб упізнати себе, і гарантує, що назва і дві
     кнопки лишаються в ОДНОМУ ряді аж до 600px. Інакше вони переносяться, і
     шапка з двох рядів стає трьома. */
  header #supportBtn{order:3}
  header #bell{order:4}
  header #profile{order:5;max-width:min(34vw,340px)}
  nav{order:7;width:100%;flex-wrap:nowrap;overflow-x:auto;padding-bottom:3px;
      scrollbar-width:thin;scrollbar-color:var(--line) transparent}
  nav button{flex:none}
  /* Тонка смуга — підказка, що ряд гортається, коли вкладки не влізли. На iOS її
     не видно (там смуги-накладки), але там про це каже обрізана остання вкладка. */
  nav::-webkit-scrollbar{height:3px}
  nav::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px}
}

@media (max-width:900px){
  header{padding:10px 14px}
  main{padding:14px}
  .panel{padding:14px}
}

/* Межі нижче — не «на око», а порахований перший ряд шапки. Раніше він містив
   назву (196px укр.), пошту, три іконки і «Вийти»: 196 + пошта + 132 + 70
   проміжків. Тепер «Вийти» і перемикач теми переїхали в меню профілю, а пошта —
   всередину його кнопки, тож ряд став коротшим — але межі лишаються ті самі, і це
   навмисно: вони заміряні, а не виведені, і послаблювати їх без нового заміру в
   браузері означало б поміняти перевірене число на здогадку. Запас іде в поля.
   Обидва «мʼякі» елементи здаються по черзі, кожен на своїй межі. */
@media (max-width:700px){
  /* Пошта — перша: вона всередині кнопки профілю, тож зникає саме напис, а ціль
     (шестерня) лишається на місці й того самого розміру. «Хто я» нікуди не
     дівається — воно першим рядком у самому меню, повністю і разом із роллю. */
  header #who{display:none}
}

@media (max-width:460px){
  /* Далі здається сама назва: без пошти ряд тримається до ~440px, нижче —
     знову три яруси. Назва тут суто марка: на якому ти екрані, каже ряд вкладок
     просто під нею, а як зветься застосунок — вкладка браузера. */
  header h1{display:none}
}

@media (max-width:600px){
  header{padding:8px 10px;gap:8px 10px}
  header h1{font-size:14px}
  /* Віконця шапки — на всю ширину під нею, а не 340px, прибиті до правого краю.
     На 355px екрана «майже на всю ширину» це смуга з двома різними полями обабіч
     (праворуч 16px відступу, ліворуч рештка), і віконце виглядає як щось, що
     зʼїхало. Рамка й заокруглення з боків теж прибрані: панель, приліплена до
     обох країв, читається як продовження шапки, а не як картка в порожнечі. */
  .hpanel{left:0;right:0;margin-top:0;border-left:0;border-right:0;border-radius:0}
  .bellpanel,#profileMenu{width:auto}
  main{padding:10px}
  .panel{padding:12px;margin-bottom:12px}
  dialog{padding:16px}
  #login{margin:6vh auto}
  .tiles{grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:8px}
  /* Довгий нерозривний рядок (uuid, маршрут, URL) інакше розпирає сторінку —
     не свій блок, а саме сторінку, бо переносити його ніде. */
  .mini,code,.msg,.cardname{overflow-wrap:anywhere}
  /* Два поля в ряд замість одного: мінімуми в 150-190px рахувались на монітор,
     і на телефоні кожне поле займало власний рядок — форма з чотирьох полів
     ставала екраном заввишки. Кнопки й підписи (.btnwrap, .mini) свідомо не
     чіпаємо: у них ширина за вмістом і вона тут доречна. */
  .row > .grow,.row > .u15,.row > .u18,.row > .u19,.row > .u20,
  .row > .u21,.row > .u28,.row > .u31{min-width:0;flex:1 1 140px}
  /* Виняток — шапка картки клієнта: назва з поштою беруть увесь ряд, бейджі
     стану йдуть під ними. За спільним правилом їм лишалося 140px, і пошта
     ламалася посеред слова («exampl / e.com») — при тому що поруч стояли два
     бейджі, яким той самий ряд не потрібен. */
  .card-head > .grow{flex-basis:100%}
  .row > .pair{flex-basis:100%}
  /* Підказка під формою — завжди власним рядом. Інакше вона стає другою
     колонкою завширшки 140px і читається як вузький стовпчик тексту збоку
     від поля, до якого вона пояснення. */
  .row > .mini{flex-basis:100%}
}

/* Таблиці. Ширину таблиці задають ДАНІ, а не екран: пошта, адреса, час і маршрут
   — нерозривні, тож вужчою за свій вміст (у журналі це ~750px) таблиця не стане
   ні переносом, ні стисканням. Без вікна прокрутки вона на планшеті виносить за
   край СТОРІНКУ — та їде вбік разом зі `sticky`-шапкою. Обгортка (`tw()` у
   app.js) лишає прокрутку всередині панелі.
   Чому обгортка, а не `display:block` на самій таблиці: `display:block` зробив би
   ширину таблиці шириною вмісту, і там, де вона все ж влазить, вона перестала б
   добирати ширину панелі — стовпчик «Дії» відліпився б від правого краю. */
.tw{overflow-x:auto;max-width:100%}
.tw::-webkit-scrollbar{height:3px}
.tw::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px}

/* На телефоні комірки ще й перестають переноситись: краще возити рядок вбік
   цілим, ніж читати «2026-08-30 / 08:10:44» у три поверхи. Виняток — комірка
   вільного тексту (причина відмови у «Флоті»): 160 символів помилки одним
   рядком розтягли б таблицю і не показали б нічого, чого не видно при переносі. */
@media (max-width:760px){
  main table th,main table td{white-space:nowrap}
  main table td.mini{white-space:normal;overflow-wrap:anywhere;min-width:180px}
}

/* Дотик. 13px у полі вводу — це не тільки дрібно: iOS Safari на фокусі в полі
   з шрифтом менше 16px збільшує сторінку, і назад вона сама не повертається,
   тобто далі людина працює в зумі. 16px прибирає причину, а не наслідок. */
@media (pointer:coarse){
  /* min-height, а не самий padding: у кнопок і полів UA-стиль ставить
     line-height:normal замість успадкованих 1.5, тож 9px відступу дають лише
     35px висоти — менше за палець. Міряно, не вгадано. */
  input,select,textarea{font-size:16px;padding:9px 11px;min-height:40px}
  button.act,nav button{padding:9px 13px;font-size:14px;min-height:40px}
  #bell,#profile,#supportBtn{padding:9px 10px}
  /* Пункти меню — теж цілі для пальця, а `.act` вони не є (у них немає рамки, це
     рядки списку). Без цього рядка вони лишалися б 38px: майже влучиш. */
  .pmitem{min-height:40px}
  .venue{padding:9px 13px;min-height:40px}
  .pwwrap input{padding-right:44px}
  .pwwrap button{width:40px}
  label.inline input[type="checkbox"]{width:18px;height:18px}
  main td{padding:10px 8px}
  details.det summary{padding:3px 0}
}
