/* Панель полупрозрачна, поэтому фотография подмешивается в её фон и съедает контраст текста.
   --muted и --accent подобраны так, что на стекле поверх самого чёрного (светлая схема) или
   самого белого (тёмная) снимка держат 4.5:1 — порог WCAG AA. Менять их или 0.86 только с
   пересчётом контраста. */
:root {
  /* Без этого свойства браузер рисует список выбора, каретку и полосы прокрутки светлыми
     даже при тёмной палитре: своими стилями внутренности этих виджетов не достать. */
  color-scheme: light;
  --bg: #fdfdfc; --fg: #1a1a18; --muted: #5f5f5a; --accent: #2a6a4a; --line: #e3e3df;
  --callout: #f1f4f2; --code-bg: #f5f5f3;
  --warn: #c98a2b; --danger: #c0503f; --tip: #2f8f5f;
  --panel: rgba(253, 253, 252, 0.86);
  --panel-solid: #fdfdfc;
  --panel-border: rgba(0, 0, 0, 0.10);
  --shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
  /* Шапка и подвал лежат прямо на фотографии. Несколько теней подряд собираются в обводку
     под цвет панели — одна тень слишком жидкая и на светлом снимке текст пропадает. */
  --top-shadow: 0 0 1px #fff, 0 0 2px rgba(255, 255, 255, 0.95), 0 0 4px rgba(255, 255, 255, 0.9), 0 0 8px rgba(255, 255, 255, 0.8);
}

/* Тёмная палитра включается в двух случаях: «как в системе» при тёмной системе и явный
   выбор «тёмная». Набор повторён дважды: обычный CSS не умеет отдать один блок двум условиям. */
@media (prefers-color-scheme: dark) {
  :root:not([data-color-scheme="light"]) {
    color-scheme: dark;
    --bg: #16171a; --fg: #e6e6e3; --muted: #a1a19b; --accent: #7fbf9a; --line: #2b2d31;
    --callout: #1e2024; --code-bg: #1b1d21;
    --warn: #d9a84e; --danger: #d97a68; --tip: #6fbf94;
    --panel: rgba(22, 23, 26, 0.86);
    --panel-solid: #16171a;
    --panel-border: rgba(255, 255, 255, 0.12);
    --shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
    --top-shadow: 0 0 1px #000, 0 0 2px rgba(0, 0, 0, 0.95), 0 0 4px rgba(0, 0, 0, 0.9), 0 0 8px rgba(0, 0, 0, 0.8);
  }
}

:root[data-color-scheme="dark"] {
  color-scheme: dark;
  --bg: #16171a; --fg: #e6e6e3; --muted: #a1a19b; --accent: #7fbf9a; --line: #2b2d31;
  --callout: #1e2024; --code-bg: #1b1d21;
  --warn: #d9a84e; --danger: #d97a68; --tip: #6fbf94;
  --panel: rgba(22, 23, 26, 0.86);
  --panel-solid: #16171a;
  --panel-border: rgba(255, 255, 255, 0.12);
  --shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  --top-shadow: 0 0 1px #000, 0 0 2px rgba(0, 0, 0, 0.95), 0 0 4px rgba(0, 0, 0, 0.9), 0 0 8px rgba(0, 0, 0, 0.8);
}

/* Выбранный цвет фона задаёт и акцент интерфейса: тон берём из --brand, а светлоту назначаем
   сами — 0.42 под светлую схему и 0.8 под тёмную. Так контраст с текстом на кнопке держится
   при любом цвете палитры, а без --brand var() отдаёт запасное значение — акцент темы.
   Браузер без relative color syntax просто не увидит этот блок и оставит акцент как был. */
