/* Вікно кліку по джерелу.

   ---- ОДНА РОДИНА З BEARING (08.08) ----

   Було: власна мова кольору й форми — кремовий папір гросбуха, сериф на
   інтерфейс, бірюза дії, золото вибору; нічна зміна міняла ще й гарнітуру.
   Мова була своя, і жодна правка не переносилась між продуктами FreiCore.

   Стало: та сама мова, що в Bearing. Примітиви — офіційні шкали Radix v3
   (light і dark), семантичний шар над ними, контроль 36/30, три радіуси,
   дві тіні, один фокус-ринг. Тема міняє ЛИШЕ колір: гарнітура в обох одна.
   Розкладка лишилась Root'івська — смуга вікна, дві панелі з рухомою межею,
   екран першого запуску: рейка Bearing веде між розділами, а тут розділ
   один, і ширина потрібна таблиці.

   ІМЕНА ТОКЕНІВ — Bearing'ові, і це не косметика: правка, зроблена там,
   переноситься сюди пошуком по імені. Ролі:

     --blue        дія: те, що можна натиснути й що нічого не зламає
     --manual-*    ВВЕДЕНЕ Й ОБРАНЕ ЛЮДИНОЮ і більше нічиє (заливка індиго)
     --red         дія, яка забирає вивчене
     --muted       те, що відступає: докази, номери рядків, підписи
     --line        волосяна межа; --table-line — та сама лінія в сітці

   Закон «індигова заливка означає рівно одне» лишається чинним і
   перевіряється тестом — змінилась тільки фарба, не правило. Дія і вибір
   тепер одного тону (у Bearing обидва індиго), і розводить їх не колір, а
   спосіб: вибір — ЗАЛИВКА, дія — літери й обвід.

   КОНТРАСТ заміряний в обох темах (tests/test_ui_design.py ганяє кожну пару
   двічі). Radix обіцяє свої щаблі, але обіцянка діє лише поки колір —
   щабель: варто підсвітити текст напівпрозорою накладкою, і рахувати
   доводиться самим. Одну таку правку заміряно й тут (див. --red-soft-2).

   ЧОГО З BEARING НЕ ВЗЯТО, і чому:
   --chart-* (серії графіків) і --rail-* — у Root немає ні графіків, ні
   рейки; мертвий токен бреше про те, що продукт уміє. Шкали violet/blue/
   orange/cyan/teal/pink/plum жили тільки в серіях — теж не поїхали.
   --steel (карта центру) був літералом без темного варіанта; центр — це
   те, що клікнула людина, тож він тепер індиго, як і решта її вибору. */

:root {
  /* Смугу гортання, вибір аркуша й поле пошуку малює САМ браузер — не CSS.
     Поки сторінка не оголосила, якого вона тону, вони лишаються світлими:
     на темній таблиці збоку стоїть біла смуга в 15 px, а список аркушів
     («КЖ-Общий») відкривається світлим вікном посеред ночі. */
  color-scheme: light;

  /* ---- примітиви: Radix v3 light ---- */
  --slate-1:#fcfcfd; --slate-2:#f9f9fb; --slate-3:#f0f0f3; --slate-4:#e8e8ec;
  --slate-5:#e0e1e6; --slate-6:#d9d9e0; --slate-7:#cdced6; --slate-8:#b9bbc6;
  --slate-9:#8b8d98; --slate-10:#80838d; --slate-11:#60646c; --slate-12:#1c2024;
  --indigo-1:#fdfdfe; --indigo-2:#f7f9ff; --indigo-3:#edf2fe; --indigo-4:#e1e9ff;
  --indigo-5:#d2deff; --indigo-6:#c1d0ff; --indigo-7:#abbdf9; --indigo-8:#8da4ef;
  --indigo-9:#3e63dd; --indigo-10:#3358d4; --indigo-11:#3a5bc7; --indigo-12:#1f2d5c;
  --red-1:#fffcfc; --red-2:#fff7f7; --red-3:#feebec; --red-4:#ffdbdc;
  --red-5:#ffcdce; --red-6:#fdbdbe; --red-7:#f4a9aa; --red-8:#eb8e90;
  --red-9:#e5484d; --red-10:#dc3e42; --red-11:#ce2c31; --red-12:#641723;

  /* ---- мутабельні білі поверхні ---- */
  --panel:#ffffff; --input-bg:#ffffff; --table-cell:#ffffff;
  --bg: var(--slate-2);

  /* ---- семантика (ролі однакові в обох темах) ---- */
  --panel-2: var(--slate-3);
  --line: var(--slate-7);
  --text: var(--slate-12);
  --muted: var(--slate-11);
  --blue: var(--indigo-9);
  --blue-ink: var(--indigo-11);   /* літери дії ПО індиговій підсвітці */
  --red: var(--red-10);
  --red-ink: var(--red-11);       /* те саме для небезпечної дії */

  --manual-bg: var(--indigo-3);
  --manual-border: var(--indigo-8);
  --manual-text: var(--indigo-12);
  --manual-focus: var(--indigo-9);
  --readonly-bg: var(--slate-3);
  --readonly-text: var(--slate-11);
  --select-bg: var(--indigo-3);
  --select-border: var(--indigo-8);
  --select-text: var(--indigo-12);

  --table-line: var(--slate-6);
  --table-header: var(--slate-3);
  --table-hover: var(--slate-2);

  /* Підсвітки — ЩАБЛІ шкал, а не напівпрозорі накладки. Bearing пише їх
     через color-mix; у Root вони стоять щаблями, і на те дві причини.
     Перша: щабель непрозорий, тож на приклеєній шапці крізь нього не видно
     рядків, які їдуть під нею. Друга: Radix обіцяє, що щаблі 11 і 12
     читаються на щаблях 1–3 — узявши накладку «14% червоного», ми цю
     обіцянку втрачаємо, і заміряно, що саме там вона й ламалась (4.29 при
     нормі 4.5 на плашці помилки). */
  --ui-hover-bg: var(--slate-4);
  --ui-press-bg: var(--slate-5);
  --blue-soft: var(--indigo-3);   /* підсвітка дії */
  --red-soft: var(--red-2);       /* спокій «забути» */
  --red-soft-2: var(--red-3);     /* воно ж під мишею, і плашка помилки */
  /* Заливна кнопка під мишею. У світлій темі це щабель 10, як велить
     Radix. У темній щабель 10 ЯСКРАВІШИЙ за 9 (шкали в темряві йдуть
     угору), і біле на ньому дає 4.28 при нормі 4.5 — заміряно. Тому в
     темній під мишею беремо 8: рух той самий, а напис лишається читним. */
  --blue-hover: var(--indigo-10);
  --ui-disabled-opacity:0.48;

  /* ---- форма, тінь, метрики, рух ---- */
  --radius-s:6px; --radius-m:8px; --radius-l:12px;
  --shadow-resting:0 1px 2px rgba(28,32,36,0.08);
  --shadow-raised:0 10px 26px rgba(28,32,36,0.14);
  --shadow-head:0 10px 12px -10px rgba(28,32,36,0.22);
  --control-h:36px; --control-h-sm:30px;
  --ui-ease:120ms ease;

  /* ---- типографіка ----
     Одна пара на обидві теми. Інтерфейс — Inter; дані з файла — JetBrains
     Mono (чому не системний моноширинний Bearing — у fonts/fonts.css).
     --mono лишається аліасом: мірку павука (spider.js) читає саме він. */
  --font-ui: Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-num: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --mono: var(--font-num);
  --text-2xs:10px; --text-xs:11px; --text-sm:12px; --text-base:13px;
  --text-md:14px; --text-lg:15px;
}

/* Темна тема: ЛИШЕ примітиви (офіційні Radix v3 dark) і білі аліаси.
   Семантичний шар не чіпаємо — щаблі Radix тримають ті самі ролі в обох
   темах, і саме тому темну не треба вигадувати. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --slate-1:#111113; --slate-2:#18191b; --slate-3:#212225; --slate-4:#272a2d;
  --slate-5:#2e3135; --slate-6:#363a3f; --slate-7:#43484e; --slate-8:#5a6169;
  --slate-9:#696e77; --slate-10:#777b84; --slate-11:#b0b4ba; --slate-12:#edeef0;
  --indigo-1:#11131f; --indigo-2:#141726; --indigo-3:#182449; --indigo-4:#1d2e62;
  --indigo-5:#253974; --indigo-6:#304384; --indigo-7:#3a4f97; --indigo-8:#435db1;
  --indigo-9:#3e63dd; --indigo-10:#5472e4; --indigo-11:#9eb1ff; --indigo-12:#d6e1ff;
  --red-1:#191111; --red-2:#201314; --red-3:#3b1219; --red-4:#500f1c;
  --red-5:#611623; --red-6:#72232d; --red-7:#8c333a; --red-8:#b54548;
  --red-9:#e5484d; --red-10:#ec5d5e; --red-11:#ff9592; --red-12:#ffd1d9;

  --bg: var(--slate-1);
  --panel: var(--slate-2);
  --input-bg: var(--slate-2);
  --table-cell: var(--slate-2);

  /* Підсвітки переоголошувати не треба: вони показують на щаблі, а щаблі
     вище вже стали темними. Переоголошено рівно те, що інакше збрехало б:
     hover заливної кнопки (причина — вище, при токені) і тіні, бо чорна
     тінь у 8% на темному просто невидима. */
  --blue-hover: var(--indigo-8);
  --shadow-resting:0 1px 2px rgba(0,0,0,0.36);
  --shadow-raised:0 10px 26px rgba(0,0,0,0.52);
  --shadow-head:0 10px 12px -10px rgba(0,0,0,0.66);
}

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

/* Клавіатури не існувало: єдиний видимий фокус був на посиланні версії
   (аудит 07.08, В-05). Тепер фокус видно на всьому клікабельному — одним
   токеном, кольором дії. */
:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

body {
  font: var(--text-base)/1.45 var(--font-ui); color: var(--text);
  background: var(--bg);
  display: flex; flex-direction: column; height: 100vh;
  -webkit-font-smoothing: antialiased;
}

/* ---------- смуга вікна: бренд · документ · чип фільтра · стан ----------

   Група лишилась та сама, що після аудиту 07.08 (бренд зі знаком і
   версією; документ одним контуром: відкрити → назва → аркуш; чип фільтра;
   праворуч розміри книги і тема) — змінились метрики: контроль 36 замість
   28, як у всій родині, і смуга під нього виросла до 52. */
header {
  display: flex; align-items: center; gap: 12px; flex: none;
  height: 52px; padding: 0 14px;
  background: var(--panel);
  box-shadow: inset 0 -1px 0 var(--line);
}
.brand { display: flex; align-items: center; gap: 9px; flex: none; }
.brand b { font: 700 var(--text-lg) var(--font-ui); letter-spacing: .01em; }
/* Знак FreiCore їде з бренд-пака ЯК Є — кольори стрілки не перемальовують
   під токени. Тому на кожну тему свій файл: color на світлому, on-dark
   (біле кільце) — у темній. display тримають САМІ класи знака: спільного
   правила на img немає навмисно, щоб сильніший селектор не показав обидва
   варіанти разом. */
.logo-light { display: block; }
.logo-dark { display: none; }
:root[data-theme="dark"] .logo-light { display: none; }
:root[data-theme="dark"] .logo-dark { display: block; }
/* Версія — чип у брендовому блоці, а не сирота посеред смуги (Д-10).
   Моноширинним, бо це число, яке звіряють, а не читають. */