@supports (color: oklch(from red l c h)) {
  :root {
    --brand-accent: oklch(from var(--brand) 0.42 c h);
    --accent: var(--brand-accent, #2a6a4a);
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-color-scheme="light"]) {
      --brand-accent: oklch(from var(--brand) 0.8 c h);
      --accent: var(--brand-accent, #7fbf9a);
    }
  }
  :root[data-color-scheme="dark"] {
    --brand-accent: oklch(from var(--brand) 0.8 c h);
    --accent: var(--brand-accent, #7fbf9a);
  }
}

/* Схемы в заметках нарисованы svg в цветах Obsidian. Незнакомая переменная в var() делает
   fill невалидным, и фигура становится чёрной, поэтому имена Obsidian заведены как псевдонимы
   палитры темы. Значения берутся из --fg и соседей, так что схема сама меняется вместе с темой. */
:root {
  --text-normal: var(--fg);
  --text-muted: var(--muted);
  --text-faint: var(--muted);
  --background-primary: var(--bg);
  --background-secondary: var(--callout);
  --background-modifier-border: var(--line);
  --color-green: var(--tip);
  --color-red: var(--danger);
  --color-accent: var(--accent);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  color: var(--fg);
  font: 17px/1.6 Georgia, "Times New Roman", serif;
  /* Картинку подставляет layout.html через --bg-image, ровный цвет — через --backdrop.
     Отдельные свойства, а не сокращение: сломанное значение переменной гасит только
     background-image, а цвет подложки остаётся. */
  background-color: var(--backdrop, var(--bg));
  background-image: var(--bg-image, none);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  background-attachment: fixed;
}

.top {
  width: calc(100% - 1rem);
  margin: 0 auto;
  padding: 1rem 0.5rem;
  color: var(--muted);
  text-shadow: var(--top-shadow);
}
/* Шапка держится над панелью: иначе выпадающее меню владельца уходит ей за край. */
.top { position: relative; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.top a { color: var(--fg); font-family: system-ui, sans-serif; font-weight: bold; text-decoration: none; }
/* Длинное название обрезается многоточием: min-width: 0 даёт ссылке сжаться во flex-строке шапки. */
.top-home { display: flex; align-items: center; gap: 0.4em; min-width: 0; }
.top-home img { flex-shrink: 0; width: 1.4em; height: 1.4em; }
.top-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Меню владельца: <details> без js, раскрывается вправо под именем. */
.user-menu { position: relative; font-family: system-ui, sans-serif; font-size: 0.9rem; }
.user-menu summary {
  cursor: pointer;
  list-style: none;
  color: var(--fg);
  font-weight: bold;
  padding: 0.3rem 0.5rem;
}
.user-menu summary::-webkit-details-marker { display: none; }
.user-menu[open] summary { color: var(--accent); }
.user-menu ul {
  position: absolute;
  right: 0;
  top: 100%;
  margin: 0.25rem 0 0;
  padding: 0.35rem;
  min-width: 9rem;
  list-style: none;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.user-menu li { margin: 0; }
.user-menu a, .user-menu button {
  display: block;
  width: 100%;
  padding: 0.4rem 0.5rem;
  border: none;
  background: none;
  color: var(--fg);
  font: inherit;
  text-align: left;
  text-decoration: none;
  border-radius: 4px;
  cursor: pointer;
}
.user-menu a:hover, .user-menu button:hover { background: var(--callout); }
.user-menu form { margin: 0; }

.top .search { margin: 0; }
.top .search input, .search-page input {
  padding: 0.3rem 0.5rem; border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg); color: var(--fg); font-family: system-ui, sans-serif; font-size: 0.9rem;
}
.top .search input { width: min(18rem, 40vw); }
.search-page { display: flex; gap: 0.5rem; margin: 1rem 0; }
.search-page input { flex: 1; }
.search-note { color: var(--muted); font-family: system-ui, sans-serif; font-size: 0.9rem; }
.snippet { color: var(--muted); }
.snippet mark { background: var(--accent); color: var(--bg); border-radius: 3px; padding: 0 0.15rem; }

/* Два блока рядом: дерево на матовом стекле и статья на плотном фоне. Сама обёртка держит
   только раскладку, своего фона у неё нет — иначе стекло боковика показывало бы её, а не снимок.
   Потолка ширины нет намеренно: блоки тянутся под любое окно, отступ от краёв одинаковый.
   Ниже 720px они складываются в одну колонку со сворачиваемым деревом сверху. */
.shell {
  width: calc(100% - 1rem);
  /* Снизу тот же зазор, что по бокам: подвала нет, панель доходит до края окна. */
  margin: 0 auto 0.5rem;
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: 0.25rem;
}

/* Страницы без дерева: вход, настройки, ошибки, статья по гостевой ссылке. */
.shell-plain { display: block; width: min(52rem, calc(100% - 1rem)); }
/* Вход — одна короткая форма, широкая панель вокруг неё смотрится пустой. */
.shell-plain:has(.login) { width: min(26rem, calc(100% - 1rem)); }
/* Статья по гостевой ссылке: панель во всю ширину окна, высоту ей даёт правило ниже. */
.shell-full { width: calc(100% - 1rem); }

/* Читалка: плотный фон, сквозь снимок длинный текст читать тяжело.
   Ширину не ограничиваем — статья тянется от боковика до правого края. */
.shell main {
  max-width: none;
  margin: 0;
  padding: 1.25rem 1.5rem 2rem;
  background: var(--panel-solid);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  box-shadow: var(--shadow);
}

/* Боковик остаётся матовым стеклом: снимок под ним видно, а мешать там нечему. */
.sidebar {
  padding: 1.25rem 1rem;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  box-shadow: var(--shadow);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
}

/* Боковик и меню владельца висят над снимком; без размытия они становятся плотными. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .sidebar, .user-menu ul { background: var(--panel-solid); }
}
/* Переключатель дерева нужен только узкому экрану. */
.nav-switch { display: none; }
/* Ход липкому блоку даёт колонка: она тянется во всю высоту панели (выравнивание грида
   по умолчанию). */
.sidebar-body {
  position: sticky;
  top: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  max-height: calc(100vh - 2rem);
}

#nav-filter {
  width: 100%;
  padding: 0.4rem 0.6rem;
  font: inherit;
  font-size: 0.9rem;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
}

/* Отступ панели перенесён внутрь дерева: так полоса прокрутки встаёт к правому краю панели. */
#nav-tree {
  overflow-y: auto;
  margin-right: -1rem;
  padding-right: 1rem;
  font-family: system-ui, sans-serif;
  font-size: 0.92rem;
}
/* Тонкая полоса без стрелок у всех прокручиваемых столбцов: дерево, статья, разделы и таблицы настроек. */
:is(#nav-tree, .shell main, .sidebar-body, .settings-panes, .table-scroll)::-webkit-scrollbar { width: 6px; height: 6px; }
:is(#nav-tree, .shell main, .sidebar-body, .settings-panes, .table-scroll)::-webkit-scrollbar-button { display: none; }
:is(#nav-tree, .shell main, .sidebar-body, .settings-panes, .table-scroll)::-webkit-scrollbar-track { background: transparent; }
:is(#nav-tree, .shell main, .sidebar-body, .settings-panes, .table-scroll)::-webkit-scrollbar-thumb { background: var(--muted); border-radius: 3px; }
/* Chrome не применяет ::-webkit-scrollbar, если задан scrollbar-color, поэтому стандартные свойства — только для Firefox. */
@supports not selector(::-webkit-scrollbar) {
  :is(#nav-tree, .shell main, .sidebar-body, .settings-panes, .table-scroll) { scrollbar-width: thin; scrollbar-color: var(--muted) transparent; }
}
#nav-tree [hidden] { display: none; }

.nav-folder summary {
  cursor: pointer;
  padding: 0.25rem 0;
  list-style: none;
  color: var(--muted);
}
.nav-folder summary::-webkit-details-marker { display: none; }
.nav-folder summary::before { content: "▸ "; }
.nav-folder[open] > summary::before { content: "▾ "; }
.nav-folder .nav-folder, .nav-folder .nav-articles { padding-left: 0.9rem; }

.nav-articles { list-style: none; margin: 0; padding: 0; }
.nav-articles li { margin: 0.1rem 0; }
.nav-articles a {
  display: block;
  padding: 0.15rem 0.35rem;
  border-radius: 4px;
  color: var(--fg);
  text-decoration: none;
  overflow-wrap: break-word;
}
.nav-articles a:hover { background: var(--callout); }
/* Подсветка текущей статьи: акцентный фон, контраст держится и в светлой, и в тёмной палитре. */
.nav-articles a[aria-current="page"] { background: var(--accent); color: var(--bg); font-weight: bold; }

/* Закрытая статья: у читателя — строка без ссылки, у владельца — ссылка с пометкой. */
.nav-closed { color: var(--muted); }
.nav-articles .nav-closed { display: block; padding: 0.15rem 0.35rem; }
.nav-private::after { content: " ·"; color: var(--muted); }

/* Разделы настроек занимают место дерева статей, поэтому и ведут себя так же. */
.settings-nav {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  font-family: system-ui, sans-serif;
  font-size: 0.95rem;
}
.settings-nav a {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.4rem 0.6rem;
  border-radius: 6px;
  color: var(--fg);
  text-decoration: none;
}
.settings-nav a:hover { background: var(--callout); }
.settings-nav a[aria-current="page"] { background: var(--accent); color: var(--bg); font-weight: bold; }
/* Подпись группы разделов — не ссылка: мелко, приглушённо, с отступом от предыдущей группы. */
.settings-group {
  margin: 0.9rem 0 0.2rem; padding: 0 0.6rem;
  font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted);
}
.settings-group:first-child { margin-top: 0; }
.nav-badge {
  margin-left: auto; min-width: 1.25rem; padding: 0 0.4rem; border-radius: 999px;
  background: var(--accent); color: var(--bg); font-size: 0.75rem; font-weight: 600; text-align: center;
}
.settings-nav a[aria-current="page"] .nav-badge { background: var(--bg); color: var(--accent); }

@media (max-width: 720px) {
  /* На iOS background-attachment: fixed рисует картинку неправильно. */
  body { background-attachment: scroll; }
  .top { width: calc(100% - 1rem); padding: 0.75rem 0.25rem; }
  .shell, .shell-plain { display: block; width: calc(100% - 1rem); margin-bottom: 0.5rem; }
  /* Отдельным правилом: браузер без :has() выбросил бы весь список селекторов вместе с ним. */
  .shell-plain:has(.login) { width: calc(100% - 1rem); }
  .shell main { padding: 0.75rem; border-radius: 10px; }
  .sidebar { margin-bottom: 0.75rem; padding: 0.75rem; border-radius: 10px; }
  .nav-switch { display: block; }
  #nav-tree { margin-right: 0; padding-right: 0; }
  .nav-switch summary {
    cursor: pointer;
    list-style: none;
    padding: 0.5rem 0;
    font-family: system-ui, sans-serif;
    font-weight: bold;
  }
  .nav-switch summary::-webkit-details-marker { display: none; }
  .nav-switch summary::before { content: "▸ "; }
  .nav-switch[open] summary::before { content: "▾ "; }
  .nav-switch:not([open]) + .sidebar-body { display: none; }
  .sidebar-body { position: static; max-height: none; padding: 0 0 0.75rem; }
}

h1, h2, h3, h4 { font-family: system-ui, sans-serif; line-height: 1.25; }
p, li { overflow-wrap: break-word; }
a { color: var(--accent); }
.date { color: var(--muted); font-size: 0.9rem; }

.articles { list-style: none; padding: 0; margin: 0; }
.articles li { border-bottom: 1px solid var(--line); padding: 0.9rem 0; }
.articles li:first-child { padding-top: 0; }
.articles li:last-child { border-bottom: none; padding-bottom: 0; }
.articles a { font-size: 1.1rem; font-weight: bold; text-decoration: none; }
.articles a:hover { text-decoration: underline; }
.articles .nav-closed { font-size: 1.1rem; font-weight: bold; }
.articles .date { margin-top: 0.2rem; }
.articles p { margin: 0.3rem 0 0; color: var(--muted); }

pre, code { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: 0.9rem; }
pre {
  background: var(--code-bg);
  padding: 0.9rem;
  overflow-x: auto;
  border-radius: 6px;
  margin: 1.2rem 0;
  line-height: 1.5;
}
code { background: var(--code-bg); padding: 0.1rem 0.3rem; border-radius: 4px; }
pre code { background: none; padding: 0; }

/* Markdown.ColorCode красит блоки кода инлайновыми стилями своей тёмной VS-схемы
   (тёмный фон, светлый текст) — не даём своему фону перекрыть их, иначе на светлой
   схеме получается светлый текст на светлом фоне. */
div[style] > pre {
  background: none;
  margin: 0;
  border-radius: 0;
}

table {
  display: block;
  overflow-x: auto;
  border-collapse: collapse;
  width: 100%;
  margin: 1.2rem 0;
}
th, td { border: 1px solid var(--line); padding: 0.4rem 0.6rem; text-align: left; }
th { font-family: system-ui, sans-serif; }

img { max-width: 100%; height: auto; border-radius: 4px; }

blockquote { margin: 1.2rem 0; padding-left: 1rem; border-left: 4px solid var(--line); color: var(--muted); }

.callout {
  background: var(--callout);
  border-left: 4px solid var(--accent);
  border-radius: 6px;
  padding: 0.75rem 1rem;
  margin: 1.2rem 0;
}
.callout-title { font-family: system-ui, sans-serif; font-weight: 600; margin-bottom: 0.3rem; }
.callout-body > :first-child { margin-top: 0; }
.callout-body > :last-child { margin-bottom: 0; }
.callout-warning, .callout-caution, .callout-attention { border-left-color: var(--warn); }
.callout-danger, .callout-error, .callout-bug, .callout-failure, .callout-fail, .callout-missing {
  border-left-color: var(--danger);
}
.callout-tip, .callout-success, .callout-check, .callout-done { border-left-color: var(--tip); }

.login { margin: 1rem auto; display: grid; gap: 0.75rem; max-width: 20rem; }
.login label { display: grid; gap: 0.3rem; font-family: system-ui, sans-serif; font-size: 0.9rem; color: var(--muted); }
.login input {
  width: 100%; padding: 0.5rem; font: inherit; color: var(--fg);
  background: var(--bg); border: 1px solid var(--line); border-radius: 4px;
}
/* Один размер для всех кнопок темы: высота 2.125rem, как в Nextcloud. Цвета задаются ниже. */
.login button,
.settings-form button,
.share-form button,
.visibility-form button,
.inline-form button,
.search-page button,
.copy-link {
  min-height: 2.125rem; padding: 0 1rem; border: none; border-radius: 8px;
  font: inherit; font-family: system-ui, sans-serif; font-size: 0.875rem; font-weight: 600;
  line-height: 1.2; cursor: pointer;
}
.login button, .settings-form button, .share-form button, .visibility-form button, .search-page button {
  background: var(--accent); color: var(--bg); --busy-ink: var(--bg);
}
/* Пока форма отправляется, подпись гаснет и на её месте крутится колечко: так ширина кнопки
   не прыгает. Цвет колечка — --busy-ink, потому что currentColor уже прозрачный. */
button.is-busy { position: relative; color: transparent; pointer-events: none; }
button.is-busy::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 0.85rem; height: 0.85rem; margin: -0.475rem 0 0 -0.475rem;
  border: 2px solid var(--busy-ink, var(--accent)); border-right-color: transparent;
  border-radius: 50%;
  animation: busy-spin 0.7s linear infinite;
}
@keyframes busy-spin { to { transform: rotate(1turn); } }
/* Без движения колечко остаётся стоять: «идёт отправка» видно и так. */
@media (prefers-reduced-motion: reduce) {
  button.is-busy::after { animation: none; }
}
.error { color: var(--danger); }
.login .invite-note { margin: 0; color: var(--muted); font-size: 0.9rem; }
.login-register { margin: 0 auto; max-width: 20rem; font-size: 0.9rem; }

/* Итог действия в настройках. Он гаснет сам, наведение мыши держит его на экране.
   Ошибка висит дольше: её надо успеть прочитать. */
.toast {
  position: fixed; top: 1rem; right: 1rem; z-index: 10;
  max-width: min(24rem, calc(100vw - 2rem));
  margin: 0; padding: 0.7rem 1rem;
  border-left: 3px solid currentColor; border-radius: 6px;
  background: var(--callout); box-shadow: var(--shadow);
  font-family: system-ui, sans-serif; font-size: 0.95rem;
  animation: toast 4s ease forwards;
}
.toast:hover { animation-play-state: paused; }
.toast-ok { color: var(--tip); }
.toast-err { color: var(--danger); animation-duration: 9s; }
@keyframes toast {
  0% { opacity: 0; transform: translateY(-0.5rem); }
  6%, 88% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-0.5rem); visibility: hidden; }
}
@media (prefers-reduced-motion: reduce) {
  @keyframes toast { 0%, 95% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
}

/* Настройки занимают окно целиком. Раньше высота страницы шла за высотой раздела, и выбор
   «Доступ к API» вместо «Оформление» дёргал всё вверх-вниз. Теперь столбцы всегда во всю высоту,
   прокрутка у каждого своя: длинный список разделов не двигает содержимое, длинное
   содержимое не двигает список. Якорь раздела тоже больше ничего не прокручивает —
   раздел и так стоит в начале своей области. */
@media (min-width: 721px) {
  body:has(.settings-nav) {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    min-height: 0;
    overflow: hidden;
  }
  body:has(.settings-nav) .shell { flex: 1; min-height: 0; }
  body:has(.settings-nav) .sidebar { display: flex; flex-direction: column; overflow: hidden; }
  /* Столбец и так во всю высоту панели, липкость с потолком высоты тут только мешают. */
  /* Отступ панели перенесён внутрь столбца: полоса прокрутки встаёт к краю панели, как у дерева. */
  body:has(.settings-nav) .sidebar-body { position: static; max-height: none; min-height: 0; overflow-y: auto; margin-right: -1rem; padding-right: 1rem; }
  body:has(.settings-nav) .shell main { display: flex; flex-direction: column; overflow: hidden; }
  /* Заголовок страницы стоит на месте, едет только раздел. */
  /* Прокрутка обрезает всё, что выходит за край, и у полей пропадала обводка фокуса (3px).
     Поля дают ей место, отрицательный отступ возвращает содержимое на прежнюю линию. */
  /* Справа отступ панели целиком, чтобы полоса встала к её краю. */
  body:has(.settings-nav) .settings-panes { flex: 1; min-height: 0; overflow-y: auto; padding: 0 1.5rem 0 4px; margin: 0 -1.5rem 0 -4px; }

  /* Страницы с деревом тоже занимают окно целиком: фон виден со всех сторон, у дерева и у статьи
     своя прокрутка, полоса статьи стоит у края панели. */
  body:has(#nav-tree) {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    min-height: 0;
    overflow: hidden;
  }
  body:has(#nav-tree) .shell { flex: 1; min-height: 0; }
  body:has(#nav-tree) .sidebar { display: flex; flex-direction: column; min-height: 0; }
  body:has(#nav-tree) .sidebar-body { position: static; flex: 1; max-height: none; min-height: 0; }
  body:has(#nav-tree) #nav-tree { flex: 1; min-height: 0; }
  body:has(#nav-tree) .shell main { overflow-y: auto; }

  /* Гостевая статья: панель тянется до низа окна, но своей прокрутки не имеет. Длинный текст
     растит страницу, и шапка уезжает вместе с ней. Высоту даёт flex: 1 при body min-height: 100vh. */
  body:has(.shell-full) { display: flex; flex-direction: column; }
  body:has(.shell-full) .shell-full { display: flex; flex-direction: column; flex: 1; }
  body:has(.shell-full) .shell-full main { flex: 1; }
}

/* Виден один раздел: тот, чей якорь стоит в адресе. Без якоря — первый: правило ниже держится
   на :has(), поэтому «первый» задан отдельным селектором, а не заранее в .settings-pane. */
.settings-pane { display: none; }
.settings-pane:target { display: block; }
.settings-panes:not(:has(.settings-pane:target)) > .settings-pane:first-child { display: block; }

.settings-pane { position: relative; }
.settings-pane h2 { margin: 0 0 1rem; font-size: 1.25rem; }
/* В «Общих» формы стоят без блоков: без отступа кнопка одной упиралась в поле следующей. */
.settings-pane > .settings-form + .settings-form { margin-top: 1.5rem; }
.settings-pane h3 { margin: 0 0 0.4rem; font-size: 1rem; }
/* Подсказки и таблицы настроек — тем же шрифтом, что подписи полей. Засечки остаются у статей. */
.settings-panes { font-family: system-ui, sans-serif; }
.settings-pane .hint { margin: 0 0 1rem; color: var(--muted); font-size: 0.9rem; }

/* Смысловые куски раздела: между ними линия, перед первым её нет. */
.settings-block + .settings-block { margin-top: 1.75rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }

/* Ширину держат сами поля, а не форма: общий потолок в 24rem ломал подпись флажка на две строки. */
.settings-form { display: grid; gap: 0.75rem; max-width: 40rem; }
.settings-form label { display: grid; gap: 0.3rem; font-family: system-ui, sans-serif; font-size: 0.9rem; color: var(--muted); }
.settings-form input, .settings-form select {
  width: 100%; max-width: 24rem; padding: 0.5rem; font: inherit; color: var(--fg);
  background: var(--bg); border: 1px solid var(--line); border-radius: 4px;
}
.settings-form input:focus-visible, .settings-form select:focus-visible {
  border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: 1px;
}
.settings-form fieldset.color-scheme {
  margin: 0; border: 1px solid var(--line); border-radius: 8px; padding: 0.4rem 0.6rem 0.6rem; max-width: 24rem;
}
.settings-form fieldset.color-scheme legend { font-family: system-ui, sans-serif; font-size: 0.85rem; color: var(--muted); padding: 0 0.3rem; }
.settings-form button { justify-self: start; }

/* Варианты схемы стоят строкой пилюль. Выбранный виден и по самой радиокнопке, заливка
   только подчёркивает его — на браузере без :has() ничего не теряется. */
.scheme-choices { display: flex; flex-wrap: wrap; gap: 0.25rem; }
.scheme-choices label {
  display: flex; align-items: center; gap: 0.4rem; flex-direction: row;
  padding: 0.35rem 0.7rem; border-radius: 999px; color: var(--fg); cursor: pointer;
}
.scheme-choices label:hover { background: var(--callout); }
.scheme-choices label:has(input:checked) { background: var(--callout); font-weight: 600; }
/* Правило ширины для полей ввода растягивало и радиокнопку, отгоняя подпись к правому краю. */
.scheme-choices input { width: auto; margin: 0; accent-color: var(--accent); }

/* Флажок-переключатель: та же поправка, что у радиокнопок схемы — иначе общее правило
   для .settings-form input растягивает его в подписанный прямоугольник. */
/* В узком окне подпись всё же переносится, и галка встаёт к первой строке, а не к середине. */
.settings-form .field-hint { font-size: 0.8rem; }
.settings-form label.check { display: flex; flex-direction: row; align-items: start; gap: 0.5rem; cursor: pointer; }
/* Галка ростом в 1em стоит по центру первой строки: отступ сверху — половина разницы высоты строки и галки. */
.settings-form label.check input { flex-shrink: 0; width: 1em; height: 1em; padding: 0; margin: calc((1lh - 1em) / 2) 0 0; font: inherit; line-height: inherit; border: none; background: none; accent-color: var(--accent); }

/* Форма загрузки и текущий фон стоят рядом, пока хватает ширины. */
.inline-form { margin: 0; }

/* Галерея фонов: ряд плиток одного размера. Форма с готовыми фонами прозрачна для сетки
   (display: contents), поэтому её кнопки становятся ячейками сами. */
.bg-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 0.6rem;
  margin-top: 0.75rem;
}
.bg-picks { display: contents; }

.bg-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  width: 100%;
  height: 100%;
  min-height: 5.5rem;
  aspect-ratio: 16 / 10;
  padding: 0.5rem;
  background-color: var(--callout);
  background-position: center;
  background-size: cover;
  border: 1px solid var(--panel-border);
  border-radius: 10px;
  color: var(--muted);
  font-family: system-ui, sans-serif;
  font-size: 0.8rem;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
}
.bg-tile:hover { border-color: var(--accent); color: var(--fg); }
.bg-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Выбранный фон: рамка акцентом и галочка в углу. */
.bg-tile-current { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
/* Плитка залита выбранным цветом — какой он, заранее не известно, поэтому подпись белая
   с тенью: так она читается и на тёмном, и на светлом. */
.bg-tile-painted, .bg-tile-painted:hover { color: #fff; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); }

.bg-icon { width: 1.5rem; height: 1.5rem; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.bg-icon circle { fill: currentColor; stroke: none; }

.bg-check {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
  background: var(--accent);
  border-radius: 50%;
  color: var(--bg);
}
/* Галочка из двух рамок квадрата — тот же приём, что у маркера «Поделиться». */
.bg-check::before {
  content: "";
  width: 0.28rem;
  height: 0.52rem;
  margin-top: -0.12rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
}

/* Файл выбирается по клику на всю плитку, поэтому само поле спрятано, но осталось в потоке
   для чтения с клавиатуры. */
.bg-upload { position: relative; margin: 0; }
.bg-upload input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; }
.bg-upload-go {
  position: absolute;
  left: 50%;
  bottom: 0.5rem;
  transform: translateX(-50%);
  min-height: 1.6rem;
  padding: 0 0.6rem;
  background: var(--accent);
  color: var(--bg);
  border: none;
  border-radius: 6px;
  font-family: system-ui, sans-serif;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
}

.bg-colors { position: relative; }
.bg-colors > summary { list-style: none; }
.bg-colors > summary::-webkit-details-marker { display: none; }
.bg-swatches {
  position: absolute;
  z-index: 3;
  top: calc(100% + 0.4rem);
  left: 0;
  display: grid;
  grid-template-columns: repeat(4, 1.7rem);
  gap: 0.45rem;
  margin: 0;
  padding: 0.6rem;
  background: var(--panel-solid);
  border: 1px solid var(--panel-border);
  border-radius: 10px;
  box-shadow: var(--shadow);
}
.bg-swatch {
  width: 1.7rem;
  height: 1.7rem;
  padding: 0;
  background: var(--swatch);
  border: 1px solid var(--panel-border);
  border-radius: 50%;
  cursor: pointer;
}
.bg-swatch:hover { transform: scale(1.1); }
.bg-swatch-current { box-shadow: 0 0 0 2px var(--panel-solid), 0 0 0 4px var(--accent); }

.bg-drop { margin-top: 0.75rem; }
.bg-drop button { background: none; color: var(--danger); border: 1px solid var(--danger); }
.bg-drop button:hover { background: var(--danger); color: var(--bg); }

/* Подпись только для чтения с экрана: плитка узнаётся по картинке, а голосу нужен текст. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.empty {
  padding: 0.9rem 1rem; border-radius: 8px; background: var(--callout);
  color: var(--muted); font-family: system-ui, sans-serif; font-size: 0.95rem;
}

/* Таблицы настроек — списки, а не сетка: только линия под строкой и приглушённая шапка.
   Прокрутку узкого экрана держит обёртка, поэтому сама таблица остаётся таблицей и тянется
   во всю ширину панели (общее правило для таблиц статьи делает их блоком и сжимает по содержимому). */
.table-scroll { overflow-x: auto; }
.settings-table { display: table; width: 100%; margin: 0; font-size: 0.9rem; }
.settings-table tbody tr:hover { background: var(--callout); }
.settings-table th, .settings-table td {
  border: none; border-bottom: 1px solid var(--line); padding: 0.5rem 0.6rem; vertical-align: middle;
}
.settings-table th {
  font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted);
}
.settings-table td:first-child, .settings-table th:first-child { padding-left: 0; }
/* Дата и срок, разбитые на две строки, раздували высоту строки. */
.settings-table .nowrap { white-space: nowrap; }
.settings-table .row-actions { text-align: right; white-space: nowrap; }
table.pending .row-actions { display: flex; gap: 0.4rem; justify-content: flex-end; white-space: nowrap; }

/* Кнопки в таблицах настроек — «Отозвать» и «Скопировать» — стоят рядом и должны быть одного
   размера. Цвет разный: отзыв необратим, копирование — нет. */
.danger-form button, .copy-link { background: none; }
.danger-form button {
  color: var(--danger); border: 1px solid var(--danger); --busy-ink: var(--danger);
}
.danger-form button:hover { background: var(--danger); color: var(--bg); }
.copy-link { color: var(--accent); border: 1px solid var(--accent); }
.copy-link:hover { background: var(--accent); color: var(--bg); }
.settings-table .approve-form button { background: none; color: var(--accent); border: 1px solid var(--accent); }
.settings-table .approve-form button:hover { background: var(--accent); color: var(--bg); }

/* Пока галка сохраняется, форма бледнеет и не принимает второй щелчок. */
form.is-saving { opacity: 0.6; pointer-events: none; }

/* Форма добавления свёрнута в кнопку. Маркер details не нужен: кнопка и так читается кнопкой. */
.add-form { margin: 0 0 1rem; }
.add-form > summary {
  display: inline-flex; align-items: center; min-height: 2.125rem; padding: 0 1rem;
  border-radius: 8px; background: var(--accent); color: var(--bg);
  font-size: 0.875rem; font-weight: 600; cursor: pointer; list-style: none;
}
.add-form > summary::-webkit-details-marker { display: none; }
/* Стрелка говорит, что кнопка раскрывает форму, а не отправляет её. */
.add-form > summary::after { content: "▾"; margin-left: 0.5rem; font-size: 0.8em; }
.add-form[open] > summary::after { content: "▴"; }
.add-form[open] > summary { margin-bottom: 0.75rem; background: none; color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

/* Меню «⋯» всплывает поверх страницы. Его блок отсчитывается от раздела, а не от ячейки:
   тогда обёртка таблицы с прокруткой его не обрезает, а right: 0 держит его в пределах раздела
   и на узком экране. Верх не задан — блок встаёт сразу под «⋯». */
.row-menu { display: inline-block; text-align: right; }
.row-menu > summary {
  display: inline-grid; place-items: center; width: 2.125rem; height: 2.125rem;
  border-radius: 8px; color: var(--muted); font-size: 1.1rem; cursor: pointer; list-style: none;
}
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu > summary:hover, .row-menu[open] > summary { background: var(--callout); color: var(--fg); }
.row-menu-body {
  position: absolute; right: 0; z-index: 5;
  display: grid; gap: 0.6rem; justify-items: start; text-align: left; white-space: normal;
  margin-top: 0.25rem; padding: 0.75rem; border: 1px solid var(--panel-border); border-radius: 10px;
  background: var(--panel-solid); box-shadow: var(--shadow);
}
.row-menu-body:has(.settings-form) { width: min(18rem, calc(100vw - 3rem)); }
.row-menu-body .settings-form { width: 100%; }
.row-menu-body > form + form { width: 100%; padding-top: 0.6rem; border-top: 1px solid var(--line); }

/* Неработающие ссылки и приглашения скрыты, пока галка снята. */
.dead-filter:has(.show-dead:not(:checked)) .gone { display: none; }
.show-dead-label { display: flex; gap: 0.5rem; align-items: center; margin: 0 0 0.75rem; font-size: 0.9rem; color: var(--muted); cursor: pointer; }
/* Галка того же размера и на той же линии, что галки в формах. */
.show-dead { width: 1em; height: 1em; margin: 0; flex-shrink: 0; accent-color: var(--accent); }
.muted { color: var(--muted); }

.visibility-form { display: flex; align-items: center; gap: 0.75rem; margin: 1rem 0 0; }
.visibility-state { font-family: system-ui, sans-serif; font-size: 0.9rem; color: var(--muted); }

/* Ссылка, а не кнопка формы, поэтому размеры повторены руками — общее правило кнопок темы
   на неё не распространяется. Вид второстепенный: рамка вместо заливки, как у «Удалить». */
.download { margin: 1rem 0 0; }
.download-link {
  display: inline-flex; align-items: center; min-height: 2.125rem; padding: 0 1rem;
  border: 1px solid var(--accent); border-radius: 8px;
  color: var(--accent); text-decoration: none;
  font-family: system-ui, sans-serif; font-size: 0.875rem; font-weight: 600; line-height: 1.2;
}
.download-link:hover { background: var(--accent); color: var(--bg); }

.share { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }
.share > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  list-style: none;
  font-family: system-ui, sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--muted);
}
.share > summary::-webkit-details-marker { display: none; }
/* Галочка из двух рамок квадрата: у шрифтовых ▸ ▾ толщина и размер пляшут от шрифта,
   а эта под текст не подстраивается и поворачивается при раскрытии. */
.share > summary::before {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  margin-left: 0.15rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
}
.share[open] > summary { color: var(--fg); }
.share[open] > summary::before { transform: rotate(45deg); }
@media (prefers-reduced-motion: no-preference) {
  .share > summary::before { transition: transform 0.15s ease; }
}

.share-form { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: end; margin-top: 0.75rem; }
.share-form label {
  display: grid;
  gap: 0.3rem;
  font-family: system-ui, sans-serif;
  font-size: 0.9rem;
  color: var(--muted);
}
/* Поле заметки тянется по свободной ширине, срок остаётся узким. */
.share-form .note { flex: 1 1 16rem; max-width: 22rem; }
.share-form input, .share-form select {
  /* Та же высота, что у кнопки рядом, — иначе строка формы выглядит ступенькой. */
  min-height: 2.125rem;
  width: 100%;
  padding: 0.25rem 0.5rem;
  font: inherit;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
}
.share-form select { min-width: 8rem; }
.share-form input:focus-visible, .share-form select:focus-visible {
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Живая ссылка статьи: адрес, срок и две формы идут столбиком. Отступ между ними держит gap,
   поэтому собственный отступ форм тут снимается. */
.share-live { display: grid; gap: 0.75rem; margin-top: 0.75rem; }
.share-live .share-form { margin-top: 0; }
.share-term { margin: 0; font-family: system-ui, sans-serif; font-size: 0.9rem; color: var(--muted); }

/* Поле с адресом занимает всю свободную ширину столбца, кнопка рядом остаётся по содержимому. */
.link-cell { display: flex; gap: 0.4rem; align-items: center; }
.link-cell .link-url, .link-cell .new-token-value {
  flex: 1 1 auto;
  min-width: 14rem;
  min-height: 2.125rem;
  padding: 0 0.5rem;
  font: inherit;
  font-size: 0.85rem;
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.dead-link { opacity: .55; }

/* Токен виден один раз, поэтому стоит отдельным блоком над формой, а не строкой в таблице. */
.new-token {
  display: grid; gap: 0.6rem; margin: 0 0 1rem;
  padding: 0.9rem 1rem; border-radius: 8px;
  background: var(--callout); border-left: 3px solid var(--accent);
}
.new-token p { margin: 0; font-family: system-ui, sans-serif; font-size: 0.9rem; }
.new-token .new-token-value { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; color: var(--fg); }

.backlinks { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.backlinks h2 { margin: 0 0 0.5rem; font-size: 1rem; color: var(--muted); }
.backlinks ul { list-style: none; margin: 0; padding: 0; }
.backlinks li { margin: 0.2rem 0; }