#version { font: var(--text-2xs) var(--font-num); color: var(--muted);
           padding: 3px 8px; border: 1px solid var(--line);
           border-radius: 999px; text-decoration: none;
           font-variant-numeric: tabular-nums; }
#version:hover, #version:focus-visible { border-color: var(--blue);
                                         color: var(--blue-ink); }

/* Група документа. Книгу відкриває сама людина, своїм провідником; поля
   для шляху немає — його не вводить руками ніхто. Назва книги й ім'я
   аркуша набрані МОНОШИРИННИМ, хоч це й смуга інтерфейсу: це дані з файла
   («КЖ-Общий»), а не наші слова, і читати їх треба тим самим шрифтом,
   яким вони стоять у таблиці. */
.doc { display: flex; align-items: stretch; height: var(--control-h);
       flex: none; border: 1px solid var(--line);
       border-radius: var(--radius-m); overflow: hidden;
       background: var(--panel-2); box-shadow: var(--shadow-resting); }
.doc[hidden] { display: none; }
#open { display: inline-flex; align-items: center; gap: 8px;
        font: 600 var(--text-base) var(--font-ui); color: var(--text);
        background: var(--panel-2); border: 0; min-height: 34px;
        padding: 0 14px; cursor: pointer;
        box-shadow: inset -1px 0 0 var(--line);
        transition: background var(--ui-ease), color var(--ui-ease); }
#open svg { width: 16px; height: 16px; flex: none; }
#open:hover { background: var(--ui-hover-bg); color: var(--blue-ink); }
/* Висота рядка їде В скороченні `font`, а не окремим рядком поруч: `font`
   без частини після скісної СКИДАЄ line-height на normal, і назва книги
   сідала на 34px-висоту своїм рядком у 15,5px — тобто на 5px вище за все
   інше в смузі (знахідка користувача 07.08). */
#file { display: block; padding: 0 12px; max-width: 360px;
        font: 600 var(--text-sm)/34px var(--font-num); color: var(--text);
        background: var(--panel);
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Селект аркуша вдягнений як свій: системний контрол упереміш зі своїми
   кнопками — найшвидший маркер недоробленості (В-03). Індиго, бо аркуш
   ОБИРАЄ людина: у цій мові індигова заливка означає рівно це. Список
   значень і далі малює браузер — він і має, а color-scheme тримає його в
   тоні. */
.sheet { position: relative; display: flex; align-items: center;
         background: var(--select-bg);
         box-shadow: inset 1px 0 0 var(--select-border); }
.sheet[hidden] { display: none; }
#sheets { appearance: none; -webkit-appearance: none; border: 0;
          background: none; font: 600 var(--text-sm) var(--font-num);
          color: var(--select-text); min-height: 34px; height: 100%;
          padding: 0 26px 0 12px; cursor: pointer; }
.sheet i { position: absolute; right: 10px; font-style: normal;
           font-size: 9px; color: var(--select-text); pointer-events: none; }

/* Фільтр — чип зі своїм «×»: стан показу видно і знімається одним рухом,
   а не реченням у рядку стану плюс окремою кнопкою (В-07). Кольором ДІЇ і
   без заливки: індигова заливка на цій сторінці означає «обране людиною», а
   фільтр — не вибір у даних, а стан показу. */
.chip { display: flex; align-items: center; gap: 6px; flex: 0 1 auto;
        min-width: 60px; overflow: hidden; min-height: 24px;
        font: 600 var(--text-sm) var(--font-ui); color: var(--blue-ink);
        border: 1px solid var(--blue); border-radius: 999px;
        background: none; padding: 0 3px 0 10px; }
.chip[hidden] { display: none; }
.chip .t { min-width: 0; overflow: hidden; text-overflow: ellipsis;
           white-space: nowrap; }
#unsift { flex: none; min-width: 22px; min-height: 22px; padding: 0;
          display: inline-flex; align-items: center; justify-content: center;
          border: 0; border-radius: 999px; background: none;
          color: var(--blue-ink); cursor: pointer; }
#unsift svg { width: 12px; height: 12px; }
#unsift:hover { background: var(--ui-hover-bg); }

/* Рядок стану відступив праворуч і тримає РОЗМІРИ книги — не фільтри й не
   помилки: у кожного з них тепер своє місце і своя вага. */
#status { flex: 1; min-width: 40px; text-align: right;
          font: var(--text-xs) var(--font-num); color: var(--muted);
          font-variant-numeric: tabular-nums;
          white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Збій, який мусить зупинити око, не має права читатись як черговий рядок
   телеметрії (В-07): плашка кольором небезпеки, повне речення — під мишею. */
#status.error { color: var(--red-ink); background: var(--red-soft-2);
                border-radius: var(--radius-s); padding: 5px 9px; }

/* ---------- перемикачі: тема і розкладка ---------- */
/* Пара кнопок, а не один тумблер: тумблер показує, куди перемкнути, і не
   каже, де ти зараз. Тут обидва стани стоять на екрані, поточний
   натиснутий — індиговою заливкою в 16%, як у всій родині. */
.seg { display: inline-flex; flex: none; border: 1px solid var(--line);
       border-radius: var(--radius-m); overflow: hidden;
       background: var(--panel-2); }
.seg button { display: inline-flex; align-items: center; gap: 6px;
              min-height: 28px; padding: 0 10px; border: 0;
              background: transparent; color: var(--muted);
              font: 600 var(--text-sm) var(--font-ui); cursor: pointer;
              white-space: nowrap;
              transition: background var(--ui-ease), color var(--ui-ease); }
.seg button svg { width: 16px; height: 16px; }
.seg button + button { box-shadow: inset 1px 0 0 var(--line); }
.seg button:hover { background: var(--ui-hover-bg); color: var(--text); }
.seg button[aria-pressed="true"] { background: var(--blue-soft);
                                   color: var(--blue-ink); }

main { flex: 1; display: flex; min-height: 0; position: relative;
       background: var(--panel); }

/* ---------- перший запуск ----------

   До відкриття книги на екрані не було НІЧОГО: порожня сітка, порожня
   панель і слово в кутку, поки Pyodide піднімається секундами (К-02). Саме
   на цьому екрані той, кому Root показали вперше, вирішує «іграшка чи
   інструмент». Екран каже, що це і навіщо; головна дія — єдина заливна
   кнопка вікна; «файл не покидає машину» — вголос; підняття двигуна —
   видимий прогрес, а не слово в кутку.

   Drop-зона носить сітку Bearing (порожній стан родини): пунктир плюс
   ледь помітна клітинка, щоб порожнє місце читалося як місце ДЛЯ чогось. */
#hello { position: absolute; inset: 0; z-index: 10; background: var(--panel);
         display: flex; align-items: center; justify-content: center;
         overflow: auto; }
#hello[hidden] { display: none; }
#hello .hello { display: flex; flex-direction: column; align-items: center;
                max-width: 470px; padding: 24px; text-align: center; }
#hello h1 { font: 700 24px/1.25 var(--font-ui); letter-spacing: -.01em;
            margin: 14px 0 0; }
#hello h1 em { font-style: italic; color: var(--blue-ink); }
#hello .sub { font: var(--text-base)/1.55 var(--font-ui); color: var(--muted);
              margin: 8px 0 0; }
/* Drop-зона клікабельна вся: падіння файла і вікно вибору — одна дія. */
#hello .drop { width: 100%; margin-top: 22px; padding: 30px 24px 22px;
               border: 1px dashed var(--line);
               border-radius: var(--radius-m);
               display: flex; flex-direction: column; align-items: center;
               gap: 6px; cursor: pointer;
               background:
                 linear-gradient(var(--blue-soft) 1px, transparent 1px)
                   0 0 / 26px 26px,
                 linear-gradient(90deg, var(--blue-soft) 1px, transparent 1px)
                   0 0 / 26px 26px;
               transition: border-color var(--ui-ease); }
#hello .drop:hover, #hello .drop.over { border-color: var(--blue); }
#hello .drop .big { font: 600 var(--text-md) var(--font-ui); }
#hello .drop .or { font: var(--text-xs) var(--font-ui); color: var(--muted); }
#hello .primary { font: 600 var(--text-base) var(--font-ui); color: #ffffff;
                  background: var(--blue); border: 1px solid var(--blue);
                  margin-top: 2px; border-radius: var(--radius-m);
                  min-height: var(--control-h); padding: 0 16px;
                  cursor: pointer; box-shadow: var(--shadow-resting);
                  transition: background var(--ui-ease); }
#hello .primary:hover { background: var(--blue-hover);
                        border-color: var(--blue-hover); }
#hello .drop .ext { font: var(--text-xs) var(--font-num); color: var(--muted);
                    margin-top: 6px; }
#hello .safe { font: var(--text-sm)/1.5 var(--font-ui); color: var(--muted);
               margin-top: 16px; }
#hello .boot { width: 100%; margin-top: 26px; display: flex;
               flex-direction: column; gap: 7px; }
#hello .boot[hidden] { display: none; }
#hello .boot .bar { height: 3px; border-radius: 999px;
                    background: var(--panel-2); overflow: hidden; }
#hello .boot .bar i { display: block; height: 100%; width: 5%;
                      background: var(--blue); border-radius: 999px;
                      transition: width .4s; }
#hello .boot p { font: var(--text-xs) var(--font-num); color: var(--muted);
                 margin: 0; }
#hello .boot p.error { color: var(--red-ink); }

#left { flex: 1; overflow: auto; background: var(--panel); }
/* «Показано не все» стоїть ТАМ, де воно закінчується — під останнім рядком,
   а не в смузі стану вгорі, куди його доводилося вичитувати. */
#more { position: sticky; left: 0; width: max-content;
        font: var(--text-xs) var(--font-ui); color: var(--muted);
        padding: 9px 12px; }
#more[hidden] { display: none; }
/* межа рухома, як у Finder: скільки віддати таблиці, а скільки зірці —
   вирішує людина, і вікно це пам'ятає */
#vsplit { flex: 0 0 6px; cursor: col-resize; background: var(--bg);
          position: relative; }
#vsplit::after { content: ''; position: absolute; inset: 0;
                 box-shadow: inset 1px 0 0 var(--line),
                             inset -1px 0 0 var(--line);
                 transition: background var(--ui-ease); }
#vsplit:hover::after, #vsplit:active::after { background: var(--blue); }
#right { width: 620px; min-width: 340px; display: flex;
         flex-direction: column; background: var(--panel); }

/* ---------- картка ядра ----------
   id / from / to / length закриває людина ДО першого кліку (спека картки).
   Стоїть НАД відповіддю: поки ядро не закрите, це єдине, що має значення.
   Кольори — ті самі ролі: --blue дія, індигова заливка вибір людини,
   --muted доказ; нової мови не заводимо. */
#core { padding: 12px 16px; box-shadow: inset 0 -1px 0 var(--line); }
#core[hidden] { display: none; }
#core.attention { box-shadow: inset 3px 0 0 var(--blue),
                              inset 0 -1px 0 var(--line); }
#core .core-ask { font: 600 var(--text-md)/1.35 var(--font-ui);
                  color: var(--text); }
#core .empty { font: var(--text-base)/1.4 var(--font-ui); }
#core .core-line { display: flex; align-items: center; gap: 8px; }
#core .core-sum { font: 600 var(--text-sm) var(--font-num);
                  color: var(--manual-text); background: var(--manual-bg);
                  border-radius: var(--radius-s); padding: 2px 7px;
                  overflow: hidden; text-overflow: ellipsis;
                  white-space: nowrap; }
#core .core-line .act { margin-left: auto; }
#core .repere { margin-top: 8px; }
#core .repere-head { display: flex; gap: 8px; align-items: baseline; }
#core .repere-kind { font: var(--text-xs) var(--font-ui); color: var(--muted);
                     min-width: 62px; }
#core .repere-chosen { font: 600 var(--text-sm) var(--font-num);
                       color: var(--text); }
#core summary { font: var(--text-xs) var(--font-ui); color: var(--blue-ink);
                cursor: pointer; margin: 3px 0 0 70px; }
#core .cands { margin: 4px 0 2px 70px; display: flex;
               flex-direction: column; gap: 3px; }
#core .cand { display: flex; gap: 7px; align-items: baseline;
              font: var(--text-sm) var(--font-ui); color: var(--text);
              cursor: pointer; }
#core .cand input { accent-color: var(--blue); }
#core .cand .proof { font: var(--text-xs) var(--font-ui); color: var(--muted); }
#core .other { font: var(--text-xs) var(--font-ui); color: var(--text);
               background: var(--panel-2); border: 1px solid var(--line);
               border-radius: var(--radius-s); padding: 4px 7px;
               max-width: 100%; }
#core .other-pair { display: flex; gap: 6px; align-items: center; }
#core .act { font: 600 var(--text-sm) var(--font-ui); border: 0;
             cursor: pointer; background: none;
             border-radius: var(--radius-s); }
#core .act.show { color: var(--blue-ink); padding: 5px 9px; }
#core .act.show:hover { background: var(--blue-soft); }
#core .act.confirm { margin-top: 10px; min-height: var(--control-h);
                     padding: 0 16px; border: 1px solid var(--blue);
                     border-radius: var(--radius-m); background: var(--blue);
                     color: #ffffff; box-shadow: var(--shadow-resting); }
#core .act.confirm:hover:not(:disabled) { background: var(--blue-hover);
                                          border-color: var(--blue-hover); }
#core .act.confirm:disabled { opacity: var(--ui-disabled-opacity);
                              cursor: default; }

#head { padding: 14px 16px 10px; box-shadow: inset 0 -1px 0 var(--line); }
#canvas { flex: 1; min-height: 0; display: flex; flex-direction: column;
          position: relative; }
.empty { color: var(--muted); }

/* полотно зірки: своя площина, яка возиться й масштабується всередині */
#stage { position: relative; flex: 1; min-height: 0; overflow: hidden; }
#stage[hidden] { display: none; }
#spider { display: block; width: 100%; height: 100%;
          touch-action: none; cursor: grab; }
#spider.holding { cursor: grabbing; }
/* Зерно паперу, не міліметрівка: дрібна темніша крапка (r у spider.js)
   показує рух на порожньому місці, не називаючи полотно кресленням. */
.dot { fill: var(--slate-8); }

#zoom { position: absolute; right: 10px; bottom: 10px; display: flex;
        border: 1px solid var(--line); border-radius: var(--radius-m);
        overflow: hidden; background: var(--panel);
        box-shadow: var(--shadow-resting); }
#zoom button { font: 600 var(--text-sm) var(--font-num); color: var(--muted);
               min-width: 30px; min-height: 28px; padding: 0 8px; border: 0;
               cursor: pointer; background: var(--panel);
               font-variant-numeric: tabular-nums;
               transition: background var(--ui-ease), color var(--ui-ease); }
#zoom button + button { box-shadow: inset 1px 0 0 var(--line); }
#zoom button:hover { background: var(--ui-hover-bg); color: var(--text); }
#zoom[hidden] { display: none; }

/* Перемикач розкладки зірки стоїть на самому полотні, у протилежному кутку
   від масштабу: це вибір про те, ЯК показано, а не про те, що показано. */
#lay { position: absolute; left: 10px; bottom: 10px; z-index: 2;
       background: var(--panel); box-shadow: var(--shadow-resting); }
#lay[hidden] { display: none; }
#lay button { background: var(--panel); }
#lay button[aria-pressed="true"] { background: var(--blue-soft); }

/* ---------- сира сітка ---------- */

/* `border-collapse: collapse` і приклеєна шапка — відома пара, на якій
   Chrome лишає під шапкою непрофарбовану смугу: крізь шапку видно рядки, що
   поїхали вгору, і вона здається напівпрозорою. Тому сітка збирається на
   `separate`, а лінії малюються тінню ВСЕРЕДИНУ клітинки — вона їде разом
   із нею й нічого не лишає позаду. */
table { border-collapse: separate; border-spacing: 0;
        font: var(--text-sm) var(--font-num); }
/* Шапка — суцільний шар над даними: заливка непрозора, лінія своя, і рукою
   в неї треба влучати (10.5px на висоті 17px були найдрібнішою ціллю кліку
   в усьому вікні). Текст ЛІВОРУЧ: дані вирівняні ліворуч, і шапка,
   вирівняна по центру, не збігалася з жодною колонкою — саме через це вона
   читалася як крива. */
thead th { position: sticky; top: 0; z-index: 4;
           background: var(--table-header); color: var(--muted);
           text-align: left; font-weight: 600; font-size: var(--text-xs);
           padding: 5px 9px; white-space: nowrap;
           box-shadow: inset 0 -1px 0 var(--table-line); }
thead .letters th { font-size: var(--text-2xs); letter-spacing: .05em;
                    font-weight: 400; }
/* Останній рядок шапки відрізає її від даних твердою лінією і кладе тінь:
   шар видно навіть там, де під ним такий самий світлий рядок. */
thead tr:last-child th { box-shadow: inset 0 -1px 0 var(--line),
                                     var(--shadow-head); }
/* приклеєна шапка з файла: ті самі рядки книги, з їхніми номерами. Читається
   як заголовок, а не як ще один рядок даних. */
thead .pin th { font-size: var(--text-base); color: var(--text);
                font-weight: 600; max-width: 260px; }
thead .pin .rownum { z-index: 5; font-weight: 400; color: var(--muted); }
thead .rownum { z-index: 5; }
tbody tr.pinned { display: none; }     /* рядок шапки живе вгорі, не двічі */

/* Ім'я колонки — ручка фільтра, і значок стоїть завжди: інакше про те, що
   колонку можна сіяти, знає тільки той, хто випадково туди навів. Поки імен
   немає (аркуш не вивчено), ручкою лишається літера — і значок на ній. */
thead .h { display: flex; align-items: center; gap: 6px; }
thead .h .t { flex: 1; min-width: 0; white-space: nowrap;
              overflow: hidden; text-overflow: ellipsis; }
/* 9px значок був присутністю без сигналу (Д-08): 10.5px і легка плашка
   під мишею повертають сигнал, не додавши шуму. */
thead .fx { flex: none; font-size: 10.5px; line-height: 1;
            color: var(--muted); padding: 2px 3px;
            border-radius: var(--radius-s); }
thead.named .letters .fx { display: none; }
thead .sift-able { cursor: pointer; }
/* Підсвітка ПРИКЛЕЄНОЇ шапки мусить бути непрозорою: крізь напівпрозору
   видно рядки, що їдуть під нею, — саме це читалось як «header наче
   прозорий». --ui-hover-bg непрозорий за побудовою (щабель slate, не
   альфа), і закон тепер тримає сама мова, а не окремий змішаний токен. */
thead .sift-able:hover { background: var(--ui-hover-bg); color: var(--text); }
thead .sift-able:hover .fx { color: var(--text); }
thead .sifted { color: var(--blue-ink); font-weight: 600; }
thead .sifted .fx { color: var(--blue-ink); }
thead .sifted .t { text-decoration: underline 2px var(--blue);
                   text-underline-offset: 3px; }
/* Колонки кінців носять позначку: порожній стан панелі називає їх словами
   (В-06), а шапка показує ті самі місця знаком. Колір дії, не вибору. */
thead th.end .h::before { content: ''; flex: none; width: 5px; height: 5px;
                          border-radius: 999px; background: var(--blue); }

#sift { position: fixed; z-index: 20; width: 260px; max-height: 60vh;
        display: flex; flex-direction: column;
        background: var(--panel); border: 1px solid var(--line);
        border-radius: var(--radius-m); box-shadow: var(--shadow-raised);
        padding: 8px; }
/* Випадайка відкривається ПОВЕРХ літери й затуляє шапку — тож мусить сама
   сказати, яку колонку зараз сіє: інакше після відкриття слова «type» на
   екрані немає взагалі. */
#sift .sift-head { font: 600 var(--text-sm) var(--font-num); color: var(--text);
                   padding: 2px 4px 8px; margin-bottom: 6px;
                   border-bottom: 1px solid var(--line);
                   white-space: nowrap; overflow: hidden;
                   text-overflow: ellipsis; }
#sift .sift-head em { font: var(--text-xs) var(--font-ui); font-style: normal;
                      color: var(--muted); }
/* Поле пошуку — індигове: те, що ЛЮДИНА вводить руками, у цій мові має
   заливку вибору, як і аркуш у смузі. */
#sift input[type=text] { font: 600 var(--text-sm) var(--font-num);
                         min-height: 28px; padding: 0 10px;
                         border: 1px solid var(--manual-border);
                         border-radius: var(--radius-s);
                         background: var(--manual-bg);
                         color: var(--manual-text); }
#sift .sift-list { overflow: auto; margin: 6px 0; }
/* рядок переліку — теж ціль кліку: 17px на 11.5px кеглю були тіснуваті */
#sift label { display: flex; align-items: center; gap: 7px; min-height: 26px;
              padding: 4px 6px; border-radius: var(--radius-s);
              font: var(--text-sm) var(--font-num); cursor: pointer; }
#sift label input[type=checkbox] { flex: none; width: 15px; height: 15px;
                                   accent-color: var(--blue); }
#sift label:hover { background: var(--ui-hover-bg); }
#sift label span { flex: 1; overflow: hidden; text-overflow: ellipsis;
                   white-space: nowrap; }
#sift label .dim { color: var(--muted); font-style: italic; }
#sift label em { color: var(--muted); font-style: normal;
                 font-size: var(--text-xs); }
#sift .sift-note { font: var(--text-xs) var(--font-ui); color: var(--muted); }
#sift .sift-note:empty { display: none; }
#sift .sift-bar { display: flex; gap: 6px; }
#sift button { flex: 1; min-height: 28px;
               font: 600 var(--text-sm) var(--font-ui);
               border: 1px solid var(--line); border-radius: var(--radius-m);
               background: var(--panel-2); color: var(--text); cursor: pointer;
               transition: background var(--ui-ease); }
#sift button:hover { background: var(--ui-hover-bg); color: var(--blue-ink); }

/* Поля клітинки — 3px, а не 10, як у Bearing. Це не дрібниця і не
   недогляд: Bearing показує десятки рядків, Root — шість тисяч, і кожен
   зайвий піксель угору-вниз забирає рядок з екрана. Мова родини тут
   лишається (кегль, лінія, колір), а щільність — наша, бо вона і є
   інструментом. */
td, tbody th { padding: 3px 9px; background: var(--table-cell);
               box-shadow: inset 0 -1px 0 var(--table-line);
               white-space: nowrap; max-width: 260px; overflow: hidden;
               text-overflow: ellipsis;
               font-variant-numeric: tabular-nums; }
td { cursor: default; }
td:not(.empty) { cursor: pointer; }
td:not(.empty):hover { background: var(--table-hover); }
/* Ринва номерів — край таблиці, а не ще одна колонка: своя лінія праворуч. */
.rownum { position: sticky; left: 0; z-index: 1;
          background: var(--table-header); color: var(--muted);
          font-weight: 400; text-align: right;
          box-shadow: inset -1px 0 0 var(--table-line),
                      inset 0 -1px 0 var(--table-line); }
thead .rownum { box-shadow: inset -1px 0 0 var(--table-line),
                            inset 0 -1px 0 var(--table-line); }
thead tr:last-child .rownum { box-shadow: inset -1px 0 0 var(--table-line),
                                          inset 0 -1px 0 var(--line),
                                          var(--shadow-head); }
/* Індигова заливка на цій сторінці означає РІВНО ОДНЕ: «ось те, що обрала
   або ввела людина». Заливка рядка й обведення клітинки — це її клік, і
   більше нічий. */
tr.hit td { background: var(--manual-bg); }
td.hitcell { outline: 2px solid var(--manual-focus); outline-offset: -2px; }

/* Дірка області — не вибір людини, а факт про дані, тож ані тепла, ані
   заливки рядка: раніше дірка і вибране різнилися на 15 одиниць, і око
   плутало їх. Лишається холодна штриховка на номері рядка (фактура, не
   колір) і приглушений текст; причина стоїть плашкою під мишею. */
tr.hole td { color: var(--readonly-text); }
tr.hole .rownum { cursor: help;
                  background: repeating-linear-gradient(
                    -45deg, var(--table-header) 0 3px, var(--slate-6) 3px 4px); }

/* ---------- панель: чотири яруси й смуга дій ----------

   Ярус несе одне: ВІДПОВІДЬ → що клікнули → доказ → дія. До правки все
   стояло вагою 400 в діапазоні 11.5–13px, і сама відповідь була ДРІБНІША
   за назву клікнутого — око починало не з того.

   Відповіді немає — веде питання або причина відмови: заголовний ярус
   тримає рівно те, чим Root відповів, і не вигадує заповнення.

   Відповідь набрана моноширинним: це число з книги, а не наше слово. */

#head .answer { font: 700 30px/1.05 var(--font-num); color: var(--text);
                letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
#head .answer + div { margin-top: 8px; }
#head b { font: 600 var(--text-sm) var(--font-num); }
#head .cell { font: var(--text-xs) var(--font-num); color: var(--muted); }
#head .because { margin-top: 4px; font: var(--text-xs)/1.5 var(--font-ui);
                 color: var(--muted); }
#head .reason { font: 600 var(--text-md)/1.35 var(--font-ui);
                color: var(--red-ink); }
#head .picked { margin-top: 5px; font: var(--text-sm) var(--font-num);
                color: var(--manual-text); }
#head .picked:empty { display: none; }

/* ---------- питання і картка групи ---------- */

#head .ask { margin-top: 7px; font: 600 var(--text-md)/1.35 var(--font-ui);
             color: var(--text); }
#head .opts { display: flex; flex-wrap: wrap; gap: 6px; margin: 7px 0 2px; }
#head .opt { font: var(--text-sm) var(--font-ui); text-align: left;
             padding: 7px 10px; border: 1px solid var(--line);
             border-radius: var(--radius-m); background: var(--panel-2);
             color: var(--text); cursor: pointer;
             transition: background var(--ui-ease); }
#head .opt:hover { border-color: var(--blue); background: var(--ui-hover-bg); }

/* Смуга дій: край у край, відрізана волосяною лінією, завжди на тому самому
   місці — байдуже, довгий доказ чи короткий.

   Безпечна дія (звузити показ) і небезпечна (забути вивчене) раніше ділили
   один клас і один колір. Тепер їх розводить УСЕ: колір, заливка й кінець
   смуги. Розрізнення не покладено на межу — обведення в 1px тут дає 1.6–2.5
   при потрібних 3.0, тобто його просто не видно. */
/* Лінія тут МЕЖА, а не внутрішня тінь: кнопка «забути» несе власну заливку,
   і внутрішню тінь батька вона б перекрила — смуга розірвалась би рівно над
   найнебезпечнішою дією. */
#head .acts { display: flex; margin: 10px -16px -10px;
              border-top: 1px solid var(--line); }
#head .acts:empty { display: none; }
#head .act { font: 600 var(--text-sm) var(--font-ui); padding: 9px 16px;
             text-align: left; border: 0; background: none; cursor: pointer; }
#head .act.show { color: var(--blue-ink); }
#head .act.show:hover { background: var(--blue-soft); }
#head .act.undo { margin-left: auto; color: var(--red-ink);
                  background: var(--red-soft);
                  box-shadow: inset 1px 0 0 var(--line); }
#head .act.undo:hover { background: var(--red-soft-2); }
/* Дія всередині речення (приїхала нова версія Root): та сама кнопка, тільки
   в рядку тексту, а не у смузі край у край, — тому без її бічних полів. */
#head .empty .act { padding: 9px 10px; }

/* рядки картки групи: гортаються самі, а без них список не займає місця.
   Номер кабелю веде, адреса відступає — 43 однакові рядки читались як
   суцільна сіра стіна. Скільки їх — стоїть заголовком, а не рахується оком. */
#list { display: flex; flex-direction: column; overflow: auto; padding: 8px; }
#list:empty { display: none; }
#list .list-head { font: var(--text-xs) var(--font-ui); color: var(--muted);
                   padding: 2px 6px 7px; }
#list .row { font: var(--text-sm) var(--font-num); color: var(--muted);
             padding: 5px 8px; border-radius: var(--radius-s);
             cursor: pointer; font-variant-numeric: tabular-nums; }
#list .row b { font-weight: 600; color: var(--text); }
#list .row:hover { background: var(--ui-hover-bg); }
#list .row:hover b { color: var(--text); }
#list .row.sel { background: var(--manual-bg); color: var(--manual-text); }
#list .row.sel b { color: var(--manual-text); }

/* ---------- реєстр: та сама зірка без полотна ----------

   На 102 сусідах полотно перестає бути інструментом: зірку не видно цілою
   за жодного масштабу. Реєстр дає ту саму правду списком — сусід, скільки
   в нього кабелів, і кожен номер окремо. Клік по імені — крок павуком,
   клік по номеру — рядок у файлі: рівно те саме, що на полотні. */
/* Перемикач розкладки плаває над полотном — а реєстр не полотно, він
   гортається. Тому знизу лишається місце РІВНО під перемикач: інакше
   останній сусід назавжди лишається під кнопками, і його не вичитати. */
#ledger { position: absolute; inset: 0; overflow: auto;
          padding: 8px 10px 52px; }
#ledger[hidden] { display: none; }
#ledger .list-head { font: var(--text-xs) var(--font-ui); color: var(--muted);
                     padding: 2px 4px 8px; }
/* Ряди вирівняні по ВЕРХУ, а не по базовій лінії: у сусіда з 30 кабелями
   плитка номерів переноситься на кілька рядків, і центрування притиснуло б
   його ім'я кудись на середину стовпчика номерів. */
#ledger .node { display: flex; align-items: flex-start; gap: 10px;
                padding: 5px 6px; border-radius: var(--radius-s);
                box-shadow: inset 0 -1px 0 var(--table-line); }
#ledger .node:hover { background: var(--ui-hover-bg); }
#ledger .name { display: inline-flex; align-items: center; min-height: 22px;
                font: 600 var(--text-sm) var(--font-num); color: var(--text);
                flex: none; min-width: 150px; max-width: 260px;
                white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
                cursor: pointer; }
#ledger .name:hover { color: var(--blue-ink); text-decoration: underline; }
#ledger .n { display: inline-flex; align-items: center; min-height: 22px;
             font: var(--text-xs) var(--font-ui); color: var(--muted);
             flex: none; min-width: 74px; }
#ledger .chips { display: flex; flex-wrap: wrap; gap: 2px 8px;
                 font: var(--text-xs) var(--font-num); }
/* Номер кабелю в реєстрі — ціль кліку, як лінія на полотні, тож мусить бути
   не дрібнішою за неї. Висоту дає min-height, а не повітря паддінгом:
   щільність тут закон, і 183 номери не мають права роз'їхатися на два
   екрани заради того, щоб у кожен було легше влучити. */
#ledger .chip { display: inline-flex; align-items: center; min-height: 22px;
                color: var(--muted); cursor: pointer; padding: 2px 6px;
                border: 0; border-radius: var(--radius-s);
                font: var(--text-xs) var(--font-num); background: none; }
#ledger .chip:hover { color: var(--text); background: var(--ui-hover-bg); }
#ledger .chip.sel { color: var(--manual-text); background: var(--manual-bg); }

svg { display: block; }

/* ---------- мова полотна (10.08) ----------

   Редизайн 08.08 переписав тут ЛИШЕ імена кольорів; форма — радіуси, тіні,
   ваги, шкала кеглів — на полотно не доїхала, і воно лишилось єдиним місцем
   вікна, яке говорить не мовою Bearing. Це й читалось як чернетка.

   Головний закон, який тут тримається: ОБВІД КАРТКИ ТЕМНІШИЙ ЗА ДРІТ.
   Було навпаки — дріт slate-8 проти обводу slate-7, тобто зв'язок важив
   більше, ніж те, що він зв'язує, і на 181 кабелі зірка ставала павутиною.
   Тепер картка бере slate-8, дріт віддає їй щабель і йде на slate-7.

   Землю НЕ заглиблюємо: у CableBlock, на який дивився користувач, полотно
   так само біле, а картку тримають обвід, радіус і тінь — не сіра підкладка.
   Це заміряно в них, а не вгадано. */

.wire { fill: none; stroke: var(--slate-7); stroke-width: 1; }
/* Риски виходів на краю картки центру: черево високої картки показує свої
   клеми — геометрія exit уже порахована, риска робить її видимою (Д-09).
   Риска — той самий дріт, тож і колір той самий. */
.tick { stroke: var(--slate-7); stroke-width: 1; }
.hit  { fill: none; stroke: transparent; stroke-width: 12; cursor: pointer; }
.cable { font: 500 var(--text-2xs) var(--font-num); fill: var(--muted);
         paint-order: stroke; stroke: var(--panel); stroke-width: 3px;
         cursor: pointer; }
/* Підпис лежить НА дроті (одна лінія з ним та ім'ям картки): ореол літер
   не закриває проміжків між словами, тож під підписом плашка паперу —
   інакше дріт світить крізь них пунктиром. */
.cable-bg { fill: var(--panel); cursor: pointer; }
.edge:hover .wire { stroke: var(--blue); stroke-width: 2; }
.edge.sel .wire { stroke: var(--manual-focus); stroke-width: 2.2; }
.cable.sel { fill: var(--manual-text); }

/* Форма картки живе тут, а не в малювальнику: `rx` — геометрична
   властивість, тож радіус береться з тих самих трьох, що й у решти вікна.
   Тінь у SVG — фільтр; його одного оголошує spider.js у <defs>. */
.nb, .ct { rx: var(--radius-m); filter: url(#card-shadow); }
.nb  { fill: var(--panel); stroke: var(--slate-8); }
.nb-t { font: 500 var(--text-sm) var(--font-num); fill: var(--text); }
/* Карта центру — те, що клікнула людина, тож вона носить її колір:
   індигова заливка й індиговий обвід, як рядок під кліком у таблиці. */
.ct  { fill: var(--manual-bg); stroke: var(--manual-focus); }
.ct-t { font: 600 var(--text-md) var(--font-num); fill: var(--text); }
.sub { font: 500 var(--text-2xs) var(--font-ui); fill: var(--muted); }
.node { cursor: pointer; }
.node:hover .nb { stroke: var(--blue); }
