
:root{
  /* --ui-base scales the site; the player multiplies it by --pl-scale. */
  --ui-base:1; --ui-scale:var(--ui-base); --bg:#07080d; --bg2:#0c0e17; --panel:#10131f; --line:rgba(255,255,255,.08);
  --ink:#eef0f7; --muted:#9aa0b5; --faint:#7a82a0;
  --gold:#e3b066; --gold2:#f4d9a3; --ember:#e2603a; --cyan:#6ad6e0; --violet:#a98bf0;
  --serif:'Cormorant Garamond',Georgia,serif; --sans:'Manrope',system-ui,sans-serif;
  /* Keep a bounded content column on large displays. */
  --maxw:min(calc(1220px * var(--ui-scale)),1220px);
  --header-h:52px; --player-h:0px;
  /* The drawer handle extends 20px above the player. */
  --player-handle-h:20px;
  --z-bg:-2; --z-stars:-1; --z-dropbackdrop:48;
  --z-header:50; --z-droppanel:55;
  /* Ящик НИЖЕ камня плеера: шторка выезжает из-под него, и стык должен
     закрывать камень. Раньше ящик лежал выше (81) и накрывал полосу плеера. */
  --z-drawer:79; --z-player:80; --z-overlay:100; --z-modalert:150;
  --z-auth:200; --z-agentfab:250; --z-agentmodal:260; --z-toast:300;
  /* Shared warm paper for reader pages and shelf edges. */
  --page-bg:#241d14; --page-bg2:#1a140d;
  --page-ink:#ece0c8; --page-ink-dim:#b3a68a;
  --page-edge:#cbbb95; --page-edge-dark:#8d7d5e;
  --page-line:rgba(227,176,102,.20);
  /* ── «дыхание светом» плеера (ведёт light.js) ─────────────────
     Значения по умолчанию, чтобы var() работал и до старта движка. */
  --pl-glow:0;               /* свечение камня 0..1 (дыхание/эквалайзер) */
  --pl-glow-d:0;             /* свечение шторки 0..1 (в рассинхрон с камнем при дыхании) */
  --pl-color:227,176,102;    /* цвет свечения (эмоц. режим красит по спектру) */
  --pl-strength:1;           /* сила внешнего ореола (ползунок) */
  --pl-bg-a:1;               /* множитель непрозрачности ФОНА плеера */
}
*{box-sizing:border-box;margin:0;padding:0}
img{pointer-events:none;-webkit-user-drag:none;user-select:none}
a img{pointer-events:auto}
::-webkit-scrollbar{width:calc(6px * var(--ui-scale));height:calc(6px * var(--ui-scale))}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.12);border-radius:calc(3px * var(--ui-scale))}
::-webkit-scrollbar-thumb:hover{background:rgba(227,176,102,.3)}
/* scrollbar-gutter:stable — резервируем место под полосу прокрутки всегда.
   Иначе при открытии шторки (body → overflow:hidden) полоса исчезала, ширина
   вьюпорта росла, и центрированный fixed-плеер (left:50%) прыгал вправо. */
html{scroll-behavior:smooth;scrollbar-gutter:stable}
body{
  background:var(--bg); color:var(--ink); font-family:var(--sans);
  font-size:16px; line-height:1.6; -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
body.locked{overflow:hidden}
.skip-link{position:absolute;top:-100%;left:0;z-index:9999;padding:12px 24px;background:var(--gold);color:#0a0a0a;font-weight:700;font-size:14px;border-radius:0 0 8px 0;transition:top .2s}
.skip-link:focus{top:0;outline:0}
a{color:inherit;text-decoration:none}
:focus-visible{outline:2px solid var(--gold);outline-offset:2px;border-radius:4px}
::selection{background:var(--gold);color:#0a0a0a}

/* ── фон: звёздное поле + сияние ──────────────────────── */
.bg-fx{position:fixed;inset:0;z-index:var(--z-bg);background:
  radial-gradient(1200px 700px at 70% -10%, rgba(227,176,102,.10), transparent 60%),
  radial-gradient(900px 600px at 10% 110%, rgba(169,139,240,.10), transparent 60%),
  linear-gradient(180deg,#07080d,#090b13 40%,#07080d);}
.stars{position:fixed;inset:0;z-index:var(--z-stars);pointer-events:none;opacity:.6}
/* Носитель звёзд — элемент нулевого размера, чей box-shadow держит всё поле:
   позиция звезды (vw/vh), её размер (spread) и яркость (альфа) живут в тени.
   Мерцает сам носитель (2–3 штуки строит core.js со своими периодами и фазами),
   а не каждая звезда: 90 анимированных слоёв композитора заменены на 2–3. */
.stars i{position:absolute;left:0;top:0;width:0;height:0;animation:tw 4s ease-in-out infinite}
@keyframes tw{0%,100%{opacity:.15}50%{opacity:.9}}

/* ── контейнер ───────────────────────────────────────── */
#page-root{min-height:calc(100vh - var(--header-h) - var(--player-h));display:block}
/* Страницы без hero-секции. --header-h меряется в core.js (syncHeaderH):
   числом её задать нельзя — высота шапки зависит от --ui-scale и на широких
   мониторах доходит до ~78px. Зазор сверху — чтобы заголовок не упирался
   в нижнюю кромку шапки. */
#page-root.no-hero{padding-top:calc(var(--header-h) + 14px * var(--ui-scale))}
/* Отступ = плеер + ручка ящика: она висит на 20px выше плеера и без этого
   перекрывала кнопки внизу страницы (проверено на 375px). */
body.player-active #page-root{padding-bottom:calc(var(--player-h) + var(--player-handle-h))}
/* Полноэкранные разделы, живущие сами по высоте (Шёпот), читают
   эту же высоту плеера: окно сжимается, композитор не перекрыт. */
body.player-active{--ws-player:calc(var(--player-h) + var(--player-handle-h))}
/* Мини-плеер парит над содержимым и не отрезает нижнюю полосу страницы. */
body.player-floating{--player-handle-h:0px}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 24px}
section{position:relative}

/* ── навигация: органические капли ──────────────────── */
.m-only{display:none!important}   /* мобильные дубликаты навигации: видны только ≤767 */
header.nav{position:fixed;top:0;left:0;right:0;z-index:var(--z-header);
  display:flex;align-items:center;justify-content:space-between;
  padding:calc(12px * var(--ui-scale)) calc(20px * var(--ui-scale));
  /* backdrop-filter снят. На тач-устройствах он и так был выключен (см. ниже),
     а на десктопе стоил перерасчёта размытия подложки на каждом кадре прокрутки
     ради эффекта, который под градиентом от rgba(7,8,13,.85) к прозрачному
     виден только в верхних 15 % полосы. Фон уплотнён сверху взамен. */
  background:linear-gradient(180deg,rgba(7,8,13,.92),rgba(7,8,13,0));
  transition:background .3s;border-bottom:0;
  gap:calc(8px * var(--ui-scale))}
header.nav.solid{background:rgba(7,8,13,.92)}
/* Мобильный GPU не тянет backdrop-blur на фиксированной шапке — на каждом кадре
   скролла переразмывает фон за ней, отсюда «тормозной» скролл. Теперь шапка без
   размытия на всех устройствах (см. header.nav выше), и это правило осталось
   только ради модалок. */
@media (hover: none) and (pointer: coarse){
  /* Оверлеи/модалки: размытие фона меньше — на мобиле дорогое, а разница на глаз
     почти незаметна (фон и так плотный). Выпадающая панель меню из списка
     убрана: у неё непрозрачность .98, размывать под ней нечего. */
  .overlay,.auth-wrap{backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
}
.brand{display:flex;align-items:center;gap:calc(10px * var(--ui-scale));font-family:var(--serif);
  font-size:calc(22px * var(--ui-scale));font-weight:600;letter-spacing:.06em;flex:0 0 auto;
  position:relative}
.brand a{text-decoration:none;color:inherit;display:flex;align-items:center}
.brand-name{cursor:pointer;transition:color .2s}
.brand-logo{height:calc(27px*var(--ui-scale));width:auto;display:block}
.brand-name:hover{color:var(--gold)}
.brand b{color:var(--gold)}
/* Бейдж стоит ПОД именем сайта, а не в одну строку с ним.
   В строке он был третьим элементом бренда и отбирал ширину у выпадающих
   кнопок: на узком экране «Сага» и «Вселенная» съезжали за край, а у владельца
   (там ещё и своя кнопка с именем) шапка превращалась в кашу. Абсолютное
   положение выносит бейдж из потока — ширина бренда от него больше не зависит
   ни при какой роли. Смещение слева равно сигилу плюс отступ бренда, поэтому
   бейдж стоит ровно под словом DiZ. */
.vip-badge{position:absolute;top:calc(100% - calc(3px * var(--ui-scale)));
  left:calc(34px * var(--ui-scale) + 10px * var(--ui-scale));
  font-family:var(--sans);font-size:max(9px,calc(9px * var(--ui-scale)));font-weight:700;
  letter-spacing:.12em;color:var(--gold);border:1px solid var(--gold);border-radius:4px;
  padding:1px 6px;text-transform:uppercase;line-height:1.3;cursor:pointer;transition:.25s;
  z-index:1}
.vip-badge:hover{background:var(--gold);color:#1a0f06}

/* ── «Дыхание» формы: каждая капля колышется по-своему ── */
/* дыхание кнопок меню — своё, не общее с плеером (см. nav-breathe ниже) */
/* Кадры анимируют ТОЛЬКО transform. Раньше здесь же менялись box-shadow и
   filter:brightness — оба не композитятся: каждый кадр браузер заново красил
   кнопку и всё вокруг неё под размытой тенью. Четыре кнопки × 60 кадров на
   слабом GPU — это и была «покадровость» шапки (замер: DMN/docs/perf/before.md,
   правила: docs/PERFORMANCE_RULES.md).
   Свет уехал на отдельный слой .drop::before, которому анимируется opacity —
   он красится один раз и дальше живёт на композиторе. */
@keyframes nav-breathe{
  0%,100%{transform:scale(1)}
  45%,55%{transform:scale(1.08)}
}
/* Свет капли: слой без фона, только тень. Внешняя box-shadow не заливает
   внутренность коробки, поэтому слой не просвечивает сквозь полупрозрачную
   кнопку — рисует ровно тот же ореол, что рисовала сама кнопка. */
@keyframes nav-glow{
  0%,100%{opacity:0}
  45%,55%{opacity:1}
}
.drop::before{content:'';position:absolute;inset:0;pointer-events:none;
  border-radius:calc(22px * var(--ui-scale));
  box-shadow:0 8px 24px -4px rgba(227,176,102,.9),0 0 12px 4px rgba(227,176,102,.3);
  opacity:0;animation:nav-breathe 8s ease-in-out infinite,nav-glow 8s ease-in-out infinite}
.drop:nth-child(2n)::before{animation-duration:9.5s}
.drop:nth-child(3n)::before{animation-duration:9s}
.drop-auth::before{animation-duration:10s}
/* Открытая капля замирает — вместе с кнопкой гасим и её свет. */
.drop.open::before{animation:none;opacity:0}
/* лёгкое колыхание «лужицы»-панели */
@keyframes pool{
  0%,100%{border-radius:calc(24px*var(--ui-scale)) calc(24px*var(--ui-scale)) calc(26px*var(--ui-scale)) calc(22px*var(--ui-scale))}
  50%{border-radius:calc(20px*var(--ui-scale)) calc(28px*var(--ui-scale)) calc(22px*var(--ui-scale)) calc(26px*var(--ui-scale))}
}

/* ── Контейнер капель ── */
.drops{display:flex;align-items:center;gap:calc(8px * var(--ui-scale));flex:1;justify-content:center;overflow:visible}

/* ── Капля-кнопка ── */
.drop{position:relative}
.drop-btn{position:relative;display:flex;align-items:center;gap:calc(6px * var(--ui-scale));
  padding:calc(8px * var(--ui-scale)) calc(18px * var(--ui-scale));
  background:radial-gradient(120% 150% at 50% 0%,rgba(227,176,102,.10),rgba(255,255,255,.03) 60%,rgba(255,255,255,.02));
  border:1px solid var(--line);
  color:var(--muted);font-family:var(--sans);font-size:max(11px,calc(12px * var(--ui-scale)));
  font-weight:600;letter-spacing:.04em;cursor:pointer;white-space:nowrap;overflow:hidden;
  border-radius:calc(22px * var(--ui-scale));
  /* Это нулевой кадр прежнего nav-breathe, а не новая тень: пока кадры
     задавали box-shadow, они перебивали объявление отсюда, и внутренние
     подсветы не показывались никогда. Переносим то, что реально рисовалось. */
  box-shadow:0 6px 18px -6px rgba(227,176,102,.6),0 0 0 0 rgba(227,176,102,.3);
  transition:color .5s,background .5s,border-color .5s,box-shadow .5s,
             transform .5s cubic-bezier(.34,1.56,.64,1);
  animation:nav-breathe 8s ease-in-out infinite}
/* max-width держит длинные имена аккаунта в узде, но должен вмещать самое
   длинное меню-слово «Вселенная» целиком — иначе оно обрезалось многоточием
   и не влезало в форму капли. */
.drop-btn .drop-label{text-overflow:ellipsis;overflow:hidden;max-width:calc(112px * var(--ui-scale));display:inline-block}
.drop:nth-child(2n) .drop-btn{animation-duration:9.5s}
.drop:nth-child(3n) .drop-btn{animation-duration:9s}
.drop-auth .drop-btn{animation-duration:10s}
.drop-btn:hover{color:var(--gold2);border-color:var(--gold);
  background:radial-gradient(120% 170% at 50% -20%,rgba(227,176,102,.28),rgba(227,176,102,.06) 60%,rgba(255,255,255,.02));
  box-shadow:inset 0 1px 2px rgba(255,255,255,.15),inset 0 -4px 10px rgba(0,0,0,.3),0 0 18px rgba(227,176,102,.25);
  transform:translateY(-1px) scale(1.05)}
.drop-btn .drop-label{transition:opacity .3s,transform .3s}
.drop-btn .drop-arrow{width:calc(10px * var(--ui-scale));height:calc(10px * var(--ui-scale));
  transition:transform .45s cubic-bezier(.34,1.56,.64,1);flex:0 0 auto}

/* ── Открытая капля: замирает и вытягивает «шейку» вниз ── */
.drop.open .drop-btn{animation:none;color:var(--gold2);border-color:var(--gold);
  background:radial-gradient(120% 200% at 50% 120%,rgba(227,176,102,.22),rgba(227,176,102,.05) 70%);
  box-shadow:inset 0 1px 2px rgba(255,255,255,.15),0 0 20px rgba(227,176,102,.22);
  transform:scale(1.06);
  border-radius:calc(22px * var(--ui-scale));}
.drop.open .drop-btn .drop-arrow{transform:rotate(180deg)}

/* ── «Шейка»: струйка воды, стекающая из кнопки в панель ── */
.drop-btn::after{content:"";position:absolute;left:50%;bottom:calc(-7px * var(--ui-scale));
  width:calc(15px * var(--ui-scale));height:calc(16px * var(--ui-scale));
  transform:translateX(-50%) scaleY(0);transform-origin:top center;
  background:linear-gradient(180deg,var(--gold),rgba(227,176,102,.3));
  border-radius:50% 50% 45% 45%;opacity:0;filter:blur(.4px);pointer-events:none;
  transition:transform .5s cubic-bezier(.34,1.56,.64,1),opacity .3s}
.drop.open .drop-btn::after{transform:translateX(-50%) scaleY(1);opacity:.55}

/* ── Панель-«лужица», в которую перетекает капля ── */
.drop-panel{position:absolute;top:calc(100% + 7px * var(--ui-scale));left:50%;z-index:var(--z-droppanel);
  min-width:calc(190px * var(--ui-scale));
  /* Размытие снято: фон панели непрозрачен на 98 %, размывать под ним нечего —
     GPU честно читал и размывал подложку, результат которой закрывался
     собственной заливкой. Оставшиеся 2 % добираем в градиенте. */
  background:radial-gradient(130% 120% at 50% 0%,rgba(22,18,12,.995),rgba(9,11,19,.995));
  border:1px solid var(--gold);
  border-radius:calc(24px * var(--ui-scale));
  padding:calc(10px * var(--ui-scale)) 0;overflow:hidden;
  box-shadow:0 18px 44px rgba(0,0,0,.5),inset 0 1px 2px rgba(227,176,102,.15);
  opacity:0;pointer-events:none;visibility:hidden;
  transform:translateX(-50%) translateY(calc(-16px * var(--ui-scale))) scale(.62);
  transform-origin:top center;
  transition:opacity .4s,transform .6s cubic-bezier(.34,1.55,.5,1)}
.drop.open .drop-panel{opacity:1;pointer-events:auto;visibility:visible;
  transform:translateX(-50%) translateY(0) scale(1);
  animation:pool 6s ease-in-out .5s infinite}
/* правый край: панель авторизации выравнивается вправо, чтобы не уходить за экран */
.drop.drop-auth .drop-panel{left:auto;right:0;transform-origin:top right;
  transform:translateY(calc(-16px * var(--ui-scale))) scale(.62)}
.drop.drop-auth.open .drop-panel{transform:translateY(0) scale(1)}
.drop-panel a,.drop-panel button{display:block;width:100%;text-align:left;
  padding:calc(10px * var(--ui-scale)) calc(18px * var(--ui-scale));
  font-size:max(11px,calc(13px * var(--ui-scale)));color:var(--muted);white-space:nowrap;
  background:none;border:none;cursor:pointer;font-family:var(--sans);
  transition:color .2s,background .2s}
.drop-panel a:hover,.drop-panel button:hover{color:var(--gold2);background:rgba(227,176,102,.08)}
.drop-panel .user-name{display:block;padding:calc(8px * var(--ui-scale)) calc(18px * var(--ui-scale));
  color:var(--gold2);font-weight:600;font-size:max(11px,calc(13px * var(--ui-scale)))}
.drop-panel .owner-link{display:flex;align-items:center;gap:calc(8px * var(--ui-scale))}
.drop-panel .owner-link svg{width:calc(16px * var(--ui-scale));height:calc(16px * var(--ui-scale));flex:0 0 auto}
.drop-panel .btn-auth{display:block;width:100%;text-align:left;padding:calc(10px * var(--ui-scale)) calc(18px * var(--ui-scale));
  font-size:max(11px,calc(13px * var(--ui-scale)));font-weight:600;cursor:pointer;
  background:none;border:none;color:var(--muted);font-family:var(--sans);transition:color .2s,background .2s}
.drop-panel .btn-auth:hover{color:var(--gold2);background:rgba(227,176,102,.08)}
.drop-panel .btn-auth.reg{color:var(--gold)}
.drop-panel .btn-auth.reg:hover{color:var(--gold2)}

/* ── Backdrop for closing drops ── */
.drop-backdrop{position:fixed;inset:0;z-index:var(--z-dropbackdrop)}
.drop-backdrop{display:none}
.drop-backdrop.open{display:block}

/* ── модалка логина ─────────────────────────────── */
.auth-wrap{position:fixed;inset:0;z-index:var(--z-auth);display:flex;align-items:center;justify-content:center;
  background:rgba(5,6,11,.75);backdrop-filter:blur(6px);opacity:0;pointer-events:none;visibility:hidden;
  transition:opacity .3s,visibility 0s .3s}
.auth-wrap.open{opacity:1;pointer-events:auto;visibility:visible;transition:opacity .3s}
.auth-box{background:linear-gradient(160deg,var(--bg2),var(--bg));border:1px solid var(--line);
  border-radius:calc(20px * var(--ui-scale));padding:calc(38px * var(--ui-scale)) calc(34px * var(--ui-scale)) calc(32px * var(--ui-scale));width:clamp(300px,calc(380px * var(--ui-scale)),92vw);position:relative}
.auth-box .close{position:absolute;top:calc(14px * var(--ui-scale));right:calc(16px * var(--ui-scale));background:none;border:0;color:var(--muted);
  font-size:calc(20px * var(--ui-scale));cursor:pointer;transition:color .2s}
.auth-box .close:hover{color:var(--gold)}
.auth-box h3{font-family:var(--serif);font-size:calc(28px * var(--ui-scale));font-weight:600;color:var(--gold2);margin-bottom:calc(4px * var(--ui-scale))}
.auth-box .sub{font-size:max(11px,calc(13px * var(--ui-scale)));color:var(--muted);margin-bottom:calc(22px * var(--ui-scale))}
.auth-box label{display:block;font-size:max(11px,calc(12px * var(--ui-scale)));color:var(--faint);letter-spacing:.06em;margin-bottom:calc(4px * var(--ui-scale))}
.auth-box input{width:100%;padding:calc(12px * var(--ui-scale)) calc(16px * var(--ui-scale));border-radius:calc(10px * var(--ui-scale));border:1px solid var(--line);
  background:rgba(255,255,255,.04);color:var(--ink);font-size:max(11px,calc(15px * var(--ui-scale)));outline:0;transition:border-color .2s;margin-bottom:calc(14px * var(--ui-scale))}
.auth-box input:focus{border-color:var(--gold)}
.auth-box .btn-submit{width:100%;padding:calc(12px * var(--ui-scale));border-radius:calc(12px * var(--ui-scale));border:0;font-weight:700;font-size:max(11px,calc(15px * var(--ui-scale)));
  cursor:pointer;background:linear-gradient(120deg,var(--gold),var(--ember));color:#1a0f06;transition:.25s}
.auth-box .btn-submit:hover{transform:translateY(calc(-2px * var(--ui-scale)));box-shadow:0 calc(10px * var(--ui-scale)) calc(24px * var(--ui-scale)) calc(-8px * var(--ui-scale)) rgba(227,176,102,.5)}
.auth-box .switch{text-align:center;margin-top:calc(16px * var(--ui-scale));font-size:max(11px,calc(13px * var(--ui-scale)));color:var(--muted)}
.auth-box .switch a{color:var(--gold);cursor:pointer}
.auth-box .switch a:hover{color:var(--gold2)}
.auth-box .err{color:var(--ember);font-size:max(11px,calc(13px * var(--ui-scale)));margin-bottom:calc(10px * var(--ui-scale));display:none}
/* Требования к паролю. Приглушённее подписи поля — это подсказка, а не ошибка;
   красным она станет только если человек всё-таки промахнётся. Поднята под
   само поле отрицательным отступом: у input снизу уже есть свой margin. */
.auth-box .auth-hint{color:var(--faint);font-size:max(10px,calc(12px * var(--ui-scale)));
  line-height:1.45;margin:calc(-8px * var(--ui-scale)) 0 calc(14px * var(--ui-scale))}
.auth-box .pass-wrap{position:relative}
.auth-box .pass-wrap input{padding-right:calc(44px * var(--ui-scale))}
#pass-eye{position:absolute;right:calc(8px * var(--ui-scale));top:50%;transform:translateY(-50%);background:none;border:0;cursor:pointer;font-size:calc(18px * var(--ui-scale));padding:calc(4px * var(--ui-scale));color:var(--muted);transition:color .2s;line-height:1}
#pass-eye:hover{color:var(--gold)}

/* ── логотип: круг с тремя точками ───────────────────── */
.sigil-img{width:calc(34px * var(--ui-scale));height:calc(34px * var(--ui-scale));flex:0 0 auto;
  border-radius:50%;filter:drop-shadow(0 0 6px rgba(227,176,102,.3))}
footer .sigil-img{width:calc(50px * var(--ui-scale));height:calc(50px * var(--ui-scale))}

/* ── HERO ────────────────────────────────────────────── */
.hero{min-height:100vh;min-height:100svh;display:flex;flex-direction:column;align-items:center;
  justify-content:center;text-align:center;padding:calc(120px * var(--ui-scale)) 24px calc(80px * var(--ui-scale));position:relative}
/* Первый экран с включённым плеером: вычитаем его высоту и высоту ручки
   ящика. Без этого hero занимает все 100svh, и нижняя кнопка («Открыть
   Мастер-Библию») оказывалась под плеером/ручкой уже при scrollY=0 —
   то есть неработающей сразу после загрузки (ловилось на 375px). */
body.player-active .hero{
  min-height:calc(100vh - var(--player-h) - var(--player-handle-h));
  min-height:calc(100svh - var(--player-h) - var(--player-handle-h));
  /* min-height мало: если контента больше, hero всё равно вырастет и уедет
     под плеер. Поэтому ещё и отодвигаем содержимое снизу.
     Исходные 80px «воздуха» тут не нужны — разделителем работает сама
     полоса плеера; оставляем небольшой зазор, иначе на невысоких экранах
     (375×812) контенту не хватает места и он лезет на ручку ящика. */
  padding-bottom:calc(24px * var(--ui-scale) + var(--player-h) + var(--player-handle-h));
}
/* Невысокие экраны: hero вырастает по контенту, и низ контента равен
   padding-top + высота контента — нижним отступом его вверх не подвинуть.
   Поэтому при включённом плеере снимаем сверху высоту ручки: иначе нижняя
   кнопка упирается в неё (375×812 — заход 9px). */
@media(max-width:767px){
  body.player-active .hero{
    padding-top:calc(100px * var(--ui-scale) - var(--player-handle-h));
  }
}
/* Телефон в ландшафте: между шапкой и плеером остаётся около 230 px, а ритм
   первого экрана отмерен под вертикальный формат. Вводный абзац из-за этого
   уходил под плеер ещё до первой прокрутки (ловилось на 812×375 — и на
   главной, и в «О саге»). Ужимаем эмблему и отступы: вступление целиком стоит
   над плеером, а тезис и подсказка «листай» честно остаются за кромкой —
   для того подсказка и нужна.
   Селекторы начинаются с body: собственные стили страниц приезжают партиалом
   ПОЗЖЕ этого файла, и при равной специфичности победили бы они. */
@media (max-height:620px) and (orientation:landscape){
  body .hero,body.player-active .hero{padding-top:calc(var(--header-h) + 10px)}

  body .hero .bigsigil{width:calc(56px * var(--ui-scale));height:calc(56px * var(--ui-scale));
    margin-bottom:calc(10px * var(--ui-scale))}
  body .hero .hero-emblem{width:clamp(76px,20vh,140px);margin-bottom:calc(8px * var(--ui-scale))}
  body .hero h1{margin-bottom:calc(8px * var(--ui-scale));font-size:clamp(34px,9.5vh,64px)}
  body .hero .slogan{font-size:clamp(16px,4.5vh,22px);margin-top:2px}
  body .hero .sub{margin-top:calc(10px * var(--ui-scale))}
  body .hero .tagline{margin-top:calc(12px * var(--ui-scale))}
  body .hero .thesis{margin-top:calc(12px * var(--ui-scale))}
}
.bigsigil{width:calc(128px * var(--ui-scale));height:calc(128px * var(--ui-scale));margin-bottom:calc(28px * var(--ui-scale));filter:drop-shadow(0 0 calc(30px * var(--ui-scale)) rgba(227,176,102,.35))}
/* Свечение — drop-shadow по силуэту рисунка, а не box-shadow по кругу
   border-radius:50%: радиус золотой рамки в рисунке «гуляет» на десятки
   пикселей, и круг обрезки оставлял между рисунком и свечением неровный
   тёмный зазор. Круговая обрезка не нужна вовсе — картинка сама круглая,
   с прозрачными углами (webp с альфой; AVIF альфу не умеет, поэтому его
   нет в <picture>). Производные — scripts/regen_emblem_assets.py. */
.hero-emblem{width:clamp(230px,36vw,calc(420px * var(--ui-scale)));height:auto;aspect-ratio:1;margin-bottom:calc(30px * var(--ui-scale));
  display:block;
  filter:drop-shadow(0 0 1px rgba(227,176,102,.45)) drop-shadow(0 0 calc(30px * var(--ui-scale)) rgba(227,176,102,.38)) drop-shadow(0 0 calc(75px * var(--ui-scale)) rgba(106,140,255,.35));
  animation:floaty 7s ease-in-out infinite}
@keyframes floaty{0%,100%{transform:translateY(0)}50%{transform:translateY(-12px)}}
.hero h1{font-family:var(--serif);font-weight:600;line-height:.95;
  font-size:clamp(64px,16vw,184px);letter-spacing:.02em;
  background:linear-gradient(180deg,#fff,var(--gold) 60%,var(--ember));
  -webkit-background-clip:text;background-clip:text;color:transparent}
.hero .slogan{font-family:var(--serif);font-style:italic;font-size:clamp(20px,3.4vw,32px);
  color:var(--gold2);letter-spacing:.18em;margin-top:6px}
/* Логотип вместо текстового h1+slogan (build_site.py, hero главной).
   line-height:0 — чтобы внутри h1 не осталось лишней высоты строки;
   лёгкий drop-shadow повторяет свечение старого текстового h1.
   Эмблема с орнаментом (05.10.2026) шире прежнего логотипа (1571×885),
   поэтому 460px — девиз и черта остаются читаемыми. */
.hero .logo-h{margin:0;line-height:0}
.hero .hero-logo{display:block;width:min(460px,80vw);height:auto;margin:0 auto;
  filter:drop-shadow(0 0 calc(24px * var(--ui-scale)) rgba(255,180,60,.22))}
.hero .sub{margin-top:calc(22px * var(--ui-scale));max-width:calc(640px * var(--ui-scale));color:var(--muted);font-size:clamp(15px,2vw,calc(18px * var(--ui-scale)))}
/* Золотой подзаголовок hero (решение владельца 05.10.2026): голос девиза
   эмблемы — сериф-курсив Cormorant Garamond (уже self-hosted в fonts.css,
   SIL OFL), градиент тот же, что у h1 и заголовков справочника. */
.hero .sub-gold{
  font-family:var(--serif);font-style:italic;font-weight:500;
  font-size:clamp(18px,2.4vw,calc(23px * var(--ui-scale)));line-height:1.55;
  background:linear-gradient(180deg,var(--gold2),var(--gold) 55%,var(--ember));
  -webkit-background-clip:text;background-clip:text;color:transparent;
  filter:drop-shadow(0 0 calc(18px * var(--ui-scale)) rgba(227,176,102,.18));
}
.hero .tagline{margin-top:calc(30px * var(--ui-scale));font-family:var(--serif);font-size:clamp(18px,2.6vw,calc(24px * var(--ui-scale)));
  color:var(--ink);opacity:.9}
.hero .tagline span{color:var(--ember)}.hero .tagline b{color:var(--cyan);font-weight:400}
.cta{margin-top:calc(42px * var(--ui-scale));display:flex;gap:calc(16px * var(--ui-scale));flex-wrap:wrap;justify-content:center}
.btn{display:inline-flex;align-items:center;gap:calc(10px * var(--ui-scale));padding:calc(14px * var(--ui-scale)) calc(30px * var(--ui-scale));border-radius:calc(40px * var(--ui-scale));
  font-weight:600;font-size:max(11px,calc(15px * var(--ui-scale)));cursor:pointer;border:1px solid var(--line);
  background:rgba(255,255,255,.03);color:var(--ink);transition:.25s}
.btn:hover{transform:translateY(calc(-2px * var(--ui-scale)))}
.btn.primary{background:linear-gradient(120deg,var(--gold),var(--ember));color:#1a0f06;border:0;
  box-shadow:0 calc(10px * var(--ui-scale)) calc(30px * var(--ui-scale)) calc(-10px * var(--ui-scale)) rgba(227,176,102,.6)}
.btn.primary:hover{box-shadow:0 calc(16px * var(--ui-scale)) calc(40px * var(--ui-scale)) calc(-12px * var(--ui-scale)) rgba(227,176,102,.8)}
.scroll-hint{position:absolute;bottom:calc(26px * var(--ui-scale));left:50%;transform:translateX(-50%);
  color:var(--faint);font-size:max(11px,calc(12px * var(--ui-scale)));letter-spacing:.3em;animation:bob 2.4s ease-in-out infinite}
@keyframes bob{0%,100%{transform:translate(-50%,0);opacity:.5}50%{transform:translate(-50%,8px);opacity:1}}

/* ── общие заголовки секций ─────────────────────────── */
.sec{padding:calc(96px * var(--ui-scale)) 0}
.kicker{font-size:max(11px,calc(12px * var(--ui-scale)));letter-spacing:.4em;text-transform:uppercase;color:var(--gold);
  font-weight:700;margin-bottom:calc(14px * var(--ui-scale))}
.sec h2{font-family:var(--serif);font-size:clamp(34px,5vw,calc(56px * var(--ui-scale)));font-weight:600;line-height:1.05}
.sec .lead{color:var(--muted);max-width:calc(680px * var(--ui-scale));margin:calc(14px * var(--ui-scale)) auto 0;font-size:max(11px,calc(17px * var(--ui-scale)))}

/* ── философия: триптих «Три силы» ────────────────────
   Кадры-вставки (промты — visual/GPT/Site/_prompts). Подписи — тонкий
   оверлей поверх кадра: текст читается частью картинки, но не выпекается
   в неё генератором (русские надписи в генерации дают опечатки).
   Пока кадра нет — фолбэк: тихий градиент-«поле» + знак силы (как было
   на старых столпах, знак — нить преемственности). */
.triptych{display:grid;grid-template-columns:repeat(3,1fr);gap:calc(22px * var(--ui-scale));margin-top:calc(48px * var(--ui-scale))}
/* Переворот (flip.js): сама карточка — только сцена (сетка 3D-граней), фон и
   рамка переехали на грани — плита обязана быть предметом с двумя сторонами,
   а не рамкой, внутри которой крутится содержимое. */
.tri-card{position:relative;margin:0;border-radius:calc(18px * var(--ui-scale));aspect-ratio:3/4;display:grid;grid-template:1fr/1fr}
.tri-card .fl-face{border:1px solid var(--line);border-radius:inherit;overflow:hidden;
  background:linear-gradient(180deg,rgba(255,255,255,.04),rgba(255,255,255,.01))}
.tri-card .fl-face.fl-back{background:linear-gradient(168deg,rgba(20,15,9,.96),rgba(10,8,6,.97))}
.tri-card .fl-front{position:relative;grid-area:1/1}
.tri-card:hover .fl-face{border-color:rgba(227,176,102,.4)}
.tri-card img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform .6s ease}

/* Keep the artwork at its original brightness; shade only the lettering. */
.tri-cap{position:absolute;left:0;right:0;bottom:0;z-index:2;padding:calc(18px * var(--ui-scale)) calc(20px * var(--ui-scale)) calc(20px * var(--ui-scale));text-shadow:0 1px 3px #000,0 0 8px #000}
.tri-cap .tri-no,.ep-cap .no{font-size:max(10px,calc(11px * var(--ui-scale)));letter-spacing:.34em;text-transform:uppercase;color:var(--gold);font-weight:700}
.tri-cap h3{font-family:var(--serif);font-size:calc(28px * var(--ui-scale));font-weight:600;line-height:1.1;margin:calc(6px * var(--ui-scale)) 0 0}
.tri-cap p{color:var(--ink);font-size:max(11px,calc(14px * var(--ui-scale)));margin:calc(6px * var(--ui-scale)) 0 0;line-height:1.5}
/* Фолбэк без кадра: знак силы и тон секции (на лицевой грани). */
.tri-card.no-img img{display:none}
.tri-card.no-img .fl-front:before{font-family:var(--serif);position:absolute;top:calc(28px * var(--ui-scale));left:calc(24px * var(--ui-scale));
  font-size:calc(64px * var(--ui-scale));line-height:1;z-index:2}
.tri-card.no-img .fl-front:after{content:"";position:absolute;inset:0;background:radial-gradient(120% 90% at 68% 14%,var(--tone),transparent 62%)}
.tri-card.tone-freedom{--tone:rgba(226,96,58,.16)} .tri-card.tone-freedom .fl-front:before{content:"▲";color:var(--ember)}
.tri-card.tone-order{--tone:rgba(106,214,224,.13)} .tri-card.tone-order .fl-front:before{content:"▽";color:var(--cyan)}
.tri-card.tone-truth{--tone:rgba(244,217,163,.14)} .tri-card.tone-truth .fl-front:before{content:"◇";color:var(--gold2)}

/* ── вопрос саги: одна строка между секциями ──────────
   Динамический диапазон (рецензия, закон 5): на всю главную — один
   золотой акцент текста; здесь он и есть. */
.q-band{padding:calc(120px * var(--ui-scale)) 0;text-align:center;position:relative;overflow:hidden}
.q-band:before{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(52% 74% at 50% 48%,rgba(227,176,102,.07),transparent 70%)}
.q-band .wrap{position:relative}
.q-band .q-kicker{font-size:max(10px,calc(11px * var(--ui-scale)));letter-spacing:.4em;text-transform:uppercase;
  color:var(--faint);margin-bottom:calc(20px * var(--ui-scale))}
.q-band .q{font-family:var(--serif);font-style:italic;font-weight:600;margin:0;
  font-size:clamp(28px,4.6vw,calc(54px * var(--ui-scale)));line-height:1.15;
  background:linear-gradient(180deg,var(--gold2),var(--gold));-webkit-background-clip:text;background-clip:text;color:transparent}

/* ── эпохи: кадры-карточки ──────────────────────────── */
.epoch-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:calc(18px * var(--ui-scale));margin-top:calc(46px * var(--ui-scale))}
/* Сцена с двумя гранями — как у триптиха (см. комментарий там). */
.ep-card{position:relative;margin:0;border-radius:calc(16px * var(--ui-scale));aspect-ratio:3/4;display:grid;grid-template:1fr/1fr}
.ep-card .fl-face{border:1px solid var(--line);border-radius:inherit;overflow:hidden;background:rgba(255,255,255,.02)}
.ep-card .fl-face.fl-back{background:linear-gradient(168deg,rgba(20,15,9,.96),rgba(10,8,6,.97))}
.ep-card .fl-front{position:relative;grid-area:1/1}
.ep-card:hover .fl-face{border-color:rgba(227,176,102,.4)}
.ep-card img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform .6s ease}

.ep-cap{position:absolute;left:0;right:0;bottom:0;z-index:2;padding:calc(16px * var(--ui-scale)) calc(18px * var(--ui-scale)) calc(18px * var(--ui-scale));text-shadow:0 1px 3px #000,0 0 8px #000}
.ep-cap h4{font-family:var(--serif);font-size:calc(23px * var(--ui-scale));margin:calc(6px * var(--ui-scale)) 0 0;font-weight:600}
/* CTA карточек эпох: ссылки перехватывает SPA-роутер — переход без
   перезагрузки, как у кнопок hero. */
.ep-cap .ep-links{margin:calc(12px * var(--ui-scale)) 0 0;display:flex;gap:calc(18px * var(--ui-scale))}
.ep-cap .ep-links a{color:var(--gold);font-size:max(10px,calc(13px * var(--ui-scale)));letter-spacing:.06em;text-decoration:none;border-bottom:1px solid rgba(227,176,102,.4);padding-bottom:2px;transition:border-color .25s}
.ep-cap .ep-links a:hover{border-bottom-color:var(--gold)}
@media(max-width:1200px){.epoch-grid{grid-template-columns:repeat(2,1fr)}}

/* ── Единый направленный переворот (flip.js) ──────────
   Механика одна на все переворачивающиеся плашки: контейнер с data-flip
   вращается по var(--flip-a), грани лежат в одной grid-ячейке и прячутся
   по backface-visibility, торец .fl-edge даёт предмету толщину.
   Анимируется только transform; will-change ставится классом .fl-active
   на время перехода. */
.fl-card,.fl-face,.pd-flip,.pd-face,.pd-lyrics,.hero-face,.ch-face,.mc-face,.ab-flip,.ab-face{user-select:none;-webkit-user-select:none}
/* Оборот: тёмная доска, голос летописи. */
.fl-back-inner{height:100%;box-sizing:border-box;display:flex;flex-direction:column;
  padding:calc(18px * var(--ui-scale)) calc(20px * var(--ui-scale));text-align:left}
.fl-kicker{font-size:max(10px,calc(11px * var(--ui-scale)));letter-spacing:.34em;text-transform:uppercase;color:var(--gold);font-weight:700}
.fl-title{font-family:var(--serif);font-size:calc(24px * var(--ui-scale));font-weight:600;line-height:1.15;margin:calc(6px * var(--ui-scale)) 0 0}
.fl-lore{font-family:var(--serif);font-size:max(16px,calc(19px * var(--ui-scale)));line-height:1.55;color:var(--ink);margin:calc(10px * var(--ui-scale)) 0 0}
.tri-card .fl-back-inner{justify-content:center}
/* При крупном интерфейсе длинный оборот эпохи превышает высоту плитки:
   safe center сохраняет доступ к началу, внутренний скролл — к концу.
   На первом scroll-контейнере нужен pan-y, чтобы горизонтальный жест
   продолжал попадать в общий механизм переворота. */
.ep-card .fl-back-inner{justify-content:safe center;overflow-y:auto;overscroll-behavior:auto;touch-action:pan-y pinch-zoom}
.tri-card .fl-lore{font-size:max(18px,calc(23px * var(--ui-scale)))}
.ep-card{min-width:0;aspect-ratio:auto;height:max(440px,calc(440px * var(--ui-scale)))}
.tri-card{min-width:0}
@media(min-width:701px) and (max-width:900px){.tri-card{aspect-ratio:auto;height:360px}}
.fl-bridge{font-size:max(10px,calc(12px * var(--ui-scale)));color:var(--muted);line-height:1.5;margin:calc(10px * var(--ui-scale)) 0 0;
  padding-top:calc(10px * var(--ui-scale));border-top:1px solid rgba(227,176,102,.14)}
/* Ссылки на обороте карточек эпох — внизу, как на лице. */
.fl-back .ep-links{margin:calc(12px * var(--ui-scale)) 0 0;display:flex;gap:calc(18px * var(--ui-scale))}
.fl-back .ep-links a{color:var(--gold);font-size:max(10px,calc(13px * var(--ui-scale)));letter-spacing:.06em;text-decoration:none;border-bottom:1px solid rgba(227,176,102,.4);padding-bottom:2px}

/* Главная: общие .sec/.kicker/.f-card не меняем на других страницах.
   Авто-поля центрируют сам ограниченный по ширине блок лида. */
#philosophy .wrap,#story .wrap,#site-features .wrap{text-align:center}
#philosophy .lead{margin-left:auto;margin-right:auto}
.tri-cap,.ep-cap{text-align:center}
.ep-cap .ep-links,.fl-back .ep-links{justify-content:center}
.tri-card .fl-back-inner,.ep-card .fl-back-inner{text-align:center}
#site-features .f-card-head{justify-content:center}
#site-features .f-chips{justify-content:center}
/* safe center сохраняет доступ к началу длинного оборота в кадре 16:9. */
#site-features .f-flip .fl-back{align-items:center;justify-content:safe center;text-align:center}
/* Кап по ширине .wrap удерживает самый длинный заголовок в одну строку. */
#philosophy .wrap,#story .wrap,#site-features .wrap{container-type:inline-size}
#philosophy .wrap h2,#story .wrap h2,#site-features .wrap h2{
  font-size:min(clamp(34px,5vw,calc(56px * var(--ui-scale))), 6.7cqw)}
/* На 1800px+/2400px+ --ui-base растёт, ширина плиток — нет.
   Капы под ширину сетки сохраняют однострочные подписи эпох. */
.ep-card{container-type:inline-size}
.ep-cap h4{font-size:min(calc(23px * var(--ui-scale)), 7.8cqw)}
.ep-cap .no{font-size:min(max(10px,calc(11px * var(--ui-scale))), 4.3cqw)}
/* Свои SVG-иконки вместо эмодзи: размер ведёт шрифт контекста. */
svg.ic{width:1.15em;height:1.15em;display:inline-block;vertical-align:-.22em;flex-shrink:0}
.saga-reaction{display:inline-block;object-fit:contain;vertical-align:middle;flex-shrink:0}
.favbtn.on .ic{fill:var(--gold);stroke:var(--gold)}
.f-card-link{display:block;color:inherit;text-decoration:none}
@media (prefers-reduced-motion:reduce){
  .fl-card{transition:none}
}
html.perf-low .fl-card{transition:none}

/* ── сетка альбомов ─────────────────────────────────── */
.albums{display:grid;grid-template-columns:repeat(auto-fill,minmax(calc(250px * var(--ui-scale)),1fr));gap:calc(24px * var(--ui-scale));margin-top:calc(48px * var(--ui-scale))}
.card{position:relative;border-radius:calc(18px * var(--ui-scale));overflow:hidden;cursor:pointer;
  border:1px solid var(--line);background:var(--panel);transition:.35s;isolation:isolate}
.card:hover{transform:translateY(-6px);border-color:rgba(227,176,102,.5);
  box-shadow:0 24px 50px -20px rgba(0,0,0,.8)}
.card .art{aspect-ratio:1;position:relative;display:flex;align-items:flex-end;
  background-size:cover;background-position:center}
.card .art img{width:100%;height:100%;object-fit:cover;position:absolute;inset:0}
.card .art .glow{position:absolute;inset:0;mix-blend-mode:overlay;opacity:.5}
.card .num{position:absolute;top:calc(14px * var(--ui-scale));left:calc(16px * var(--ui-scale));font-family:var(--serif);font-size:calc(54px * var(--ui-scale));
  font-weight:700;color:rgba(255,255,255,.9);text-shadow:0 calc(2px * var(--ui-scale)) calc(14px * var(--ui-scale)) rgba(0,0,0,.7);z-index:2}
.card .veil{position:absolute;inset:0;z-index:1;
  background:linear-gradient(180deg,rgba(7,8,13,.1) 30%,rgba(7,8,13,.92))}
.card .meta{position:relative;z-index:2;padding:0 calc(18px * var(--ui-scale)) calc(18px * var(--ui-scale));width:100%}
.card .meta .t{font-family:var(--serif);font-size:calc(23px * var(--ui-scale));font-weight:600;line-height:1.1}
.card .meta .c{font-size:max(11px,calc(12px * var(--ui-scale)));color:var(--gold2);letter-spacing:.06em;margin-top:calc(6px * var(--ui-scale))}
.card .meta .d{font-size:max(11px,calc(13px * var(--ui-scale)));color:var(--muted);margin-top:calc(8px * var(--ui-scale));
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.card.ep{grid-column:1/-1;justify-self:center;max-width:calc(540px * var(--ui-scale));width:100%}
.card.ep .art{aspect-ratio:2/1}
.card.ep .veil{background:linear-gradient(135deg,rgba(7,8,13,.05) 40%,rgba(7,8,13,.92))}
.card.ep .meta .t{font-size:calc(25px * var(--ui-scale))}
.card.ep .num{right:calc(16px * var(--ui-scale));left:auto;text-align:right}

/* ── оверлеи ────────────────────────────────────────── */
.overlay{position:fixed;inset:0;z-index:var(--z-overlay);background:rgba(5,6,11,.7);
  backdrop-filter:blur(8px);opacity:0;pointer-events:none;visibility:hidden;
  transition:opacity .3s,visibility 0s .3s}
.overlay.open{opacity:1;pointer-events:auto;visibility:visible;transition:opacity .3s}
.sheet{position:absolute;top:0;right:0;height:100%;width:min(calc(680px * var(--ui-scale)),100%);
  background:linear-gradient(180deg,var(--bg2),var(--bg));border-left:1px solid var(--line);
  transform:translateX(calc(40px * var(--ui-scale)));opacity:0;transition:.35s;display:flex;flex-direction:column;
  box-shadow:calc(-30px * var(--ui-scale)) 0 calc(80px * var(--ui-scale)) calc(-30px * var(--ui-scale)) rgba(0,0,0,.9)}
.overlay.open .sheet{transform:translateX(0);opacity:1}
.sheet{overflow:hidden}
.sheet.wide{width:min(calc(920px * var(--ui-scale)),100%)}
/* обёртка панели альбома/трека должна заполнять шторку, чтобы тело могло скроллиться */
#album-pane{display:flex;flex-direction:column;height:100%;min-height:0}
.sheet-head{padding:calc(26px * var(--ui-scale)) calc(30px * var(--ui-scale)) calc(18px * var(--ui-scale));border-bottom:1px solid var(--line);position:relative;flex:0 0 auto}
.sheet-head .close{position:absolute;top:calc(22px * var(--ui-scale));right:calc(24px * var(--ui-scale));background:rgba(255,255,255,.06);
  border:1px solid var(--line);color:var(--ink);width:calc(38px * var(--ui-scale));height:calc(38px * var(--ui-scale));border-radius:50%;
  cursor:pointer;font-size:calc(18px * var(--ui-scale));transition:.2s}
.sheet-head .close:hover{background:var(--gold);color:#0a0a0a;transform:rotate(90deg)}
.sheet-head .eyebrow{font-size:max(11px,calc(12px * var(--ui-scale)));letter-spacing:.3em;color:var(--gold);text-transform:uppercase}
.sheet-head h3{font-family:var(--serif);font-size:calc(34px * var(--ui-scale));font-weight:600;margin-top:calc(6px * var(--ui-scale));line-height:1.05;max-width:88%}
.sheet-head p{color:var(--muted);font-size:max(11px,calc(14px * var(--ui-scale)));margin-top:calc(8px * var(--ui-scale));max-width:90%}
.sheet-body{overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;
  padding:calc(14px * var(--ui-scale)) calc(30px * var(--ui-scale)) calc(60px * var(--ui-scale));flex:1;min-height:0}
/* individual scrollbar overrides removed — using global */

/* ── собственные диалоги (dizConfirm/dizPrompt/dizAlert) ──
   Замена нативных confirm/alert/prompt (решение владельца 07.10):
   окно в токенах сайта, интонация летописи. Закрытые слои —
   visibility:hidden по закону производительности (§2.3). */
.dizdlg{position:fixed;inset:0;z-index:var(--z-modalert);background:rgba(5,6,11,.78);
  display:flex;align-items:center;justify-content:center;padding:20px;
  opacity:0;visibility:hidden;transition:opacity .22s ease,visibility 0s .22s}
.dizdlg.open{opacity:1;visibility:visible;transition:opacity .22s ease}
.dizdlg-box{background:var(--panel);border:1px solid var(--line);
  border-radius:calc(18px * var(--ui-scale));
  max-width:calc(460px * var(--ui-scale));width:94vw;
  padding:calc(22px * var(--ui-scale)) calc(24px * var(--ui-scale));
  transform:translateY(calc(10px * var(--ui-scale)));
  transition:transform .22s ease}
.dizdlg.open .dizdlg-box{transform:translateY(0)}
.dizdlg-kicker{color:var(--gold2);font-size:max(10px,calc(11px * var(--ui-scale)));
  letter-spacing:.18em;text-transform:uppercase;margin-bottom:calc(8px * var(--ui-scale))}
.dizdlg-msg{font-family:var(--serif);font-size:calc(19px * var(--ui-scale));
  line-height:1.45;color:var(--ink);white-space:pre-line}
.dizdlg-input{width:100%;box-sizing:border-box;margin-top:calc(12px * var(--ui-scale));
  background:var(--bg);color:var(--ink);border:1px solid var(--line);
  border-radius:calc(12px * var(--ui-scale));
  padding:calc(10px * var(--ui-scale)) calc(14px * var(--ui-scale));
  font-family:var(--sans);font-size:max(13px,calc(14px * var(--ui-scale)))}
.dizdlg-input:focus{outline:none;border-color:rgba(227,176,102,.5)}
.dizdlg-acts{display:flex;justify-content:flex-end;gap:calc(10px * var(--ui-scale));
  margin-top:calc(18px * var(--ui-scale))}
.dizdlg-btn{padding:calc(9px * var(--ui-scale)) calc(20px * var(--ui-scale));min-height:44px}
.dizdlg-ok{background:linear-gradient(120deg,var(--gold),var(--ember));color:#1a0f06;border:0}
.dizdlg-ok.dizdlg-danger{background:linear-gradient(120deg,var(--ember),#b3482e);color:#fff}

/* ── список треков ──────────────────────────────────── */
.track{display:flex;align-items:center;gap:calc(16px * var(--ui-scale));padding:calc(15px * var(--ui-scale)) calc(14px * var(--ui-scale));border-radius:calc(12px * var(--ui-scale));
  cursor:pointer;border:1px solid transparent;transition:.2s}
.track:hover{background:rgba(255,255,255,.04);border-color:var(--line)}
.track .tn{font-family:var(--serif);font-size:calc(20px * var(--ui-scale));color:var(--gold);width:calc(34px * var(--ui-scale));text-align:center;flex:0 0 auto}
.track .ti{flex:1;min-width:0}
.track .ti .tt{font-weight:600;font-size:max(11px,calc(16px * var(--ui-scale)))}
.track .ti .tg{font-size:max(11px,calc(12px * var(--ui-scale)));color:var(--faint);margin-top:calc(2px * var(--ui-scale))}
.track .dur{font-size:max(11px,calc(13px * var(--ui-scale)));color:var(--muted);font-variant-numeric:tabular-nums}
.track .play{flex:0 0 auto;color:var(--faint);transition:.2s;width:calc(30px * var(--ui-scale));height:calc(30px * var(--ui-scale));
  display:flex;align-items:center;justify-content:center;border-radius:50%}
.track .play svg{width:calc(17px * var(--ui-scale));height:calc(17px * var(--ui-scale));display:block}
.track:hover .play{color:var(--gold);background:rgba(227,176,102,.08)}
.track.empty{cursor:default;opacity:.5}.track.empty:hover{background:none;border-color:transparent}

/* ── текст песни ────────────────────────────────────── */
.lyrics{font-size:max(11px,calc(17px * var(--ui-scale)));line-height:1.85;white-space:pre-wrap;color:#dfe2ee;
  font-weight:300;letter-spacing:.01em}
.lyrics .tag{color:var(--gold);font-weight:600;font-size:max(11px,calc(13px * var(--ui-scale)));letter-spacing:.05em;
  text-transform:uppercase;display:inline-block}
.back-link{display:inline-flex;align-items:center;gap:calc(8px * var(--ui-scale));color:var(--gold2);font-size:max(11px,calc(14px * var(--ui-scale)));
  font-weight:600;cursor:pointer;margin-bottom:calc(18px * var(--ui-scale));transition:.2s}
.back-link:hover{gap:calc(12px * var(--ui-scale))}
.lyr-meta{display:flex;gap:calc(18px * var(--ui-scale));flex-wrap:wrap;margin:calc(4px * var(--ui-scale)) 0 calc(24px * var(--ui-scale));font-size:max(11px,calc(13px * var(--ui-scale)));color:var(--muted)}
.lyr-meta b{color:var(--gold2);font-weight:600}

/* ── Мастер-Библия ──────────────────────────────────── */
.bible-layout{display:flex;height:100%;min-height:0}
.toc{width:clamp(240px,calc(300px * var(--ui-scale)),40%);flex:0 0 auto;border-right:1px solid var(--line);overflow-y:auto;padding:calc(20px * var(--ui-scale)) calc(14px * var(--ui-scale)) calc(60px * var(--ui-scale))}
/* toc scrollbar — global */
.toc a{display:block;padding:calc(8px * var(--ui-scale)) calc(12px * var(--ui-scale));border-radius:calc(8px * var(--ui-scale));font-size:max(11px,calc(13px * var(--ui-scale)));color:var(--muted);
  cursor:pointer;transition:.18s;border-left:calc(2px * var(--ui-scale)) solid transparent}
.toc a:hover{color:var(--gold2);background:rgba(255,255,255,.04)}
.toc a.book{font-family:var(--serif);font-size:max(11px,calc(15px * var(--ui-scale)));color:var(--gold);font-weight:600;margin-top:calc(12px * var(--ui-scale));letter-spacing:.04em}
.toc a.ch{padding-left:calc(22px * var(--ui-scale))}
.bible-read{flex:1;overflow-y:auto;padding:calc(40px * var(--ui-scale)) calc(56px * var(--ui-scale)) calc(100px * var(--ui-scale));min-width:0}
/* Плашка «сохранённая версия»: runtime-документ недоступен, показан
   офлайн-запас сборки (T04) */
.bible-stale-note{margin:0 0 calc(24px * var(--ui-scale));padding:calc(10px * var(--ui-scale)) calc(14px * var(--ui-scale));border:1px solid rgba(224,122,58,.4);border-radius:calc(10px * var(--ui-scale));
  background:rgba(224,122,58,.08);color:var(--ember);font-size:max(11px,calc(13px * var(--ui-scale)));letter-spacing:.03em}
/* bible-read scrollbar — global */
.bible-read{font-size:max(11px,calc(17px * var(--ui-scale)));line-height:1.85;color:#d7dae6;font-weight:300}
.bible-read .b-h1{font-family:var(--serif);font-size:calc(40px * var(--ui-scale));font-weight:700;margin:calc(54px * var(--ui-scale)) 0 calc(8px * var(--ui-scale));
  background:linear-gradient(180deg,var(--gold2),var(--ember));-webkit-background-clip:text;background-clip:text;color:transparent}
.bible-read .b-h2{font-family:var(--serif);font-size:calc(27px * var(--ui-scale));color:var(--gold2);font-weight:600;margin:calc(30px * var(--ui-scale)) 0 calc(6px * var(--ui-scale))}
.bible-read .b-h3{font-family:var(--serif);font-size:calc(23px * var(--ui-scale));color:#fff;font-weight:600;margin:calc(34px * var(--ui-scale)) 0 calc(8px * var(--ui-scale))}
.bible-read p{margin:calc(14px * var(--ui-scale)) 0;max-width:70ch}
.bible-read strong{color:var(--gold2);font-weight:600}
.bible-read em{color:var(--cyan);font-style:italic}
.bible-read hr{border:0;height:1px;background:var(--line);margin:calc(30px * var(--ui-scale)) 0;max-width:70ch}
.bible-read blockquote{border-left:calc(3px * var(--ui-scale)) solid var(--gold);padding:calc(8px * var(--ui-scale)) calc(22px * var(--ui-scale));margin:calc(24px * var(--ui-scale)) 0;
  font-family:var(--serif);font-style:italic;font-size:calc(21px * var(--ui-scale));color:var(--gold2);max-width:70ch}
.bible-read .b-table{border-collapse:collapse;margin:calc(20px * var(--ui-scale)) 0;max-width:70ch;width:100%;
  font-size:max(11px,calc(15px * var(--ui-scale)));line-height:1.6}
.bible-read .b-table th,.bible-read .b-table td{border:1px solid var(--line);
  padding:calc(10px * var(--ui-scale)) calc(14px * var(--ui-scale));text-align:left;vertical-align:top}
.bible-read .b-table thead{background:rgba(227,176,102,.08)}
.bible-read .b-table th{font-family:var(--serif);font-size:max(11px,calc(17px * var(--ui-scale)));color:var(--gold2);font-weight:600}
.bible-read .b-table td{color:#d7dae6}

/* ── drawer плейлиста ────────────────────────────────── */
/* Ящик выезжает ИЗ камня вверх, поэтому его положение считает player.js
   (positionDrawer): камень таскают по экрану, и привязка к низу окна оставила
   бы шторку висеть отдельно от плеера. Слой ящика НИЖЕ камня (--z-drawer <
   --z-player): стык накрывает камень, а не наоборот. */
/* Clip-обёртка (.pd-clip, разметка в _chrome_tail): её нижняя (при выезде
   вверх) или верхняя (вниз) граница — линия стыка с камнем, ставит её
   positionDrawer(). overflow:hidden режет выдвижной ход шторки по этой
   линии — шторка выезжает из-под грани плеера, а не поверх страницы.
   Шторка внутри absolute: fixed-потомок чужим overflow не режется.
   pointer-events:none на обёртке — клики проходит, шторка включает свои. */
.pd-clip{position:fixed;left:0;right:0;top:0;bottom:0;
  z-index:var(--z-drawer);overflow:hidden;pointer-events:none}
.player-drawer{position:absolute;left:var(--pd-cx,50%);bottom:0;
  z-index:var(--z-drawer);transform:translateX(-50%) translateY(calc(100% + 2px));
  width:min(calc(100vw - 16px),calc(920px * var(--ui-scale)));
  /* Непрозрачность ФОНА управляется ползунком (--pl-bg-a); контент шторки
     (текст, обложка, кнопки) — отдельные слои и остаются плотными. Размытие
     слабеет вместе с альфой ради глубокой прозрачности. */
  background:linear-gradient(0deg,rgba(10,12,20,calc(.98*var(--pl-bg-a,1))),rgba(14,16,26,calc(.95*var(--pl-bg-a,1))));
  border:1px solid rgba(227,176,102,.18);border-bottom:0;
  /* тот же «камень», но срез снизу — там стык с плеером. Радиусы левого и
     правого угла равны: прежняя асимметрия (левый 34/46 против правого 26/34)
     читалась ступенькой на левой кромке. Эллипс мягкий (verticаl > horizontal),
     переход к прямой боковине плавный. */
  border-radius:calc(30px * var(--ui-scale)) calc(30px * var(--ui-scale)) 0 0 /
                calc(42px * var(--ui-scale)) calc(42px * var(--ui-scale)) 0 0;
  box-shadow:0 -18px 50px -24px rgba(0,0,0,.9);
  max-height:var(--pd-max,85vh);overflow:hidden;
  /* Закрытый ящик не рисуется вовсе: при max-height:0 рамка и тень всё
     равно красились — в мини-плеере тень читалась отдельной полосой под
     камнем, а backdrop-filter закрытого слоя грел композитор (перф-закон
     §2.3: закрытые полноэкранные слои — visibility:hidden). */
  visibility:hidden;opacity:0;pointer-events:none;
  transition:transform .55s cubic-bezier(.19,1,.25,1),opacity .55s ease,visibility 0s .55s;
  /* Колесо внутри панели не должно докручивать страницу под ней — ни на упоре
     списка, ни над областями, которым скроллить нечего. */
  overscroll-behavior:contain}
/* Камень утащили к верхней кромке окна — места над ним нет, и шторка выезжает
   ВНИЗ из-под него. Иначе её верх уходил бы за край окна и обрезался.
   Вместе с направлением переворачивается и срез: он всегда на стыке. */
.player-drawer.pd-down{bottom:auto;top:0;
  transform:translateX(-50%) translateY(calc(-100% - 2px));
  border-top:0;border-bottom:1px solid rgba(227,176,102,.18);
  border-radius:0 0 calc(30px * var(--ui-scale)) calc(30px * var(--ui-scale)) /
                0 0 calc(42px * var(--ui-scale)) calc(42px * var(--ui-scale));
  box-shadow:0 18px 50px -24px rgba(0,0,0,.9)}
.player-drawer.pd-down .pd-inner{
  padding-top:calc(30px * var(--ui-scale));padding-bottom:calc(18px * var(--ui-scale))}
/* При выезде вниз верхняя полоса ящика заходит под камень — крестик уводим
   ниже неё, иначе он оказывался наполовину под плеером и не нажимался. */
.player-drawer.pd-down .pd-close{top:calc(16px * var(--ui-scale))}
.player-drawer.open{visibility:visible;opacity:1;pointer-events:auto;transform:translateX(-50%) translateY(0);
  will-change:transform;transition:transform .55s cubic-bezier(.19,1,.25,1),opacity .55s ease,visibility 0s}
/* Высота ящика режется свободным местом, поэтому внутренности обязаны
   прокручиваться: иначе при зажатом камне низ панели просто отрезало бы. */
.pd-inner{max-height:var(--pd-max,85vh);overflow-y:auto;overscroll-behavior:contain}
/* Выдвижная часть — продолжение камня, значит и дышит так же: кромкой.
   Кольцо отдельным слоем, потому что у ящика overflow:hidden и собственная
   тень на нём срезалась бы по стыку. */
/* Кромка шторки — продолжение камня и дышит так же. Свечение ведёт light.js
   через --pl-glow/--pl-color: яркость кромки следует за эквалайзером, внешний
   ореол масштабируется --pl-strength (ползунок «сила свечения»). */
/* Шторка светит своей переменной --pl-glow-d: при медитативном дыхании она в
   лёгком рассинхроне с камнем (--pl-glow), при полной силе — синхронна (обе
   ведёт музыка). */
/* Кромка шторки живёт на отдельном пустом узле .pd-glow (разметка:
   template/_chrome_tail.html). Прежде это был ::after самой шторки, и
   --pl-glow-d приходилось писать в неё — вместе со всем треклистом внутри.
   Правило прежнее, сменился носитель. */
/* Кромка шторки — прежняя альфа .90 на максимуме (.32 + .58), покой .32.
   Тень/цвет здесь тоже постоянные, пульс ведёт opacity — композитор. */
.pd-glow{position:absolute;inset:0;border-radius:inherit;
  border:2px solid rgba(var(--pl-color,227,176,102),.90);
  border-bottom:0;pointer-events:none;z-index:3;
  opacity:calc(.35 + .65*var(--pl-glow-d,0));
  will-change:opacity;
  transition:opacity .1s linear}
.player-drawer.pd-down .pd-glow{border-bottom:2px solid rgba(var(--pl-color,227,176,102),.90);border-top:0}
/* overflow:hidden срезает box-shadow у ::after, поэтому ореол живёт на самой
   шторке через filter:drop-shadow — его overflow не режет.
   Ореол ПОСТОЯННЫЙ, без переменных: фильтр перерисовывается только когда
   панель действительно перерисовалась (прокрутка списка, смена трека), а не
   тридцать раз в секунду вслед за --pl-glow-d. Пульсирует кромка (.pd-glow,
   композиторная opacity); на глаз связка читается как «шторка дышит кромкой
   и ровно светит ореолом». Цвет фиксированный золотой: запись --pl-color в
   панель заставляла бы пересчитывать стиль всего треклиста. */
body.player-playing .player-drawer.open{
  box-shadow:0 -18px 50px -24px rgba(0,0,0,.9),0 0 30px rgba(227,176,102,.3)}

/* Мастер-тумблер выключил весь свет: гасим кромку и ореол, оставляя лишь
   тонкий статичный контур предмета. Прежняя альфа .22 у кромки = .84 × .26. */
body.light-off .p-stone .p-glow{box-shadow:none!important;opacity:.26!important}
body.light-off .p-stone .p-halo{opacity:0!important}
body.light-off .player-drawer .pd-glow{border-color:rgba(var(--pl-color,227,176,102),.20)!important}
body.light-off .player-drawer.open{box-shadow:0 -18px 50px -24px rgba(0,0,0,.9)!important}
/* Нижний отступ больше верхнего: на эту полосу заходит камень, и контент
   не должен под него уезжать. */
/* Три доли, а не фиксированные пиксели: сумма 220+340+340+gap перебивала
   ширину панели (830px), сетка вылезала за overflow:hidden — левый столбик
   уезжал влево, а центр «раздувался». Доли всегда делят наличную ширину. */
/* Центр заметно шире боковых (решение владельца: карта крупная, посадка
   плотная). Доля центра подобрана так, чтобы потолок карты (формула в
   .pd-flip) срабатывал с запасом — размер карты держит ВЫСОТА колонки,
   зоны кнопок и капли остаются ровно 96px при любой ширине панели. */
/* Треклист шире влево (решение владельца): панель 830→920, правая доля
   0.72→0.9 за счёт центра 1→0.95 и левой 0.72→0.68. Центр в пикселях почти
   стоит (+8px — в пределах запаса потолка карты), треклист +35%. */
.pd-inner{padding:calc(18px * var(--ui-scale)) calc(22px * var(--ui-scale)) calc(30px * var(--ui-scale));
  display:grid;justify-content:center;align-items:stretch;position:relative;
  grid-template-columns:minmax(0,0.68fr) minmax(0,0.95fr) minmax(0,0.9fr);
  grid-template-areas:'albums flip tracks';
  gap:calc(18px * var(--ui-scale))}
/* Высота панели равняется на центральную flip-карту. Треклист длиннее её не
   растит панель, а прокручивается внутри своей доли — «блок по высоте как
   соседний, треки листаются». Потолок в vh страхует на низких экранах. */
.pd-col{display:flex;flex-direction:column;min-width:0;min-height:0;gap:calc(6px * var(--ui-scale));
  max-height:min(58vh,calc(500px * var(--ui-scale)))}
.pd-col-albums{grid-area:albums;align-items:center;text-align:center}
.pd-col-flip{grid-area:flip;justify-content:center;align-items:center}
.pd-col-tracks{grid-area:tracks}
/* Шапка левой колонки: капля поднята к самому верху, в левое закругление
   шторки (решение владельца) — зона сжата со 96px, freed место уходит
   описанию и «сейчас играет». Высота зоны больше не привязана к зоне
   кнопок центральной колонки. */
.pd-albums-head{display:flex;align-items:center;justify-content:center;flex:0 0 auto;
  width:100%;min-height:calc(64px * var(--ui-scale))}

/* ── Надписи-кнопки над обложкой: «Аудио книга» и «DiZ Видео» ──────────
   Статно и дорого: сериф, разрядка, золото с бликом, тонкая оправа. При нажатии
   продавливается внутрь (translateY + inset-тень). Обе — заглушки «скоро»
   (разделы закрыты в реестре features.json), стеком в .pd-media-btns.
   Зона кнопок тянется на всё свободное место над flip-картой (которая
   уступает им 88px своим потолком) — стек отцентрирован по вертикали. */
.pd-media-btns{flex:1 1 auto;min-height:0;align-self:center;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:calc(14px * var(--ui-scale))}
.pd-video-btn{flex:0 0 auto;align-self:center;display:inline-flex;align-items:center;
  gap:calc(8px * var(--ui-scale));
  padding:calc(6px * var(--ui-scale)) calc(18px * var(--ui-scale));
  font-family:var(--serif);font-size:calc(19px * var(--ui-scale));font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;text-decoration:none;line-height:1;
  color:var(--gold2);white-space:nowrap;
  border:1px solid rgba(227,176,102,.38);border-radius:calc(11px * var(--ui-scale));
  background:linear-gradient(180deg,rgba(227,176,102,.12),rgba(227,176,102,.02));
  box-shadow:0 calc(3px * var(--ui-scale)) calc(14px * var(--ui-scale)) calc(-6px * var(--ui-scale)) rgba(0,0,0,.8),
             inset 0 1px 0 rgba(255,255,255,.08),0 0 calc(16px * var(--ui-scale)) -6px rgba(227,176,102,.5);
  transition:transform .14s cubic-bezier(.4,0,.2,1),box-shadow .18s,border-color .25s,color .25s,background .25s}
.pd-video-btn .vb-play,.pd-video-btn .vb-ico{width:calc(19px * var(--ui-scale));height:calc(19px * var(--ui-scale));
  flex:0 0 auto;color:var(--gold);filter:drop-shadow(0 1px 2px rgba(0,0,0,.6))}
/* «DiZ»/«Аудио» — золотой блик градиентом, «Видео»/«книга» — светлее и тоньше */
.pd-video-btn .vb-diz{font-weight:700;letter-spacing:.10em;
  background:linear-gradient(180deg,#fbe7c4,#e3b066 60%,#c8934a);
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
  text-shadow:none}
.pd-video-btn .vb-video{font-weight:500;color:var(--gold2);opacity:.92}
.pd-video-btn .vb-diz-img{height:calc(15px*var(--ui-scale));width:auto;display:block}
.pd-video-btn:hover{border-color:var(--gold);color:#fff7e8;
  background:linear-gradient(180deg,rgba(227,176,102,.22),rgba(227,176,102,.05));
  box-shadow:0 calc(6px * var(--ui-scale)) calc(20px * var(--ui-scale)) calc(-6px * var(--ui-scale)) rgba(227,176,102,.45),
             inset 0 1px 0 rgba(255,255,255,.12),0 0 calc(24px * var(--ui-scale)) -6px rgba(227,176,102,.7)}
/* продавливание внутрь интерфейса */
.pd-video-btn:active{transform:translateY(2px) scale(.975);
  border-color:rgba(227,176,102,.5);
  background:linear-gradient(180deg,rgba(0,0,0,.22),rgba(227,176,102,.03));
  box-shadow:inset 0 calc(3px * var(--ui-scale)) calc(9px * var(--ui-scale)) rgba(0,0,0,.6),
             inset 0 1px 2px rgba(0,0,0,.5)}
.pd-close{position:absolute;top:calc(8px * var(--ui-scale));right:calc(16px * var(--ui-scale));
  background:none;border:0;color:var(--muted);font-size:calc(22px * var(--ui-scale));cursor:pointer;z-index:2}
.pd-close:hover{color:var(--gold)}
/* режимы очереди — низ правой колонки */
.pd-modes{display:flex;align-items:center;justify-content:center;position:relative;
  gap:calc(10px * var(--ui-scale));
  padding-top:calc(6px * var(--ui-scale));border-top:1px solid var(--line);flex:0 0 auto}
/* Порядок: [подсветка] [перемешать] [повтор] [эмоция] по центру,
   шестерёнка прозрачности — прижата к правому краю. */
.pd-mode-group{display:flex;align-items:center;gap:calc(8px * var(--ui-scale))}
.pd-gear{position:absolute;right:0;top:50%;transform:translateY(-50%)}
.p-btn.pd-light.active,.p-btn.pd-emotion.active,.p-btn.pd-gear.active{color:var(--gold)}
.p-btn.pd-light.active::after,.p-btn.pd-emotion.active::after{content:'';position:absolute;
  bottom:calc(2px * var(--ui-scale));left:50%;transform:translateX(-50%);
  width:calc(4px * var(--ui-scale));height:calc(4px * var(--ui-scale));border-radius:50%;background:var(--gold)}
/* Эмоция подчиняется мастеру: выключил всю подсветку — эмоция бледнеет и не жмётся. */
.p-btn.pd-emotion.disabled{opacity:.3;pointer-events:none}

/* ── настройки: прозрачность + сила свечения (выезжают из шестерёнки) ──── */
/* Панель настроек — поповер НАД рядом кнопок (position:absolute от .pd-modes,
   шаг 100px вверх занимает зона кнопок в центральной колонке). Раньше панель
   раскрывалась в потоке: треклист отдавал ей высоту, ряд с кнопкой настроек
   перескакивал вверх, и клик по старому месту уходил мимо — «кнопка не всегда
   нажимается с первого раза». Теперь ряд неподвижен. Закрыта: max-height:0,
   в проход не попадает. Открыта: плотная подложка, чтобы читаться поверх
   треклиста. */
.pd-transparency{position:absolute;left:0;right:0;bottom:calc(100% + 10px);z-index:5;
  overflow:hidden;max-height:0;opacity:0;
  display:flex;flex-direction:column;gap:calc(8px * var(--ui-scale));
  padding:0 calc(10px * var(--ui-scale));
  background:linear-gradient(180deg,rgba(30,24,16,.98),rgba(12,14,23,.98));
  border:1px solid var(--line);border-radius:calc(12px * var(--ui-scale));
  box-shadow:0 14px 34px -12px rgba(0,0,0,.9);
  transition:max-height .42s cubic-bezier(.4,0,.2,1),opacity .3s ease,padding .42s cubic-bezier(.4,0,.2,1)}
/* Высота под три ряда: прозрачность, сила свечения, качество картинки.
   Панель раскрывается по max-height, поэтому число здесь — не оформление,
   а предел: не хватит его — нижний ряд просто обрежется. */
.pd-transparency.open{max-height:calc(190px * var(--ui-scale));opacity:1;
  padding:calc(12px * var(--ui-scale)) calc(12px * var(--ui-scale)) calc(8px * var(--ui-scale))}
.pd-tr-row{display:flex;align-items:center;gap:calc(12px * var(--ui-scale))}
.pd-tr-label{font-size:calc(10px * var(--ui-scale));color:var(--faint);letter-spacing:.1em;
  text-transform:uppercase;white-space:nowrap;flex:0 0 auto;
  width:calc(120px * var(--ui-scale))}
/* ── выбор качества картинки (perf.js) ──────────────────
   Стоит здесь же, рядом с тумблерами света: и то и другое — «сколько
   красоты потянет это устройство», решается в одном месте. */
.pd-q-row .pd-tr-label em{display:block;font-style:normal;text-transform:none;
  letter-spacing:.04em;color:var(--gold2);font-size:calc(9px * var(--ui-scale));
  margin-top:calc(2px * var(--ui-scale))}
/* Раскладка качества (решение владельца): подпись и «Авто» — в одну строку,
   три уровня — ниже, в ряд. #pdQuality — обёртка обоих рядов. */
#pdQuality{display:grid;grid-template-columns:1fr auto;row-gap:calc(8px * var(--ui-scale));align-items:center;flex:1;min-width:0}
.pd-q-levels{display:flex;gap:calc(4px * var(--ui-scale));grid-column:1/-1}
.pd-q-levels .pd-q-btn{flex:1}
.pd-quality{display:flex;flex-wrap:wrap;gap:calc(4px * var(--ui-scale));flex:1;min-width:0}
.pd-q-btn{flex:0 0 auto;padding:calc(4px * var(--ui-scale)) calc(9px * var(--ui-scale));
  border:1px solid var(--line);border-radius:calc(7px * var(--ui-scale));
  background:rgba(255,255,255,.03);color:var(--muted);cursor:pointer;
  font-family:var(--sans);font-size:max(10px,calc(11px * var(--ui-scale)));font-weight:600;
  letter-spacing:.04em;transition:color .2s,border-color .2s,background .2s}
.pd-q-btn:hover{color:var(--gold2);border-color:var(--gold)}
.pd-q-btn.active{color:#1a0f06;border-color:var(--gold);background:var(--gold)}
/* золотая линия = дорожка range, круглый золотой бегунок */
.pd-tr-range{-webkit-appearance:none;appearance:none;flex:1;min-width:0;height:calc(4px * var(--ui-scale));
  border-radius:999px;outline:none;cursor:pointer;
  background:linear-gradient(90deg,rgba(227,176,102,.85),rgba(227,176,102,.18));
  box-shadow:0 0 calc(8px * var(--ui-scale)) rgba(227,176,102,.35);
  /* линия «прорисовывается» при раскрытии панели */
  transform:scaleX(0);transform-origin:left center;transition:transform .5s cubic-bezier(.4,0,.2,1) .08s}
.pd-transparency.open .pd-tr-range{transform:scaleX(1)}
.pd-tr-range::-webkit-slider-thumb{-webkit-appearance:none;width:calc(15px * var(--ui-scale));
  height:calc(15px * var(--ui-scale));border-radius:50%;background:var(--gold2);border:0;cursor:pointer;
  box-shadow:0 0 calc(9px * var(--ui-scale)) rgba(227,176,102,.7)}
.pd-tr-range::-moz-range-thumb{width:calc(15px * var(--ui-scale));height:calc(15px * var(--ui-scale));
  border-radius:50%;background:var(--gold2);border:0;cursor:pointer;
  box-shadow:0 0 calc(9px * var(--ui-scale)) rgba(227,176,102,.7)}

/* flip card — 3 faces. Пропорцию 3:4 держит .pd-flip-inner (бокс картинки),
   а не .pd-flip: внутри flip лежат ещё точки-граней, и aspect-ratio на общем
   боксе делал inner выше кадра 3:4 — contain честно оставлял полосу снизу. */
.pd-flip{perspective:1000px;cursor:pointer;width:100%;position:relative;
/* Карточка не шире, чем влезает по высоте колонки в пропорции 3:4 (минус место
   под точки): иначе бокс выше кадра, а contain оставляет пустую полосу.
   Вторая скидка (124px) — зона кнопок «Аудио книги»/«DiZ Видео» над карточкой
   (зона = 124 − 28 точек = 96px: сами кнопки и чуть воздуха — плотная посадка).
   Она же задаёт высоту зоны капли в левой колонке (.pd-albums-head) — верх
   шторки строится симметрично. margin:auto центрирует карту по остатку.
   Потолок — та же формула, что max-height у .pd-col ниже. */
  max-width:calc((min(58vh,calc(500px * var(--ui-scale))) - calc(124px * var(--ui-scale))) * 0.75);margin-top:auto;margin-bottom:auto}
.pd-flip-inner{position:relative;width:100%;aspect-ratio:3/4;overflow:hidden;border-radius:calc(14px * var(--ui-scale));
  border:1px solid var(--line);background:rgba(255,255,255,.04)}
.pd-flip-inner.is-turning{overflow:visible;
  /* Бокс хоста на время оборота невидим: панели разъезжаются в перспективе и
     перестают закрывать его целиком — по углам проглядывали его фон и рамка,
     читаясь как «крышки». Живые грани несут собственный непрозрачный фон,
     поэтому подложка хоста нужна только в покое. */
  background:transparent;border-color:transparent}
.pd-face{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;backface-visibility:hidden;
  padding:calc(10px * var(--ui-scale));background:linear-gradient(160deg,rgba(7,8,13,.95),rgba(10,12,20,.9))}
/* Грань обложки: сама картинка целиком (contain), а пустоту вокруг заполняет
   её же размытая копия. Так блок любой формы заполнен, но ни одна деталь
   обложки не срезана — а срезала бы её любая заливка через cover. */
#pdFaceCover,.pd-cover-face{position:relative;overflow:hidden;padding:0;height:100%}
/* height:100% обязателен: грань relative (не absolute, как остальные — нужно
   для слоя блюра), поэтому без явной высоты она растягивалась под контент,
   а height:100% у img не резолвился — шторки 4:7 (05/08/09/11/12) вылезали
   за низ блока 3:4 и «DiZ Saga» срезался. Шторки 3:4 влезали случайно. */
#pdFaceCover::before,.pd-cover-face::before{content:'';position:absolute;inset:calc(-12% - 12px);
  background-image:var(--cover-bg,none);background-size:cover;background-position:center;
  filter:blur(20px) brightness(.5) saturate(1.1)}
#pdFaceCover img,.pd-cover-face img{position:relative;z-index:1;display:block;width:100%;height:100%;object-fit:contain;
  border-radius:calc(8px * var(--ui-scale))}
#pdFaceCover .pd-face-fallback,.pd-cover-face .pd-face-fallback{position:relative;z-index:1}
.pd-face-fallback{width:calc(80px * var(--ui-scale));height:calc(80px * var(--ui-scale));border-radius:50%;
  display:flex;align-items:center;justify-content:center;font-family:var(--serif);font-size:calc(40px * var(--ui-scale));
  color:var(--gold);background:rgba(227,176,102,.12);border:2px solid var(--gold);flex:0 0 auto}
/* Портрет героя занимает грань целиком: прежние 120×160 оставляли вокруг
   пустое поле в половину карточки. Кадрирование прежнее (cover), меняется
   только масштаб — лицо ближе к верху, поэтому и точка привязки там. */
.pd-portrait-img{width:100%;height:100%;flex:1 1 auto;min-height:0;object-fit:cover;
  object-position:center 22%;
  border-radius:calc(8px * var(--ui-scale));border:2px solid var(--gold);margin-bottom:calc(6px * var(--ui-scale))}
.pd-char-name{font-family:var(--serif);font-size:calc(16px * var(--ui-scale));font-weight:600;color:var(--gold2);
  text-align:center;flex:0 0 auto}
.pd-lyrics{font-size:max(13px,calc(12px * var(--ui-scale)));color:var(--ink);line-height:1.6;white-space:pre-wrap;
  overflow-y:auto;overscroll-behavior:contain;width:100%;height:100%;padding:calc(12px * var(--ui-scale))}
/* dot indicators for flip-card faces */
.pd-flip-dots{display:flex;justify-content:center;gap:calc(6px * var(--ui-scale));margin-top:calc(6px * var(--ui-scale))}
.pd-flip-dot{width:7px;height:7px;border-radius:50%;background:var(--faint);opacity:.3;transition:.2s}
.pd-flip-dot.active{opacity:1;background:var(--gold2)}

/* flip animation */
@keyframes flipOut{0%{transform:rotateY(0);opacity:1}100%{transform:rotateY(-90deg);opacity:0}}
@keyframes flipIn{0%{transform:rotateY(90deg);opacity:0}100%{transform:rotateY(0);opacity:1}}
.pd-face.flip-out{animation:flipOut .25s ease-in forwards}
.pd-face.flip-in{animation:flipIn .25s ease-out forwards}

/* колонки: подписи */
.pd-label{font-size:calc(11px * var(--ui-scale));color:var(--faint);letter-spacing:.14em;
  text-transform:uppercase;display:block;flex:0 0 auto;margin-bottom:calc(4px * var(--ui-scale))}
/* «Треклист» — в цвете номеров треков (.pd-tn — var(--gold). Остальные
   подписи колонок остаются приглушёнными. */
.pd-col-tracks > .pd-label{color:var(--gold)}

/* ── альбом «каплей» ──────────────────────────────────
   Органическую форму отдали альбомам: у плеера она мешала (там постоянный
   прямоугольник с содержимым), а на пилюле альбома читается как предмет.
   Дышит целиком — ровно как кнопка меню: размер, свечение, яркость. Форма при
   этом статична, как у капель в шапке: колыхание радиусов было незаметным. */
/* Амплитуду, период и фазу задаёт JS (--br/--bd/--bdl) — каждая капля дышит
   по-своему. Общая анимация на всех означала бы, что весь столбик раздувается
   одновременно: соседние капли на пике наезжали друг на друга, и вместо живых
   предметов получалась пульсирующая стопка. */
/* Кадры — только transform, ровно по тем же причинам, что у nav-breathe:
   box-shadow и filter каждый кадр требуют полного перепейнта пилюли вместе с
   размытой тенью вокруг. Свет вынесен на ::before, ему анимируется opacity. */
@keyframes album-breathe{
  0%,100%{transform:scale(1)}
  45%,55%{transform:scale(var(--br,1.05))}
}
@keyframes album-glow{
  0%,100%{opacity:0}
  45%,55%{opacity:1}
}
.pd-album-one,.pd-album-btn{
  /* по размеру текста внутри, а не по ширине колонки */
  width:max-content;max-width:100%;
  display:flex;align-items:center;gap:calc(10px * var(--ui-scale));
  flex:0 0 auto;cursor:pointer;text-align:left;font-family:var(--sans);
  border-radius:calc(22px * var(--ui-scale));
  position:relative;
  animation:album-breathe var(--bd,8s) ease-in-out var(--bdl,0s) infinite;
  transition:color .5s,background .5s,border-color .5s}
/* Слой света: без фона, только тень — внутренность коробки он не закрашивает,
   поэтому содержимое пилюли остаётся нетронутым. Период и задержка те же
   переменные, что у самой пилюли: свет обязан дышать с ней в фазе. */
.pd-album-one::before,.pd-album-btn::before{content:'';position:absolute;inset:0;
  border-radius:inherit;pointer-events:none;
  box-shadow:0 8px 24px -4px rgba(227,176,102,.9),0 0 14px 4px rgba(227,176,102,.3);
  opacity:0;animation:album-glow var(--bd,8s) ease-in-out var(--bdl,0s) infinite}
.pd-album-one{padding:calc(12px * var(--ui-scale)) calc(24px * var(--ui-scale));
  background:radial-gradient(120% 150% at 50% 0%,rgba(227,176,102,.14),rgba(255,255,255,.03) 60%,rgba(255,255,255,.02));
  border:1px solid rgba(227,176,102,.45);color:var(--gold2)}
.pd-album-one:hover{border-color:var(--gold);color:#fff7e8;
  background:radial-gradient(120% 170% at 50% -20%,rgba(227,176,102,.30),rgba(227,176,102,.06) 60%,rgba(255,255,255,.02))}
/* Цифра живёт в своей коробке по центру (текст-аглин): у однозначных номеров
   она иначе «гуляла» относительно тире и названия — у каждого альбома свой
   зазор. Коробка одинаковая для 1 и 14 — зазор везде симметричный. */
.pd-one-num{font-family:var(--serif);font-size:calc(26px * var(--ui-scale));font-weight:700;
  color:var(--gold);line-height:1;flex:0 0 auto;
  min-width:calc(26px * var(--ui-scale));text-align:center}
.pd-one-title{font-size:calc(14px * var(--ui-scale));font-weight:700;letter-spacing:.06em;
  line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ── выбор альбома: столбик, вырастающий из кнопки ────
   Не окно во весь экран и без размытия. Слой .pd-picker — прозрачный ловец
   кликов «мимо списка»; сам столбик стоит там, куда его поставил
   positionPicker(): вплотную к кнопке альбома и растёт от неё вверх.
   Размеры (--pk-l/--pk-b/--pk-t/--pk-max) считает JS: кнопка ездит вместе с
   плеером, и прибить столбик к фиксированной точке нельзя. */
.pd-picker{position:fixed;inset:0;z-index:var(--z-overlay);display:none;
  background:transparent;opacity:0;transition:opacity .2s ease}
/* .open — в DOM, .shown — уже проявился. Между снятием .shown и снятием .open
   идёт затухание: всё это время слой растянут на весь экран и без
   pointer-events:none глотал бы клики по странице под ним. */
.pd-picker.open{display:block;pointer-events:none}
.pd-picker.open.shown{opacity:1;pointer-events:auto}
.pd-picker-list{position:absolute;left:var(--pk-l,50%);bottom:var(--pk-b,auto);
  display:flex;flex-direction:column;align-items:flex-start;
  /* На пике дыхания капля вырастает — при тесном зазоре соседи наезжали. */
  gap:calc(13px * var(--ui-scale));
  max-height:var(--pk-max,60vh);overflow-y:auto;overscroll-behavior:contain;
  /* Поля равны разлёту свечения (--pk-pad). Как только по вертикали включается
     overflow-y:auto, горизонтальный тоже перестаёт быть visible — и тень капли
     срезалась ровно по левому краю списка. Поля держат свечение внутри, а
     positionPicker() на ту же величину сдвигает столбик влево, чтобы капля
     осталась выровненной по кнопке. */
  padding:var(--pk-pad,26px);
  /* столбик шире кнопки, но не шире экрана */
  max-width:calc(100vw - 12px);
  /* Окно на три пилюли (высоту считает positionPicker): альбомы у краёв
     плавно растворяются — видно, что список продолжается, и он докручивается
     колесом мыши или пальцем. Короткий список (≤3) открывается без маски
     (класс .short ставит drawer.js). */
  mask-image:linear-gradient(180deg,transparent 0,#000 34px,#000 calc(100% - 34px),transparent 100%);
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 34px,#000 calc(100% - 34px),transparent 100%)}
.pd-picker-list.short{mask-image:none;-webkit-mask-image:none}
/* Не хватило места сверху (телефон, планшет) — растём вниз от той же кнопки. */
.pd-picker.down .pd-picker-list{bottom:auto;top:var(--pk-t,auto)}
.pd-picker-list::-webkit-scrollbar{width:3px}
.pd-picker-list::-webkit-scrollbar-thumb{background:rgba(227,176,102,.25);border-radius:2px}
/* Блоки выталкиваются из кнопки по очереди — ближний первым. Развороты и
   свечение живут на самой кнопке (nav-breathe), поэтому вырастание вынесено
   на обёртку: две анимации на одном элементе дрались бы за transform. */
@keyframes pick-grow{
  from{opacity:0;transform:translateY(calc(16px * var(--ui-scale))) scale(.72)}
  to{opacity:1;transform:none}
}
.pd-pick-item{display:block;flex:0 0 auto;max-width:100%;opacity:0;
  transform-origin:bottom left;
  animation:pick-grow .32s cubic-bezier(.34,1.5,.64,1) forwards;
  animation-delay:var(--d,0s)}
.pd-picker.down .pd-pick-item{transform-origin:top left;animation-name:pick-grow-down}
@keyframes pick-grow-down{
  from{opacity:0;transform:translateY(calc(-16px * var(--ui-scale))) scale(.72)}
  to{opacity:1;transform:none}
}
/* Закрытие — тот же каскад задом наперёд: блоки втягиваются обратно в кнопку,
   дальний уходит первым. Раньше столбик просто гас целиком, и связь с
   кнопкой, из которой он вырос, на глаз пропадала. */
@keyframes pick-shrink{
  from{opacity:1;transform:none}
  to{opacity:0;transform:translateY(calc(16px * var(--ui-scale))) scale(.66)}
}
@keyframes pick-shrink-down{
  from{opacity:1;transform:none}
  to{opacity:0;transform:translateY(calc(-16px * var(--ui-scale))) scale(.66)}
}
/* both, а не forwards: у .pd-pick-item базовая opacity:0 (стартовое состояние
   появления), и пока задержка блока не наступила, он падал бы в неё мгновенно —
   весь столбик, кроме первого блока, исчезал разом. backwards-часть держит
   блок видимым до его очереди. */
.pd-picker.closing .pd-pick-item{animation:pick-shrink .24s cubic-bezier(.4,0,.85,.35) both;
  animation-delay:var(--dc,0s)}
.pd-picker.down.closing .pd-pick-item{animation-name:pick-shrink-down}
/* Слой при закрытии НЕ гаснет: прозрачность дают сами блоки, каждый в свой
   черёд. Общее затухание съедало весь каскад — столбик пропадал разом, ради
   чего этот каскад и делался. Слой снимается из DOM по таймеру в
   closeAlbumPicker(), когда втянулся последний блок. */
.pd-picker.open.closing{opacity:1;pointer-events:none}
/* Блок альбома в столбике выбора: та же «капля», что и у текущего альбома, —
   человек выбирает предмет того же вида, каким он потом стоит в панели.
   Фон почти непрозрачный: страница под столбиком не размывается, и полупрозрачная
   заливка давала бы читать сквозь буквы. */
.pd-album-btn{background:radial-gradient(120% 150% at 50% 0%,rgba(30,24,16,.96),rgba(12,14,23,.95) 60%,rgba(9,11,19,.96));
  border:1px solid var(--line);color:var(--muted);
  font-size:max(12px,calc(12.5px * var(--ui-scale)));font-weight:600;
  padding:calc(9px * var(--ui-scale)) calc(20px * var(--ui-scale));
  box-shadow:0 10px 28px -14px rgba(0,0,0,.9),inset 0 1px 1px rgba(255,255,255,.05)}
.pd-album-btn b{font-family:var(--serif);color:var(--gold);font-weight:700;flex:0 0 auto;
  font-size:calc(20px * var(--ui-scale));line-height:1;min-width:calc(20px * var(--ui-scale));
  text-align:center}
.pd-album-btn.active b{color:var(--gold2)}
.pd-album-btn .pd-ab-t{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pd-album-btn:hover{border-color:var(--gold);color:var(--gold2)}
.pd-album-btn.active{background:radial-gradient(120% 150% at 50% 0%,rgba(72,52,26,.97),rgba(14,16,25,.96) 65%);
  border-color:var(--gold);color:var(--gold2);
  box-shadow:0 10px 28px -14px rgba(0,0,0,.9),0 0 calc(14px * var(--ui-scale)) rgba(227,176,102,.28)}
/* Нулевой кадр album-breathe, перенесённый в обычное объявление. Пока кадры
   задавали box-shadow, они перебивали тени ВСЕХ вариантов пилюли — и обычной,
   и .active, — поэтому на экране всегда была только эта пара. Правило стоит
   после всех вариантов, чтобы повторить прежнюю картину один в один. */
.pd-album-one,.pd-album-btn,.pd-album-btn.active{
  box-shadow:0 6px 18px -6px rgba(227,176,102,.55),0 0 0 0 rgba(227,176,102,.25)}

/* ── тезисы: об альбоме и о звучащем треке ───────────── */
/* Блок «Описание альбома» начинается от верха колонки: строка-лейбл стоит
   ровно по верхней линии блока обложки (зона капли = зоне кнопок = 100px),
   «Сейчас играет» и кнопки скачивания идут следом, а не прижаты к низу
   (решение владельца). При длинном тексте блок сжимается и листается. */
.pd-album-info{flex:0 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  display:flex;flex-direction:column;
  /* Точные 10px сажают лейбл «Описание альбома» на верхнюю линию блока
     обложки: зона кнопок центральной колонки выше зоны капли ровно на них. */
  padding-top:calc(10px * var(--ui-scale))}
.pd-album-info .album-era{flex:0 0 auto;margin-bottom:calc(8px * var(--ui-scale))}
.pd-album-desc{font-size:calc(12px * var(--ui-scale));color:var(--muted);line-height:1.55;
  margin-top:calc(3px * var(--ui-scale))}
.pd-album-desc .pd-motto{display:block;margin-top:calc(5px * var(--ui-scale));
  font-family:var(--serif);font-style:italic;font-size:calc(13px * var(--ui-scale));color:var(--gold2)}
.pd-now{flex:0 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  border-top:1px solid var(--line);padding-top:calc(8px * var(--ui-scale))}
.pd-now-title{font-family:var(--serif);font-size:calc(17px * var(--ui-scale));font-weight:600;
  color:var(--ink);line-height:1.2}
.pd-now-story{font-size:calc(12px * var(--ui-scale));color:var(--muted);line-height:1.55;
  margin-top:calc(4px * var(--ui-scale))}
.pd-now-mood{font-family:var(--serif);font-style:italic;font-size:calc(13px * var(--ui-scale));
  color:var(--gold2);line-height:1.4;margin-top:calc(4px * var(--ui-scale))}
/* tracklist */
.pd-tracklist-area{overflow-y:auto;overscroll-behavior:contain;flex:1;min-height:0;max-height:none}
/* ── спойлер треклиста (только мобильная версия) ──────
   На узком экране список из 15 строк съедал всю шторку, поэтому там видны
   играющий трек и два соседа. На десктопе места хватает — список полный,
   и подпись-переключатель не нужна.
   pan-y — чтобы браузер оставил нам горизонтальное движение пальца
   (смахивание вправо разворачивает), забрав себе только вертикальное. */
.pd-track{touch-action:pan-y;-webkit-user-select:none;user-select:none}
.pd-tl-more{display:none;width:100%;margin-top:calc(4px * var(--ui-scale));
  padding:calc(7px * var(--ui-scale)) calc(10px * var(--ui-scale));
  background:none;border:1px dashed rgba(227,176,102,.25);
  border-radius:calc(8px * var(--ui-scale));cursor:pointer;font-family:var(--sans);
  font-size:calc(10.5px * var(--ui-scale));letter-spacing:.06em;color:var(--faint);
  text-transform:uppercase;line-height:1.35;transition:color .2s,border-color .2s}
.pd-tl-more:hover{color:var(--gold2);border-color:var(--gold)}
.pd-tracklist-area:not(.tl-open) .pd-tl-more-close{display:none}
.pd-tracklist-area.tl-open .pd-tl-more-open{display:none}
.pd-track{display:flex;align-items:center;gap:calc(8px * var(--ui-scale));padding:calc(6px * var(--ui-scale)) calc(10px * var(--ui-scale));
  border-radius:calc(8px * var(--ui-scale));cursor:pointer;transition:.2s;border:1px solid transparent}
.pd-track:hover{background:rgba(255,255,255,.04);border-color:var(--line)}
.pd-track:active{background:rgba(227,176,102,.12)}
.pd-track.playing{background:rgba(227,176,102,.08);border-color:rgba(227,176,102,.3)}
.pd-track .pd-tn{font-family:var(--serif);font-size:calc(16px * var(--ui-scale));color:var(--gold);
  width:calc(24px * var(--ui-scale));text-align:center;flex:0 0 auto}
.pd-track .pd-tt{flex:1;font-size:max(13px,calc(13px * var(--ui-scale)));font-weight:500;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pd-track .pd-ts{display:block;font-size:max(10px,calc(10px * var(--ui-scale)));font-weight:400;color:var(--gold);opacity:.72;margin-top:calc(1px * var(--ui-scale));white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pd-track .pd-na{font-size:calc(10px * var(--ui-scale));color:var(--faint);font-style:italic}
.pd-track .pd-play{flex:0 0 auto;width:calc(24px * var(--ui-scale));height:calc(24px * var(--ui-scale));
  display:flex;align-items:center;justify-content:center;color:var(--muted)}
.pd-track.playing .pd-play{color:var(--gold)}
.pd-track .dl{font-size:max(11px,calc(11px * var(--ui-scale)));font-weight:700;letter-spacing:.04em;color:var(--faint);
  border:1px solid var(--line);border-radius:calc(6px * var(--ui-scale));padding:calc(3px * var(--ui-scale)) calc(7px * var(--ui-scale));cursor:pointer;transition:.2s}
.pd-track .dl:hover{color:var(--gold2);border-color:var(--gold);background:rgba(227,176,102,.06)}
.pd-track .dl:active{background:rgba(227,176,102,.15)}
/* Кнопки скачивания альбома — нижний левый угол шторки (решение владельца):
   один ряд на всю ширину колонки, обе кнопки равные — читаются симметрично
   относительно блока описания. margin-top:auto прижимает ряд к низу колонки.
   Класс .dl описан только внутри .pd-track/.track, поэтому в #pdWavArchive
   он не давал ничего — кнопка «WAV-архив» висела голым текстом. */
.pd-album-dl{display:flex;gap:calc(8px * var(--ui-scale));width:100%;margin-top:auto}
.pd-album-dl .dl{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;
  gap:calc(6px * var(--ui-scale));min-height:calc(30px * var(--ui-scale));
  font-size:max(11px,calc(11px * var(--ui-scale)));font-weight:700;letter-spacing:.04em;
  color:var(--faint);border:1px solid var(--line);border-radius:calc(8px * var(--ui-scale));
  padding:calc(5px * var(--ui-scale)) calc(10px * var(--ui-scale));cursor:pointer;transition:.2s;white-space:nowrap}
.pd-album-dl .dl .ic{color:var(--gold)}
.pd-album-dl .dl:hover{color:var(--gold2);border-color:var(--gold);background:rgba(227,176,102,.06)}
.pd-album-dl .dl:active{background:rgba(227,176,102,.15)}
.pd-album-dl .dl.busy{opacity:.55;pointer-events:none}
@media (hover:none) and (pointer:coarse){
  .pd-album-dl .dl{min-height:40px}
}
.pd-empty{color:var(--faint);font-size:calc(14px * var(--ui-scale));text-align:center;padding:calc(20px * var(--ui-scale))}
.pd-soon{display:flex;flex-direction:column;align-items:center;text-align:center;padding:calc(28px * var(--ui-scale)) calc(16px * var(--ui-scale));gap:calc(6px * var(--ui-scale))}
.pd-soon-icon{font-size:calc(24px * var(--ui-scale));color:var(--gold);animation:pulse-glow 3s ease-in-out infinite}
.pd-soon-title{font-family:var(--serif);font-size:calc(18px * var(--ui-scale));font-weight:600;color:var(--gold2)}
.pd-soon-desc{font-size:calc(13px * var(--ui-scale));color:var(--muted);max-width:280px}
.pd-soon-meta{font-size:calc(11px * var(--ui-scale));color:var(--faint);letter-spacing:.06em;text-transform:uppercase}
.pd-soon-badge{font-size:calc(10px * var(--ui-scale));color:var(--gold);margin-left:2px;vertical-align:middle;animation:pulse-glow 3s ease-in-out infinite}

/* ── мобильный ящик: обложка + альбомы сверху, треклист снизу ──────
   Колонка альбомов раскрывается через display:contents — её содержимое
   становится элементами общей сетки, и описание альбома уезжает ПОД
   треклист, оставаясь в левой колонке на десктопе. Иначе пришлось бы
   держать две копии одного блока. */
@media(max-width:767px){
  /* Верх панели поделён ровно пополам: слева обложка, справа — капля альбома
     и оба тезиса под ней. Треклист лежит ниже во всю ширину. Обложка занимает
     всю левую половину по высоте (три строки правого столбца), поэтому
     половины остаются равными и по ширине, и по высоте. */
  .pd-inner{
    grid-template-columns:1fr 1fr;
    grid-template-areas:'flip one' 'flip info' 'flip now' 'tracks tracks' 'dl dl';
    /* Все строки auto, без 1fr. При развёрнутом треклисте панель упирается в
       78vh, становится скролл-контейнером, и доля 1fr у строки «сейчас играет»
       схлопывалась в минимум — тезис вылезал из своей строки и наезжал на
       первый трек. auto-строки всегда вмещают свой текст. */
    grid-template-rows:auto auto auto auto auto;
    /* Потолок считает positionDrawer по свободному месту над камнем. Жёсткие
       78vh перебивали его, и шторка вырастала за верхнюю кромку окна —
       у перетащенного вверх плеера её верх просто срезало. */
    max-height:var(--pd-max,78vh);overflow-y:auto;
    -webkit-overflow-scrolling:touch;overscroll-behavior:contain;
    padding:calc(14px * var(--ui-scale)) calc(14px * var(--ui-scale)) calc(26px * var(--ui-scale))}
  .pd-col{max-height:none}
  /* Обложка тянется на всю высоту правой половины: если тезисы выше квадрата,
     пустовать левой половине незачем — картинка забирает место себе, а вместе
     с ней и грань с текстом песни, куда влезает больше строк. */
  .pd-col-flip{grid-area:flip;width:100%;align-self:stretch}
  .pd-flip{flex:1 1 auto;min-height:calc(150px * var(--ui-scale))}
  /* На мобильном бокс заливает зону (aspect нет): картинка вписывается
     contain в то, что дала сетка, — как было до десктопного фикса. */
  .pd-flip-inner{aspect-ratio:auto;height:100%}
  /* Колонка раскрывается в общую сетку: её блоки становятся её элементами и
     встают каждый на свою строку. Иначе пришлось бы держать вторую копию
     разметки под мобильный. */
  .pd-col-albums{display:contents}
  /* Шапка тоже растворяется, чтобы капля попала прямо в grid-area 'one';
     на мобильном отдельная зона под каплю не нужна. */
  .pd-albums-head{display:contents}
  /* justify-self, а не align-self: колонка раскрыта в общую сетку через
     display:contents, и центрирование по горизонтали капли теперь задаётся
     ей самой, а не колонкой (та растворилась). Без этого капля липла к левому
     краю своей ячейки. */
  .pd-album-one{grid-area:one;align-self:center;justify-self:center}
  .pd-album-info{grid-area:info}
  .pd-now{grid-area:now}
  .pd-col-tracks{grid-area:tracks}
  #pdWavArchive{grid-area:dl}
  .pd-char-name{font-size:calc(13px * var(--ui-scale))}
  .pd-lyrics{font-size:calc(11px * var(--ui-scale));padding:calc(8px * var(--ui-scale))}
  body.drawer-open{overflow:hidden}
  /* Скроллер на мобильном ровно один — .pd-inner. Внутренним блокам скроллить
     нечего, но overflow-y:auto всё равно делал их скролл-контейнерами, а
     overscroll-behavior:contain запрещал отдавать жест наверх — палец упирался
     в границу, достигнутую всегда, и панель не прокручивалась. */
  .pd-tracklist-area,.pd-album-info,.pd-now{overflow-y:visible;overscroll-behavior:auto;max-height:none}
  /* спойлер треклиста живёт только здесь — см. комментарий у .pd-tl-more */
  .pd-tracklist-area:not(.tl-open) .pd-track:not(.near){display:none}
  .pd-tl-more{display:block}
}
@media(max-width:479px){
  .pd-inner{padding:calc(12px * var(--ui-scale)) calc(12px * var(--ui-scale)) calc(24px * var(--ui-scale))}
  .pd-album-one{padding:calc(9px * var(--ui-scale)) calc(14px * var(--ui-scale))}
  .pd-one-num{font-size:calc(18px * var(--ui-scale))}
  .pd-one-title{font-size:calc(11px * var(--ui-scale))}
  .pd-album-btn{min-height:44px;font-size:max(12px,calc(12px * var(--ui-scale)))}
  /* В половине экрана строки узкие — поджимаем кегль, чтобы тезис читался
     абзацем, а не лесенкой из одного слова. */
  .pd-album-desc{font-size:calc(11.5px * var(--ui-scale))}
  .pd-album-desc,.pd-now-story{font-size:calc(11px * var(--ui-scale));line-height:1.45}
  .pd-now-title{font-size:calc(15px * var(--ui-scale))}
  .pd-now-mood,.pd-album-desc .pd-motto{font-size:calc(11.5px * var(--ui-scale))}
}
/* ── drawer handle (chevron) ─────────────────────
   Полоску-язычок над плеером убрали по просьбе: она мозолила глаз. Шторка и без
   неё открывается кликом по телу плеера (обработчик на .player-inner в player.js,
   кроме кнопок/шкалы) и свайпом вверх. Элемент остаётся в DOM (на нём висит
   свайп-логика и aria), но не рисуется. */
.p-drawer-handle{display:none}

/* ── глобальный плеер: «камень Аскета» ────────────────
   .player — слой во весь экран, событий не ловит: он даёт камню систему
   координат, в которой его двигает drag. Всё видимое и кликаемое — .p-stone.
   Позицию камень держит через transform-переменные (--p-shift-*), а не через
   inline-transform: анимация появления, мобильный коллапс и центрирование
   пишут РАЗНЫЕ части одного и того же свойства, и любая из них, поставленная
   целиком, стирала остальные. */
/* ── Свой масштаб плеера ────────────────────────────────
   Камень, шторка и окно выбора альбома целиком построены на
   calc(Npx * var(--ui-scale)), поэтому отдельный размер им даёт одна строка:
   внутри этих трёх поддеревьев --ui-scale = базовый масштаб экрана, умноженный
   на пользовательский множитель. Ставит его JS (кнопка со стрелками в правом
   углу плеера), предел
   считает там же по ширине окна — камень не должен вылезти за экран, а текст
   опуститься ниже читаемого. Шторка и выбор альбома живут отдельными узлами,
   не внутри .player, поэтому перечислены явно. */
#player,#playerDrawer,#pdPicker{--ui-scale:calc(var(--ui-base) * var(--pl-scale,1))}

.player{position:fixed;inset:0;z-index:var(--z-player);pointer-events:none}
.p-stone{position:absolute;left:50%;bottom:calc(var(--p-view-bottom,0px) + 18px * var(--ui-scale) + env(safe-area-inset-bottom,0px));
  width:min(calc(100vw - 24px),calc(980px * var(--ui-scale)));
  max-width:calc(var(--p-view-width,100vw) - 12px);
  pointer-events:auto;
  --p-shift-x:-50%; --p-shift-y:0%; --p-par:0px;
  /* Последнее слагаемое — возврат камня на место. Отдельный член цепочки, а не
     подмена left/top: те при доке просто снимаются (inline-координаты исчезают),
     и камень перескакивал в исходную точку рывком — анимировать было нечего.
     Здесь JS замеряет разницу между «где стоял» и «куда встал», ставит её сюда
     и следующим кадром гонит в ноль (приём FLIP). */
  --p-ret-x:0px; --p-ret-y:0px;
  transform:translate(var(--p-shift-x),var(--p-shift-y)) translateY(var(--p-par))
            translate(var(--p-ret-x),var(--p-ret-y))
            translate(var(--p-morph-x,0px),var(--p-morph-y,0px))
            scale(var(--p-morph-sx,1),var(--p-morph-sy,1));
  transition:transform .4s cubic-bezier(.4,0,.2,1);
  /* Непрозрачность ФОНА камня — по ползунку (--pl-bg-a); контент внутри плотный.
     Размытие тоже слабеет вместе с альфой — иначе матовый фон не давал уйти в
     глубокую прозрачность даже при альфе 0. */
  background:linear-gradient(180deg,rgba(14,16,26,calc(.96*var(--pl-bg-a,1))),rgba(10,12,20,calc(.975*var(--pl-bg-a,1))));
  /* Здесь backdrop-filter был самым вредным на сайте: поверхность камня лежит
     ровно над областью, где каждый кадр перерисовывается ореол, и мобильный GPU
     обязан был перечитывать эту подложку заново на каждом кадре свечения. При
     положении ползунка по умолчанию (--pl-bg-a: 1) фон камня и без того
     непрозрачен на 96 %, то есть размытие не было видно вовсе.
     Когда владелец уводит ползунок в прозрачность, камень теперь честно
     просвечивает, без матового стекла — это его выбор и его настройка. */
  border:1px solid rgba(255,255,255,calc(.08*var(--pl-bg-a,1) + .02));
  border-top:1px solid rgba(227,176,102,.35);
  /* прямоугольник с мягкими скруглениями — форма постоянна и не колышется */
  border-radius:calc(20px * var(--ui-scale));
  box-shadow:0 4px 24px rgba(0,0,0,.5)}
/* Тень текста наследуется всем содержимым камня, шторки и списка альбомов:
   при полностью прозрачном фоне и на светлых участках страницы буквы остаются
   читаемыми. Плотный многослойный ореол (тёмный контур + мягкая тень) держит
   контраст на любом фоне. На иконки (SVG) не влияет, на золотой кнопке play
   тёмная тень незаметна. */
.p-stone,.player-drawer,.pd-picker{
  text-shadow:0 0 2px rgba(0,0,0,.95),0 1px 4px rgba(0,0,0,.9),
              0 0 8px rgba(0,0,0,.65),0 2px 10px rgba(0,0,0,.55)}
.player.active .p-stone{--p-shift-y:0%}
/* камень перенесён руками: центрирование выключено, координаты ставит JS */
.player.p-placed .p-stone{--p-shift-x:0px;bottom:auto;right:auto}
/* пока тащат — никаких доводок: камень обязан стоять под пальцем */
.player.p-moving .p-stone{transition:none}
/* Возврат «на место»: ход длиннее обычной доводки и с затуханием в конце —
   камень не щёлкает в исходную точку, а доплывает до неё.
   .p-home-jump — кадр установки стартового смещения, транзишен на нём выключен,
   иначе браузер склеит установку и обнуление в одно движение из ниоткуда.
   Специфичность .player.p-homing .p-stone (0,3,0) совпадает с p-moving, но
   одновременно эти состояния не встречаются: тащат — значит не возвращают. */
.player.p-homing .p-stone{transition:transform .62s cubic-bezier(.22,1,.36,1)}
.player.p-home-jump .p-stone{transition:none}
.player.p-morphing .p-stone{transition:transform .72s cubic-bezier(.19,1,.25,1)}
.player.p-morph-jump .p-stone{transition:none!important}
.player-inner{display:grid;
  grid-template-columns:minmax(0,auto) auto auto minmax(calc(150px * var(--ui-scale)),1fr) auto;
  grid-template-areas:'left drag controls progress vol';
  align-items:center;gap:calc(14px * var(--ui-scale));
  padding:calc(11px * var(--ui-scale)) calc(26px * var(--ui-scale))}
.p-left{grid-area:left;display:flex;align-items:center;gap:calc(12px * var(--ui-scale));min-width:0}
.p-cover{width:calc(46px * var(--ui-scale));height:calc(46px * var(--ui-scale));border-radius:calc(9px * var(--ui-scale));flex:0 0 auto;background-size:cover;
  background-position:center;background-color:#11131f;border:1px solid var(--line);
  box-shadow:0 3px 12px -5px rgba(0,0,0,.7)}
/* Ширину блока считает player.js по длине названия альбома (sizeInfoBlock):
   рамок у блока нет, и «пустое место справа от короткого названия» читалось
   как дыра в камне. */
.player-info{display:flex;flex-direction:column;min-width:0;overflow:hidden;flex:0 1 auto}
.player-album{font-size:max(10px,calc(10.5px * var(--ui-scale)));color:var(--gold2);letter-spacing:.14em;text-transform:uppercase;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:calc(2px * var(--ui-scale));
  font-family:var(--sans)}
/* Обёртка режет, строка ездит. text-overflow:ellipsis тут не нужен: длинную
   строку показывает прокрутка, а многоточие обрезало бы её на первом же кадре. */
.player-track-wrap{display:block;overflow:hidden;width:100%}
.player-track{font-family:var(--serif);font-size:calc(16px * var(--ui-scale));font-weight:600;
  white-space:nowrap;display:inline-block;will-change:transform;color:var(--ink)}

/* ── зона переноса камня ─────────────────────────────── */
.p-drag-zone{grid-area:drag;display:flex;align-items:center;justify-content:center;gap:3px;
  width:calc(24px * var(--ui-scale));height:calc(34px * var(--ui-scale));
  cursor:grab;touch-action:none;border-radius:calc(6px * var(--ui-scale));
  transition:background .2s}
.p-drag-zone::before,.p-drag-zone::after{content:'';width:2px;height:calc(16px * var(--ui-scale));
  background:rgba(255,255,255,.15);border-radius:1px;transition:background .2s}
.p-drag-zone:hover{background:rgba(255,255,255,.05)}
.p-drag-zone:hover::before,.p-drag-zone:hover::after{background:rgba(227,176,102,.55)}
.p-drag-zone:active,.player.p-moving .p-drag-zone{cursor:grabbing}
.player.p-moving .p-drag-zone::before,.player.p-moving .p-drag-zone::after{background:var(--gold)}

.player-controls{grid-area:controls;display:flex;align-items:center;gap:calc(8px * var(--ui-scale))}
.p-btn{background:none;border:0;color:var(--muted);cursor:pointer;width:calc(36px * var(--ui-scale));height:calc(36px * var(--ui-scale));padding:0;
  display:flex;align-items:center;justify-content:center;border-radius:50%;
  transition:color .2s,background .2s,transform .15s;position:relative}
.p-btn svg{width:calc(20px * var(--ui-scale));height:calc(20px * var(--ui-scale));display:block}
@media(hover:hover){.p-btn:hover{color:var(--gold2);background:rgba(255,255,255,.07)}}
.p-btn.p-pressed{transform:scale(.92)}
.p-btn.p-play{width:calc(44px * var(--ui-scale));height:calc(44px * var(--ui-scale));color:#120a02;
  background:linear-gradient(135deg,var(--gold2),var(--gold));
  box-shadow:0 calc(6px * var(--ui-scale)) calc(18px * var(--ui-scale)) calc(-6px * var(--ui-scale)) rgba(227,176,102,.6)}
.p-btn.p-play svg{width:calc(22px * var(--ui-scale));height:calc(22px * var(--ui-scale))}
@media(hover:hover){.p-btn.p-play:hover{color:#120a02;background:linear-gradient(135deg,#fff7e8,var(--gold2));transform:scale(1.05)}}
.p-btn.p-shuffle.active,.p-btn.p-repeat.active{color:var(--gold)}
.p-btn.p-shuffle.active::after,.p-btn.p-repeat.active::after{content:'';position:absolute;bottom:calc(2px * var(--ui-scale));
  left:50%;transform:translateX(-50%);width:calc(4px * var(--ui-scale));height:calc(4px * var(--ui-scale));border-radius:50%;background:var(--gold)}
.p-btn.p-loading{pointer-events:none}
.p-btn .spin{animation:spin .8s linear infinite;transform-origin:center}
.player-progress{grid-area:progress;display:flex;align-items:center;gap:calc(10px * var(--ui-scale));min-width:0}
.p-time{font-size:max(11px,calc(11px * var(--ui-scale)));color:var(--faint);font-variant-numeric:tabular-nums;width:calc(40px * var(--ui-scale));
  text-align:center;flex:0 0 auto;letter-spacing:.02em}
/* Спектр и ползунок громкости делят одно место: один сворачивается по
   вертикали, второй разворачивается. Стопка нужна, чтобы при переключении
   ничего не прыгало — оба элемента лежат друг на друге. */
.p-prog-stack{position:relative;flex:1;min-width:0;display:flex;align-items:center}
.p-eq{flex:1;min-width:0;cursor:pointer;position:relative;padding:calc(4px * var(--ui-scale)) 0}
.p-eq-time{display:flex;justify-content:space-between;padding:0 2px;margin-bottom:calc(3px * var(--ui-scale));
  font-size:max(10px,calc(9px * var(--ui-scale)));color:var(--faint);font-variant-numeric:tabular-nums;pointer-events:none}
.p-eq-bars{display:flex;align-items:flex-end;gap:2px;height:calc(24px * var(--ui-scale));position:relative}
/* Полоска тянется вверх масштабом, а не высотой. Спектр обновляется каждый
   кадр, и запись height сорока полоскам заставляла браузер пересчитывать
   раскладку всей шкалы 60 раз в секунду; scaleY меняет только композицию —
   на телефоне это разница между рваной и ровной анимацией. */
.eq-bar{flex:1;background:rgba(255,255,255,.08);border-radius:1px;height:100%;
  transform:scaleY(.04);transform-origin:bottom;transition:transform .06s ease}
.eq-bar.filled{background:linear-gradient(180deg,var(--gold2),var(--ember))}
.eq-bar.buffered{background:rgba(255,255,255,.06)}
.eq-bar.active{background:var(--gold);transition:background .15s}
/* Ползунок ездит трансформом, а не left. Ширина у него во всю шкалу, точку
   рисует ::after у левого края — поэтому translateX в процентах отмеряет долю
   ТРЕКА, а не собственные четыре пикселя, и позиция считается без единого
   замера ширины из JS. */
.p-eq-thumb{position:absolute;left:0;bottom:0;width:100%;height:100%;pointer-events:none;z-index:2;
  transform:translateX(var(--eq-pos,0%))}
.p-eq-thumb::after{content:'';position:absolute;top:50%;left:0;transform:translate(-50%,-50%);width:14px;height:14px;border-radius:50%;background:var(--gold2);box-shadow:0 0 7px rgba(227,176,102,.5)}
.p-eq{padding:calc(6px * var(--ui-scale)) 0;touch-action:none;
  transition:opacity .3s,transform .3s;transform-origin:center}
.p-eq.grabbing{cursor:grabbing}

/* ── режим размера: третье состояние того же места ───────
   Шкала трека уступает место ползунку, который тянет за собой весь плеер и его
   шторку. Буквы «A» по краям показывают, куда мельче, куда крупнее, — подписи
   в проценты никому ничего не говорят. */
.p-size{position:absolute;left:0;right:0;top:50%;transform:translateY(-50%) scaleY(0);
  display:flex;align-items:center;gap:calc(8px * var(--ui-scale));
  opacity:0;pointer-events:none;transform-origin:center;
  transition:opacity .3s,transform .3s}
.p-size-mode .p-size{opacity:1;transform:translateY(-50%) scaleY(1);pointer-events:auto}
.p-size-mode .p-eq{opacity:0;transform:scaleY(0);pointer-events:none}
.p-size-cap{color:var(--gold2);font-family:var(--serif);font-weight:600;line-height:1;
  flex:0 0 auto;font-size:max(10px,calc(11px * var(--ui-scale)))}
.p-size-cap-lg{font-size:max(15px,calc(18px * var(--ui-scale)))}
.p-size input[type=range]{flex:1;min-width:0;height:calc(5px * var(--ui-scale));
  -webkit-appearance:none;appearance:none;background:rgba(255,255,255,.13);
  border-radius:calc(5px * var(--ui-scale));cursor:pointer;touch-action:none;outline:none}
.p-size input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
  width:calc(14px * var(--ui-scale));height:calc(14px * var(--ui-scale));border-radius:50%;
  background:var(--gold2);box-shadow:0 0 calc(7px * var(--ui-scale)) rgba(227,176,102,.5);cursor:grab}
.p-size input[type=range]::-moz-range-thumb{width:calc(14px * var(--ui-scale));height:calc(14px * var(--ui-scale));
  border:0;border-radius:50%;background:var(--gold2);box-shadow:0 0 calc(7px * var(--ui-scale)) rgba(227,176,102,.5);cursor:grab}
.p-size input[type=range]:focus-visible{box-shadow:0 0 0 2px rgba(227,176,102,.45)}
.p-size-ok{flex:0 0 auto;border:0;background:none;cursor:pointer;color:var(--gold);
  font-size:max(13px,calc(14px * var(--ui-scale)));line-height:1;padding:0 calc(2px * var(--ui-scale))}
.p-size-ok:hover{color:var(--gold2)}
/* Регулировать нечего — экран уже не даёт запаса ни вверх, ни вниз. */
.p-size.p-size-locked input[type=range]{opacity:.35;pointer-events:none}

/* ── громкость: две кнопки в столбик ─────────────────── */
/* Правый угол: слева столбик из двух кнопок (динамик-mute и стрелки размера
   плеера), справа от них — шкала громкости во всю их высоту. */
.p-right{grid-area:vol;display:flex;flex-direction:row;align-items:center;
  gap:calc(9px * var(--ui-scale));justify-content:center}
.p-vol-btns{display:flex;flex-direction:column;align-items:center;
  gap:calc(2px * var(--ui-scale));justify-content:center}

/* ── столбик громкости справа от кнопок ─────────────────
   Показывает уровень постоянно, без разворачивания ползунка на месте шкалы
   трека. Тонкая полоска сама по себе была бы неудобной мишенью, поэтому у неё
   невидимый запас по краям (::before, inset -7px) — курсор ловится задолго до
   попадания в саму линию, а колесо работает над всем правым углом. */
.p-vol-scale{position:relative;width:calc(4px * var(--ui-scale));
  height:calc(46px * var(--ui-scale));border-radius:calc(4px * var(--ui-scale));
  background:rgba(255,255,255,.13);cursor:pointer;flex:0 0 auto;touch-action:none;
  transition:width .18s ease,box-shadow .25s ease,background .25s ease}
.p-vol-scale::before{content:'';position:absolute;inset:calc(-7px * var(--ui-scale));border-radius:inherit}
.p-vol-scale-fill{position:absolute;left:0;right:0;bottom:0;top:0;border-radius:inherit;
  background:linear-gradient(180deg,var(--gold2),var(--gold) 55%,var(--ember));
  box-shadow:0 0 calc(6px * var(--ui-scale)) rgba(227,176,102,.45);
  transform:scaleY(var(--vol-y,.7));transform-origin:bottom;
  transition:transform .12s ease}
.p-vol-scale:hover,.p-vol-scale.dragging{width:calc(7px * var(--ui-scale));
  background:rgba(255,255,255,.2);box-shadow:0 0 calc(12px * var(--ui-scale)) -2px rgba(227,176,102,.5)}
.p-vol-scale:focus-visible{outline:none;box-shadow:0 0 0 2px rgba(227,176,102,.45)}
/* Звук выключен — столбик гаснет до контура, как и кнопка «без звука». */
.p-vol-scale.muted .p-vol-scale-fill{background:var(--faint);box-shadow:none;opacity:.6}
.p-btn.p-vol{width:auto;min-width:calc(38px * var(--ui-scale));height:calc(26px * var(--ui-scale));
  border-radius:calc(13px * var(--ui-scale));gap:1px;padding:0 calc(4px * var(--ui-scale))}
.p-btn.p-vol svg{width:calc(15px * var(--ui-scale));height:calc(15px * var(--ui-scale))}
/* Кнопка размера плеера: геометрия — точь-в-точь как у динамика сверху,
   а иконка-стрелки растянута на ширину всей его композиции «динамик + волны»
   (~47px), чтобы пара кнопок смотрелась цельно, а не иконками разной длины. */
.p-btn.p-resize{width:auto;min-width:calc(38px * var(--ui-scale));height:calc(26px * var(--ui-scale));
  border-radius:calc(13px * var(--ui-scale));padding:0 calc(4px * var(--ui-scale))}
.p-btn.p-resize svg{width:calc(47px * var(--ui-scale));height:calc(15px * var(--ui-scale))}
/* Анимация стрелок: каждая стрелка — отдельная группа (.rz-a/.rz-b). Стрелки
   сходятся остриями почти вплотную к центру → плавно переворачиваются на 180°
   по горизонтали (scaleX через ноль — в «ребро», смена цвета приходится на этот
   момент и читается как часть переворота) → в отзеркаленном виде расходятся до
   исходного размаха → обратный переворот. Цвет на каждом перевороте мигает
   золотом, в остальное время — обычный цвет кнопки: hover и .active-подсветка
   работают как раньше (currentColor). translateX в px = единицы viewBox,
   а fill-box держит переворот вокруг центра самой стрелки, не всего svg. */
.p-btn.p-resize svg g{transform-box:fill-box;transform-origin:center;
  animation:rz-squeeze-l 5.6s ease-in-out infinite}
.p-btn.p-resize svg .rz-b{animation-name:rz-squeeze-r}
@keyframes rz-squeeze-l{
  0%,6%{transform:translateX(0) scaleX(1);stroke:currentColor}
  36%{transform:translateX(7px) scaleX(1);stroke:currentColor}
  46%{transform:translateX(7px) scaleX(-1);stroke:var(--gold)}
  76%{transform:translateX(0) scaleX(-1);stroke:var(--gold)}
  86%,100%{transform:translateX(0) scaleX(1);stroke:currentColor}}
@keyframes rz-squeeze-r{
  0%,6%{transform:translateX(0) scaleX(1);stroke:currentColor}
  36%{transform:translateX(-7px) scaleX(1);stroke:currentColor}
  46%{transform:translateX(-7px) scaleX(-1);stroke:var(--gold)}
  76%{transform:translateX(0) scaleX(-1);stroke:var(--gold)}
  86%,100%{transform:translateX(0) scaleX(1);stroke:currentColor}}
.p-btn.p-resize.active{color:var(--gold)}
/* Иконка «развернуть плеер» — уголки живут тем же циклом, что стрелки
   размера: сжатие к центру, переворот через ребро (в этот момент вспышка
   золотом), расхождение обратно. Имена кадров свои: rz-* глобально
   уникальны, две пары не должны перебивать друг друга. */
.p-btn.p-expand svg g{transform-box:fill-box;transform-origin:center;
  animation:rz-corner-l 5.6s ease-in-out infinite}
.p-btn.p-expand svg .rz-b{animation-name:rz-corner-r}
@keyframes rz-corner-l{
  0%,6%{transform:translateX(0) scaleX(1);stroke:currentColor}
  36%{transform:translateX(9px) scaleX(1);stroke:currentColor}
  46%{transform:translateX(9px) scaleX(-1);stroke:var(--gold)}
  76%{transform:translateX(0) scaleX(-1);stroke:var(--gold)}
  86%,100%{transform:translateX(0) scaleX(1);stroke:currentColor}}
@keyframes rz-corner-r{
  0%,6%{transform:translateX(0) scaleX(1);stroke:currentColor}
  36%{transform:translateX(-9px) scaleX(1);stroke:currentColor}
  46%{transform:translateX(-9px) scaleX(-1);stroke:var(--gold)}
  76%{transform:translateX(0) scaleX(-1);stroke:var(--gold)}
  86%,100%{transform:translateX(0) scaleX(1);stroke:currentColor}}
/* Звук выключен (верхняя кнопка — mute): гаснет в цвет «без звука», как и
   столбик справа; волны замирают почти прозрачными. */
.p-btn.p-vol.muted{color:var(--ember)}
.p-btn.p-vol.muted .p-sound-wave .wave{opacity:.15;animation:none}
.p-spk{display:flex;align-items:center}
/* Дуги-скобки: каждая правее и крупнее предыдущей. Полукруг справа (радиус
   100%/50%) вместо прежней четвертинки — дуги гнутые, а не «палки». */
.p-sound-wave{display:flex;align-items:center;gap:calc(1.5px * var(--ui-scale));height:100%}
.p-sound-wave .wave{width:calc(5px * var(--ui-scale));border-right:1.7px solid currentColor;
  border-radius:0 100% 100% 0 / 0 50% 50% 0;
  height:var(--h);opacity:0;transition:opacity .25s,height .12s ease}
/* Пауза/нет анализа — знакомый мягкий пульс (как раньше). */
.p-sound-wave .wave.active{opacity:.5;animation:sound-pulse 1.2s ease-in-out infinite;
  animation-delay:var(--d)}
/* Играет — тот же пульс, но ведёт его музыка, а не таймер.
   Раньше здесь стояла глухая opacity:.82 и animation:none: дыхание пропадало
   совсем, оставалась только высота дуги — тонкую полоску глаз не ловит, и
   динамик читался неподвижным набором скобок. Теперь спектр правит и высоту,
   и прозрачность (--a от JS) в том же диапазоне, что у sound-pulse, поэтому
   характер анимации в тишине и под музыку один и тот же. */
.p-sound-wave.p-sw-live .wave.active{animation:none;opacity:var(--a,.6);
  transition:opacity .08s linear,height .08s linear}
@keyframes sound-pulse{0%,100%{opacity:.4}50%{opacity:1}}
/* элементы мобильного коллапса: вне свёрнутого состояния скрыты ВЕЗДЕ,
   иначе span/div без grid-area ломают сетку плеера на десктопе */
.p-track-num{display:none}
.p-drag-handle{display:none}
/* ── инлайн-иконки (меню, кнопки) ─────────────────────── */
.nav-links a svg{width:calc(15px * var(--ui-scale));height:calc(15px * var(--ui-scale));vertical-align:calc(-3px * var(--ui-scale));margin-right:calc(5px * var(--ui-scale))}
.btn svg{width:calc(18px * var(--ui-scale));height:calc(18px * var(--ui-scale));vertical-align:calc(-3px * var(--ui-scale));margin-right:calc(8px * var(--ui-scale))}
.track.playing{background:rgba(227,176,102,.08);border-color:rgba(227,176,102,.3)}
.track.playing .play{color:var(--gold)}
.track .play{pointer-events:auto}
.track .play.muted{color:var(--faint);cursor:default;opacity:.4}
.track .play.muted:hover{background:none}
.track .ti{cursor:pointer}.track .play{cursor:pointer}
.track .dlwrap{display:flex;gap:calc(6px * var(--ui-scale));flex:0 0 auto}
.track .dl{font-size:max(11px,calc(11px * var(--ui-scale)));font-weight:700;letter-spacing:.04em;color:var(--faint);
  border:1px solid var(--line);border-radius:calc(6px * var(--ui-scale));padding:calc(3px * var(--ui-scale)) calc(7px * var(--ui-scale));cursor:pointer;transition:.2s}
.track .dl:hover{color:var(--gold2);border-color:var(--gold);background:rgba(227,176,102,.06)}
/* album-player removed — replaced by eq-wave in albums.html */

@media(max-width:767px){
  .p-stone{width:calc(100vw - 12px);bottom:calc(10px + env(safe-area-inset-bottom,0px))}
  .player-inner{grid-template-columns:minmax(0,1fr) auto auto;
    grid-template-areas:'left drag controls' 'progress progress progress';
    gap:6px 8px;padding:8px 12px}
  /* Громкости на телефоне нет: ею управляют аппаратными клавишами, а место
     в камне дороже. Вместе с ней уходит и переключатель режима — режим
     громкости на узком экране включить нечем, поэтому спектр всегда виден. */
  .p-right{display:none}
  .player-progress{width:100%}
  .player-controls{gap:4px}
  .p-left{align-items:center}
  /* Ширину блока на телефоне не считаем — инлайн-стиль от sizeInfoBlock() тут
     переполнил бы сетку, поэтому перебиваем его. */
  .player-info{width:auto !important}
  .p-cover{width:calc(40px * var(--ui-scale));height:calc(40px * var(--ui-scale))}
  .player-track{font-size:calc(14px * var(--ui-scale))}
  .p-eq-time{display:none}
  .p-drag-zone{height:calc(30px * var(--ui-scale))}
}
@media(max-width:479px){
  .player-inner{padding:7px 11px;gap:6px 8px}
  .p-cover{width:calc(38px * var(--ui-scale));height:calc(38px * var(--ui-scale));border-radius:calc(8px * var(--ui-scale))}
  .p-time{width:calc(34px * var(--ui-scale));font-size:calc(10px * var(--ui-scale))}
  .p-btn{width:calc(34px * var(--ui-scale));height:calc(34px * var(--ui-scale))}
  .p-btn.p-play{width:calc(42px * var(--ui-scale));height:calc(42px * var(--ui-scale))}
}

/* ── кнопка оглавления библии (только мобильные) ─────── */
.toc-btn{display:none;align-items:center;gap:calc(8px * var(--ui-scale));margin-top:calc(14px * var(--ui-scale));
  padding:calc(9px * var(--ui-scale)) calc(16px * var(--ui-scale));border-radius:calc(30px * var(--ui-scale));font-size:max(11px,calc(13px * var(--ui-scale)));font-weight:600;cursor:pointer;
  background:rgba(255,255,255,.06);border:1px solid var(--line);color:var(--gold2)}
.toc-btn:active{background:var(--gold);color:#0a0a0a}

/* ── футер ──────────────────────────────────────────── */
footer{border-top:1px solid var(--line);padding:calc(60px * var(--ui-scale)) 0 calc(50px * var(--ui-scale));margin-top:calc(60px * var(--ui-scale));text-align:center}
/* Подвал лежит ПОСЛЕ #page-root, поэтому запас под плеер с его ручкой ему
   нужен свой: на /albums (375px) кнопка «Читать Мастер-Библию» стоит в
   подвале и в самом низу прокрутки упиралась в ручку ящика — тап по центру
   кнопки открывал плейлист вместо Библии. */
body.player-active footer{padding-bottom:calc(50px * var(--ui-scale) + var(--player-h) + var(--player-handle-h))}
footer .fsigil{width:calc(50px * var(--ui-scale));height:calc(50px * var(--ui-scale));margin:0 auto calc(18px * var(--ui-scale))}
footer .fbrand-img{height:calc(38px*var(--ui-scale));width:auto;display:block;margin:calc(6px*var(--ui-scale)) auto 0}
footer .fbrand b{color:var(--gold)}
footer .fsl{font-family:var(--serif);font-style:italic;color:var(--gold2);letter-spacing:.16em;margin-top:calc(4px * var(--ui-scale))}
footer .stats{display:flex;justify-content:center;gap:calc(46px * var(--ui-scale));margin:calc(30px * var(--ui-scale)) 0;flex-wrap:wrap}
footer .stat .n{font-family:var(--serif);font-size:calc(40px * var(--ui-scale));color:var(--gold2)}
footer .stat .l{font-size:max(11px,calc(12px * var(--ui-scale)));letter-spacing:.2em;color:var(--faint);text-transform:uppercase}
footer .small{color:var(--faint);font-size:max(11px,calc(13px * var(--ui-scale)));margin-top:calc(14px * var(--ui-scale))}

/* ── адаптив ────────────────────────────────────────── */
@media(max-width:1023px){:root{--ui-base:.88}}
@media(max-width:767px){:root{--ui-base:.78}}
@media(max-width:479px){:root{--ui-base:.72}}
@media(max-width:360px){:root{--ui-base:.65}}
@media(min-width:1800px){:root{--ui-base:1.15}}
@media(min-width:2400px){:root{--ui-base:1.3}}

@keyframes mnFade{0%{opacity:0}100%{opacity:1}}

/* ── M (768–1023): компактная шапка. Планшет вертикально и телефон в
   ландшафте: капли-меню ещё помещаются, но впритык — держим их компактными
   и не даём панели уходить за правый край. */
@media(max-width:1023px){
  header.nav{padding:calc(8px * var(--ui-scale)) calc(10px * var(--ui-scale))}

  /* drops как на десктопе, компактнее */
  .drops{gap:calc(4px * var(--ui-scale));flex:1;min-width:0}
  .drop-btn{padding:calc(6px * var(--ui-scale)) calc(10px * var(--ui-scale));font-size:max(11px,calc(11px * var(--ui-scale)))}
  .drop-btn::after{display:none!important}
  .drop-btn .drop-label{max-width:calc(92px * var(--ui-scale))}
  .drop-btn .drop-arrow{width:calc(8px * var(--ui-scale));height:calc(8px * var(--ui-scale))}
  .drop-panel{min-width:calc(160px * var(--ui-scale))}
  .drop.drop-auth .drop-panel{left:auto;right:0}
}

/* ── S/XS (≤767px): перестроение колонок и шторок ───── */
@media(max-width:767px){
  .triptych,.epoch-grid{grid-template-columns:1fr;gap:16px}
  .q-band{padding:calc(84px * var(--ui-scale)) 0}
  .sec{padding:calc(72px * var(--ui-scale)) 0}
  .sec h2{font-size:clamp(30px,8vw,calc(44px * var(--ui-scale)))}

  /* шторки выезжают снизу, на весь экран */
  .sheet,.sheet.wide{width:100%;border-left:0;border-top:1px solid var(--line);
    transform:translateY(60px)}
  .overlay.open .sheet{transform:translateY(0)}
  .sheet-head{padding:calc(22px * var(--ui-scale)) calc(20px * var(--ui-scale)) calc(16px * var(--ui-scale))}
  .sheet-head h3{font-size:calc(28px * var(--ui-scale));max-width:80%}
  .sheet-body{padding:calc(14px * var(--ui-scale)) calc(18px * var(--ui-scale)) calc(60px * var(--ui-scale))}

  /* мастер-библия: оглавление как выезжающая панель */
  .toc-btn{display:inline-flex}
  .bible-layout{flex-direction:column;position:relative}
  .toc{position:absolute;inset:0;width:100%;max-height:none;z-index:3;
    background:var(--bg2);border-right:0;border-bottom:0;padding:16px 14px 60px;
    transform:translateX(-102%);transition:transform .3s}
  .bible-layout.toc-open .toc{transform:translateX(0)}
  .toc a{padding:calc(13px * var(--ui-scale)) calc(14px * var(--ui-scale));font-size:max(11px,calc(15px * var(--ui-scale)))}
  .bible-read{padding:calc(24px * var(--ui-scale)) calc(20px * var(--ui-scale)) calc(90px * var(--ui-scale));font-size:max(11px,calc(16px * var(--ui-scale)))}
  .bible-read .b-h1{font-size:calc(30px * var(--ui-scale))}.bible-read .b-h2{font-size:calc(23px * var(--ui-scale))}.bible-read .b-h3{font-size:calc(20px * var(--ui-scale))}
  .bible-read blockquote{font-size:calc(18px * var(--ui-scale));padding:calc(8px * var(--ui-scale)) calc(16px * var(--ui-scale))}
}
@media(max-width:767px){
  .wrap{padding:0 16px}
  .hero{padding:calc(100px * var(--ui-scale)) 18px calc(70px * var(--ui-scale))}
  .albums{grid-template-columns:1fr;gap:calc(16px * var(--ui-scale))}
  .card.ep{grid-column:1/-1;max-width:100%}
  .card .num{font-size:calc(46px * var(--ui-scale))}
  .card .meta .t{font-size:calc(21px * var(--ui-scale))}
  footer .stats{gap:calc(24px * var(--ui-scale))}
  footer .stat .n{font-size:calc(34px * var(--ui-scale))}
  .lyrics{font-size:max(11px,calc(16px * var(--ui-scale)));line-height:1.8}
  .track{padding:calc(14px * var(--ui-scale)) calc(8px * var(--ui-scale));gap:calc(12px * var(--ui-scale))}
  .track .tn{width:calc(26px * var(--ui-scale));font-size:calc(18px * var(--ui-scale))}
  /* iOS: нижняя «плексора»-полоска не должна съедать низ подвала. */
  footer{padding-bottom:calc(50px * var(--ui-scale) + env(safe-area-inset-bottom,0px))}
}
@media(max-width:479px){
  .hero .cta{flex-direction:column;width:100%;max-width:calc(300px * var(--ui-scale))}
  .hero .cta .btn{width:100%;justify-content:center}
}

/* ── touch-цели (сенсорные экраны) ───────────────── */
@media(hover:none) and (pointer:coarse), (pointer:coarse){
  .track{padding:calc(16px * var(--ui-scale)) calc(10px * var(--ui-scale))}
  .track .play{width:38px;height:38px;min-width:38px;min-height:38px}
  .track .play svg{width:20px;height:20px}
  .track .dl{padding:calc(8px * var(--ui-scale)) calc(12px * var(--ui-scale));min-width:38px;min-height:38px;display:inline-flex;align-items:center;justify-content:center}
  .card .meta{padding:0 calc(14px * var(--ui-scale)) calc(14px * var(--ui-scale))}
  .drop-btn{padding:calc(10px * var(--ui-scale)) calc(16px * var(--ui-scale))}
  .btn{padding:calc(16px * var(--ui-scale)) calc(28px * var(--ui-scale));min-height:48px}
  .chapter-item{padding:calc(14px * var(--ui-scale)) 10px}
  .h-btn{min-width:44px;min-height:44px;display:inline-flex;align-items:center;justify-content:center}
  .nav-arrow{width:44px;height:44px}
  .p-btn{width:44px;height:44px;min-width:44px;min-height:44px}
  .p-btn.p-play{width:52px;height:52px}
  .p-btn svg{width:22px;height:22px}
  .track .dl,.pd-track .dl{min-width:44px;min-height:40px;display:inline-flex;align-items:center;justify-content:center}
  .pd-track{padding:calc(10px * var(--ui-scale)) calc(8px * var(--ui-scale));min-height:48px}
  .pd-album-one{min-height:48px}
  .pd-album-btn{min-height:48px}
  .track:active{background:rgba(227,176,102,.12)}
  .track .dl:active,.pd-track .dl:active{background:rgba(227,176,102,.15)}
  .track .play:active{background:rgba(227,176,102,.2);color:var(--gold2)}
  .pd-album-btn:active{background:rgba(227,176,102,.2)}
  .slide-btn,.bm-popup-add{padding:calc(14px * var(--ui-scale)) 0;min-height:48px}
}

/* таблицы: горизонтальный скролл */
.b-table{overflow-x:auto;display:block;max-width:100%}

/* Мерцание значков ✦ «Скоро». Кадры жили во внутреннем <style> партиала
   albums.html, а нужны они и ящику плеера (.pd-soon-icon/.pd-soon-badge) —
   тот открывается на любой странице. Роутер удаляет style[data-partial] при
   переходе, поэтому вне /albums значки застывали. Место кадров — здесь. */
@keyframes pulse-glow{
  0%,100%{opacity:.6;transform:scale(1)}
  50%{opacity:1;transform:scale(1.1)}
}

/* ── микро-анимации плеера ────────────────────────────
   Имя своё: раньше эти кадры назывались breathe и перебивали одноимённые
   кадры меню — кнопки шапки молча дышали анимацией плеера. Теперь правка
   одной анимации не задевает другую. */
/* Пульс играющей кнопки разложен по слоям, как у капель меню и пилюль
   альбомов (album-breathe/album-glow). Прежние кадры анимировали на самой
   кнопке box-shadow И filter:brightness — обе величины некомпозитные: каждый
   кадр перекрашивал кнопку вместе с размытой тенью под ней, все 8 секунд,
   что звучит трек. Теперь кнопка дышит только масштабом (композитор),
   а разницу свечения покоя (тень .6) и пика (тень .9 + ореол 12px) несёт
   слой ::before с ПОСТОЯННОЙ тенью максимальной яркости — пульс ведёт
   его opacity. brightness(1.15) ушёл вместе с перекраской: пик теперь
   читается ореолом вокруг, а не подкраской самой кнопки — на глаз то же
   разгорание, без единого repaint за цикл. */
@keyframes pulse-play{
  0%,100%{transform:scale(1)}
  45%,55%{transform:scale(1.08)}
}
@keyframes play-ring{
  0%,100%{opacity:0}
  45%,55%{opacity:1}
}
.p-btn.p-play.playing{animation:pulse-play 8s cubic-bezier(0.4,0,0.2,1) infinite}
.p-btn.p-play.playing::before{content:'';position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;
  box-shadow:0 8px 24px -4px rgba(227,176,102,.9),0 0 12px 4px rgba(227,176,102,.3);
  opacity:0;animation:play-ring 8s cubic-bezier(0.4,0,0.2,1) infinite}

/* ── дыхание граней плеера ────────────────────────────
   Дышит ТОЛЬКО кромка: отдельное кольцо поверх камня, которое разгорается и
   гаснет. Ни форма, ни размер, ни содержимое не трогаются — масштаб или
   filter на самом камне тянул бы за собой всю начинку (текст, обложку,
   спектр), а размер камня обязан быть постоянным: под ним стоит контент
   страницы, отступ которого считается по --player-h. */
/* Амплитуда взята с кнопок меню (nav-breathe): прежняя — opacity .35→1 на
   однопиксельной кромке с тенью в минусовый spread — на глаз не читалась
   вовсе, и плеер выглядел неподвижным. Разница в том, ЧТО дышит: у кнопок
   пульсирует вся капля вместе с размером, у плеера — только кромка и её
   свечение, размер и содержимое неподвижны. */
/* Кромку и ореол камня собирает light.js через --pl-glow (0..1) и --pl-color.
   Прежние кадры edge-breathe убраны: свет теперь пересобирается каждый кадр
   из паттерна дыхания и (в эмоц. режиме) из спектра трека. transition сглаживает
   поразрядные скачки переменных между кадрами. */
/* Свет камня рисуют два ПУСТЫХ узла внутри .p-stone (разметка:
   template/_chrome_tail.html). Прежде это был ::after самого камня, но тогда
   --pl-glow приходилось писать в камень, а вместе с ним пересчитывался стиль
   всей начинки плеера — тридцать раз в секунду. Правило оставлено ровно тем
   же, сменился только носитель.

   Дороже всего стоила сама тень: её радиус и альфа собирались из --pl-glow
   на каждом кадре, то есть браузер тридцать раз в секунду ПЕРЕРИСОВЫВАЛ
   размытый ореол — самый дорогой пейнт на сайте. Теперь тени у слоёв
   ПОСТОЯННЫЕ (намертво на максимальной яркости), а за пульс отвечает
   opacity — композиторная величина: слой уже отрисован, и на каждом кадре
   меняется только его прозрачность, без пересчёта стиля начинки и без
   перекраски размытия. Цена кадра падает на порядок, вид остаётся тем же:
   кромка и ореол разгораются и гаснут. */
.p-glow{position:absolute;inset:-1px;border-radius:inherit;pointer-events:none;
  border:1.6px solid rgba(var(--pl-color,227,176,102),.84);
  /* Тень одна и ПОСТОЯННАЯ (радиус не «дышит»), пульс ведёт opacity.
     Прежние три слоя с расчётным радиусом/альфой перерисовывали размытие
     тридцать раз в секунду; широкий ореол и внутренняя подсветка переехали
     в отдельный слой .p-halo (ниже). */
  /* Базовая видимость .40 (строкой ниже): при погашенном свете (glow 0)
     кромка всё равно видна на прежнюю альфу .34 (.84 × .40) — у камня всегда
     есть тонкое золотое кольцо, дыхание лишь разгоняет его до полного. */
  box-shadow:0 0 26px rgba(var(--pl-color,227,176,102),.60);
  opacity:calc(.40 + .60*var(--pl-glow,0));
  will-change:opacity;
  transition:opacity .1s linear}
/* Широкий внешний ореол + внутренняя подсветка — второй слой света; его
   видимость дополнительно масштабирует --pl-strength (ползунок «сила
   свечения»), как прежняя третья тень. Отдельный узел нужен именно поэтому:
   у кромки силы нет, у ореола — есть, одной opacity на двоих не развести. */
.p-halo{position:absolute;inset:-1px;border-radius:inherit;pointer-events:none;
  box-shadow:
    0 0 46px rgba(var(--pl-color,227,176,102),.42),
    inset 0 0 18px rgba(var(--pl-color,227,176,102),.30);
  opacity:calc(var(--pl-glow,0)*var(--pl-strength,1));
  will-change:opacity;
  transition:opacity .1s linear}
/* Пока камень тащат, свечение мешает целиться — гасим: у кромки остаётся
   прежняя альфа .35 (.84 × .42), тени и ореол уходят в ноль. */
.player.p-moving .p-stone .p-glow{box-shadow:none;opacity:.42}
.player.p-moving .p-stone .p-halo{opacity:0}

@keyframes fade-in{from{opacity:0}to{opacity:1}}
.p-eq-bars.eq-idle .eq-bar{animation:fade-in .3s ease}
@keyframes shimmer{
  0%{opacity:.6}
  50%{opacity:1}
  100%{opacity:.6}
}
.p-eq-bars.eq-idle .eq-bar:nth-child(odd){animation:shimmer 1.2s ease-in-out infinite}
.p-eq-bars.eq-idle .eq-bar:nth-child(even){animation:shimmer 1.2s ease-in-out infinite .2s}

/* ── мини-плеер: тот же камень, класс .mini ─────────────────
   Вход — ПКМ по камню или долгий тап ~0.5 с (player.js), выход — кнопка
   «развернуть» или симметричный жест. Сетка inner перестраивается на
   «транспорт · ручка · громкость», обложка/названия/шкала трека прячутся.
   Состояние shuffle/repeat/света живёт в JS и переживает сворачивание. */
.p-expand{display:none}
.p-tab{display:none}
.p-stone.mini{width:auto;max-width:calc(100vw - 12px)}
.p-stone.mini .player-inner{
  grid-template-columns:auto auto auto;
  grid-template-areas:'controls drag vol';
  gap:calc(4px * var(--ui-scale)) calc(8px * var(--ui-scale));
  padding:calc(8px * var(--ui-scale)) calc(12px * var(--ui-scale))}
.p-stone.mini .p-left,
.p-stone.mini .player-progress,
.p-stone.mini .p-track-num,
.p-stone.mini .p-drag-handle{display:none}
.p-stone.mini .p-right{display:flex}
.p-stone.mini .p-resize{display:none}
.p-stone.mini .p-expand{display:flex}
/* Язычок шторки: маленький pill на ВЕРХНЕЙ кромке мини-камня. Hover
   выдвигает на пару пикселей — только transform/opacity (перф-закон).
   Ездит вместе со шторкой (transform пишет player.js), поэтому выше
   обёртки шторки по стеку. */
.p-stone.mini .p-tab{
  display:flex;position:absolute;top:calc(-4px * var(--ui-scale));left:50%;
  width:calc(26px * var(--ui-scale));height:calc(8px * var(--ui-scale));
  transform:translateX(-50%);border-radius:calc(4px * var(--ui-scale));
  background:rgba(227,176,102,.45);opacity:.45;cursor:pointer;border:0;padding:0;
  z-index:31;
  transition:transform .18s ease,opacity .18s ease}
.p-stone.mini .p-tab.snap{transition:transform .32s cubic-bezier(.22,1,.36,1),opacity .18s ease}
.p-stone.mini .p-tab:hover{transform:translateX(-50%) translateY(calc(-2px * var(--ui-scale)));opacity:1}
.p-stone.mini .p-tab[aria-expanded="true"]{opacity:.9}
/* Шторка-треклист: обёртка-clip над камнем (в режиме below — под ним),
   внутри неё список выезжает трансформом ровно настолько, насколько его
   вытянули за хват. Шторка по ширине самого мини-камня. Обёртка — свой
   композиторный слой: без изоляции слой шторки мерцал на телефонах над
   пульсирующим светом камня. */
.p-curtain-wrap{
  position:absolute;left:calc(-1px * var(--ui-scale));right:calc(-1px * var(--ui-scale));
  bottom:calc(100% - 1px);height:var(--pc-max,300px);
  overflow:hidden;visibility:hidden;pointer-events:none;z-index:30;
  transform:translateZ(0);
  transition:visibility 0s .26s}
.p-curtain-wrap.below{bottom:auto;top:calc(100% - 1px)}
.p-curtain-wrap.open{visibility:visible;transition:visibility 0s}
/* Хват: широкий язычок у самой кромки камня, за который тянут. Спрятан,
   пока вытянут не был; выдвигается классом grip-out. */
.p-grip{
  position:absolute;left:50%;bottom:calc(-2px * var(--ui-scale));
  width:calc(64px * var(--ui-scale));height:calc(18px * var(--ui-scale));
  transform:translate(-50%,110%);border-radius:calc(9px * var(--ui-scale));
  background:linear-gradient(180deg,rgba(20,23,36,.98),rgba(12,14,23,.98));
  border:1px solid rgba(227,176,102,.28);
  display:flex;align-items:center;justify-content:center;gap:calc(4px * var(--ui-scale));
  cursor:grab;pointer-events:auto;touch-action:none;
  transition:transform .22s ease}
.p-grip span{width:calc(14px * var(--ui-scale));height:calc(3px * var(--ui-scale));
  border-radius:2px;background:rgba(227,176,102,.5)}
.p-curtain-wrap.grip-out .p-grip{transform:translate(-50%,var(--pc-travel,0px))}
.p-grip:active{cursor:grabbing}
.p-curtain-wrap.below .p-grip{bottom:auto;top:calc(-2px * var(--ui-scale));
  transform:translate(-50%,-110%)}
.p-curtain-wrap.below.grip-out .p-grip{transform:translate(-50%,var(--pc-travel,0px))}
.p-curtain-wrap.dragging .p-grip{transition:none}
.p-curtain-wrap.snap .p-grip{transition:transform .32s cubic-bezier(.22,1,.36,1)}
.p-stone.mini.curtain-below .p-tab{top:auto;bottom:calc(-4px * var(--ui-scale))}
.p-stone.mini.curtain-open:not(.curtain-below){border-top-left-radius:0;border-top-right-radius:0}
.p-stone.mini.curtain-open.curtain-below{border-bottom-left-radius:0;border-bottom-right-radius:0}
/* Список: прижат к кромке камня (снизу в верхнем режиме, сверху в нижнем),
   выезжает translateY по мере тяги — пишет JS. */
.p-curtain{
  position:absolute;left:0;right:0;bottom:0;
  height:100%;box-sizing:border-box;pointer-events:auto;
  background:linear-gradient(180deg,rgba(14,16,26,.97),rgba(10,12,20,.98));
  border:1px solid rgba(227,176,102,.22);border-bottom:0;
  border-radius:calc(14px * var(--ui-scale)) calc(14px * var(--ui-scale)) 0 0;
  transform:translateY(100%);
  backface-visibility:hidden;will-change:transform}
.p-curtain-wrap.below .p-curtain{
  bottom:auto;top:0;border:1px solid rgba(227,176,102,.22);border-top:0;
  border-radius:0 0 calc(14px * var(--ui-scale)) calc(14px * var(--ui-scale))}
.p-curtain-wrap.dragging .p-curtain{transition:none}
.p-curtain-wrap.snap .p-curtain{transition:transform .32s cubic-bezier(.22,1,.36,1)}
@media(prefers-reduced-motion:reduce){
  .p-stone.mini .p-tab,.p-grip,.p-curtain-wrap.snap .p-curtain{transition:none}
}
.pc-scroll{height:100%;box-sizing:border-box;overflow-y:auto;overscroll-behavior:contain;touch-action:pan-y;
  padding:calc(24px * var(--ui-scale)) calc(6px * var(--ui-scale))}
.pc-album{font-family:var(--sans);font-size:max(9px,calc(10px * var(--ui-scale)));letter-spacing:.14em;
  text-transform:uppercase;color:var(--gold2);opacity:.75;
  margin:calc(10px * var(--ui-scale)) calc(8px * var(--ui-scale)) calc(4px * var(--ui-scale))}
.pc-scroll .pc-album:first-child{margin-top:calc(2px * var(--ui-scale))}
.pc-track{display:flex;align-items:baseline;gap:calc(8px * var(--ui-scale));width:100%;text-align:left;
  background:none;border:0;border-radius:calc(8px * var(--ui-scale));cursor:pointer;
  padding:calc(6px * var(--ui-scale)) calc(8px * var(--ui-scale));color:var(--muted);
  font-family:var(--serif);font-size:calc(14px * var(--ui-scale))}
.pc-track:hover{color:var(--ink);background:rgba(255,255,255,.05)}
.pc-track .pc-num{font-family:var(--sans);font-size:max(9px,calc(10px * var(--ui-scale)));color:var(--faint);
  min-width:calc(18px * var(--ui-scale));text-align:right;font-variant-numeric:tabular-nums}
.pc-track.on{color:var(--gold)}
.pc-track.on .pc-num{color:var(--gold)}
.pc-empty{padding:calc(14px * var(--ui-scale));color:var(--faint);
  font-size:calc(12px * var(--ui-scale));text-align:center}
@media(max-width:767px){
  .p-stone.mini .player-inner{padding:6px 10px}
}
@media(prefers-reduced-motion:reduce){
  .p-stone.mini .p-tab,.p-curtain{transition:none}
}

/* ── экстремально узкие экраны (<360px) ─────────── */
@media(max-width:360px){
  .drops{gap:2px}
  .drop-btn .drop-arrow{display:none}
  .drop-btn{padding:calc(5px * var(--ui-scale)) calc(8px * var(--ui-scale));font-size:10px}
  .drop-btn .drop-label{max-width:80px}
  .brand{font-size:calc(18px * var(--ui-scale))}
}

/* ── Поле-ловушка для ботов (honeypot) ─────────────────────────
   Не display:none и не hidden: продвинутые боты такие поля пропускают.
   Убираем поле за пределы экрана — человек его не видит и не может
   сфокусировать (tabindex=-1, aria-hidden), а бот-автозаполнялка видит
   обычный input name="website" и попадается. */
.hp-field{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none}

/* ── PWA: баннеры установки и обновления (pwa.js) ─────────────
   Садятся над плеером: --player-h считает player.js по факту. */
.pwa-bar{position:fixed;z-index:var(--z-overlay);
  left:50%;transform:translateX(-50%) translateY(140%);
  bottom:calc(var(--player-h,0px) + 16px + env(safe-area-inset-bottom,0px));
  width:min(calc(100vw - 32px),460px);
  display:flex;align-items:center;gap:12px;
  padding:12px 12px 12px 18px;
  /* Размытие снято: полоса и так непрозрачна на 97–98 %. */
  background:linear-gradient(180deg,rgba(20,23,36,.99),rgba(12,14,23,.99));
  border:1px solid rgba(227,176,102,.22);border-radius:12px;
  box-shadow:0 18px 50px -20px rgba(0,0,0,.9);
  opacity:0;transition:transform .35s cubic-bezier(.4,0,.2,1),opacity .3s}
.pwa-bar.show{transform:translateX(-50%) translateY(0);opacity:1}
.pwa-bar-text{flex:1;font-size:14px;font-weight:500;color:var(--ink);line-height:1.35}
.pwa-bar-btn{flex-shrink:0;padding:9px 18px;border:1px solid var(--gold);border-radius:8px;
  background:transparent;color:var(--gold);font:inherit;font-size:14px;font-weight:600;
  cursor:pointer;transition:background .2s,color .2s}
.pwa-bar-btn:hover{background:var(--gold);color:#0a0a0a}
.pwa-bar-x{flex-shrink:0;width:30px;height:30px;padding:0;border:0;border-radius:6px;
  background:transparent;color:rgba(232,230,227,.5);font-size:20px;line-height:1;
  cursor:pointer;transition:color .2s,background .2s}
.pwa-bar-x:hover{color:var(--ink);background:rgba(255,255,255,.06)}
@media(max-width:479px){
  .pwa-bar{padding-left:14px;gap:8px}
  .pwa-bar-text{font-size:13px}
  .pwa-bar-btn{padding:9px 14px;font-size:13px}
}

/* ── PWA: модалка-инструкция установки (pwa.js, матрица «браузер × ОС») ──
   Стиль сайта: панель на --bg2, золотая кромка, серифный заголовок.
   Внутренний скролл тела — на невысоких ландшафтах шаги не режутся. */
.pwa-modal{position:fixed;inset:0;z-index:var(--z-modalert,250);display:none;
  align-items:center;justify-content:center;padding:calc(18px * var(--ui-scale));
  background:rgba(3,4,8,.84);backdrop-filter:blur(6px)}
.pwa-modal.open{display:flex}
.pwa-panel{position:relative;background:var(--bg2,#0d0f16);border:1px solid var(--line);
  border-radius:calc(14px * var(--ui-scale));width:min(calc(420px * var(--ui-scale)),100%);
  max-height:88svh;display:flex;flex-direction:column;
  padding:calc(22px * var(--ui-scale)) calc(22px * var(--ui-scale)) calc(18px * var(--ui-scale));
  box-shadow:0 18px 50px -20px rgba(0,0,0,.9);overflow-y:auto}
.pwa-panel h3{font-family:var(--serif);font-size:calc(21px * var(--ui-scale));color:var(--gold2);
  margin:0 0 calc(12px * var(--ui-scale));padding-right:calc(28px * var(--ui-scale))}
.pwa-steps{margin:0 0 calc(16px * var(--ui-scale));padding-left:calc(20px * var(--ui-scale));
  display:flex;flex-direction:column;gap:calc(8px * var(--ui-scale))}
.pwa-steps li{font-size:max(11px,calc(14px * var(--ui-scale)));line-height:1.55;color:var(--muted)}
.pwa-steps li::marker{color:var(--gold);font-weight:600}
.pwa-modal-x{position:absolute;top:calc(10px * var(--ui-scale));right:calc(12px * var(--ui-scale));
  width:30px;height:30px;padding:0;border:0;border-radius:6px;background:transparent;
  color:rgba(232,230,227,.5);font-size:20px;line-height:1;cursor:pointer;
  transition:color .2s,background .2s}
.pwa-modal-x:hover{color:var(--ink);background:rgba(255,255,255,.06)}
.pwa-modal-actions{display:flex;gap:calc(10px * var(--ui-scale));justify-content:flex-end;flex-wrap:wrap}
.pwa-bar-btn.gold{background:var(--gold);color:#1a0f06}
.pwa-bar-btn.gold:hover{background:var(--ember);border-color:var(--ember)}
@media(prefers-reduced-motion:reduce){
  .pwa-bar{transition:opacity .2s}
  .pwa-bar,.pwa-bar.show{transform:translateX(-50%)}
  /* Грани перестают дышать, прокрутка названия трека не запускается
     (проверка есть и в player.js), пилюля альбома замирает. */
  /* Кромка замирает на середине разгорания, а не гаснет: анимация задаёт
     border-color и box-shadow, и просто снять её — значит оставить плеер
     с еле заметным контуром. */
  .p-stone .p-glow{animation:none;border-color:rgba(227,176,102,.7);
    box-shadow:0 6px 18px -6px rgba(227,176,102,.6)}
  .p-sound-wave .wave.active{animation:none}
  .p-btn.p-play.playing{animation:none}
  .p-btn.p-resize svg g{animation:none}
  .pd-album-one,.pd-album-btn{animation:none}
  /* Свет капель и пилюль уехал на отдельные слои — гасим и их, иначе они
     остались бы дышать вместо самих кнопок. */
  .drop-btn,.drop::before,.pd-album-one::before,.pd-album-btn::before{animation:none}
  .drop::before,.pd-album-one::before,.pd-album-btn::before{opacity:0}
  .pd-pick-item{animation:none !important;opacity:1 !important;transform:none !important}
}

/* ── Реестр функций (features.js) ──────────────────────────────
   Блоки «что умеет сайт» рисуются из /api/features, поэтому их стили живут в
   общем бандле, а не в <style> конкретной страницы: одна и та же карточка
   нужна и на /create, и на /about-saga, и на главной. */
.f-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(calc(280px * var(--ui-scale)),1fr));
  gap:calc(20px * var(--ui-scale));margin-top:calc(28px * var(--ui-scale))}
.f-card{display:block;background:linear-gradient(180deg,rgba(255,255,255,.04),rgba(255,255,255,.01));
  border:1px solid var(--line);border-radius:calc(18px * var(--ui-scale));
  padding:calc(24px * var(--ui-scale));transition:transform .3s,border-color .3s;text-decoration:none;color:inherit}
a.f-card:hover{transform:translateY(calc(-4px * var(--ui-scale)));border-color:rgba(227,176,102,.4)}
/* Кадр раздела (window.SECTION_IMG, assets/img/site/section-*.jpg): во всю
   ширину карточки, 16:9, углы держит флип-зона (.f-flip) — она же вынесена
   в отрицательные поля, чтобы картинка шла от кромки карточки. */
.f-card .f-img{display:block;width:100%;height:auto;
  aspect-ratio:16/9;object-fit:cover}
/* Переворот живёт на картинке (верхняя часть), переход — по остальной
   карточке (решение владельца 08.10). Золотой уголок — аффорданс. */
/* Кадр зафиксирован 16:9: лицо с картинкой не растягивается длинным
   оборотом (раньше клетка росла по обороту, и под картинкой лица оставался
   ненужный отступ). Не помещается текст оборота — он прокручивается. */
.f-flip{display:grid;grid-template:1fr/1fr;position:relative;cursor:pointer;aspect-ratio:16/9;
  margin:calc(-24px * var(--ui-scale)) calc(-24px * var(--ui-scale)) calc(16px * var(--ui-scale));
  border-radius:calc(18px * var(--ui-scale))}
.f-flip .fl-face{border-radius:inherit;border-bottom:1px solid var(--line)}
.f-flip .fl-front{display:block;position:relative;grid-area:1/1;overflow:hidden;background:rgba(255,255,255,.02)}
.f-flip .fl-back{background:linear-gradient(168deg,rgba(20,15,9,.97),rgba(10,8,6,.97));
  display:flex;flex-direction:column;align-items:flex-start;gap:calc(5px * var(--ui-scale));
  padding:calc(13px * var(--ui-scale)) calc(16px * var(--ui-scale));text-align:left;
  overflow-y:auto;overscroll-behavior:contain}
.f-flip .fl-back .fl-kicker{font-size:max(9px,calc(10px * var(--ui-scale)));letter-spacing:.3em}
.f-flip .fl-back p{margin:0;color:var(--muted);font-size:max(14px,calc(14px * var(--ui-scale)));line-height:1.5}
/* Оборот всегда показывает полное описание (решение владельца 14.10.2026:
   описания на ПК и мобильном совпадают). Прежняя подмена на короткий
   back_short на экранах ≤768px убрана: кадр 16:9 на мобильном занимает всю
   ширину экрана и вмещает текст, при нехватке места оборот прокручивается
   (safe center центрирует содержимое, когда оно помещается). */

.f-card-head{display:flex;align-items:center;gap:calc(10px * var(--ui-scale));margin-bottom:calc(10px * var(--ui-scale));flex-wrap:wrap}
.f-card-head h3{font-family:var(--serif);font-size:calc(21px * var(--ui-scale));font-weight:600;margin:0}
.f-icon{font-size:calc(24px * var(--ui-scale));line-height:1}
.f-card p{color:var(--muted);font-size:max(11px,calc(14px * var(--ui-scale)));line-height:1.65;margin:0}
.f-chips{list-style:none;display:flex;flex-wrap:wrap;gap:calc(6px * var(--ui-scale));margin:calc(12px * var(--ui-scale)) 0 0;padding:0}
.f-chips li{padding:calc(3px * var(--ui-scale)) calc(10px * var(--ui-scale));border:1px solid var(--line);
  border-radius:calc(20px * var(--ui-scale));font-size:max(11px,calc(11.5px * var(--ui-scale)));color:var(--gold2);background:rgba(227,176,102,.06)}
.f-note{margin-top:calc(10px * var(--ui-scale));font-size:max(11px,calc(12px * var(--ui-scale)));color:var(--faint);font-style:italic}
.f-state-closed,.f-state-locked{opacity:.72}
.f-badge{padding:calc(2px * var(--ui-scale)) calc(8px * var(--ui-scale));border-radius:calc(6px * var(--ui-scale));
  font-size:max(10px,calc(10px * var(--ui-scale)));font-weight:700;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap}
.f-badge-soon{background:rgba(226,96,58,.15);color:var(--ember)}
.f-badge-beta{background:rgba(106,214,224,.14);color:var(--cyan)}
.f-badge-lock{background:rgba(169,139,240,.16);color:var(--violet)}
.f-table{width:100%;border-collapse:collapse;margin-top:calc(24px * var(--ui-scale));font-size:max(11px,calc(14px * var(--ui-scale)))}
.f-table th,.f-table td{padding:calc(11px * var(--ui-scale)) calc(12px * var(--ui-scale));text-align:left;border-bottom:1px solid var(--line)}
.f-table th{font-size:max(11px,calc(11.5px * var(--ui-scale)));letter-spacing:.1em;text-transform:uppercase;color:var(--faint);font-weight:700}
.f-table td:first-child{color:var(--ink)}
.f-table tr:hover{background:rgba(255,255,255,.02)}
.f-table .f-chk{text-align:center;font-size:max(11px,calc(17px * var(--ui-scale)))}
.f-table .f-chk.y{color:var(--gold)}
.f-table .f-chk.n{color:var(--faint);opacity:.35}
.f-row-closed td:first-child{color:var(--muted)}
.f-scroll{overflow-x:auto}
.soon-badge{display:inline-block;margin-left:calc(6px * var(--ui-scale));padding:1px calc(6px * var(--ui-scale));
  border-radius:4px;font-size:max(9px,calc(9px * var(--ui-scale)));background:rgba(226,96,58,.15);color:var(--ember);
  font-weight:600;letter-spacing:.04em;vertical-align:middle}
@media(max-width:767px){
  .f-grid{grid-template-columns:1fr}
  .f-table{font-size:max(11px,calc(12px * var(--ui-scale)))}
  /* Кегль в таблице ролей ограничен снизу (11 px — порог читаемости), поэтому
     ширину отыгрываем на другом: боковые поля ячеек и разрядка заголовков.
     Разрядка в .1em на заглавных стоила таблице по десятку пикселей на слово —
     на телефоне это дороже, чем даёт. Остаток ширины по-прежнему забирает
     .f-scroll: пять колонок сравнения ролей в 375 px не влезают ни при каком
     кегле, и таблица честно листается вбок. */
  .f-table th,.f-table td{padding:calc(8px * var(--ui-scale)) calc(4px * var(--ui-scale))}
  .f-table th{letter-spacing:.02em}
  .f-table .f-chk{font-size:max(11px,calc(14px * var(--ui-scale)))}
}
/* Пошаговые инструкции функций (features.js → data-features="guides") */
.f-guide{border-top:1px solid var(--line);padding:calc(36px * var(--ui-scale)) 0}
.f-guide:first-child{border-top:0}
.f-guide-lead{color:var(--muted);font-size:max(11px,calc(15.5px * var(--ui-scale)));line-height:1.7;max-width:calc(720px * var(--ui-scale))}
.f-steps{list-style:none;margin:calc(22px * var(--ui-scale)) 0 0;padding:0;display:flex;flex-direction:column;gap:calc(14px * var(--ui-scale))}
.f-step{display:flex;gap:calc(18px * var(--ui-scale));align-items:flex-start;
  background:linear-gradient(180deg,rgba(255,255,255,.03),rgba(255,255,255,.01));
  border:1px solid var(--line);border-radius:calc(14px * var(--ui-scale));padding:calc(18px * var(--ui-scale))}
.f-step-n{flex:0 0 auto;width:calc(34px * var(--ui-scale));height:calc(34px * var(--ui-scale));border-radius:50%;
  background:linear-gradient(135deg,var(--gold),var(--ember));color:#1a0f06;font-family:var(--serif);
  font-size:max(11px,calc(17px * var(--ui-scale)));font-weight:700;display:flex;align-items:center;justify-content:center}
.f-step-body h4{font-family:var(--serif);font-size:calc(19px * var(--ui-scale));font-weight:600;margin-bottom:calc(4px * var(--ui-scale))}
.f-step-body p{color:var(--muted);font-size:max(11px,calc(14.5px * var(--ui-scale)));line-height:1.6}
.f-step-body .f-tip{color:var(--gold2);font-style:italic;font-size:max(11px,calc(13px * var(--ui-scale)));margin-top:calc(6px * var(--ui-scale))}
.f-guide-link{display:inline-block;margin-top:calc(18px * var(--ui-scale));color:var(--gold);
  border-bottom:1px solid rgba(227,176,102,.35);font-weight:600;font-size:max(11px,calc(14px * var(--ui-scale)))}
.f-guide-link:hover{color:var(--gold2)}
@media(max-width:767px){.f-step{flex-direction:column;gap:calc(10px * var(--ui-scale))}}

/* ══════════════════════════════════════════════════════════════
   АДАПТИВНОЕ КАЧЕСТВО: html.perf-low / .perf-mid / .perf-high
   ══════════════════════════════════════════════════════════════
   Уровень ставит инлайновый скрипт в <head> по признакам устройства, а
   уточняет фактическим замером кадров src/js/perf.js. Владелец устройства
   может выбрать вручную — шторка плеера → «Качество картинки».

   perf-high — то, что было всегда: здесь НЕТ НИ ОДНОГО правила, и это условие
   задачи, а не экономия строк. Всё, что ниже, только отнимает.

   perf-low собран под критерий приёмки: не больше 10 одновременных бесконечных
   анимаций (было 104) и ни одного backdrop-filter вне модальных окон. Живыми
   остаются только те, что композитор тянет даром: вращение сигила (transform)
   и три полоски громкости (opacity).

   Правила для полки, доната и читалки живут в своих файлах: их бандлы
   подключаются ПОСЛЕ bundle-core.css, и отсюда бы их не переспорить. */

/* ── звёзды: 90 мерцающих слоёв → 2–3 слоя-носителя ──── */
/* Всё поле звёзд кодируется box-shadow на элементе-носителе (core.js):
   90 анимированных узлов заменены на 2–3 носителя со своими периодами.
   Единственное, что остаётся уровням, — погасить мерцание на perf-low. */
html.perf-low .stars i{animation:none;opacity:.55}

/* ── дыхания ──────────────────────────────────────────── */
html.perf-low .drop-btn,
html.perf-low .drop::before,
html.perf-low .pd-album-one,html.perf-low .pd-album-btn,
html.perf-low .pd-album-one::before,html.perf-low .pd-album-btn::before,
html.perf-low .p-btn.p-play.playing,
html.perf-low .hero-emblem,
html.perf-low .scroll-hint,
html.perf-low .pd-soon-icon,html.perf-low .pd-soon-badge,
html.perf-low .p-eq-bars.eq-idle .eq-bar{animation:none}
/* Слои света без анимации обязаны остаться невидимыми: их opacity в покое
   равна нулю только внутри кадров. */
html.perf-low .drop::before,
html.perf-low .pd-album-one::before,html.perf-low .pd-album-btn::before{opacity:0}
/* Индикатор загрузки крутится и после того, как его спрятали: visibility
   анимацию не останавливает. На слабом устройстве это бесплатный расход. */
html.perf-low.css-ready #boot:not(.boot-on) .boot-ring{animation:none}
/* Колыхание «лужицы» открытого меню (pool) анимирует border-radius —
   некомпозитное свойство: панель с размытой тенью перекрашивается каждый
   кадр, пока меню открыто. На perf-high остаётся (полная картинка), на
   perf-mid/low панель просто стоит с базовым радиусом. */
html.perf-mid .drop.open .drop-panel,
html.perf-low .drop.open .drop-panel{animation:none}

/* ── ореол плеера: один слой малого радиуса вместо трёх ── */
/* Три слоя с размытием до 32 px и внутренней подсветкой — это площадь,
   которую слабый GPU перекрашивает каждый кадр. Оставляем ближний контур. */
html.perf-low .p-stone .p-glow{
  box-shadow:0 0 18px rgba(var(--pl-color,227,176,102),.55)}
html.perf-low .p-stone .p-halo{display:none}
html.perf-low body.player-playing .player-drawer.open{filter:none}

/* ── backdrop-filter: только модальные окна ───────────── */
/* Чтение подложки на каждый кадр — самое дорогое, что есть в CSS на мобильном
   GPU. Взамен плотный фон: разница на глаз мала, потому что подложка и так
   тёмная. Модалки оставляем — там размытие несёт смысл (фокус на окне). */
/* Шапка, панель меню, камень плеера, полоса PWA и кнопки книги размытия уже не
   имеют вовсе (правка 4) — здесь остались те, что на мощном устройстве его
   сохраняют: шторка плеера и затемнение под выезжающими панелями. На perf-mid
   их снимаем тоже: средний уровень — уже компромисс, а перерасчёт подложки под
   большой полупрозрачной областью — самая дорогая строка в его бюджете. */
html.perf-low .overlay,
html.perf-mid .overlay,
html.perf-low .player-drawer,
html.perf-mid .player-drawer{backdrop-filter:none;-webkit-backdrop-filter:none}
html.perf-low .overlay,
html.perf-mid .overlay{background:rgba(5,6,11,.92)}
html.perf-low .player-drawer,
html.perf-mid .player-drawer{
  background:linear-gradient(0deg,rgba(10,12,20,calc(.99*var(--pl-bg-a,1))),rgba(14,16,26,calc(.985*var(--pl-bg-a,1))))}

/* ── пауза декора в простое ───────────────────────────── */
/* Класс is-idle на <html> ставит perf.js: минута без ввода (клавиши, мышь,
   колесо, касание, скролл) — и вкладка, которую никто не смотрит, перестаёт
   перерисовывать себя сотни раз в секунду. Пауза держит анимацию В ТЕКУЩЕЙ
   ФАЗЕ (animation-play-state, не display:none): первый ввод возвращает
   движение с места остановки, без рывка. Играющий плеер и загрузочные
   индикаторы не замирают никогда — исключения ниже. Побочный эффект: уведомле-
   ние, всплывшее в простое с CSS-анимацией появления, дойдёт до полной
   непрозрачности после возвращения пользователя — прозрачность в этот момент
   для него всё равно не видна. */
html.is-idle *,
html.is-idle *::before,
html.is-idle *::after{animation-play-state:paused!important}
/* Всегда живое: загрузочные индикаторы (по имени класса), вращение сигила —
   фирменное медленное движение, которое жаль замораживать, и играющий плеер:
   пока звучит музыка, свет и полоски — само действо, а не декор. Полоски
   эквалайзера в карточках альбомов лежат ВНЕ узла плеера — потому .eq-bars
   отдельной строкой. */
html.is-idle [class*="loading"],
html.is-idle .loading-spinner,
html.is-idle .spin,
html.is-idle .admin-loading,
html.is-idle .an-loading,
html.is-idle body.player-playing .player,
html.is-idle body.player-playing .player *,
html.is-idle body.player-playing .player-drawer,
html.is-idle body.player-playing .player-drawer *,
html.is-idle body.player-playing .eq-bars,
html.is-idle body.player-playing .eq-bars *,
html.is-idle body.player-playing .p-sound-wave,
html.is-idle body.player-playing .p-sound-wave *{animation-play-state:running!important}
/* prefers-reduced-motion — та же пауза на постоянной основе: системный запрос
   «меньше движения» сильнее наших уровней качества, даже ручного «Полное». */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-play-state:paused!important}
  [class*="loading"],.loading-spinner,.spin,.admin-loading,.an-loading,
  body.player-playing .player,body.player-playing .player *,
  body.player-playing .player-drawer,body.player-playing .player-drawer *,
  body.player-playing .eq-bars,body.player-playing .eq-bars *,
  body.player-playing .p-sound-wave,body.player-playing .p-sound-wave *
  {animation-play-state:running!important}
}

/* ── content-visibility: вне кадра не рендерим ────────── */
/* Длинные офскрин-блоки браузер не раскладывает и не рисует, пока они не
   подойдут к экрану. auto запоминает фактическую высоту после первого рендера,
   оценка в intrinsic-size нужна только до неё. */
.card{content-visibility:auto;contain-intrinsic-size:auto 480px}
.ab-section{content-visibility:auto;contain-intrinsic-size:auto 420px}


/* ── Мобильная навигация (≤767): три камня на всю ширину шапки ───────
   Решение владельца (05.10): на телефоне кнопок три — «Сага», «Вселенная»
   и кнопка пользователя. Кнопки «Участие» нет: её разделы разошлись —
   Мастер-Библия в «Сагу», Сообщество во «Вселенную», личное и служебное
   (VIP, Моя вселенная, Управление) — в кнопку пользователя (элементы
   .m-only: на десктопе скрыты, здесь видны; роль ведёт auth.js через
   data-role-visible). Камни крупные, вплотную друг к другу (касаются
   краями, не накладываются) и занимают всю верхнюю полосу шапки; бренд
   сжат до сигила. Форма камня и «дыхание» — как на десктопе. */
@media(max-width:767px){
  .m-only{display:block!important}
  .drop[data-drop="drop-participate"]{display:none}

  header.nav{padding:0 calc(10px*var(--ui-scale));gap:calc(8px*var(--ui-scale));
    align-items:center;height:calc(56px*var(--ui-scale));max-height:64px}
  /* overflow:hidden здесь нельзя: панели дропдаун-меню выезжают из шапки
     вниз и обрезались (репорт владельца 06.10). Цикл высоты уже разорван
     константой — распир больше не страшен. */
  /* Колонка бренда: сигил по центру, VIP-бейдж ровно под ним — своё место,
     ничего не обрезается и не наезжает на кнопки. */
  .brand{flex:0 0 auto;padding:0;width:calc(60px*var(--ui-scale));
    justify-content:center;position:relative}
  header.nav .brand-name{display:none}   /* специфичнее .brand a: остаётся сигил */
  .vip-badge{left:50%;transform:translateX(-50%);white-space:nowrap;
    font-size:8px;padding:1px 5px;top:calc(100% + 6px)}
  .drops{flex:1 1 auto;display:flex;gap:0;min-width:0;padding:0}
  .drop,.drop.drop-auth{flex:1 1 0;min-width:0;display:flex}
  .drop-btn{width:100%;height:calc(42px*var(--ui-scale));justify-content:center;
    padding:0 calc(4px*var(--ui-scale));
    font-size:max(12px,calc(13.5px*var(--ui-scale)))}
  .drop-btn .drop-label{max-width:none}

  /* панели компактные, как на десктопе; края подогнаны под экран */
  .drop[data-drop="drop-saga"] .drop-panel{left:6px;transform-origin:top left;
    transform:translateY(calc(-16px*var(--ui-scale))) scale(.62)}
  .drop[data-drop="drop-saga"].open .drop-panel{transform:translateY(0) scale(1)}
  .drop[data-drop="drop-universe"] .drop-panel{left:auto;right:6px;transform-origin:top right;
    transform:translateY(calc(-16px*var(--ui-scale))) scale(.62)}
  .drop[data-drop="drop-universe"].open .drop-panel{transform:translateY(0) scale(1)}
}

/* ── Поворот телефона (решение владельца 06.10): мобильный сайт свёрстан
      вертикально; в ландшафте телефона показываем просьбу повернуть.
      Исключение — фуллскрин карты вселенной (body.map-fs): там горизонталь
      легальна и включается сама (map.js). Планшеты не затронуты: условие
      по высоте ≤520 и ширине ≤920 отсекает их в обеих ориентациях. ── */
.rotate-hint { display: none; }
@media (orientation: landscape) and (max-height: 520px) and (max-width: 920px) and (pointer: coarse) {
  body:not(.map-fs) .rotate-hint {
    display: flex; flex-direction: column; gap: 12px;
    position: fixed; inset: 0; z-index: 9999;
    align-items: center; justify-content: center; text-align: center;
    background: var(--bg); padding: 24px;
  }
  .rotate-hint-icon { font-size: 46px; color: var(--gold); line-height: 1;
    animation: rotate-swing 2.4s ease-in-out infinite; }
  .rotate-hint-t { font-family: var(--serif); font-size: 24px; color: var(--ink); }
  .rotate-hint-s { font-size: 13px; color: var(--muted); max-width: 430px; line-height: 1.5; }
}
@keyframes rotate-swing { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(90deg); } }

/* ── Авторизация: табы вход/регистрация, сила пароля, Яндекс ID
      (редизайн 06.10 — решение владельца: удобная и понятная форма) ── */
.auth-tabs { display: flex; gap: 4px; margin-bottom: calc(12px * var(--ui-scale));
  border-bottom: 1px solid var(--line); }
.auth-tab {
  flex: 1; background: transparent; border: 0; border-bottom: 2px solid transparent;
  color: var(--muted); font-family: var(--serif); font-size: calc(19px * var(--ui-scale));
  padding: calc(6px * var(--ui-scale)) 0; cursor: pointer; transition: color .2s, border-color .2s;
}
.auth-tab:hover { color: var(--ink); }
.auth-tab.active { color: var(--gold); border-bottom-color: var(--gold); }
.auth-wrap .auth-box label { display: block; margin: calc(10px * var(--ui-scale)) 0 calc(4px * var(--ui-scale)); }
.auth-strength { margin-top: calc(6px * var(--ui-scale)); }
.auth-strength-bar { height: 4px; border-radius: 2px; background: rgba(255,255,255,.08);
  overflow: hidden; }
.auth-strength-bar i { display: block; height: 100%; width: 0; border-radius: 2px;
  transition: width .25s ease, background .25s ease; }
.auth-strength-t { font-size: calc(11px * var(--ui-scale)); color: var(--faint); margin-top: 3px; }
.auth-or { display: flex; align-items: center; gap: 10px; margin: calc(14px * var(--ui-scale)) 0;
  color: var(--faint); font-size: calc(11px * var(--ui-scale)); text-transform: uppercase;
  letter-spacing: .2em; }
.auth-or::before, .auth-or::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.btn-yandex {
  display: flex; width: 100%; align-items: center; justify-content: center; gap: 8px;
  background: #f2f0ea; color: #1c1c1c; border: 0; border-radius: calc(8px * var(--ui-scale));
  padding: calc(10px * var(--ui-scale)) 14px; font-family: var(--sans);
  font-size: calc(13.5px * var(--ui-scale)); font-weight: 600; cursor: pointer;
  transition: filter .2s;
}
.btn-yandex:hover { filter: brightness(1.06); }
.btn-yandex .ya-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: calc(20px * var(--ui-scale)); height: calc(20px * var(--ui-scale));
  border-radius: 4px; background: #fc3f1d; color: #fff;
  font-weight: 800; font-size: calc(13px * var(--ui-scale));
}

/* ── Профиль читателя (/profile) — панели в стиле сайта ─────────── */
.pf-wrap { padding-top: calc(96px * var(--ui-scale)); padding-bottom: calc(60px * var(--ui-scale)); }
.pf-head { text-align: center; margin-bottom: calc(24px * var(--ui-scale)); }
.pf-head h1 {
  font-family: var(--serif); font-size: clamp(36px, 4.6vw, calc(60px * var(--ui-scale)));
  font-weight: 700; margin: calc(6px * var(--ui-scale)) 0 0;
  background: linear-gradient(180deg, var(--gold2), var(--gold), var(--ember));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.pf-lead { color: var(--muted); max-width: calc(560px * var(--ui-scale)); margin: 0 auto;
  font-size: max(12px, calc(15px * var(--ui-scale))); line-height: 1.55; }
.pf-grid { display: grid; grid-template-columns: calc(260px * var(--ui-scale)) 1fr;
  gap: calc(18px * var(--ui-scale)); align-items: start; }
/* Карточки кабинета — сеткой 2×2 без пустых мест (разметка владельца
   06.10): «Имя и e-mail» | «Пароль», ниже «Яндекс ID» | «Сессии».
   На узких экранах колонка одна. */
.pf-main { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: calc(18px * var(--ui-scale)); min-width: 0; align-items: stretch; }
.pf-main .pf-card { margin-bottom: 0; }
@media (max-width: 1100px) { .pf-main { grid-template-columns: 1fr; } }
.pf-card { background: rgba(16, 19, 31, .82); border: 1px solid var(--line);
  border-radius: calc(14px * var(--ui-scale)); padding: calc(20px * var(--ui-scale));
  margin-bottom: calc(18px * var(--ui-scale)); }
.pf-card.pf-id { text-align: center; }
.pf-h { font-family: var(--serif); font-size: calc(22px * var(--ui-scale));
  font-weight: 600; color: var(--gold2); margin: 0 0 calc(12px * var(--ui-scale)); }
.pf-avatar { width: calc(112px * var(--ui-scale)); height: calc(112px * var(--ui-scale));
  border-radius: 50%; object-fit: cover; border: 2px solid rgba(227,176,102,.55);
  display: block; margin: 0 auto calc(10px * var(--ui-scale)); }
.pf-avatar-letter { display: flex; align-items: center; justify-content: center;
  font-family: var(--serif); font-size: calc(44px * var(--ui-scale)); color: var(--gold2);
  background: rgba(227,176,102,.08); }
.pf-id-name { font-family: var(--serif); font-size: calc(24px * var(--ui-scale)); color: var(--ink); }
.pf-id-user { color: var(--faint); font-size: calc(13px * var(--ui-scale)); margin-top: 2px; }
.pf-role { display: inline-block; margin-top: calc(8px * var(--ui-scale));
  border: 1px solid rgba(227,176,102,.45); color: var(--gold);
  border-radius: calc(999px * var(--ui-scale)); font-size: calc(11px * var(--ui-scale));
  letter-spacing: .12em; text-transform: uppercase; padding: calc(3px * var(--ui-scale)) calc(10px * var(--ui-scale)); }
.pf-id-meta { color: var(--faint); font-size: calc(12px * var(--ui-scale)); margin-top: calc(10px * var(--ui-scale)); }
.pf-label { display: block; color: var(--faint); font-size: calc(12px * var(--ui-scale));
  margin: calc(12px * var(--ui-scale)) 0 calc(4px * var(--ui-scale)); }
.pf-input { width: 100%; background: var(--panel); color: var(--ink);
  border: 1px solid var(--line); border-radius: calc(8px * var(--ui-scale));
  padding: calc(9px * var(--ui-scale)) calc(12px * var(--ui-scale));
  font-family: var(--sans); font-size: calc(13.5px * var(--ui-scale)); }
.pf-input:focus { outline: none; border-color: rgba(227,176,102,.5); }
.pf-btn { margin-top: calc(14px * var(--ui-scale)); }
.pf-btn-danger { background: transparent; color: #c96a5a; border: 1px solid rgba(201,106,90,.4);
  border-radius: calc(8px * var(--ui-scale)); padding: calc(9px * var(--ui-scale)) calc(14px * var(--ui-scale));
  font-family: var(--sans); font-size: calc(13px * var(--ui-scale)); cursor: pointer; transition: border-color .2s; }
.pf-btn-danger:hover { border-color: rgba(201,106,90,.8); }
.pf-err { display: none; color: #d98a7c; font-size: calc(12.5px * var(--ui-scale)); margin-top: calc(8px * var(--ui-scale)); }
.pf-note { color: var(--muted); font-size: calc(13px * var(--ui-scale)); line-height: 1.5; }
.pf-danger .pf-note { margin-bottom: calc(12px * var(--ui-scale)); }
.pf-center { text-align: center; padding: calc(48px * var(--ui-scale)) calc(24px * var(--ui-scale)); }
.pf-center .pf-lead { margin-bottom: calc(18px * var(--ui-scale)); }
@media (max-width: 767px) {
  .pf-grid { grid-template-columns: 1fr; }
  .pf-wrap { padding-top: calc(78px * var(--ui-scale)); }
}

/* Кнопка сохранения в профильных карточках — как основная кнопка сайта. */
.pf-card .pf-btn {
  background: linear-gradient(180deg, var(--gold), var(--ember)); color: #1c1c1c;
  border: 0; border-radius: calc(8px * var(--ui-scale));
  padding: calc(10px * var(--ui-scale)) calc(18px * var(--ui-scale));
  font-family: var(--sans); font-size: calc(13.5px * var(--ui-scale));
  font-weight: 600; cursor: pointer; transition: filter .2s;
}
.pf-card .pf-btn:hover { filter: brightness(1.12); }

/* Гостевая кнопка на странице профиля — градиентная, как основная. */
.pf-center .btn-auth {
  background: linear-gradient(180deg, var(--gold), var(--ember)); color: #1c1c1c;
  border: 0; border-radius: calc(8px * var(--ui-scale));
  padding: calc(11px * var(--ui-scale)) calc(22px * var(--ui-scale));
  font-family: var(--sans); font-size: calc(14px * var(--ui-scale)); font-weight: 600;
  cursor: pointer; transition: filter .2s; display: inline-block;
}
.pf-center .btn-auth:hover { filter: brightness(1.12); }
/* Во время тяги язычок движется кадром — без доводки, как шторка. */
.p-stone.mini .p-tab.drag{transition:none}
/* Native tap flash is not part of the site's interaction palette. */
* { -webkit-tap-highlight-color:transparent; }
.dialog-close { margin-left:auto;display:flex;width:fit-content;text-decoration:none;min-height:44px; }
.people-results { display:flex;flex-direction:column;gap:4px;max-height:220px;overflow:auto;flex-basis:100%; }
.people-results:empty { display:none; }
.people-results button { text-align:left;white-space:normal;min-height:44px; }
@media(max-width:600px) {
 .admin-layout,.admin-main,#admin-body,.admin-cols,.admin-cols > * { min-width:0;max-width:100%;box-sizing:border-box; }
 .admin-cols { grid-template-columns:minmax(0,1fr)!important; }
 .admin-users .table-wrap { overflow:visible; }
 .admin-users table,.admin-users tbody { display:block;width:100%;min-width:0; }
 .admin-users thead { display:none; }
 .admin-users tr { display:grid;grid-template-columns:minmax(0,1fr);padding:12px;border-bottom:1px solid var(--line); }
 .admin-users td { display:flex;gap:12px;align-items:center;justify-content:space-between;white-space:normal;overflow-wrap:anywhere;padding:6px 0;border:0;min-width:0; }
 .admin-users td[data-label]::before { content:attr(data-label);color:var(--muted);font-size:13px;flex-shrink:0; }
 .admin-users input,.admin-users select { width:100%;min-width:0;box-sizing:border-box;font-size:16px; }
 .admin-users td select { width:auto;min-width:110px; }
 .admin-users .an-block { padding:14px; }
 .p-stone.mini { width:64px!important;height:64px!important;border-radius:50%; }
 .p-stone.mini .player-inner { display:flex;justify-content:center;padding:0!important;width:64px;height:64px;min-height:0; }
 .p-stone.mini .p-right,.p-stone.mini .p-tab,.p-stone.mini .p-move-zone,.p-stone.mini .p-drag-zone,.p-stone.mini .p-btn:not(#player-play),.p-stone.mini .p-track-num { display:none!important; }
 .p-stone.mini .player-controls { gap:0; }
 .p-stone.mini #player-play { width:64px;height:64px;margin:0;box-shadow:0 0 18px rgba(227,176,102,.35); }
 .player-playing .p-stone.mini #player-play { animation:phone-play-glow 1.8s ease-in-out infinite; }
 .pd-inner { grid-template-columns:minmax(0,1fr);grid-template-areas:'one' 'flip' 'info' 'now' 'tracks' 'dl';grid-template-rows:auto;gap:16px; }
 .pd-col-flip { width:min(100%,280px);justify-self:center;align-self:start; }
 .pd-media-btns { width:100%;gap:8px;flex:none;align-items:stretch; }
 .pd-video-btn { align-self:stretch;justify-content:center;box-sizing:border-box;min-height:44px;font-size:16px; }
 .pd-flip { flex:none;min-height:0;width:100%; }
 .pd-flip-inner { height:auto;aspect-ratio:var(--drawer-cover-ratio,3 / 4); }
 .pd-lyrics { font-size:15px;overflow-y:auto; }
 .pd-album-one { min-height:44px;height:auto;width:fit-content;max-width:100%;padding:10px 18px;border-radius:22px; }
 .pd-one-title { font-size:14px;white-space:normal; }
 .pd-album-desc,.pd-now-story,.pd-now-mood,.pd-album-desc .pd-motto { font-size:16px;line-height:1.55; }
 .pd-now-title,.pd-char-name { font-size:19px; }
 .pd-tracklist-area,.pd-tracklist-area.tl-open { flex:none;height:216px;max-height:216px;overflow-y:auto;overscroll-behavior:contain;scroll-snap-type:y mandatory; }
 .pd-tracklist-area .pd-track:not(.near) { display:flex!important; }
 .pd-track { box-sizing:border-box;height:72px;min-height:72px;scroll-snap-align:start;margin:0; }
 .pd-track .pd-tt { font-size:16px; }
 .pd-track .pd-ts { font-size:13px; }
 .pd-tl-more { display:none!important; }
 .pd-track-modal { position:absolute;inset:0;z-index:10;background:var(--bg2);border-radius:inherit;display:flex;flex-direction:column;padding:12px;box-sizing:border-box;animation:track-sheet-in .25s ease-out; }
 .pd-track-modal header { display:flex;align-items:center;gap:12px;flex-shrink:0; }
 .pd-track-modal header h3 { margin:0;font-size:22px; }
 .pd-track-modal-list { overflow-y:auto;overscroll-behavior:contain;min-height:0; }
 .pd-track-modal .pd-track { display:flex!important; }
}
@keyframes phone-play-glow { 50% { box-shadow:0 0 22px rgba(var(--pl-color,227,176,102),.8),0 0 40px rgba(var(--pl-color,227,176,102),.4); } }
@keyframes track-sheet-in { from { opacity:0;transform:translateX(24px); } to { opacity:1;transform:translateX(0); } }
@media(prefers-reduced-motion:reduce) { .player-playing .p-stone.mini #player-play,.pd-track-modal { animation:none; } }

/* No opacity shimmer may compete with the live FFT, including under the drawer. */
#p-eq-bars .eq-bar{animation:none!important;opacity:1;transition:transform .06s ease;backface-visibility:hidden}
body.reader-immersive #player,body.reader-immersive #pdClip,body.reader-immersive #pdPicker,body.map-fs #player,body.map-fs #pdClip,body.map-fs #pdPicker{visibility:hidden;pointer-events:none}

/* ==============================================================
   DiZ Saga — shelf.css: физическая книжная полка (CSS 3D)
   Стоячие тома в 3/4-перспективе на деревянной доске.
   ============================================================== */

:root {
  --shelf-bg: #07080d;
  --wood: #2a1a0e;
  --wood-light: #4a2f18;
  --wood-lit: #6b4423;
  --wood-dark: #140c06;
  --gold: #e3b066;
  --gold-dim: #b88c4a;
  --gold-bright: #f0c87a;
  --ember: #c4582e;
  --ink: #dadde9;
  --muted: #8a90a6;
  --faint: #4a4f5e;
  --line: rgba(227,176,102,0.18);
  /* геометрия книги */
  --bw: 152px;   /* ширина обложки  */
  --bh: 226px;   /* высота книги    */
  --bd: 17px;    /* полуглубина (толщина = 2·bd) */
  --book-tilt: -26deg;
}

/* ==============================================================
   ПАНЕЛЬ РАЗДЕЛА (бывший hero)
   Заголовок занимал пол-экрана, и до самих полок приходилось прокручивать.
   Теперь это компактная панель со скруглёнными краями и живой каймой —
   такой же, как у кнопок навигации, только медленнее и вразнобой, чтобы
   свет шёл по контуру неровно. Язычок снизу сворачивает её в узкую
   плашку под шапкой сайта: полка остаётся на виду целиком.
   ============================================================== */
/* Размер ведёт содержимое, а не полоса в 880 px.
   Было: жёсткая ширина и вертикальные отступы в vh — панель занимала одинаково
   много и при трёх строках, и при одной, а по бокам оставалось пустое поле.
   Стало: width:fit-content, отступы в пикселях по --ui-scale. Ширину задаёт
   самая длинная строка (её держит .shelf-subtitle{max-width}), высоту — сам
   текст. Верхний предел оставлен, чтобы на широком мониторе строка не
   растянулась во всю ширину и не стала нечитаемой.
   Ужимается панель через max-width, а не width: fit-content анимировать
   нельзя, а max-width — можно, и переход остаётся плавным. */
/* .shelf-panel — только сцена: размер, положение и перспектива. Ничего
   видимого на ней нет. Плита живёт в .sp-card, иначе переворачивалось бы
   содержимое внутри неподвижной рамки, а не сам блок. */
.shelf-panel{
  position: relative;
  margin: calc(var(--header-h) + 14px) auto 6px;
  width: fit-content;
  max-width: min(880px, calc(100% - 28px));
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  perspective: calc(1500px * var(--ui-scale));
  transition: max-width .9s cubic-bezier(.2,.75,.2,1), margin .9s cubic-bezier(.2,.75,.2,1), top .9s cubic-bezier(.2,.75,.2,1);
}
/* Плита. Переворот идёт по Y, обе грани разведены по Z на 6 px в разные
   стороны — между ними остаётся тело толщиной 12 px (.sp-edge). */
/* Направленный переворот (flip.js, 08.10): угол копится в --flip-a — тап
   правее центра крутит вправо, левее — влево, всегда той стороной, которой
   тапнули. Перспективу даёт .shelf-panel. */
/* Высоту ведёт ЛИЦО: оборот вынут из раскладки (position:absolute, ниже), и
   при перевороте JS подставляет высоту той стороны, что открывается. Переход
   высоты — осознанное одноразовое исключение из «только transform»
   (PERFORMANCE_RULES §2): без циклов, на клик, с деградацией на
   reduced-motion. */
.sp-card{
  position: relative;
  display: grid; grid-template-columns: 1fr;
  transform: rotateY(var(--flip-a, 0deg));
  transform-style: preserve-3d;
  transition: transform .9s cubic-bezier(.2,.75,.2,1),
              height .9s cubic-bezier(.2,.75,.2,1);
}
html.perf-low .sp-card{transition:none}
.sp-face [data-shelf-collapse]{position:absolute;top:4px;right:4px;width:44px;height:44px;display:grid;place-items:center;padding:0;z-index:3;color:var(--gold)}
.sp-face [data-shelf-collapse] svg{width:20px;height:20px}
.shelf-panel-inner h1,.sp-hints-title{padding-inline:32px}
.shelf-panel.collapsed [data-shelf-collapse]{visibility:hidden}

.sp-face{
  border-radius: calc(20px * var(--ui-scale));
  padding: calc(16px * var(--ui-scale)) calc(30px * var(--ui-scale)) calc(18px * var(--ui-scale));
  text-align: center;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  /* Подсвет сверху — тот же, что у кнопок шапки (.drop-btn в app.css):
     радиальный градиент из верхнего края плюс тонкая рамка по --line. */
  background:
    radial-gradient(120% 150% at 50% 0%, rgba(227,176,102,.10), rgba(255,255,255,.03) 60%, rgba(255,255,255,.02)),
    linear-gradient(168deg, rgba(20,15,9,.92), rgba(10,8,6,.92));
  border: 1px solid var(--line);
  /* Дыхание — по образцу nav-breathe: те же опорные кадры 0/45/55/100 и та же
     ease-in-out, поэтому темп читается родственным. Длительность 9 с против 8 у
     меню: в точности совпадающие циклы выглядели бы механической синхронностью.
     Масштаб не трогаем — у кнопки в шапке scale(1.08) уместен, у плиты во всю
     ширину он бы дёргал вёрстку. */
  /* Плита больше не анимирует свою тень: box-shadow не композитится, и плита
     во всю ширину экрана перекрашивалась целиком каждый кадр — на полке это
     был самый дорогой пейнт страницы (замер: DMN/docs/perf/before.md, 279 слоёв,
     правила: docs/PERFORMANCE_RULES.md;
     Composite 7.2 с на секунду записи). Здесь остаётся НУЛЕВОЙ кадр прежнего
     shelfPanelGlow — ровно то, что рисовалось в покое, — а разгорается
     отдельный слой .sp-face::after через opacity. */
  box-shadow: 0 24px 60px -30px rgba(0,0,0,.9),
              inset 0 1px 1px rgba(255,255,255,.05),
              0 0 14px -6px rgba(227,176,102,.28);
  transition: padding .55s cubic-bezier(.4,0,.2,1), border-radius .55s cubic-bezier(.4,0,.2,1),
              border-color .4s ease, background .4s ease;
}
/* Слой света плиты. Без фона — только золотой ореол пикового кадра; тёмная
   тень и внутренний подсвет остались статикой на самой плите: их размах в
   кадрах (60→66 px и .05→.16 на однопиксельной кромке) глаз не ловит, а
   платить за него полным перепейнтом пришлось бы каждый кадр. */
.sp-face::after{
  content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  box-shadow: 0 0 40px 5px rgba(227,176,102,.44);
  opacity:0; animation: shelfPanelGlow 9s ease-in-out infinite;
}
/* Лицо в потоке — оно задаёт размер плите. Оборот вынут из раскладки
   (решение владельца 08.10: размер по лицевой стороне): его высоту при
   перевороте подставляет shelf.js в height плиты, лишнее срезает overflow. */
.sp-face{ grid-area: 1 / 1; }
.sp-front{ position: relative; transform: translateZ(6px); }
.sp-back{
  position: absolute; inset: 0; overflow: hidden;
  transform: rotateY(180deg) translateZ(6px);
  display: flex; align-items: center; justify-content: center;
}
/* Свёрнутая плита — полоска в 24 px; оборот убираем совсем. */
.shelf-panel.collapsed .sp-back{ display: none; }
/* Тело плиты между гранями: тёмный слой, который видно на повороте. */
/* Радиус задаём числом, а не inherit: наследовать было не от чего — у .sp-card
   скругления нет, и торец оставался прямоугольником, торчащим острыми углами
   из-под закруглённых граней. Именно эти углы и были видны по краям блока. */
.sp-edge{
  position: absolute; inset: 0; pointer-events: none;
  border-radius: calc(20px * var(--ui-scale));
  background: linear-gradient(180deg, #1b130b, #090705);
  box-shadow: 0 0 0 6px #17110b, 0 0 0 7px rgba(227,176,102,.18);
  transform: translateZ(0);
}
.shelf-panel.collapsed .sp-edge{ border-radius: calc(14px * var(--ui-scale)); }
/* Дыхание было почти не видно по двум причинам.
   Первая: свет менялся с 18px/-10 spread до 26px/+1 — разница в пару пикселей
   ореола, глаз её не ловит. Размах увеличен до 40px/+5.
   Вторая: в кадрах стоял filter:brightness. filter принудительно уплощает
   элемент, вырывая его из 3D-контекста плиты (preserve-3d) — грань переставала
   быть гранью объёмного предмета, а сам подсвет гасился. Яркость теперь ведёт
   только тень, без filter. */
@keyframes shelfPanelGlow{
  0%,100%{ opacity: 0; }
  45%,55%{ opacity: 1; }
}
/* Наведение — как у кнопки меню: подсвет ярче, кромка золотая. Дыхание на
   паузе, иначе оно перебивало бы наведённое состояние. */
.shelf-panel:hover .sp-face{
  border-color: var(--gold);
  background:
    radial-gradient(120% 170% at 50% -20%, rgba(227,176,102,.26), rgba(227,176,102,.06) 60%, rgba(255,255,255,.02)),
    linear-gradient(168deg, rgba(24,18,11,.94), rgba(12,9,7,.94));
  box-shadow: 0 26px 66px -28px rgba(0,0,0,.9), inset 0 1px 2px rgba(255,255,255,.15),
              0 0 26px -2px rgba(227,176,102,.35);
}
/* Пауза переехала на слои: дышит теперь не сама плита, а её свет и кайма. */
.shelf-panel:hover .sp-face::after,
.shelf-panel:hover .sp-face::before{ animation-play-state: paused; }
.shelf-panel:focus-visible{ outline: none; }
.shelf-panel:focus-visible .sp-face{
  border-color: var(--gold-bright);
  box-shadow: 0 0 0 2px rgba(227,176,102,.35), 0 0 26px -6px rgba(227,176,102,.8);
}
/* Дыхание вместо бегущих огней. Панель живёт тем же ритмом, что кнопки
   навигации (nav-breathe в app.css): свечение по краю мягко разгорается и
   гаснет, размер почти не меняется. Вращающиеся конические градиенты, что
   стояли здесь раньше, выглядели светомузыкой и тянули взгляд на себя. */
.sp-face::before{
  content:''; position:absolute; inset:0; border-radius:inherit; padding:1px;
  background:linear-gradient(140deg,
    rgba(227,176,102,.55) 0%, rgba(227,176,102,.14) 34%,
    rgba(226,96,58,.30) 62%, rgba(227,176,102,.5) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  pointer-events:none;
  /* Тот же цикл, что у свечения самой панели (shelfPanelGlow): кайма и подсвет
     обязаны дышать в такт, иначе на 8 и 9 секундах они расходятся по фазе и
     блок начинает мерцать двумя независимыми ритмами. */
  animation: shelfPanelBreath 9s ease-in-out infinite;
}
/* Кайма дышит вместе со светом, но своим размахом: от еле тлеющей до полной.
   filter:brightness убран: он не композитится, и каждый кадр каймы означал
   перерисовку градиента под маской по всему периметру плиты. Размах теперь
   ведёт одна opacity — нижняя точка опущена с .40 до .30, чтобы разница между
   тлением и полной каймой осталась той же на глаз. */
@keyframes shelfPanelBreath{
  0%,100%{opacity:.30}
  45%,55%{opacity:1}
}

.shelf-panel-inner{
  transition: opacity .32s ease, transform .45s cubic-bezier(.4,0,.2,1);
  transform-origin: top center;
}
/* Ритм внутри плиты. Размеры переведены с vw на --ui-scale: блок теперь
   подгоняется под текст, и заголовок в 44 px на широком мониторе распирал его
   обратно до прежней полосы. Отступы кратны одному шагу, поэтому строки стоят
   ровной лесенкой, а не вразнобой. */
.shelf-panel h1{
  font-family: var(--serif); font-size: calc(30px * var(--ui-scale)); font-weight: 700;
  background: linear-gradient(180deg, var(--gold-bright), var(--gold), var(--ember));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  line-height: 1.15; margin: 0 0 calc(8px * var(--ui-scale)); letter-spacing: .01em;
}
.shelf-subtitle{
  font-size:max(11px,calc(14px * var(--ui-scale))); color: var(--muted);
  max-width: calc(430px * var(--ui-scale)); line-height: 1.5;
  margin: 0 auto calc(12px * var(--ui-scale));
}
/* Легенда отделена волоском: три пометки о статусе — это подпись к полке, а не
   продолжение вводного текста, и глазу нужна граница между ними. */
.shelf-legend{
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: calc(16px * var(--ui-scale));
  padding-top: calc(10px * var(--ui-scale));
  border-top: 1px solid rgba(227,176,102,.12);
}
.legend-item{
  display: flex; align-items: center; gap: calc(7px * var(--ui-scale));
  font-size:max(11px,calc(12px * var(--ui-scale))); color: var(--muted); font-family: var(--sans);
}
.legend-dot{ width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
.legend-dot.unread{ background: var(--faint); }
.legend-dot.reading{ background: linear-gradient(135deg, var(--gold), var(--ember)); }
.legend-dot.complete{ background: linear-gradient(135deg, var(--gold-bright), var(--gold)); box-shadow: 0 0 8px var(--gold); }

/* Надпись свёрнутого состояния лежит на месте заголовка и проявляется,
   когда панель ужимается: переход читается как перетекание, а не как
   подмена одного блока другим. */
.shelf-panel-mini{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-family: var(--serif); font-size: 17px; letter-spacing:.04em;
  color: var(--gold-bright); opacity:0; pointer-events:none;
  transition: opacity .3s ease .12s;
}
/* Язычка-кнопки больше нет. Сворачивает и разворачивает сам блок: клик по
   баннеру ужимает его в надпись «Книжная полка», клик по надписи —
   разворачивает обратно. Отдельная кнопка на кромке дублировала это
   действие и всё время спорила с содержимым панели за место. */

/* ── свёрнутое состояние ──────────────────────────────────
   Панель уходит вверх под шапку и превращается в узкую плашку. Анимируем
   отступы и прозрачность, а не display: иначе вместо перетекания получился
   бы рывок. */
.shelf-panel.collapsed{
  margin-top: calc(var(--header-h) + 8px);
  max-width: calc(240px * var(--ui-scale));
}
.shelf-panel.collapsed .sp-face{
  padding: calc(11px * var(--ui-scale)) calc(26px * var(--ui-scale));
  /* Форма прежняя — прямоугольник со скруглением, только радиус мельче под
     новый размер. Прежние `0 0 16px 16px` срезали верх в прямой угол, и блок
     переставал быть тем же блоком, что был развёрнут. */
  border-radius: calc(14px * var(--ui-scale));
}
.shelf-panel.collapsed .shelf-panel-inner{ opacity:0; transform: scaleY(.55) translateY(-14px); height:0; overflow:hidden; }
.shelf-panel.collapsed .shelf-panel-mini{ opacity:1; }

/* ── оборот плиты: подсказки по управлению ───────────────── */
.sp-hints{ width:100%; font-family: var(--sans); color: var(--muted); }
/* Оборот вынут из раскладки: высоту плиты ведёт лицо, а высоту подсказок
   подставляет shelf.js при перевороте. Строку «правый клик — вернуться» не
   возвращаем: подсказка о подсказке места не стоит. */
.sp-hints-title{
  font-family: var(--serif); font-size: calc(18px * var(--ui-scale));
  color: var(--gold-bright); letter-spacing:.04em;
  margin: 0 0 calc(8px * var(--ui-scale));
}
/* Таблица в две колонки: клавиши прижаты к центру справа, подписи начинаются
   от одной вертикали. Прежний вариант центрировал каждую строку целиком, и
   клавиши прыгали по горизонтали — читалось как случайный набор. */
.sp-hints-kbd{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns: auto auto;
  column-gap: calc(12px * var(--ui-scale)); row-gap: calc(5px * var(--ui-scale));
  justify-content:center; align-items:center;
  font-size:max(11px,calc(13px * var(--ui-scale)));
}
.sp-hints li{ display:contents; }
.sp-hints .k{ display:flex; gap: calc(4px * var(--ui-scale)); justify-content:flex-end; }
.sp-hints .t{ text-align:left; }
.sp-hints kbd{
  font-family: var(--sans); font-size:max(11px,calc(11px * var(--ui-scale))); line-height:1;
  color: var(--gold2, var(--gold-bright)); background: rgba(227,176,102,.10);
  border: 1px solid rgba(227,176,102,.28); border-radius: calc(5px * var(--ui-scale));
  padding: calc(3px * var(--ui-scale)) calc(7px * var(--ui-scale)); white-space:nowrap;
}
.sp-hints-foot{
  margin-top: calc(10px * var(--ui-scale));
  font-size:max(11px,calc(11px * var(--ui-scale))); color: var(--faint); letter-spacing:.06em;
}
/* Тач-подсказки: свой оборот без клавиш ПК (решение владельца 08.10).
   Показываются на тачах и на узких окнах; условие то же, что у шаблона. */
.sp-hints-touch{
  list-style:none; margin:0; padding:0;
  display:grid; row-gap: calc(7px * var(--ui-scale)); justify-items:center;
  font-size:max(11px,calc(13px * var(--ui-scale)));
}
.sp-hints-touch li{ display:list-item; }
.sp-hints-touch{ display:none; }
@media (pointer:coarse){
  .sp-hints-kbd{ display:none; }
  .sp-hints-touch{ display:grid; }
}
/* Свёрнутая плита в 24 px подсказки не вместит — переворот там запрещён
   (см. shelf.js), но на всякий случай прячем и стилями. */
.shelf-panel.collapsed .sp-hints{ display:none; }

@media (prefers-reduced-motion: reduce){
  .sp-face,.sp-face::before,.sp-face::after{ animation: none; }
  .sp-face::before{ opacity:.8; }
  /* Свет плиты уехал на ::after — без него слой замер бы на opacity:0 и полка
     осталась бы без золотого ореола вовсе. Ставим середину размаха. */
  .sp-face::after{ opacity:.5; }
  .shelf-panel,.sp-card,.sp-face,.shelf-panel-inner{ transition: none; }
}

/* ==============================================================
   VIEWPORT + STAGE
   ============================================================== */
/* Полка занимает страницу целиком.
   Раньше это была автономная страница без футера, и обзору доставалось всё
   окно. В SPA-оболочке под #page-root приехал общий футер (610 px), полка
   ужалась в остаток и получила лишнюю вертикальную прокрутку — рассматривать
   книги стало негде. На /shelf футер убираем, а обзор растягиваем по остатку
   окна: #page-root становится колонкой во всю высоту, полка забирает всё, что
   не занял заголовок раздела. Класс ставит app.js по адресу. */
body.shelf-body{ overflow: hidden; }
body.shelf-body footer{ display: none; }
body.shelf-body #page-root{
  display: flex; flex-direction: column;
  height: calc(100vh - var(--player-h));
  position: relative;
}
/* Плеер поверх полок, а не нож поверх полок. Общее правило player-active
   (app.css) оставляет под плеером запас padding-bottom на каждой странице —
   на полке он поднимал кромку обзора на высоту плеера с ручкой, и опущенные
   перетаскиванием полки резались прямой линией посреди окна, в пустоте над
   плеером. Убираем запас: обзор доходит до кромки плеера, нижний ряд
   останавливается вровень с ней (clampStagePan в shelf.js), а саму кромку
   с глаз закрывает плеер. */
body.shelf-body.player-active #page-root{ padding-bottom: 0; }
/* Панель вынута из потока и лежит НАД обзором. Пока она была обычным блоком,
   её высота входила в раскладку: сворачивание освобождало полторы сотни
   пикселей, обзор становился выше, и полки вместе с книгами уезжали вниз —
   при каждом клике по заголовку. Теперь обзор занимает всю высоту раздела
   независимо от состояния панели, книги стоят на месте, а панель только
   всплывает над ними. */
body.shelf-body .shelf-panel{
  position: absolute; z-index: 20;
  top: calc(var(--header-h) + 14px); left: 50%;
  transform: translateX(-50%);
  margin: 0;
}
body.shelf-body .shelf-panel.collapsed{ top: calc(var(--header-h) + 8px); margin: 0; }
body.shelf-body .shelf-viewport{ flex: 1 1 auto; height: auto; min-height: 0; }

/* ── полки выше обзора: ничего не обрезаем ───────────────
   У обзора есть кромка (overflow + маска сверху). Пока все ряды помещались,
   её никто не видел; как только рядов становится больше — на ПК с длинной
   сагой, на планшете, на телефоне — нижний ряд попадал ровно на эту кромку и
   книги оказывались разрезаны прямой линией поперёк корешков.
   Класс ставит syncShelfOverflow() в shelf.js: он меряет, помещается ли сцена,
   и это честнее медиазапроса — высота полок зависит не от размера окна, а от
   числа книг. В этом состоянии обзор ничего не режет, а листает полки обычная
   прокрутка страницы. Снизу оставлено место под плеер с его ручкой. */
body.shelf-body.shelf-loose{ overflow: visible; }
body.shelf-body.shelf-loose #page-root{
  height: auto; min-height: calc(100vh - var(--player-h));
}
body.shelf-body.shelf-loose .shelf-viewport{
  height: auto; min-height: 0; overflow: visible;
  -webkit-mask-image: none; mask-image: none;
}
body.shelf-body.shelf-loose .shelf-stage{
  height: auto; min-height: auto; justify-content: flex-start;
  padding-bottom: calc(var(--player-h) + var(--player-handle-h) + 24px);
}

/* ── полноэкранный режим полки ───────────────────────────
   Клик по свободному месту обзора (не по книге и не по заголовку) убирает весь
   интерфейс — как тап по центру в читалке. На экране остаются полки с книгами
   и больше ничего. Повторный клик или Esc возвращают всё назад.
   Прячем через visibility+opacity, а не display:none: элементы сохраняют свои
   размеры, поэтому --player-h не обнуляется и раскладка не прыгает. */
body.shelf-immersive .nav,
body.shelf-immersive .shelf-panel,
body.shelf-immersive .donate-fab,
body.shelf-immersive .agent-fab{
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .3s ease, visibility 0s .3s;
}

/* Плеер спрятан — его полосу внизу возвращаем полке, а резерв под заголовок
   больше не нужен: заголовка на экране нет. */
body.shelf-immersive.shelf-body #page-root{ height:100vh; }
body.shelf-immersive .shelf-stage{
  padding-top: clamp(20px, 4vh, 60px);
}

/* Своего фона у обзора нет.
   Раньше он заливался градиентом #06070c → #07080d → #04050a. Тона близкие, но
   не равные фону страницы, поэтому по верхней и нижней кромке шли еле заметные
   ступеньки — они и читались стенками, ограничивающими пространство полок.
   Сверху вдобавок висел золотой ореол (radial at 50% -8%), который выглядел
   потолком над верхней полкой. Ни того, ни другого здесь быть не должно:
   полки стоят в том же пространстве, что и остальная страница, и звёздный фон
   сайта проходит за ними насквозь.
   Обрезка (overflow:hidden) остаётся — она нужна перетаскиванию и зуму, — но
   край размыт маской, поэтому содержимое растворяется, а не упирается в
   прямую линию. */
.shelf-viewport {
  position: relative; width: 100%;
  height: auto; min-height: calc(100vh - var(--header-h) - var(--player-h) - 210px);
  perspective: 2200px; perspective-origin: 50% 42%;
  overflow: hidden; cursor: grab;
  background: none;
  /* Затухание только сверху. Снизу маски нет: нижняя полка упиралась в
     растворяющуюся кромку, и последний ряд книг выглядел обрезанным — а внизу
     ничего ограничивать пространство полок не должно. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 40px);
  mask-image: linear-gradient(180deg, transparent 0, #000 40px);
}
.shelf-viewport.dragging { cursor: grabbing; }
.shelf-stage {
  position: relative; width: 100%; height: 100%;
  /* align-items:center — иначе ряды как flex-элементы растягиваются на всю
     ширину сцены и width:fit-content у них не срабатывает. */
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  gap: clamp(18px, 4vh, 64px);
  /* Сверху отмерено место под заголовок. Панель вынута из потока (иначе полки
     ездили бы при каждом сворачивании), поэтому её высоту раскладка сама не
     учитывает — резерв задан явно и ОДИНАКОВ в обоих состояниях. Так книги не
     заезжают под панель и при этом стоят на месте, когда её сворачивают.
     52 (шапка) + 14 (отступ панели) + 171 (её высота) + 38 (воздух) = 275.
     Но на невысоком окне жёсткие 275 выдавливали нижний ряд за кадр — он
     обрезался кромкой обзора. Поэтому резерв и отступы сжимаются вместе с
     окном: обе полки обязаны помещаться целиком. */
  padding: clamp(calc(150px * var(--ui-scale)), 26vh, calc(275px * var(--ui-scale)))
           0 clamp(12px, 3vh, 60px);
  transform-style: preserve-3d; transform-origin: center center;
  will-change: transform; transition: filter .1s linear, padding-top .9s cubic-bezier(.2,.75,.2,1);
}

/* ==============================================================
   РЯД = ОДНА ПОЛКА (деревянная доска)
   ============================================================== */
/* Доска по размеру книг, а не по ширине экрана.
   Ряд был растянут на всю ширину сцены (flex-элемент тянется по умолчанию), и
   на широком мониторе доска уезжала за книги на сотни пикселей — полка
   переставала быть полкой и превращалась в перекладину от края до края.
   Теперь ширину ряда задают сами тома, а padding даёт доске запас по краям:
   книги стоят не впритык к торцам. */
.shelf-row {
  position: relative;
  width: fit-content; max-width: 100%;
  display: flex; justify-content: center; align-items: flex-end;
  gap: clamp(12px, 1.6vw, 26px);
  padding: 0 clamp(20px, 3vw, 48px);
  min-height: var(--bh);
  transform-style: preserve-3d;
}

/* верхняя плоскость доски — уходит вглубь.
   left/right по нулю: границы доски — это границы ряда, а запас по краям уже
   отмерен его padding-ом. Прежние clamp-отступы вычитались из доски и на узком
   экране подъедали её под крайние книги. */
.shelf-row::before {
  content: ''; position: absolute; z-index: 0; pointer-events: none;
  left: 0; right: 0;
  bottom: -2px; height: 120px;
  transform: rotateX(78deg); transform-origin: bottom center;
  background:
    repeating-linear-gradient(90deg, rgba(0,0,0,0.16) 0 2px, transparent 2px 26px),
    linear-gradient(180deg, var(--wood-lit) 0%, var(--wood-light) 40%, var(--wood) 100%);
  border-radius: 3px;
  box-shadow: inset 0 6px 14px -6px rgba(255,220,160,0.18);
}
/* передний торец доски + тень под полкой */
.shelf-row::after {
  content: ''; position: absolute; z-index: 1; pointer-events: none;
  left: calc(6px * var(--ui-scale)); right: calc(6px * var(--ui-scale));
  bottom: -22px; height: 22px;
  background:
    repeating-linear-gradient(90deg, rgba(0,0,0,0.22) 0 3px, transparent 3px 30px),
    linear-gradient(180deg, var(--wood-light) 0%, var(--wood) 45%, var(--wood-dark) 100%);
  border-radius: 0 0 5px 5px;
  box-shadow:
    inset 0 2px 0 rgba(255,210,150,0.22),
    inset 0 -3px 8px rgba(0,0,0,0.6),
    0 26px 40px -14px rgba(0,0,0,0.85),
    0 8px 16px -8px rgba(0,0,0,0.7);
}

/* ==============================================================
   КНИГА (стоячий том, 3/4 перспектива)
   ============================================================== */
.book-3d {
  position: relative; z-index: 2;
  width: var(--bw); height: var(--bh);
  flex-shrink: 0;
  transform-style: preserve-3d;
  transform: rotateY(var(--book-tilt)) translateZ(0);
  transition: transform 0.7s cubic-bezier(.32,.72,.22,1), filter 0.5s ease, opacity 0.5s ease;
  cursor: pointer; user-select: none;
}
/* лёгкая вариация наклона — «живой» ряд */
.book-3d:nth-child(3n)   { --book-tilt: -22deg; }
.book-3d:nth-child(3n+1) { --book-tilt: -28deg; }
/* состояние входа (снимается по стаггеру в animateBooksIn) */
.book-3d.book-enter { opacity: 0; transform: rotateY(var(--book-tilt)) translateY(48px) scale(0.92); }

/* Наведение обрабатывает .book-slot (см. блок «СЛОТ КНИГИ» ниже): том
   уезжает из-под курсора, и hover на нём самом бесконечно переключался. */

/* контактная тень на доске */
.book-3d::after {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  left: 50%; bottom: -4px; width: 78%; height: 26px;
  transform: translateX(-50%) translateZ(calc(-1 * var(--bd))) rotateX(82deg);
  transform-origin: center bottom;
  background: radial-gradient(ellipse at center, rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.28) 55%, transparent 78%);
  filter: blur(5px);
  transition: opacity 0.4s; opacity: 0.9;
}


/* ── грани ─────────────────────────────────────────────── */
.book-cover, .book-spine, .book-back, .book-thickness {
  position: absolute; backface-visibility: hidden;
}

/* обложка — фронт */
.book-cover {
  width: var(--bw); height: var(--bh);
  transform: translateZ(var(--bd));
  border-radius: 2px 4px 4px 2px;
  box-shadow: 0 12px 40px -12px rgba(0,0,0,0.85);
  overflow: hidden;
}
.book-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* тёплый свет + виньетка на обложке */
.cover-overlay {
  position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  background:
    linear-gradient(115deg, rgba(255,235,200,0.20) 0%, rgba(255,235,200,0.05) 22%, transparent 45%),
    linear-gradient(180deg, transparent 55%, rgba(0,0,0,0.35) 100%),
    radial-gradient(120% 100% at 0% 0%, transparent 60%, rgba(0,0,0,0.28) 100%);
}
/* жёлоб у корешка (свет уходит в переплёт) */
.book-cover::after {
  content: ''; position: absolute; inset: 0 auto 0 0; width: 16px; pointer-events: none;
  background: linear-gradient(90deg, rgba(0,0,0,0.5), transparent);
}

/* корешок — левая грань (переплёт).
   Грани обязаны занимать ту же глубину, что тело тома: обложка стоит на
   z = +bd, задняя крышка на z = −bd. Поворот вокруг своей кромки без сдвига
   выносил грань на z ∈ [0, 2bd] — на bd впереди обложки: сверху (второй ряд
   полки) страницы выглядели ступенькой над обложкой. translateZ ДО поворота
   (внешним оператором) утапливает грань на bd назад — заподлицо. */
.book-spine {
  width: calc(var(--bd) * 2); height: var(--bh);
  left: 0; transform-origin: left center;
  transform: translateZ(calc(-1 * var(--bd))) rotateY(-90deg);
  border-radius: 3px 0 0 3px; overflow: hidden;
  background: linear-gradient(90deg, #140a05, #241408 45%, #150b06);
  box-shadow: inset -6px 0 12px -6px rgba(0,0,0,0.8);
}
.book-spine img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: 0.96; }
/* бинты/рёбра переплёта + блик */
.book-spine::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(90deg, rgba(255,225,180,0.16) 0%, transparent 30%, transparent 70%, rgba(0,0,0,0.5) 100%),
    repeating-linear-gradient(180deg, transparent 0 30px, rgba(0,0,0,0.35) 30px 32px);
}

/* фор-эдж (правая грань) — блок страниц */
.book-thickness {
  width: calc(var(--bd) * 2); height: var(--bh);
  right: 0; transform-origin: right center;
  transform: translateZ(calc(-1 * var(--bd))) rotateY(90deg);
  border-radius: 0 3px 3px 0;
  background:
    repeating-linear-gradient(180deg, #e8dcc2 0 1px, #cdbf9e 1px 2.5px),
    linear-gradient(90deg, #b6a888 0%, #e6dabd 40%, #d8caa6 100%);
  background-blend-mode: multiply, normal;
  box-shadow: inset 4px 0 8px -4px rgba(0,0,0,0.45), inset -2px 0 3px rgba(255,255,255,0.4);
}

/* задняя крышка */
.book-back {
  width: var(--bw); height: var(--bh);
  transform: translateZ(calc(-1 * var(--bd))) rotateY(180deg);
  border-radius: 2px 4px 4px 2px;
  background: linear-gradient(160deg, var(--wood-light), var(--wood-dark));
  box-shadow: inset 0 0 40px rgba(0,0,0,0.6);
}
/* верхний торец — страницы сверху (глубина та же, что у тела: см. .book-spine) */
.book-3d .book-top {
  position: absolute; top: 0; left: 0; width: var(--bw); height: calc(var(--bd) * 2);
  transform-origin: top center; transform: translateZ(calc(-1 * var(--bd))) rotateX(90deg);
  background: repeating-linear-gradient(90deg, #e8dcc2 0 1px, #cdbf9e 1px 3px);
  box-shadow: inset 0 4px 8px -4px rgba(0,0,0,0.4);
  backface-visibility: hidden;
}

/* ── открытие книги (снимается с полки) ────────────────── */
.book-3d.opening {
  transform: rotateY(0deg) translateY(-40px) translateZ(320px) scale(1.35);
  opacity: 0; pointer-events: none;
  transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.6s ease-in;
}

/* ── хендофф: обложка летит в центр перед переходом в читалку ── */
/* Полёт обложки идёт трансформом, а не раскладкой.
   Раньше анимировались left/top/width/height — каждый кадр браузер пересчитывал
   геометрию и перерисовывал слой на основном потоке, отсюда и дёрганье. Теперь
   клон сразу создаётся в КОНЕЧНОМ размере и месте, а в начальное положение его
   уводит transform (приём FLIP): вся анимация уходит на композитор, и заодно
   object-fit у картинки считается по целевому кадру, а не по промежуточному. */
.fly-book {
  position: fixed; z-index: 1200; border-radius: 2px 5px 5px 2px; overflow: hidden;
  box-shadow: 0 14px 44px -14px rgba(0,0,0,0.85);
  transform-origin: top left; will-change: transform;
}
.fly-book.go {
  transition: transform 0.62s cubic-bezier(.32,.72,.22,1), box-shadow 0.62s ease;
  box-shadow: 0 40px 90px -20px rgba(0,0,0,0.9), 0 0 60px -10px rgba(227,176,102,0.25);
}
.fly-book img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* тёмная вуаль поверх полки во время вылета */
body.shelf-opening::after {
  content: ''; position: fixed; inset: 0; z-index: 1100; pointer-events: none;
  background: radial-gradient(circle at 50% 50%, rgba(7,8,13,0.4), rgba(4,5,10,0.9));
  animation: shelfVeil 0.5s ease forwards;
}
@keyframes shelfVeil { from { opacity: 0; } to { opacity: 1; } }

/* ==============================================================
   ПРОГРЕСС + СТАТУС (плывут перед обложкой)
   ============================================================== */
.book-progress {
  position: absolute; bottom: 8px; left: 50%; width: 82%; height: 4px;
  transform: translateX(-50%) translateZ(calc(var(--bd) + 2px));
  background: rgba(0,0,0,0.65); border-radius: 2px; overflow: hidden; z-index: 5;
  opacity: 0; transition: opacity 0.3s;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.4);
}

.progress-bar { height: 100%; width: var(--progress, 0%); border-radius: 2px; background: linear-gradient(90deg, var(--ember), var(--gold)); transition: width 0.5s ease-out; }
.progress-text { position: absolute; right: 0; top: -14px; font-size: 9px; color: var(--gold); font-family: var(--sans); font-weight: 600; white-space: nowrap; }

.book-status {
  position: absolute; top: -20px; left: 50%; font-size: 10px; font-weight: 600;
  font-family: var(--sans); letter-spacing: 0.05em; padding: 2px 9px; border-radius: 10px;
  white-space: nowrap; z-index: 6; opacity: 0;
  transform: translateX(-50%) translateZ(calc(var(--bd) + 20px)) translateY(4px);
  transition: opacity 0.3s, transform 0.3s;
}
/* Показ статуса — по наведению на слот (правило в блоке «СЛОТ КНИГИ»). */
.book-status.unread { background: rgba(74,79,94,0.9); color: #cfd3e0; border: 1px solid var(--faint); }
.book-status.reading { background: linear-gradient(135deg, rgba(227,176,102,0.95), rgba(196,88,46,0.95)); color: #0a0a0a; }
.book-status.complete { background: linear-gradient(135deg, rgba(240,200,122,0.97), rgba(227,176,102,0.97)); color: #0a0a0a; box-shadow: 0 0 12px rgba(227,176,102,0.4); }

/* ==============================================================
   HINT
   ============================================================== */
/* Строка подсказок под полками удалена вместе со своими стилями:
   справка переехала на оборот заголовка (.sp-hints в блоке панели), а
   место под полками отдано самим полкам. */

/* ==============================================================
   LOADING
   ============================================================== */
.loading-overlay {
  /* Размытие снято: экран загрузки закрывает страницу на 95 %, а показывается
     ровно в тот момент, когда устройству и без того тяжело. */
  position: fixed; inset: 0; background: rgba(7,8,13,0.985);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  z-index: 1000; color: var(--ink); font-family: var(--serif);
}
.loading-spinner { width: 48px; height: 48px; border: 3px solid var(--line); border-top-color: var(--gold); border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: 16px; }
@keyframes spin { to { transform: rotate(360deg); } }
.loading-overlay p { font-size: 16px; color: var(--muted); }

/* ==============================================================
   RESPONSIVE
   ============================================================== */
/* планшет */
@media (max-width: 1023px) {
  :root { --bw: 132px; --bh: 196px; --bd: 15px; }
  .shelf-viewport { perspective: 1800px; }
}
/* мелкий планшет / крупный телефон */
@media (max-width: 767px) {
  :root { --bw: 112px; --bh: 168px; --bd: 13px; --book-tilt: -22deg; }
  .shelf-row { gap: clamp(10px, 2vw, 20px); padding: 0 clamp(16px, 4vw, 40px); }
  .book-3d:nth-child(3n)   { --book-tilt: -20deg; }
  .book-3d:nth-child(3n+1) { --book-tilt: -24deg; }
}
/* ── невысокое окно: планшет и телефон в ландшафте, ноутбук 768 ──
   Свободной высоты здесь меньше, чем нужно панели и всем рядам книг вместе.
   Раскладка этого не замечала: резерв под панель задан долей высоты окна
   (26vh), а ряды центрируются в .shelf-stage — на 768 и 375 пикселях высоты
   резерва не хватало, ряды всплывали ПОД панель и она накрывала заголовки
   верхнего тома, а нижний ряд уходил под плеер.
   Ряды теперь идут сверху вниз от нижней кромки плиты, а обзор прокручивается
   (так же, как на телефоне-портрете): ни один том не остаётся ни под панелью,
   ни за нижней кромкой. Колесо в этом состоянии листает полки, а не
   приближает, — см. wheel в shelf.js. */
@media (max-height: 880px) {
  :root { --bw: 118px; --bh: 176px; --bd: 13px; }
  /* Прокручивается страница, а не обзор. У внутреннего обзора есть кромка, и
     ряд, попавший на неё, обрезался ровной линией поперёк книг — том выглядел
     разрезанным пополам. Ни высоты, ни обрезки у обзора здесь нет: полки
     свободно продолжаются вниз, а листает их обычная прокрутка страницы.
     Маску верхнего края тоже снимаем — она растворяла верхушки книг. */
  .shelf-stage {
    justify-content: flex-start;
    gap: clamp(14px, 3vh, 28px);
    /* --shelf-reserve ставит syncPanelReserve() по настоящей высоте плиты;
       запасное значение нужно на первый кадр, пока замер не сделан. */
    padding-top: var(--shelf-reserve, calc(var(--header-h) + 190px));
  }
  .sp-face { padding: calc(12px * var(--ui-scale)) calc(24px * var(--ui-scale)); }
  .shelf-panel h1 { font-size: calc(24px * var(--ui-scale)); margin-bottom: calc(6px * var(--ui-scale)); }
  .shelf-subtitle { font-size:max(11px,calc(13px * var(--ui-scale))); margin-bottom: calc(10px * var(--ui-scale)); }
  .shelf-legend { padding-top: calc(8px * var(--ui-scale)); }
}

/* Совсем низкое окно — телефон в ландшафте: та же прокрутка, но книги и плита
   ещё мельче, иначе на 375 пикселях высоты в кадр не попадает даже один ряд. */
@media (max-height: 620px) {
  :root { --bw: 80px; --bh: 120px; --bd: 10px; }
  .shelf-stage {
    gap: clamp(12px, 3vh, 24px);
    padding-top: var(--shelf-reserve, calc(var(--header-h) + 150px));
  }
  .shelf-panel { margin-top: calc(var(--header-h) + 8px); }
  .sp-face { padding: calc(10px * var(--ui-scale)) calc(20px * var(--ui-scale)); }
  .shelf-panel h1 { font-size: calc(20px * var(--ui-scale)); margin-bottom: calc(4px * var(--ui-scale)); }
  .shelf-subtitle { font-size:max(11px,calc(12px * var(--ui-scale))); margin-bottom: calc(8px * var(--ui-scale)); }
  .shelf-legend { gap: calc(12px * var(--ui-scale)); padding-top: calc(6px * var(--ui-scale)); }
}

/* телефон-портрет: скролл, книги чуть более фронтально */
@media (max-width: 479px) {
  :root { --bw: 108px; --bh: 162px; --bd: 12px; --book-tilt: -16deg; }
  /* Как и на невысоких окнах: обзор ничего не обрезает, прокручивается страница.
     Раньше здесь стоял собственный скролл с overflow — на его кромке книги
     резались поперёк. */
  .shelf-viewport { perspective: 1300px; perspective-origin: 50% 46%; }
  .shelf-row { gap: clamp(12px, 3.5vw, 22px); }
  .book-3d:nth-child(3n), .book-3d:nth-child(3n+1) { --book-tilt: -16deg; }
}

/* ==============================================================
   ДОСТУПНОСТЬ / ПЕЧАТЬ
   ============================================================== */
@media (prefers-reduced-motion: reduce) {
  .book-3d, .progress-bar, .book-status { transition: none !important; }
  .book-slot:hover .book-3d, .book-slot:focus-visible .book-3d { transform: rotateY(var(--book-tilt)) translateY(-14px); }
  .book-3d.opening { transition: opacity 0.3s; }
  .loading-spinner { animation: none; }
}
@media (prefers-contrast: high) {
  .book-status { border-width: 2px; }
  .progress-bar { background: var(--gold); }
}
@media print {
  .shelf-viewport, .shelf-panel, .loading-overlay { display: none !important; }
}

/* ==============================================================
   СЛОТ КНИГИ — неподвижная зона наведения
   Том при наведении поднимается и наклоняется, то есть уезжает из-под
   курсора. Пока hover висел на самом томе, это давало бесконечное
   переключение состояний: книга дёргалась, особенно у краёв обложки.
   Теперь наведение ловит слот — он стоит на месте всегда.
   ============================================================== */
.book-slot{
  position:relative;width:var(--bw);height:var(--bh);flex-shrink:0;
  transform-style:preserve-3d;cursor:pointer;outline:none;
  /* Запас вокруг тома: поднявшаяся книга остаётся внутри своей зоны,
     поэтому курсор не выходит за неё во время анимации. */
  padding:0;
}
.book-slot::before{
  content:'';position:absolute;left:-14px;right:-14px;top:-42px;bottom:-14px;
  /* Прозрачная «подушка» — часть зоны наведения, а не отдельный элемент:
     иначе при подъёме книги курсор проваливался в промежуток. */
  transform:translateZ(-1px);
}
.book-slot .book-3d{position:absolute;inset:0;pointer-events:none}
/* Том выходит с полки мягко. Прежняя кривая cubic-bezier(.2,.8,.2,1) почти весь
   путь проходила за первую четверть времени — книга «выстреливала» из ряда и
   замирала. Ход удлинён и разложен ровнее, а сам вынос уменьшен: 70 px по Z
   при перспективе 2200 давали скачок масштаба, который глаз читал как рывок. */
.book-slot:hover .book-3d,
.book-slot:focus-visible .book-3d{
  transform:rotateY(-6deg) translateY(-22px) translateZ(48px) scale(1.03);
  z-index:20;filter:drop-shadow(0 30px 34px rgba(0,0,0,0.65));
}
.book-slot:focus-visible .book-3d{filter:drop-shadow(0 0 0 2px var(--gold)) drop-shadow(0 30px 34px rgba(0,0,0,0.6))}
.book-slot:hover{z-index:20}
.book-slot:hover .book-3d::after{opacity:.5}
.book-slot:hover .book-progress,.book-slot:focus-within .book-progress{opacity:1}
.book-slot:hover .book-status,.book-slot:focus-within .book-status{opacity:1;transform:translateX(-50%) translateZ(calc(var(--bd) + 20px)) translateY(0)}

/* Обрез страниц — тот же тон, что и страницы внутри книги (--page-edge):
   раньше корешок с кремовым обрезом обещал светлую бумагу, а внутри книга
   открывалась тёмной. Один предмет должен выглядеть одним предметом. */
.book-thickness{
  /* Тот же принцип, что в читалке: листы видны вертикальными линиями,
     повторяющимися по толщине книги. Полосы поперёк выглядели гофрой. */
  background:
    repeating-linear-gradient(90deg, rgba(255,255,255,0.10) 0 0.6px, transparent 0.6px 1.8px),
    repeating-linear-gradient(90deg, rgba(0,0,0,0.20) 0 0.5px, transparent 0.5px 3px),
    linear-gradient(90deg, var(--page-edge-dark) 0%, var(--page-edge) 42%, #b8a680 100%) !important;
  background-blend-mode: overlay, multiply, normal !important;
}
.book-3d .book-top{
  background:repeating-linear-gradient(90deg, var(--page-edge) 0 1px, var(--page-edge-dark) 1px 3px);
}

/* ── Адаптивное качество (см. блок в app.css) ──────────────────
   Правила полки живут здесь, а не в app.css: bundle-shelf.css подключается
   ПОСЛЕ bundle-core.css, и оттуда их было бы не переспорить. */
html.perf-low .sp-face::after,
html.perf-low .sp-face::before{ animation: none; }
html.perf-low .sp-face::after{ opacity: 0; }
html.perf-low .sp-face::before{ opacity: .55; }
html.perf-low .loading-overlay{ backdrop-filter: none; -webkit-backdrop-filter: none;
  background: rgba(7,8,13,0.985); }
@media(prefers-reduced-motion:reduce){.shelf-stage{transition:none}}

/* ==============================================================
   DZS — albums.css: страница «Альбомы» (/albums).
   Дословный порт <style>-блока DMN public/albums.html: один альбом —
   один экран (scroll-snap), eq-wave перемотка, блок «Скоро».
   body.albums-body ставит app.js по адресу (spa-navigate).
   ============================================================== */
/* ── HERO ────────────────────────────────────────── */
body.albums-body .hero{min-height:50vh;min-height:50svh;display:flex;flex-direction:column;align-items:center;
  justify-content:center;text-align:center;
  padding:calc(100px * var(--ui-scale)) 24px calc(60px * var(--ui-scale));position:relative;
  background:linear-gradient(180deg,rgba(7,8,13,.3) 0%,rgba(7,8,13,.7) 100%);
  overflow:hidden}
body.albums-body .hero-bg{position:absolute;inset:0;z-index:-1;background-size:cover;background-position:center;
  opacity:.35;filter:blur(2px)}
body.albums-body .hero h1{font-family:var(--serif);font-weight:600;line-height:1;
  font-size:clamp(48px,10vw,calc(96px * var(--ui-scale)));letter-spacing:.02em;
  background:linear-gradient(180deg,#fff,var(--gold) 60%,var(--ember));
  -webkit-background-clip:text;background-clip:text;color:transparent}
body.albums-body .hero .sub{font-family:var(--serif);font-style:italic;font-size:clamp(18px,2.8vw,calc(26px * var(--ui-scale)));
  color:var(--gold2);letter-spacing:.12em;margin-top:8px}
body.albums-body .hero .desc{margin-top:calc(16px * var(--ui-scale));max-width:calc(680px * var(--ui-scale));
  color:var(--muted);font-size:clamp(14px,1.8vw,calc(17px * var(--ui-scale)))}

/* ── ALBUM BLOCKS ────────────────────────────────── */
/* Высота блока считается от видимой части окна, а не набирается из слагаемых.
   Раньше складывались шапка (~90) + обложка с aspect-ratio 2.8/1 (на широком
   экране это 420+) + тело в 420 — под тысячу пикселей, и блок не помещался на
   экран целиком ни при каком размере окна.
   Теперь:
     • из высоты окна вычитаются шапка сайта, плеер с его ручкой и поля;
     • берётся 92 % остатка, чтобы снизу оставался «хвост» следующего блока —
       так видно, что список продолжается;
     • clamp снизу держит порог читаемости (ниже 430 треклист превращается в
       щель), сверху — 720: на большом мониторе блок не растёт до бесконечности,
       строки не разъезжаются, и всё содержимое остаётся в одном взгляде. */
/* Один альбом — один экран.
   Блок занимает всё свободное место между шапкой сайта и плеером, минус две
   «щёлки» сверху и снизу: в них выглядывают соседние альбомы, и по ним видно,
   что список продолжается в обе стороны.
   Раньше высота набиралась из слагаемых (шапка + обложка с aspect-ratio 2.8/1 +
   тело в 420) и под тысячу пикселей не помещалась ни в одно окно; потом я
   зажал её сверху 720 — стало мелко на большом мониторе. Теперь потолок
   поднят до 980: выше строки описания и треклист начинают разъезжаться. */
.albums-page{
  padding:0;
  --album-peek:clamp(26px, 5vh, 56px);
  --album-space:calc(100vh - var(--header-h) - var(--player-h) - var(--player-handle-h));
  --album-h:clamp(
    calc(430px * var(--ui-scale)),
    calc(var(--album-space) - var(--album-peek) * 2),
    calc(980px * var(--ui-scale)));
}
.album-block{
  margin:0 auto var(--album-peek);
  /* Своя ширина, уже общей .wrap: строка описания и треклист в 1220 px читаются
     тяжело — глаз теряет начало следующей строки. Потолок 1060px фиксирует
     эталонный вид (~1280 окно): min() режет только широкие экраны, где блок
     разъезжался до 1219+ и рвал пропорции с .wrap. */
  max-width:min(calc(1060px * var(--ui-scale)),1060px);
  height:var(--album-h);
  display:flex;flex-direction:column;
  scroll-snap-align:center;
  background:linear-gradient(180deg,rgba(255,255,255,.02),rgba(255,255,255,.005));
  border:1px solid var(--line);border-radius:calc(20px * var(--ui-scale));overflow:hidden;
  /* Объём вместо плоского блока: внешняя тень + светлая кромка сверху
     и тёмная снизу — как у опор и флипов на «О саге». */
  box-shadow:0 30px 60px -20px rgba(0,0,0,.9),0 0 0 1px rgba(0,0,0,.35),inset 0 2px 0 rgba(255,236,200,.2),inset 0 -3px 8px rgba(0,0,0,.55);
  transition:.3s}

/* Прокрутка живёт внутри раздела, а не на странице: только так работает
   привязка (scroll-snap) и только так колесо можно перехватить и листать по
   одному альбому за жест. Класс ставит app.js по адресу. */
body.albums-body{overflow:hidden}
body.albums-body #page-root{
  position:relative;
  height:calc(100vh - var(--player-h));
  overflow-y:auto;overscroll-behavior:contain;
  scroll-snap-type:y mandatory;
  scroll-padding-top:var(--header-h);
  padding-bottom:var(--album-peek);
}
body.albums-body .hero{scroll-snap-align:start}
body.albums-body .albums-page{padding-top:var(--album-peek)}
.album-block:hover{border-color:rgba(227,176,102,.25)}
/* Подсветка посадки с «О саге»: карточка, на которую привёл клик по
   обложке (/albums#album-N), на секунду вспыхивает золотом — иначе на
   длинной дискографии непонятно, куда именно привёл переход. */
.album-block.album-flash{border-color:var(--gold);box-shadow:0 0 0 2px rgba(227,176,102,.55),0 0 34px rgba(227,176,102,.25)}
@media (prefers-reduced-motion:reduce){.album-block.album-flash{box-shadow:none}}
/* Шапка: текст отцентрирован по вертикали в своей полосе (решение владельца) —
   одинаковый воздух сверху и снизу, а не прижат к баннеру. */
.album-header{padding:calc(12px * var(--ui-scale)) calc(28px * var(--ui-scale));
  display:flex;align-items:center;gap:calc(14px * var(--ui-scale));flex-wrap:wrap}
.album-era{font-size:max(12px,calc(14px * var(--ui-scale)));letter-spacing:.3em;text-transform:uppercase;
  color:var(--gold);font-weight:700}
/* Заголовок h3 и строку-лид убрали по макету владельца: имя альбома вшито
   в баннер, полное описание живёт в правой колонке верхней сетки. */
/* ── верхняя сетка альбома: баннер слева, информация справа ──
   Баннер крупнее (решение владельца): левая колонка 1.7fr — баннер ~667×333
   при блоке 1060 (2:1 сохранён, высота как у красного прямоугольника на
   макете), правая — 1fr. Слева живёт то же, что в левой колонке шторки
   плеера: полное описание, «сейчас играет», кнопки скачивания. */
.album-top{display:grid;grid-template-columns:1.7fr 1fr;align-items:stretch;
  border-bottom:1px solid var(--line)}
.album-top-media{min-width:0;min-height:0;overflow:hidden;background:var(--panel)}
/* Слот баннера — строго 2/1, самая узкая пропорция исходников (решение
   владельца, не менять). 2:1 ложится точно; у 2.5:1 подрезаются только бока,
   симметрично — вшитый в картинку титул остаётся по центру блока. Верх/низ
   не режутся никогда: у 2.5:1 избыток только в ширине. object-fit:cover
   обязателен (заливка всего слота), contain здесь запрещён. */
.album-cover{display:block;width:100%;height:auto;aspect-ratio:2/1;
  object-fit:cover;object-position:center;background:var(--panel)}
/* Правая колонка растягивается на высоту строки: высоту задаёт баннер слева —
   это и есть «строгая пропорция по высоте». */
.album-top-info{display:flex;flex-direction:column;text-align:center;min-width:0;min-height:0;
  padding:calc(14px * var(--ui-scale)) calc(20px * var(--ui-scale)) calc(16px * var(--ui-scale));
  gap:calc(10px * var(--ui-scale));overflow:hidden}
/* Описание занимает всё между верхом колонки (оно начинается на одном уровне
   с номером альбома в левой колонке) и «сейчас играет», при нехватке высоты
   листается внутри. flex-basis:0, а не auto: иначе высота самого длинного
   текста раздувала бы строку сетки, баннер переставал задавать высоту блока, и
   под ним оставалась бы дыра (узкие высокие окна). Текст отцентрирован по
   вертикали (решение владельца): свободное место делят авто-поля обёртки —
   при переполнении они схлопываются в ноль, и скролл начинается с верха,
   ничего не обрезая. Кегль — тот же, что у увеличенной шапки альбома. */
.album-top-desc{flex:1 1 0;min-height:0;overflow-y:auto;
  display:flex;flex-direction:column;
  font-size:max(12px,calc(14px * var(--ui-scale)));line-height:1.6}
.album-top-desc-in{margin:auto 0}
/* Строка «Описание альбома» над текстом: стиль — у .album-era (как «МИФИЧЕСКАЯ
   ЭПОХА» в шапке), здесь только посадка в блок центрирования. */
.album-top-desc-label{display:block;margin-bottom:calc(8px * var(--ui-scale))}
.album-top-desc .pd-motto{font-size:max(12px,calc(15px * var(--ui-scale)))}
.album-top-now{flex:0 0 auto}
/* Ничего не звучит из этого альбома — приглашение вместо трека, тише по тону. */
.pd-now-title.idle{color:var(--muted);font-style:italic}
/* Кнопки прижаты к низу колонки; .dl им стилизует .pd-album-dl из app.css. */
.album-top-dl{margin-top:auto;justify-content:center}

/* Тело забирает весь остаток высоты блока. min-height:0 обязателен: без него
   grid-элемент не даёт треклисту прокручиваться внутри себя и распирает блок. */
.album-body{display:grid;grid-template-columns:1.3fr 1.5fr;flex:1 1 auto;min-height:0}
.album-hero{
  border-right:1px solid var(--line);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:calc(32px * var(--ui-scale));text-align:center;
  background:rgba(255,255,255,.015);
  transition:opacity .35s;min-height:0;overflow:hidden}
.album-hero .hero-portrait{width:calc(220px * var(--ui-scale));max-height:calc(320px * var(--ui-scale));
  border-radius:calc(12px * var(--ui-scale));object-fit:contain;border:2px solid var(--gold);margin-bottom:calc(16px * var(--ui-scale));
  transition:opacity .35s,transform .35s;background:var(--panel)}
.album-hero .hero-portrait.fade{opacity:.3;transform:scale(.95)}
body.albums-body .hero-portrait-fallback{width:calc(200px * var(--ui-scale));height:calc(200px * var(--ui-scale));
  border-radius:calc(12px * var(--ui-scale));display:flex;align-items:center;justify-content:center;
  border:2px solid var(--faint);margin-bottom:calc(16px * var(--ui-scale));
  font-family:var(--serif);font-size:calc(80px * var(--ui-scale));color:var(--faint);
  background:var(--panel)}
.album-hero .hero-name{font-family:var(--serif);font-size:calc(26px * var(--ui-scale));
  font-weight:600;color:var(--gold2);margin-bottom:calc(4px * var(--ui-scale))}
/* Карточка персонажа с гранями (решение владельца): клик листает
   «певец → второй певец (дуэт) → текст песни» — как флип в шторке плеера.
   Текст — выбранного в альбоме трека; без выбора клик ничего не делает. */
.hero-card{position:relative;cursor:pointer;margin:0 auto;
  /* Плитка: все грани одного размера. Высота = вся свободная высота hero-
     колонки (решение владельца: блок «вот таких размеров», отцентрован по
     горизонтали и вертикали) — в невысоком окне плитка сжимается целиком,
     а не обрезается: у img внутри max-height в процентах. */
  height:100%;max-height:100%;aspect-ratio:11/18;
  overflow:hidden;border-radius:calc(12px * var(--ui-scale));
  border:1px solid var(--line);background:var(--panel)}
/* На повороте плитка выходит ЗА пределы блока: overflow:hidden обрезал бы
   призму по углам, и плитка выглядела обрезанной (решение владельца).
   Бокс на время оборота прозрачен: граням он больше не нужен, они
   непрозрачны сами, а рамка за панелями читалась бы как артефакт. */
.hero-card.is-turning{overflow:visible;background:transparent;border-color:transparent}
/* Грани непрозрачны: сквозь прозрачную грань певца просвечивала нижняя
   крышка призмы — плитка теряла объём (решение владельца). */
.hero-face{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;margin:0;background:var(--panel)}
.hero-face .hero-portrait{width:auto;max-width:calc(260px * var(--ui-scale));
  max-height:min(calc(400px * var(--ui-scale)), 80%);
  margin-bottom:calc(10px * var(--ui-scale))}
.hero-face .hero-name{padding:0 calc(10px * var(--ui-scale))}
.hero-face-lyr{background:var(--panel)}
.hero-lyr-scroll{position:absolute;inset:0;overflow-y:auto;overscroll-behavior:contain;
  padding:calc(16px * var(--ui-scale));font-size:calc(15px * var(--ui-scale));
  line-height:1.75;color:var(--muted);white-space:pre-wrap;text-align:left}
/* Флип «физической плитки» — те же кейфреймы, что у граней шторки плеера. */
.hero-face.flip-out{animation:flipOut .25s ease-in forwards}
.hero-face.flip-in{animation:flipIn .25s ease-out forwards}

/* ── TRACK LIST ──────────────────────────────────── */
/* Треклист прокручивается внутри своей половины и ровно по её высоте: жёсткий
   потолок в 420 не совпадал с высотой блока, и список то обрывался раньше
   границы, то вылезал за неё. */
.album-tracks{padding:calc(12px * var(--ui-scale)) calc(16px * var(--ui-scale));
  overflow-y:auto;min-height:0;max-height:none;overscroll-behavior:contain}
.track{display:flex;align-items:center;gap:calc(12px * var(--ui-scale));
  padding:calc(10px * var(--ui-scale)) calc(12px * var(--ui-scale));
  border-radius:calc(10px * var(--ui-scale));cursor:pointer;
  border:1px solid transparent;transition:.2s}
.track:hover{background:rgba(255,255,255,.04);border-color:var(--line)}
.track .tn{font-family:var(--serif);font-size:calc(18px * var(--ui-scale));color:var(--gold);
  width:calc(30px * var(--ui-scale));text-align:center;flex:0 0 auto}
.track .ti{flex:1;min-width:0}
.track .ti .tt{font-weight:600;font-size:max(11px,calc(15px * var(--ui-scale)))}
.track .ti .tg{font-size:max(11px,calc(11px * var(--ui-scale)));color:var(--faint);margin-top:calc(2px * var(--ui-scale))}
.track .dur{font-size:max(11px,calc(12px * var(--ui-scale)));color:var(--muted);font-variant-numeric:tabular-nums}
.track .play{flex:0 0 auto;color:var(--faint);transition:.2s;width:calc(28px * var(--ui-scale));
  height:calc(28px * var(--ui-scale));display:flex;align-items:center;justify-content:center;
  border-radius:50%;cursor:pointer}
.track .play:hover{color:var(--gold2);background:rgba(227,176,102,.12)}
.track .play.muted{opacity:.3;cursor:not-allowed}
.track .play svg{width:calc(16px * var(--ui-scale));height:calc(16px * var(--ui-scale))}
.track.playing{background:rgba(227,176,102,.08);border-color:rgba(227,176,102,.3)}
.track.playing .tn{color:var(--gold2)}
/* Кнопка играющей строки — золотая пауза: по ней видно, какой именно
   трек звучит, не глядя на нижний плеер. */
.track.playing .play{color:var(--gold2)}

.dlwrap{display:flex;gap:calc(4px * var(--ui-scale));flex:0 0 auto}
.dl{display:inline-flex;align-items:center;padding:calc(3px * var(--ui-scale)) calc(8px * var(--ui-scale));
  font-size:max(10px,calc(10px * var(--ui-scale)));font-weight:700;letter-spacing:.04em;border-radius:4px;
  cursor:pointer;transition:.2s;border:1px solid var(--line);color:var(--muted)}
.dl:hover{background:var(--gold);color:#1a0f06;border-color:var(--gold)}

/* ── album eq-wave ──────────────────────────────── */
.eq-wave{padding:calc(12px * var(--ui-scale)) calc(20px * var(--ui-scale));
  border-top:1px solid var(--line);background:rgba(255,255,255,.02)}
.eq-wave .eq-time{display:flex;justify-content:space-between;padding:0 2px;margin-bottom:calc(4px * var(--ui-scale));
  font-size:calc(10px * var(--ui-scale));color:var(--faint);font-variant-numeric:tabular-nums;pointer-events:none}
.eq-wave .eq-bars{display:flex;align-items:flex-end;gap:2px;height:calc(32px * var(--ui-scale));cursor:pointer;position:relative}
/* Как и в плеере: полоска растёт масштабом, ползунок едет трансформом —
   раскладка шкалы на каждом кадре не пересчитывается. */
.eq-wave .eq-bar{flex:1;background:rgba(255,255,255,.08);border-radius:1px;height:100%;cursor:pointer;
  transform:scaleY(.04);transform-origin:bottom;transition:transform .06s ease}
.eq-wave .eq-bar.filled{background:linear-gradient(180deg,var(--gold2),var(--ember))}
.eq-wave .eq-bar.buffered{background:rgba(255,255,255,.06)}
.eq-album-thumb{position:absolute;left:0;bottom:0;width:100%;height:100%;pointer-events:none;z-index:2;
  transform:translateX(var(--eq-pos,0%))}
.eq-album-thumb::after{content:'';position:absolute;top:50%;left:0;transform:translate(-50%,-50%);width:24px;height:24px;border-radius:50%;background:var(--gold2);box-shadow:0 0 12px rgba(227,176,102,.5)}
/* Чужая шкала стоит на месте (решение владельца): временная шкала идёт
   только у альбома играющего трека. У остальных полоски лежат базовой
   высотой, время и ползунок чужому треку не принадлежат. visibility —
   чтобы высота строки времени не прыгала при смене состояния. */
.eq-wave.idle .eq-time{visibility:hidden}
.eq-wave.idle .eq-album-thumb{display:none}
.eq-wave.idle .eq-bars,.eq-wave.idle .eq-bar{cursor:default}

/* ── футер в фикс-режиме ───────────────────────────
   На время раздела футер со статистикой переносится внутрь #page-root
   (переносит albums.js только в фикс-режиме: десктоп выше 1160px): на теле
   страницы он лежит ПОД скроллером, и долистать до него нельзя. Как
   последняя остановка перелистывания прижимается низом к краю скроллера.
   Сам скроллер уже заканчивается над плеером, поэтому запас под плавающий
   плеер из app.css (body.player-active footer) тут двойной — гасим. */
@media(min-width:768px){
  body.albums-body #page-root > footer{margin-top:0;scroll-snap-align:end;
    padding-bottom:calc(50px * var(--ui-scale))}
}

/* ── SOON BLOCK (Albums 02-14) ──────────────────── */
.album-soon{
  display:flex;align-items:center;gap:calc(24px * var(--ui-scale));
  padding:calc(28px * var(--ui-scale)) calc(28px * var(--ui-scale));
  background:linear-gradient(135deg,rgba(227,176,102,.04),rgba(169,139,240,.03));
  border-top:1px solid var(--line)}
.album-soon-cover{
  flex:0 0 auto;width:calc(180px * var(--ui-scale));height:calc(180px * var(--ui-scale));
  border-radius:calc(14px * var(--ui-scale));overflow:hidden;border:2px solid var(--gold);
  background:var(--panel);transition:.3s}
.album-soon-cover:hover{border-color:var(--gold2);transform:scale(1.03)}
.album-soon-cover img{width:100%;height:100%;object-fit:cover}
.album-soon-cover.no-cover{
  display:flex;align-items:center;justify-content:center;
  font-family:var(--serif);font-size:calc(48px * var(--ui-scale));color:var(--faint)}
.album-soon-cover.no-cover::before{content:attr(data-letter);opacity:.4}
.album-soon-info{flex:1;min-width:0}
.album-soon-icon{
  font-size:calc(28px * var(--ui-scale));color:var(--gold);
  animation:pulse-glow 3s ease-in-out infinite;margin-bottom:calc(8px * var(--ui-scale))}
/* Кадры pulse-glow объявлены в src/css/app.css: они нужны ещё и значкам ✦
   в ящике плеера (.pd-soon-icon/.pd-soon-badge), а он открывается на любой
   странице. Здесь роутер удалял их вместе со стилями партиала. */
.album-soon-info h4{
  font-family:var(--serif);font-size:calc(22px * var(--ui-scale));
  font-weight:600;color:var(--gold2);margin-bottom:calc(6px * var(--ui-scale))}
.album-soon-info p{
  font-size:max(11px,calc(14px * var(--ui-scale)));color:var(--muted);
  margin-bottom:calc(8px * var(--ui-scale))}
.album-soon-meta{
  font-size:max(11px,calc(11px * var(--ui-scale)));color:var(--faint);
  letter-spacing:.06em;text-transform:uppercase}

@media(max-width:767px){
  .album-soon{flex-direction:column;text-align:center}
  .album-soon-cover{width:calc(140px * var(--ui-scale));height:calc(140px * var(--ui-scale))}
}

/* ── невысокие окна: колонки не сжимаются в щель (решение владельца) ──
   Фикс-высота блока при низком окне выдавливала hero-колонку: карточка
   обрезалась, треклист съёживался. Ниже порога блок получает минимальную
   высоту и растёт: карточка-плитка держит свой размер (портрет сжимается,
   а не режется), треклист СОХРАНЯЕТ внутреннюю прокрутку — область
   фиксированной высоты, список листается внутри. Раздел прокручивается
   страницей; на высоких окнах остаётся поэкранная привязка. */
@media(min-width:768px) and (max-height:1160px){
  body.albums-body{overflow:visible}
  body.albums-body #page-root{height:auto;overflow-y:visible;scroll-snap-type:none;padding-bottom:0}
  .album-block{height:auto;min-height:var(--album-h);scroll-snap-align:none}
  /* Баннеру своей правила не нужно: его высота — половина ширины блока
     (слот 2/1), от режима прокрутки не зависит. */
  .album-hero{overflow:visible;min-height:calc(480px * var(--ui-scale))}
  .album-tracks{height:calc(480px * var(--ui-scale));min-height:calc(480px * var(--ui-scale))}
}

/* ── планшет/узкое окно: столбик раньше мобилы ──
   Окно 768–1023: две колонки (баннер+описание, портрет+треклист) давятся
   в щель — текст описания вьётся змейкой, портрет и строки съёживаются
   (эталон — полный блок 1060). Ниже 1024 блок строится столбиком по тем
   же правилам, что мобила: баннер 2/1 на всю ширину, описание естественной
   высоты, портрет, треклист фиксированной высоты с внутренней прокруткой.
   Только свободный режим (высота ≤1160): в фикс-режиме с привязкой
   (высокое узкое окно) столбик вылез бы за экран и сломал snap-остановки —
   там остаётся ужатая, но целая двухколоночная версия. */
@media(min-width:768px) and (max-width:1023px) and (max-height:1160px){
  .album-top{grid-template-columns:1fr}
  .album-top-desc{flex:0 0 auto;overflow:visible}
  .album-body{grid-template-columns:1fr}
  .album-hero{border-right:0;border-bottom:1px solid var(--line);overflow:visible}
  .album-soon{flex-direction:column;text-align:center}
}

/* ── responsive ──────────────────────────────────── */
@media(max-width:767px){
  body.albums-body .hero{min-height:40vh;min-height:40svh;padding:calc(80px * var(--ui-scale)) 18px calc(40px * var(--ui-scale))}
  /* На телефоне половины встают друг под друга — держать блок в высоту экрана
     нельзя, портрет и треклист в неё вдвоём не влезут. Возвращаем свободную
     высоту и обычную прокрутку страницы.
     Вместе с высотой ОБЯЗАТЕЛЬНО снимаем привязку прокрутки. Она осталась от
     десктопа, а блок стал выше окна (1319 против 741 на 390×844): обязательная
     привязка возвращала прокрутку на точку притяжения, и низ блока — треклист
     целиком — был недостижим. Раздел снова прокручивается как обычная
     страница, без внутреннего скроллера с фиксированной высотой: на телефоне
     он ещё и дерётся со схлопыванием адресной строки. */
  body.albums-body{overflow:visible}
  body.albums-body #page-root{
    height:auto;overflow-y:visible;scroll-snap-type:none;padding-bottom:0}
  .album-block{height:auto;max-width:100%;scroll-snap-align:none}
  /* Верхняя сетка в столбик: баннер сверху — тот же слот 2/1 на всю ширину,
     информация под ним естественной высоты, без принудительного matching.
     Пропорция баннера не меняется и здесь (решение владельца). */
  .album-top{grid-template-columns:1fr}
  .album-top-desc{flex:0 0 auto;overflow:visible}
  .album-body{grid-template-columns:1fr}
  .album-hero{border-right:0;border-bottom:1px solid var(--line);padding:calc(16px * var(--ui-scale));overflow:visible}
  .album-tracks{max-height:none;overflow-y:visible}
  .album-header{padding:calc(8px * var(--ui-scale)) calc(18px * var(--ui-scale))}
  /* Плитке персонажа нужна явная высота: в столбике авто-высот height:100%
     не от чего считать (треклист не задаёт высоту строки, как на десктопе) —
     без этого карточка схлопывается в ноль. */
  .hero-card{height:calc(380px * var(--ui-scale));max-height:none}
  .album-hero .hero-portrait,.hero-portrait-fallback{width:calc(170px * var(--ui-scale));max-height:calc(260px * var(--ui-scale))}
  .albums-page{padding:calc(30px * var(--ui-scale)) 0 calc(100px * var(--ui-scale))}
}

/* Компоненты DZS, которых нет в app.css (порте DMN).
   Правило: здесь НЕТ селекторов, которые определяет app.css (шапка, капли,
   кнопки .btn, оверлеи, hero, секции) — иначе перебивали бы визуал старого
   сайта. Только вспомогательные сетки/бейджи/таблицы для разделов, чей
   дизайн ещё не перенесён (карта-карточки, сообщество, админка). */

/* ── Сетка карточек ─────────────────────────────────────────── */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; margin-top: 28px; }
/* .card в app.css — карточка альбома (арт + занавес). Карточки текстовых
   разделов живу только внутри .grid, чтобы не задевать альбомные. */
.grid .card {
    padding: 22px;
}
.grid .card h3 { margin-bottom: 8px; }
.grid .card p { color: var(--muted); font-size: 14.5px; }
.card .card-meta { margin-top: 14px; font-size: 13px; color: var(--faint); }
.muted { color: var(--muted); }

/* Заголовки разделов без hero-секции: app.css глобальных h1–h3 не задаёт,
   а разделам-карточкам нужен сериф — как на всём сайте. :not(.b-h*) обязателен:
   заголовки книги читалки и Библии имеют свои размеры из reader-book.css,
   и id-селектор их перебивал (заголовок «КНИГА 0: ПРОЛОГ» уезжал в 56px). */
.sec h1:not(.b-h1), .sec h2:not(.b-h2), .sec h3:not(.b-h3) { font-family: var(--serif); font-weight: 600; line-height: 1.15; }
.sec h1:not(.b-h1) { font-size: clamp(34px, 5vw, 56px); }
.sec h2:not(.b-h2) { font-size: clamp(30px, 4vw, 44px); }
.sec h3:not(.b-h3) { font-size: 22px; }

/* ── Бейджи ролей и статусов ────────────────────────────────── */
.badge {
    display: inline-block; padding: 3px 10px; border-radius: 20px;
    font: 700 11px/1.6 var(--sans); letter-spacing: .06em; text-transform: uppercase;
    border: 1px solid var(--line); color: var(--muted);
}
.badge.gold { color: var(--gold); border-color: rgba(227,176,102,.45); }
.badge.violet { color: var(--violet); border-color: rgba(169,139,240,.4); }
.badge.ember { color: var(--ember); border-color: rgba(226,96,58,.4); }
.badge.cyan { color: var(--cyan); border-color: rgba(106,214,224,.4); }

/* Модификаторы кнопок поверх .btn из app.css (панели, формы) */
.btn.small { padding: 8px 16px; font-size: 13px; }
.btn.danger { border-color: var(--ember); color: var(--ember); }
.btn.danger:hover { background: rgba(226,96,58,.1); }

/* ── Формы (админка, VIP, справочники) ──────────────────────── */
.form-row { margin-bottom: 16px; }
label { display: block; margin-bottom: 6px; font: 600 13px var(--sans); color: var(--muted); }
input[type="text"], input[type="password"], input[type="search"], input[type="email"],
textarea, select {
    width: 100%; padding: 12px 14px; border-radius: 12px;
    background: rgba(255,255,255,.03); border: 1px solid var(--line);
    color: var(--ink); font: 15px/1.4 var(--sans); outline: none;
    transition: border-color .2s;
}
input:focus, textarea:focus, select:focus { border-color: var(--gold); }
textarea { min-height: 110px; resize: vertical; }
.err { color: var(--ember); font-size: 13.5px; margin-top: 10px; min-height: 1.2em; }

/* ── Тост-уведомления ───────────────────────────────────────── */
#toast-wrap { position: fixed; bottom: 24px; right: 24px; z-index: 300; display: flex; flex-direction: column; gap: 10px; }
.toast {
    background: var(--panel); border: 1px solid var(--line); border-left: 3px solid var(--gold);
    color: var(--ink); padding: 13px 18px; border-radius: 12px; font: 14px/1.4 var(--sans);
    box-shadow: 0 12px 30px rgba(0,0,0,.4); opacity: 0; transform: translateY(8px);
    transition: opacity .25s, transform .25s; max-width: 340px;
}
.toast.show { opacity: 1; transform: none; }
.toast.err { border-left-color: var(--ember); }
.toast.ok { border-left-color: #5ec98a; }

/* ── Списки и таблицы (справочники, админка) ────────────────── */
.list { list-style: none; margin-top: 12px; }
.list li {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 13px 4px; border-bottom: 1px solid var(--line); font-size: 15px;
}
.list li .muted { color: var(--muted); }

.table { width: 100%; border-collapse: collapse; margin-top: 20px; font-size: 14.5px; }
.table th {
    text-align: left; padding: 10px 12px; color: var(--muted);
    font: 700 12px var(--sans); letter-spacing: .08em; text-transform: uppercase;
    border-bottom: 1px solid var(--line);
}
.table td { padding: 11px 12px; border-bottom: 1px solid rgba(255,255,255,.05); }
.table tr:hover td { background: rgba(255,255,255,.02); }

/* ── Вкладки-фильтры ────────────────────────────────────────── */
.tabs { display: flex; gap: 4px; flex-wrap: wrap; margin: 24px 0 8px; }
.tab-btn {
    padding: 9px 18px; border: 1px solid var(--line); border-radius: 20px;
    background: transparent; color: var(--muted); cursor: pointer; font: 600 14px var(--sans);
    transition: color .2s, border-color .2s, background .2s;
}
.tab-btn:hover { color: var(--gold); }
.tab-btn.on { color: var(--gold); border-color: var(--gold); background: rgba(227,176,102,.08); }

/* ── Заглушки разделов ──────────────────────────────────────── */
.steps { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 22px; }
.step {
    flex: 1 1 200px; padding: 18px; border: 1px dashed var(--line); border-radius: 14px;
    color: var(--muted); font-size: 14px;
}
.step b { display: block; color: var(--gold); font-family: var(--serif); font-size: 20px; margin-bottom: 6px; }

/* Заглушка закрытого владельцем раздела — точный порт DMN/public/coming-soon.html.
   Классы .cs-* принадлежат только заглушке; общие .wrap/.sec не трогаем.
   Keyframes носят уникальное имя soonSpin (общее правило бандла: имена
   @keyframes обязаны быть уникальными на весь app.css).
   Подключена в build_site.py: css_files — после components.css, до fonts.css. */
.cs-wrap{min-height:calc(100vh - var(--header-h));display:flex;align-items:center;justify-content:center;padding:calc(72px * var(--ui-scale)) calc(24px * var(--ui-scale));position:relative}
.cs-box{text-align:center;max-width:calc(520px * var(--ui-scale))}
.cs-sigil{width:calc(80px * var(--ui-scale));height:calc(80px * var(--ui-scale));margin:0 auto calc(28px * var(--ui-scale));opacity:.85;
  border-radius:50%;filter:drop-shadow(0 0 8px rgba(227,176,102,.25))}
@keyframes soonSpin{to{transform:rotate(360deg)}}
.cs-label{font-size:max(11px,calc(11px * var(--ui-scale)));letter-spacing:.35em;text-transform:uppercase;color:var(--gold);font-weight:700;margin-bottom:calc(14px * var(--ui-scale))}
.cs-title{font-family:var(--serif);font-size:clamp(28px,4vw,calc(42px * var(--ui-scale)));font-weight:600;line-height:1.15;margin-bottom:calc(16px * var(--ui-scale));background:linear-gradient(180deg,var(--gold2),var(--gold),var(--ember));-webkit-background-clip:text;background-clip:text;color:transparent}
.cs-desc{font-size:clamp(14px,1.4vw,calc(17px * var(--ui-scale)));color:var(--muted);line-height:1.7;margin-bottom:calc(28px * var(--ui-scale))}
.cs-btn{display:inline-flex;align-items:center;gap:calc(6px * var(--ui-scale));padding:calc(10px * var(--ui-scale)) calc(24px * var(--ui-scale));border-radius:calc(8px * var(--ui-scale));border:1px solid var(--gold);background:rgba(227,176,102,.08);color:var(--gold2);font-family:var(--sans);font-weight:600;font-size:max(11px,calc(14px * var(--ui-scale)));cursor:pointer;transition:.2s;text-decoration:none}
.cs-btn:hover{background:var(--gold);color:var(--bg)}
.cs-note{margin-top:calc(18px * var(--ui-scale));font-size:max(11px,calc(12px * var(--ui-scale)));color:var(--faint);letter-spacing:.04em}
.cs-list{list-style:none;padding:0;margin:calc(-8px * var(--ui-scale)) 0 calc(26px * var(--ui-scale));display:flex;flex-wrap:wrap;gap:calc(8px * var(--ui-scale));justify-content:center}
.cs-list li{padding:calc(5px * var(--ui-scale)) calc(12px * var(--ui-scale));border:1px solid var(--line);border-radius:calc(20px * var(--ui-scale));font-size:max(11px,calc(12px * var(--ui-scale)));color:var(--gold2);background:rgba(227,176,102,.06)}
.cs-list:empty{display:none}
html.perf-low .cs-sigil .spin{animation:none}

/* Планеты: SVG-превью в карточках и мастерской. */
.planet-svg { margin-bottom: 10px; }
.planet-svg svg { display: block; width: 100%; height: 100%; }
.planet-card .planet-svg { margin: 0 auto 10px; }
.planet-fallback {
    display: inline-block; width: 34px; height: 34px; border-radius: 50%;
    background: var(--violet); margin-bottom: 10px;
}
/* Тег бога на карточке */
.god-tag {
    display: inline-block; padding: 2px 10px; border-radius: 20px;
    font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
    border: 1px solid var(--line); color: var(--muted); margin-top: 8px;
}

/* Справочник персонажей: hero, фильтры, карточки-обороты, лайтбокс. */
.ch-hero {
    min-height: 32vh; display: flex; flex-direction: column;
    align-items: center; justify-content: center; text-align: center;
    padding: 110px 24px 30px;
}
.ch-hero h1 {
    font-family: var(--serif); font-size: clamp(36px, 5vw, 62px); font-weight: 700;
    background: linear-gradient(180deg, var(--gold2), var(--gold), var(--ember));
    -webkit-background-clip: text; background-clip: text; color: transparent;
    margin-bottom: 12px;
}
.ch-hero .sub { font-size: clamp(15px, 1.5vw, 19px); color: var(--muted); max-width: 600px; margin:1em auto; }
.filters { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 28px; align-items: center; }
.filters select, .filters input[type="search"] {
    background: var(--panel); border: 1px solid var(--line); color: var(--ink);
    padding: 8px 14px; border-radius: 8px; font-size: 13px; font-family: var(--sans);
    /* Глобальные компоненты дают select/поиску width:100% — в строке фильтров
       они от этого разъезжались на всю ширину друг под другом. */
    width: auto; min-width: 150px;
}
.filters select:focus, .filters input[type="search"]:focus { border-color: var(--gold); outline: 0; }
.filters input[type="search"] { width: auto; }
.filters .count { font-size: 13px; color: var(--faint); margin-left: auto; }

/* ── Сетка: размер карточек как был до волны 1.2 (решение владельца) ── */
.char-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
.char-card {
    position: relative; margin: 0; cursor: pointer; outline: none;
    /* Радиус нужен самой плитке как источник для призмы переворота: панели
       временной 3D-призмы тянут его цепочкой inherit от хоста (см.
       .fl-prism-stage в app.css). Сами грани несут те же 14px. */
    border-radius: 14px;
}
.char-card.is-turning { z-index: 8; }
.char-card:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 14px; }

/* Переворот плитки: лицо (портрет) и оборот (безопасная биография).
   Паттерн оборотов — albums.js: flip-out → flip-in, 250 мс; ключевые кадры
   flipOut/flipIn — глобальные в app.css, новых имён не заводим. */
.ch-flip { position: relative; perspective: 1000px; }
.ch-face {
    background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
    transition: border-color .3s; backface-visibility: hidden; overflow:hidden;
}
.char-card:hover .ch-face { border-color: rgba(227, 176, 102, .4); }
.ch-front { position: relative; }
/* Оборот лежит поверх лица и по умолчанию спрятан (правило visibility:hidden
   для закрытых слоёв); высоту контейнера держит фронт — сетка остаётся ровной. */
.ch-back {
    position: absolute; inset: 0; visibility: hidden;
    background: linear-gradient(180deg, var(--panel), rgba(227, 176, 102, .05) 140%);
}
.ch-face.flip-out { animation: flipOut .25s ease-in forwards; }
.ch-face.flip-in { animation: flipIn .25s ease-out forwards; }
@media (prefers-reduced-motion: reduce) {
    .ch-face.flip-out, .ch-face.flip-in { animation: none; }
}

/* Лицевая грань: портрет 2:3 + бейдж-титул + имя. */
.ch-front .ch-img {
    width: 100%; aspect-ratio: 2 / 3; object-fit: cover; display: block;
    border-bottom: 1px solid var(--line); border-radius: 13px 13px 0 0;
    transition: transform .6s ease;
}
.char-card:hover:not(.is-turning) .ch-img { transform: none; }
.char-card.is-turning .ch-img { transform: none; }
.char-card.is-turning [data-portrait] { transform:none!important;transition:none!important; }
.ch-body { padding: 12px 16px 14px; }
.ch-badge {
    font-size: 10px; letter-spacing: 2px; text-transform: uppercase;
    color: var(--gold); font-weight: 600; margin-bottom: 4px;
}
.ch-name { font-family: var(--serif); font-size: 19px; font-weight: 600; color: var(--gold2); margin-bottom: 2px; }
.ch-book { font-size: 11px; color: var(--faint); }

/* Оборот: титул/вид/книги + безопасная биография + деталь; если текст
   не влезает — скролл внутри грани (как у оборотов полки/альбомов). */
.ch-back .ch-scroll {
    position: absolute; inset: 0; overflow-y: auto;
    padding: 16px 18px 14px; display: flex; flex-direction: column;
    scrollbar-width: thin; scrollbar-color: rgba(227, 176, 102, .35) transparent;
}
.ch-back .ch-scroll::-webkit-scrollbar { width: 6px; }
.ch-back .ch-scroll::-webkit-scrollbar-thumb { background: rgba(227, 176, 102, .35); border-radius: 3px; }
.ch-back .ch-name { font-size: 18px; }
.ch-meta { font-size: 11px; color: var(--faint); margin-bottom: 10px; }
.ch-bio, .ch-desc-full {
    font-size: 16px; color: var(--muted); line-height: 1.55; margin-bottom: 10px;
}
/* Деталь без спойлеров — голосом Летописца: сериф-курсив, тихо. */
.ch-safe {
    font-family: var(--serif); font-style: italic; font-size: 14px;
    color: var(--gold2); line-height: 1.5;
    border-left: 2px solid rgba(227, 176, 102, .4); padding-left: 10px;
}
.ch-hint {
    margin-top: auto; padding-top: 10px; font-size: 10px; color: var(--faint);
    letter-spacing: .5px;
}

/* ── Лайтбокс: портрет полного разрешения (клик/тап по карточке) ── */
.ch-lightbox {
    position: fixed; inset: 0; z-index: 200;
    background: rgba(4, 5, 9, .93);
    display: flex; align-items: center; justify-content: center; padding: 24px;
}
.ch-lightbox[hidden] { display: none; }
.ch-lb-fig { margin: 0; max-width: min(92vw, 760px); text-align: center; }
.ch-lb-fig img {
    max-width: 100%; max-height: 84vh; width: auto; height: auto;
    border: 1px solid rgba(227, 176, 102, .35); border-radius: 10px;
    display: block; margin: 0 auto;
}
.ch-lb-fig figcaption {
    margin-top: 12px; font-family: var(--serif); font-size: 17px; color: var(--gold2);
}
.ch-lb-close {
    position: absolute; top: 18px; right: 22px;
    background: transparent; border: 1px solid rgba(227, 176, 102, .4); color: var(--gold);
    width: 40px; height: 40px; border-radius: 50%; font-size: 16px; cursor: pointer;
}
.ch-lb-close:hover { border-color: var(--gold); }

/* Тумблер полных судеб: явное действие вместо спойлер-стены. */
.fate-toggle {
    background: transparent; border: 1px solid rgba(227, 176, 102, .4); color: var(--gold);
    padding: 8px 14px; border-radius: 8px; font-size: 13px; font-family: var(--sans);
    cursor: pointer; transition: border-color .25s, color .25s;
}
.fate-toggle:hover { border-color: var(--gold); }
.fate-toggle[aria-pressed="true"] { background: rgba(227, 176, 102, .12); border-color: var(--gold); }

@media (max-width: 767px) {
    .ch-hero { padding: 100px 16px 30px; }
    /* Портреты в две колонки даже на телефоне: одна колонка раздувает
       карточки до бесполезного размера, в двух лицо читается. */
    .char-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
    .filters select, .filters input[type="search"] { flex: 1; }
    .fate-toggle { width: 100%; }
    /* Вертикальный скролл страницы важнее жеста: горизонтальный свайп по
       карточке ловим в JS, вертикальный прокрут не блокируем. */
    .char-card { touch-action: pan-y; }
    .ch-back .ch-scroll { padding: 13px 14px 12px; }
    .ch-lb-close { top: 10px; right: 10px; }
}

/* Социал: звёзды, избранное, комментарии, лента, страница планеты. */

/* Рейтинг-звёзды: класс .stars занят звёздным полем сайта (app.css —
   fixed-фон на весь экран). Контейнеру рейтинга — собственное имя. */
.rating-stars { display: inline-block; font-size: 22px; letter-spacing: 2px; }
.star { color: #555; cursor: default; }
.star.on { color: var(--gold, #e8b64c); }
.star[data-score] { cursor: pointer; }
.star[data-score]:hover { color: #fff; }
.star.mine { text-shadow: 0 0 6px var(--gold, #e8b64c); }

.favbtn {
    background: none; border: none; cursor: pointer;
    font-size: 20px; color: #777; padding: 0 2px; vertical-align: middle;
}
.favbtn.on { color: var(--gold, #e8b64c); }
.favbtn:hover { color: #fff; }

.comment { border-top: 1px solid #333; padding: 8px 0; }
.comment p { margin: 4px 0; }
.comment .muted, .muted { color: #888; font-size: 0.9em; }
.linklike {
    background: none; border: none; cursor: pointer;
    color: #888; font-size: 0.85em; padding: 0; text-decoration: underline;
}
.linklike:hover { color: #c00; }
.comment-form textarea {
    width: 100%; min-height: 70px; margin: 8px 0;
    background: #111; color: #eee; border: 1px solid #444; border-radius: 6px;
    padding: 8px; box-sizing: border-box;
}

.feed-item { border-top: 1px solid #333; padding: 10px 0; }
.feed-item p { margin: 4px 0 0; }

.planet-hero { display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap; }
.planet-actions { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
.planet-actions .btn.small { padding: 4px 12px; }
.planet-page h1 { margin-top: 0; }

.card-meta { color: #888; font-size: 0.85em; margin-top: 8px; }

/* ==============================================================
   DZS — ochag.css: Площадь соцсети «Очаг» (Э1).
   Законы дизайн-роли:
   — одна золотая доминанта на экран: ею служит цитата дня (закон 5);
   — интонация Летописца: сериф для вести/вопроса/цитаты, без маркетинга;
   — анимации только transform/opacity, heavy-эффектов нет вовсе.
   Разметку строит src/js/ochag.js (ленивый bundle-social.js).
   ============================================================== */

/* ── Герой Площади: тихий вход, голос Летописца ─────────────── */
.oc-hero { padding: calc(72px * var(--ui-scale)) 0 calc(8px * var(--ui-scale)); text-align: center; }
.oc-hero h1 {
    font-family: var(--serif); font-weight: 600; line-height: 1.05;
    font-size: clamp(40px, 6vw, calc(64px * var(--ui-scale)));
    margin-top: calc(10px * var(--ui-scale));
}
.oc-hero .lead { margin-left: auto; margin-right: auto; }

/* ── Цитата дня: деталь без кнопок (план §4) ──────────────────
   Единственный золотой акцент страницы — строка из Библии серифом.
   Никаких рамок и иконок: слово само держит место. */
.oc-quote {
    margin: calc(34px * var(--ui-scale)) auto calc(6px * var(--ui-scale));
    max-width: calc(760px * var(--ui-scale));
    text-align: center;
    padding: calc(30px * var(--ui-scale)) calc(26px * var(--ui-scale)) calc(22px * var(--ui-scale));
    position: relative;
}
.oc-quote::before {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(58% 80% at 50% 42%, rgba(227,176,102,.06), transparent 72%);
}
.oc-quote-text {
    font-family: var(--serif); font-style: italic; font-weight: 600;
    font-size: clamp(22px, 3vw, calc(30px * var(--ui-scale))); line-height: 1.3;
    background: linear-gradient(180deg, var(--gold2), var(--gold));
    -webkit-background-clip: text; background-clip: text; color: transparent;
    position: relative;
}
.oc-quote-src {
    margin-top: calc(10px * var(--ui-scale));
    font-size: max(11px, calc(12px * var(--ui-scale)));
    letter-spacing: .22em; text-transform: uppercase; color: var(--faint);
    position: relative;
}

/* ── Секции Площади: плотнее типового .sec ──────────────────── */
.oc-wrap .sec { padding: calc(46px * var(--ui-scale)) 0 0; }
.oc-wrap .sec > h2 {
    font-size: clamp(26px, 3.4vw, calc(36px * var(--ui-scale)));
    margin-bottom: calc(6px * var(--ui-scale));
}
.oc-note { color: var(--faint); font-size: max(11px, calc(14px * var(--ui-scale))); margin: calc(4px * var(--ui-scale)) 0 calc(14px * var(--ui-scale)); }
.oc-empty {
    border: 1px dashed rgba(255,255,255,.1); border-radius: calc(14px * var(--ui-scale));
    padding: calc(22px * var(--ui-scale)); color: var(--faint);
    font-size: max(11px, calc(14px * var(--ui-scale)));
    background: rgba(255,255,255,.015);
}

/* ── Вести ──────────────────────────────────────────────────── */
.oc-newscard {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(16px * var(--ui-scale));
    padding: calc(20px * var(--ui-scale)) calc(24px * var(--ui-scale));
    margin-top: calc(14px * var(--ui-scale));
}
.oc-newscard.pinned { border-color: rgba(227,176,102,.35); }
.oc-newscard .oc-body p { margin: 0 0 calc(10px * var(--ui-scale)); color: var(--ink); font-size: max(12px, calc(15.5px * var(--ui-scale))); line-height: 1.65; }
.oc-newscard .oc-body p:last-child { margin-bottom: 0; }
.oc-newscard .oc-body { font-family: var(--serif); font-size: calc(19px * var(--ui-scale)); }
.oc-pin {
    display: block;
    margin-bottom: calc(6px * var(--ui-scale));
    color: var(--gold); font-size: max(10px, calc(11px * var(--ui-scale)));
    letter-spacing: .14em; text-transform: uppercase;
}
.oc-meta { color: var(--faint); font-size: max(10px, calc(12.5px * var(--ui-scale))); }
.oc-newscard .oc-meta { display: block; margin-top: calc(10px * var(--ui-scale)); }
.oc-compose { border-style: dashed; }
.oc-compose textarea, .oc-answer-form textarea, .oc-review-form textarea {
    width: 100%; min-height: calc(72px * var(--ui-scale)); resize: vertical;
    background: var(--bg2); color: var(--ink);
    border: 1px solid var(--line); border-radius: calc(10px * var(--ui-scale));
    padding: calc(12px * var(--ui-scale)) calc(14px * var(--ui-scale));
    font-family: var(--sans); font-size: max(12px, calc(14.5px * var(--ui-scale)));
    box-sizing: border-box;
}
.oc-compose textarea:focus, .oc-answer-form textarea:focus, .oc-review-form textarea:focus { outline: none; border-color: rgba(227,176,102,.5); }
.oc-compose-row { display: flex; align-items: center; gap: calc(14px * var(--ui-scale)); margin-top: calc(12px * var(--ui-scale)); flex-wrap: wrap; }
.oc-check { display: inline-flex; align-items: center; gap: calc(6px * var(--ui-scale)); color: var(--muted); font-size: max(11px, calc(13px * var(--ui-scale))); cursor: pointer; }
.oc-status { color: var(--gold2); font-size: max(11px, calc(13px * var(--ui-scale))); }
.oc-more-line { text-align: center; margin-top: calc(18px * var(--ui-scale)); }

/* ── Вопрос недели ──────────────────────────────────────────── */
.oc-question {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(16px * var(--ui-scale));
    padding: calc(24px * var(--ui-scale)) calc(26px * var(--ui-scale));
    margin-top: calc(14px * var(--ui-scale));
}
.oc-q-body {
    font-family: var(--serif); font-style: italic; font-weight: 600;
    font-size: clamp(22px, 3vw, calc(28px * var(--ui-scale))); line-height: 1.3;
    color: var(--gold2); margin-bottom: calc(16px * var(--ui-scale));
}
.oc-answer { border-top: 1px solid rgba(255,255,255,.05); padding: calc(12px * var(--ui-scale)) 0; }
.oc-answer:first-child { border-top: none; }
.oc-answer p { margin: calc(4px * var(--ui-scale)) 0 0; color: var(--muted); font-size: max(12px, calc(14.5px * var(--ui-scale))); }
.oc-answer.best { border-top-color: rgba(227,176,102,.25); }
.oc-answer-form { margin-top: calc(14px * var(--ui-scale)); }
.oc-answer-form .btn { margin-top: calc(10px * var(--ui-scale)); }
.oc-ask { margin-top: calc(14px * var(--ui-scale)); }
.oc-ask summary { cursor: pointer; color: var(--gold); font-size: max(11px, calc(13px * var(--ui-scale))); min-height: 44px; display: flex; align-items: center; }
.oc-ask textarea { margin: calc(10px * var(--ui-scale)) 0; }
.oc-ask details textarea {
    width: 100%; min-height: calc(64px * var(--ui-scale)); resize: vertical;
    background: var(--bg2); color: var(--ink);
    border: 1px solid var(--line); border-radius: calc(10px * var(--ui-scale));
    padding: calc(12px * var(--ui-scale)) calc(14px * var(--ui-scale));
    font-family: var(--sans); font-size: max(12px, calc(14.5px * var(--ui-scale))); box-sizing: border-box;
}

/* ── Отзывы ─────────────────────────────────────────────────── */
.oc-reviews-grid {
    display: grid; gap: calc(14px * var(--ui-scale));
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-top: calc(14px * var(--ui-scale));
}
.oc-review {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(16px * var(--ui-scale));
    padding: calc(18px * var(--ui-scale)) calc(22px * var(--ui-scale));
}
.oc-review-head { display: flex; align-items: center; gap: calc(10px * var(--ui-scale)); flex-wrap: wrap; }
.oc-review-head b { font-size: max(12px, calc(14.5px * var(--ui-scale))); }
.oc-review .oc-body { margin-top: calc(8px * var(--ui-scale)); }
.oc-review .oc-body p { margin: 0 0 calc(8px * var(--ui-scale)); color: var(--muted); font-size: max(12px, calc(14.5px * var(--ui-scale))); line-height: 1.6; }
.oc-review .oc-body p:last-child { margin-bottom: 0; }
.oc-review-form {
    background: var(--bg2); border: 1px solid var(--line);
    border-radius: calc(16px * var(--ui-scale));
    padding: calc(18px * var(--ui-scale)) calc(22px * var(--ui-scale));
    margin-top: calc(6px * var(--ui-scale));
}
.oc-form-line { display: flex; align-items: center; gap: calc(14px * var(--ui-scale)); margin-bottom: calc(10px * var(--ui-scale)); }
.oc-form-label { color: var(--faint); font-size: max(11px, calc(13px * var(--ui-scale))); }
/* Пикер оценки: тач-цели ≥44px, звёзды крупные (WCAG). */
.oc-picker { display: inline-flex; gap: calc(4px * var(--ui-scale)); }
.oc-pick {
    width: calc(44px * var(--ui-scale)); height: calc(44px * var(--ui-scale));
    background: none; border: none; cursor: pointer;
    font-size: calc(26px * var(--ui-scale)); line-height: 1;
    color: rgba(255,255,255,.18); padding: 0;
    transition: color .15s, transform .15s;
}
.oc-pick.on { color: var(--gold); }
.oc-pick:active { transform: scale(.9); }
/* Свои отзывы: статусы «на проверке / отклонён / снят» (план §2.3). */
#oc-mine { margin-top: calc(16px * var(--ui-scale)); }
#oc-mine h3 { margin-bottom: calc(8px * var(--ui-scale)); }
.oc-mine-row { border-top: 1px solid rgba(255,255,255,.05); padding: calc(10px * var(--ui-scale)) 0; }
.oc-mine-row .rating-stars { font-size: max(14px, calc(16px * var(--ui-scale))); letter-spacing: 1px; vertical-align: -2px; }
.oc-mine-text { color: var(--faint); font-size: max(11px, calc(13px * var(--ui-scale))); margin-top: calc(4px * var(--ui-scale)); }
/* Honeypot-поле: вне экрана и мимо скринридеров. */
.oc-hp {
    position: absolute; left: -9999px; top: -9999px;
    width: 1px; height: 1px; opacity: 0; pointer-events: none;
}

/* ── Миры: прежняя витрина живёт здесь ──────────────────────── */
#oc-worlds h3, #oc-feed h3 { margin-top: calc(30px * var(--ui-scale)); }
#oc-feed { margin-top: calc(6px * var(--ui-scale)); }

/* ── Порядок отзывов: одна колонка на узких экранах ─────────── */
@media (max-width: 768px) {
    .oc-reviews-grid { grid-template-columns: 1fr; }
    .oc-newscard, .oc-question, .oc-review, .oc-review-form { padding: calc(16px * var(--ui-scale)) calc(16px * var(--ui-scale)); }
    .oc-compose-row .btn { flex: 1 1 100%; justify-content: center; min-height: 44px; }
    .oc-quote { margin-top: calc(20px * var(--ui-scale)); }
}
@media (max-width: 480px) {
    .oc-hero { padding-top: calc(40px * var(--ui-scale)); }
    .oc-form-line { flex-wrap: wrap; }
}

/* Понижение движения: страница почти без анимаций, глушим переходы. */
@media (prefers-reduced-motion: reduce) {
    .oc-pick { transition: none; }
}

/* ==============================================================
   DZS — house.css: «Дом» /u/<username> (Э2).
   Продолжение системы Очага (ochag.css): те же панели, тот же сериф.
   Анимации только transform/opacity; закрытый лайтбокс —
   visibility:hidden (закон perf §2.3).
   ============================================================== */

/* ── Шапка Дома: кто в нём живёт ────────────────────────────── */
.oc-hero-house { padding-bottom: calc(4px * var(--ui-scale)); }
.hs-id { display: flex; align-items: center; gap: calc(22px * var(--ui-scale)); justify-content: center; text-align: left; }
.hs-id h1 { font-family: var(--serif); font-weight: 600; line-height: 1.05;
    font-size: clamp(32px, 4.6vw, calc(52px * var(--ui-scale))); margin: 0; }
.hs-id-meta { color: var(--muted); font-size: max(11px, calc(14px * var(--ui-scale))); margin-top: calc(4px * var(--ui-scale)); }
.hs-id-meta .badge { margin: 0 calc(4px * var(--ui-scale)); }
.hs-avatar {
    width: calc(96px * var(--ui-scale)); height: calc(96px * var(--ui-scale));
    border-radius: 50%; object-fit: cover; flex-shrink: 0;
    border: 1px solid rgba(227,176,102,.35);
    background: var(--panel);
}
.hs-avatar-letter {
    display: flex; align-items: center; justify-content: center;
    font-family: var(--serif); font-size: calc(42px * var(--ui-scale)); color: var(--gold2);
}
.hs-privacy { margin-top: calc(10px * var(--ui-scale)); display: flex; gap: calc(8px * var(--ui-scale)); align-items: center; flex-wrap: wrap; justify-content: center; }
.hs-priv {
    background: none; border: 1px solid var(--line); border-radius: calc(20px * var(--ui-scale));
    color: var(--faint); cursor: pointer; padding: calc(7px * var(--ui-scale)) calc(14px * var(--ui-scale));
    font-size: max(11px, calc(12.5px * var(--ui-scale))); min-height: 36px;
}
.hs-priv.on { color: var(--gold); border-color: rgba(227,176,102,.5); }
.hs-priv:hover { color: var(--ink); }

/* ── Композитор: та же панель, что вести на Площади ─────────── */
.hs-compose {
    background: var(--panel); border: 1px dashed var(--line);
    border-radius: calc(16px * var(--ui-scale));
    padding: calc(18px * var(--ui-scale)) calc(22px * var(--ui-scale));
    margin-top: calc(20px * var(--ui-scale));
}
.hs-compose textarea, .hs-edit {
    width: 100%; min-height: calc(72px * var(--ui-scale)); resize: vertical;
    background: var(--bg2); color: var(--ink);
    border: 1px solid var(--line); border-radius: calc(10px * var(--ui-scale));
    padding: calc(12px * var(--ui-scale)) calc(14px * var(--ui-scale));
    font-family: var(--sans); font-size: max(12px, calc(14.5px * var(--ui-scale)));
    box-sizing: border-box; margin-top: calc(12px * var(--ui-scale));
}
.hs-compose textarea:focus, .hs-edit:focus { outline: none; border-color: rgba(227,176,102,.5); }
.hs-compose-media { display: flex; align-items: center; gap: calc(14px * var(--ui-scale)); margin-top: calc(12px * var(--ui-scale)); flex-wrap: wrap; }
/* Скрытый input[type=file]: кнопкой служит label.btn — тач-цель 44px. */
.hs-compose .btn { min-height: 44px; cursor: pointer; }
.hs-thumbs { display: flex; gap: calc(10px * var(--ui-scale)); flex-wrap: wrap; margin-top: calc(12px * var(--ui-scale)); }
.hs-thumb { position: relative; width: calc(84px * var(--ui-scale)); height: calc(84px * var(--ui-scale)); }
.hs-thumb img { width: 100%; height: 100%; object-fit: cover; border-radius: calc(10px * var(--ui-scale)); border: 1px solid var(--line); }
.hs-thumb button {
    position: absolute; top: calc(-6px * var(--ui-scale)); right: calc(-6px * var(--ui-scale));
    width: calc(24px * var(--ui-scale)); height: calc(24px * var(--ui-scale));
    border-radius: 50%; border: 1px solid var(--line);
    background: var(--bg); color: var(--ink); cursor: pointer; line-height: 1;
    font-size: max(10px, calc(12px * var(--ui-scale)));
}

/* ── Лента: записи и заметки ────────────────────────────────── */
#hs-composer .tabs, .sec .tabs { margin-top: calc(8px * var(--ui-scale)); }
.hs-post {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(16px * var(--ui-scale));
    padding: calc(18px * var(--ui-scale)) calc(22px * var(--ui-scale));
    margin-top: calc(14px * var(--ui-scale));
}
.hs-post-head { display: flex; align-items: center; gap: calc(10px * var(--ui-scale)); flex-wrap: wrap; }
.hs-post-head b { font-size: max(12px, calc(14.5px * var(--ui-scale))); }
.hs-post-body p { margin: 0 0 calc(8px * var(--ui-scale)); color: var(--ink); font-size: max(12px, calc(15px * var(--ui-scale))); line-height: 1.65; }
.hs-post-body p:last-child { margin-bottom: 0; }
.hs-post-acts { display: flex; align-items: center; gap: calc(14px * var(--ui-scale)); margin-top: calc(12px * var(--ui-scale)); flex-wrap: wrap; }

/* Реакция «согрел»: статичный вид, живёт по нажатию; тач-цель 44px. */
.hs-warm {
    display: inline-flex; align-items: center; gap: calc(6px * var(--ui-scale));
    background: none; border: 1px solid var(--line); border-radius: calc(20px * var(--ui-scale));
    color: var(--muted); cursor: pointer; min-height: 44px;
    padding: calc(6px * var(--ui-scale)) calc(16px * var(--ui-scale));
    font-size: max(12px, calc(14px * var(--ui-scale)));
    transition: color .15s, border-color .15s, transform .15s;
}
.hs-warm.on { color: var(--ember); border-color: rgba(226,96,58,.5); }
.hs-warm:active { transform: scale(.94); }
.hs-warm-label { font-size: max(10px, calc(12px * var(--ui-scale))); color: var(--faint); }
.hs-warm.on .hs-warm-label { color: var(--ember); }

/* ── Медиа записи: 1 / 2–4 / 5+ картинки ────────────────────── */
.hs-media { display: grid; gap: calc(6px * var(--ui-scale)); margin-top: calc(12px * var(--ui-scale)); }
.hs-media.cols1 { grid-template-columns: 1fr; }
.hs-media.cols2 { grid-template-columns: repeat(2, 1fr); }
.hs-media.cols3 { grid-template-columns: repeat(3, 1fr); }
/* Плитки одинаковой высоты: aspect-ratio держит сетку независимо от
   пропорций исходников (иначе height:100% в grid-ячейке раздувает ряд). */
.hs-media img {
    /* height:auto обязателен: без него атрибутная height (presentational
       hint) держит высоту строки и aspect-ratio не срабатывает. */
    width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover;
    border-radius: calc(10px * var(--ui-scale)); border: 1px solid var(--line);
    cursor: zoom-in; display: block;
    /* app.css глушит pointer-events у всех img — плитка должна открывать
       лайтбокс (user-select/-webkit-user-drag остаются от глобального). */
    pointer-events: auto;
}
.hs-media.cols1 img { aspect-ratio: auto; max-height: calc(520px * var(--ui-scale)); object-fit: contain; background: var(--bg2); width: 100%; }

/* ── Лайтбокс ───────────────────────────────────────────────── */
.hs-lightbox {
    position: fixed; inset: 0; z-index: var(--z-overlay);
    background: rgba(7,8,13,.92);
    display: flex; align-items: center; justify-content: center;
    opacity: 0; visibility: hidden; cursor: zoom-out;
    transition: opacity .25s ease, visibility 0s .25s;
}
.hs-lightbox.open { opacity: 1; visibility: visible; transition: opacity .25s ease; }
.hs-lightbox img { max-width: 94vw; max-height: 94dvh; width: auto; height: auto; }

/* ── Адаптив ────────────────────────────────────────────────── */
@media (max-width: 768px) {
    .hs-id { gap: calc(14px * var(--ui-scale)); }
    .hs-avatar { width: calc(72px * var(--ui-scale)); height: calc(72px * var(--ui-scale)); }
    .hs-post, .hs-compose { padding: calc(14px * var(--ui-scale)) calc(14px * var(--ui-scale)); }
    .hs-media { gap: calc(4px * var(--ui-scale)); }
    .hs-media.cols3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
    .hs-id { flex-direction: column; text-align: center; }
    .hs-id-meta, .hs-privacy { justify-content: center; }
    .hs-compose-row .btn { flex: 1 1 100%; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
    .hs-warm, .hs-lightbox { transition: none; }
}

/* ── Пикер персонажей в Профиле ─────────────────────────────── */
.pf-av-row { display: flex; gap: calc(10px * var(--ui-scale)); flex-wrap: wrap; align-items: center; }
.pf-btn-file { cursor: pointer; display: inline-flex; align-items: center; }
.pf-charpick {
    position: fixed; inset: 0; z-index: var(--z-overlay);
    background: rgba(7,8,13,.9);
    display: flex; align-items: center; justify-content: center;
    opacity: 0; visibility: hidden;
    transition: opacity .25s ease, visibility 0s .25s;
}
.pf-charpick.open { opacity: 1; visibility: visible; transition: opacity .25s ease; }
.pf-charpick-box {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(18px * var(--ui-scale));
    max-width: calc(720px * var(--ui-scale)); width: 94vw;
    max-height: 88dvh; overflow-y: auto;
    padding: calc(20px * var(--ui-scale));
}
.pf-charpick-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: calc(12px * var(--ui-scale)); }
.pf-charpick-head h3 { font-family: var(--serif); font-size: calc(22px * var(--ui-scale)); color: var(--gold2); }
.pf-charpick-grid {
    display: grid; gap: calc(10px * var(--ui-scale));
    grid-template-columns: repeat(auto-fill, minmax(calc(88px * var(--ui-scale)), 1fr));
}
.pf-char {
    background: var(--bg2); border: 1px solid var(--line); border-radius: calc(10px * var(--ui-scale));
    cursor: pointer; padding: calc(6px * var(--ui-scale)); color: var(--muted);
    font-size: max(10px, calc(11px * var(--ui-scale))); text-align: center;
    transition: border-color .15s, color .15s;
}
.pf-char img { width: 100%; aspect-ratio: 2/3; object-fit: cover; border-radius: calc(6px * var(--ui-scale)); display: block; margin-bottom: calc(4px * var(--ui-scale)); }
.pf-char:hover { border-color: rgba(227,176,102,.5); color: var(--ink); }
.pf-charpick-more { text-align: center; margin-top: calc(14px * var(--ui-scale)); }

/* ── Имя-ссылка на Дом (Площадь: отзывы, ответы) ───────────── */
.hs-ulink { color: inherit; border-bottom: 1px dashed rgba(227,176,102,.4); }
.hs-ulink:hover { color: var(--gold2); border-bottom-color: var(--gold); }

/* ── Круг своих (Э3) ────────────────────────────────────────── */
.fr-invite { display: flex; gap: calc(10px * var(--ui-scale)); justify-content: center; align-items: center; margin-top: calc(18px * var(--ui-scale)); flex-wrap: wrap; }
.fr-invite-input {
    background: var(--bg2); color: var(--ink);
    border: 1px solid var(--line); border-radius: calc(24px * var(--ui-scale));
    padding: calc(11px * var(--ui-scale)) calc(18px * var(--ui-scale));
    font-family: var(--sans); font-size: max(12px, calc(14px * var(--ui-scale)));
    min-width: calc(220px * var(--ui-scale)); min-height: 44px;
}
.fr-invite-input:focus { outline: none; border-color: rgba(227,176,102,.5); }
.fr-row {
    display: flex; align-items: center; gap: calc(14px * var(--ui-scale));
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(14px * var(--ui-scale));
    padding: calc(12px * var(--ui-scale)) calc(18px * var(--ui-scale));
    margin-top: calc(12px * var(--ui-scale));
}
.fr-person { display: flex; align-items: center; gap: calc(14px * var(--ui-scale)); flex: 1; min-width: 0; }
.fr-av {
    width: calc(48px * var(--ui-scale)); height: calc(48px * var(--ui-scale));
    border-radius: 50%; object-fit: cover; flex-shrink: 0;
    border: 1px solid rgba(227,176,102,.3); background: var(--bg2);
}
.fr-av.hs-avatar-letter { display: flex; align-items: center; justify-content: center; font-family: var(--serif); font-size: calc(22px * var(--ui-scale)); color: var(--gold2); }
.fr-name { font-weight: 600; font-size: max(12px, calc(14.5px * var(--ui-scale))); color: var(--ink); }
.fr-acts { display: flex; align-items: center; gap: calc(8px * var(--ui-scale)); flex-wrap: wrap; justify-content: flex-end; }

/* Бейдж заявок в меню (ядро — видно на всех страницах). */
.menu-badge {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: calc(18px * var(--ui-scale)); height: calc(18px * var(--ui-scale));
    padding: 0 calc(5px * var(--ui-scale));
    border-radius: calc(9px * var(--ui-scale));
    background: var(--ember); color: #fff;
    font-size: max(9px, calc(10.5px * var(--ui-scale))); font-weight: 700;
    line-height: 1; vertical-align: 1px;
}
.hs-friend { margin-top: calc(10px * var(--ui-scale)); display: flex; gap: calc(10px * var(--ui-scale)); align-items: center; justify-content: center; }
@media (max-width: 768px) {
    .fr-row { flex-wrap: wrap; }
    .fr-acts { width: 100%; justify-content: flex-start; }
}

/* ── Центр уведомлений (Э5) ─────────────────────────────────── */
.nt-ov {
    position: fixed; inset: 0; z-index: var(--z-overlay);
    background: rgba(7,8,13,.85);
    display: flex; align-items: flex-start; justify-content: center;
    padding-top: calc(70px * var(--ui-scale));
    opacity: 0; visibility: hidden;
    transition: opacity .25s ease, visibility 0s .25s;
}
.nt-ov.open { opacity: 1; visibility: visible; transition: opacity .25s ease; }
.nt-box {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(18px * var(--ui-scale));
    max-width: calc(520px * var(--ui-scale)); width: 94vw;
    max-height: 78dvh; overflow-y: auto;
    padding: calc(18px * var(--ui-scale));
}
.nt-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: calc(12px * var(--ui-scale)); }
.nt-head h3 { font-family: var(--serif); color: var(--gold2); font-size: calc(22px * var(--ui-scale)); }
.nt-body h4 { margin: calc(14px * var(--ui-scale)) 0 calc(6px * var(--ui-scale)); color: var(--muted); font-size: max(11px, calc(12.5px * var(--ui-scale))); letter-spacing: .1em; text-transform: uppercase; }
.nt-row {
    display: flex; align-items: center; gap: calc(10px * var(--ui-scale));
    background: var(--bg2); border: 1px solid var(--line);
    border-radius: calc(10px * var(--ui-scale));
    padding: calc(10px * var(--ui-scale)) calc(14px * var(--ui-scale));
    margin-bottom: calc(6px * var(--ui-scale)); color: var(--ink);
}
.nt-row a { flex: 1; min-width: 0; }
.nt-acts { display: flex; gap: calc(6px * var(--ui-scale)); align-items: center; flex-shrink: 0; }

/* ═══════════════════════════════════════════════════════════════
   ЭW3 — запись «на бумаге» (план workshop-masterica §3.3).
   Лист старой бумаги из токенов книги (--page-*); рваные края —
   clip-path без картинок; тень листа — drop-shadow на ОБЁРТКЕ
   (clip-path режет тень самого листа; статичный blur — разрешён).
   ═══════════════════════════════════════════════════════════════ */
.hs-paper-wrap {
    filter: drop-shadow(0 calc(10px * var(--ui-scale)) calc(16px * var(--ui-scale)) rgba(0, 0, 0, .45));
    margin: calc(10px * var(--ui-scale)) 0 calc(6px * var(--ui-scale));
}
.hs-paper {
    background: linear-gradient(168deg, var(--page-bg) 0%, var(--page-bg2) 100%);
    color: var(--page-ink);
    border-radius: 2px;
    transform: rotate(-0.4deg);
    padding: calc(18px * var(--ui-scale)) calc(20px * var(--ui-scale));
    clip-path: polygon(0% 2%, 3% .4%, 7% 1.6%, 12% .2%, 18% 1.8%, 24% .3%, 31% 1.5%,
        38% .2%, 45% 1.7%, 52% .4%, 59% 1.6%, 66% .2%, 73% 1.5%, 80% .3%, 87% 1.7%,
        93% .4%, 100% 1.8%, 100% 98%, 96% 99.6%, 90% 98.3%, 84% 99.8%, 77% 98.2%,
        70% 99.7%, 63% 98.4%, 56% 99.8%, 49% 98.3%, 42% 99.6%, 35% 98.2%, 28% 99.8%,
        21% 98.4%, 14% 99.7%, 7% 98.3%, 3% 99.8%, 0% 98%);
}
.hs-paper .hs-post-body { color: var(--page-ink); }
.hs-paper .hs-post-body p {
    font-family: var(--serif);
    font-size: calc(16px * var(--ui-scale));
    line-height: 1.65;
}
/* Прилепленная картинка: белый кант, как у фото на бумаге */
.hs-paper .hs-media { margin-top: calc(12px * var(--ui-scale)); }
.hs-paper .hs-media img {
    background: #f4efe4;
    border: calc(5px * var(--ui-scale)) solid #f4efe4;
    border-bottom-width: calc(14px * var(--ui-scale));
    box-shadow: 0 calc(6px * var(--ui-scale)) calc(12px * var(--ui-scale)) rgba(0, 0, 0, .55);
}
.hs-paper .hs-media.cols2 img:nth-child(odd),
.hs-paper .hs-media.cols3 img:nth-child(3n+1) { transform: rotate(-1.1deg); }
.hs-paper .hs-media.cols2 img:nth-child(even),
.hs-paper .hs-media.cols3 img:nth-child(3n+2) { transform: rotate(.9deg); }

/* Варианты типографики (кнопками в композиторе) */
.hs-paper-list .hs-post-body p { font-size: calc(14.5px * var(--ui-scale)); }
.hs-paper-list .hs-post-body p::before { content: '— '; color: var(--page-ink-dim); }
.hs-paper-quote .hs-post-body p:first-child {
    font-style: italic;
    font-size: calc(21px * var(--ui-scale));
    line-height: 1.5;
    text-align: center;
    color: var(--page-edge);
    margin: calc(6px * var(--ui-scale)) 0 calc(10px * var(--ui-scale));
}

/* ── Композитор: тумблер и варианты ─────────────────────────── */
.hs-paper-row {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: calc(8px * var(--ui-scale));
    margin: 0 0 calc(8px * var(--ui-scale));
}
.hs-paper-toggle {
    padding: calc(7px * var(--ui-scale)) calc(13px * var(--ui-scale));
    border-radius: 999px; cursor: pointer;
    border: 1px solid var(--line); background: transparent;
    color: var(--muted); font-family: var(--sans);
    font-size: calc(12.5px * var(--ui-scale));
    transition: transform .15s ease, border-color .15s ease, color .15s ease;
}
.hs-paper-toggle:hover { color: var(--gold2); border-color: rgba(227, 176, 102, .5); }
.hs-paper-toggle.on { color: var(--gold2); border-color: var(--gold); background: rgba(227, 176, 102, .1); }
.hs-paper-toggle:active { transform: scale(.97); }
.hs-paper-variants { display: inline-flex; gap: calc(6px * var(--ui-scale)); }
.hs-paper-variants.hidden { display: none; }
.hs-paper-variant {
    padding: calc(6px * var(--ui-scale)) calc(11px * var(--ui-scale));
    border-radius: 999px; cursor: pointer;
    border: 1px solid var(--line); background: transparent;
    color: var(--faint); font-family: var(--sans);
    font-size: calc(12px * var(--ui-scale));
    transition: color .15s ease, border-color .15s ease;
}
.hs-paper-variant:hover { color: var(--gold2); }
.hs-paper-variant.on { color: var(--gold2); border-color: rgba(227, 176, 102, .6); }
/* Живое превью бумаги — на самом поле ввода */
#hs-body.on-paper {
    background: linear-gradient(168deg, var(--page-bg) 0%, var(--page-bg2) 100%);
    color: var(--page-ink);
    border-color: var(--page-line);
    font-family: var(--serif);
    font-size: calc(16px * var(--ui-scale));
    line-height: 1.6;
}
@media (prefers-reduced-motion: reduce) {
    .hs-paper-toggle, .hs-paper-variant { transition: none; }
}

/* ── Пикер персонажей: выбор на самой плитке (репорт владельца 07.10:
   «Выбрать» в конце списка заставляла листать 61 портрет) ── */
.pf-char { position: relative; }
.pf-char.on {
    border-color: var(--gold); color: var(--gold2);
    box-shadow: 0 0 0 1px var(--gold) inset;
    cursor: pointer;
}
.pf-char-confirm {
    position: absolute; left: 8%; right: 8%;
    bottom: calc(30px * var(--ui-scale));
    padding: calc(7px * var(--ui-scale)) 0;
    border: 0; border-radius: 999px; cursor: pointer;
    background: linear-gradient(120deg, var(--gold), var(--gold2));
    color: #1a0f06; font-family: var(--sans); font-weight: 700;
    font-size: max(11px, calc(12px * var(--ui-scale)));
    box-shadow: 0 4px 14px rgba(0, 0, 0, .5);
}
.pf-char-confirm:active { transform: scale(.96); }
.pf-charpick-foot {
    display: flex; align-items: center; gap: calc(12px * var(--ui-scale));
    margin-top: calc(14px * var(--ui-scale));
    padding-top: calc(12px * var(--ui-scale));
    border-top: 1px solid var(--line);
}
.pf-charpick-foot .oc-meta { flex: 1; }
@media (max-width: 480px) {
    .pf-charpick-foot { flex-wrap: wrap; }
    .pf-charpick-foot [data-cancel] { min-height: 44px; }
}
.notification-bell { position:absolute;top:calc(100% + 6px);right:64px;display:flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:12px;border:1px solid var(--gold);background:var(--panel);color:var(--gold);cursor:pointer; }
.notification-bell[hidden] { display:none; }
.notification-bell span { position:absolute;right:-5px;top:-5px;background:var(--ember);color:white;border-radius:12px;padding:1px 5px;font-size:11px; }
.nt-ov { height:var(--visual-height,100dvh);padding:12px;box-sizing:border-box;align-items:center; }
.nt-box { width:min(100%,580px);max-width:100%;max-height:100%;box-sizing:border-box; }
.nt-device { margin:12px 0; }
.nt-row { flex-wrap:wrap;overflow-wrap:anywhere; }
.nt-acts { flex-wrap:wrap; }
@media(max-width:600px) { .nt-box { padding:16px;font-size:16px; }.nt-head h3 { font-size:24px; } }

/* ==============================================================
   DZS — whisper.css: «Шёпот» /whisper (Э4). Список и переписка —
   весь экран (100dvh, план §6); клавиатура через --ws-kb
   (visualViewport), тач-цели ≥44px, safe-area, анимации только
   transform/opacity. Разметку строит src/js/whisper.js.
   ============================================================== */

.ws-wrap { min-height: 40vh; }
.ws-hbtn svg{width:22px;height:22px}.ws-connection{padding:4px 12px;font-size:12px}
.ws-root:not(.in-chat) .ws-image-draft,.ws-root:not(.in-chat) .ws-down,.ws-root:not(.in-chat) .ws-reportbar{display:none}
.ws-av svg{width:24px;height:24px}.ws-bubble-line{touch-action:pan-y;transition:transform .12s ease-out}.ws-swipe-ready{filter:drop-shadow(-3px 0 2px rgba(227,176,102,.4))}
@media(prefers-reduced-motion:reduce){.ws-bubble-line{transition:none}}
.ws-image-draft{display:flex;align-items:center;gap:12px;padding:10px 16px;background:var(--panel);border-top:1px solid var(--line);min-height:88px;box-sizing:border-box}.ws-image-draft[hidden]{display:none}.ws-image-draft img{width:72px;height:72px;object-fit:cover;border-radius:10px;flex-shrink:0}.ws-image-draft div{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px;font:13px var(--sans)}.ws-image-draft b{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ws-image-draft span{color:var(--muted)}.ws-image-caption{margin-top:8px}.ws-unread-divider{padding:9px;text-align:center;color:var(--gold2);font:12px var(--sans);background:rgba(227,176,102,.06);margin:12px -16px}
.ws-list-heading{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}.ws-list-heading h1{margin:0;font:26px var(--serif);color:var(--gold2)}
.ws-list-acts{display:flex;gap:8px;margin-top:10px}.ws-list-acts .btn{flex:1;justify-content:center}
.ws-filters{display:flex;gap:4px;padding:8px;overflow-x:auto;flex-shrink:0;border-bottom:1px solid var(--line)}
.ws-filters button{background:none;border:0;color:var(--muted);font:13px var(--sans);border-radius:10px;padding:10px 9px;min-height:44px;white-space:nowrap;cursor:pointer;flex:1}.ws-filters button.on{color:var(--gold2);background:rgba(227,176,102,.12)}.ws-filters span{font-size:11px;opacity:.8}
.ws-row-wrap{position:relative;border-radius:12px}.ws-row-wrap.on{background:rgba(227,176,102,.07)}.ws-row-wrap .ws-row{padding-right:48px;min-height:76px}
.ws-row-menu{position:absolute;right:3px;top:50%;transform:translateY(-50%);width:44px;height:44px;display:grid;place-items:center;background:none;border:0;color:var(--muted);cursor:pointer;border-radius:12px}
.ws-row-menu:hover,.ws-msg-more:hover{color:var(--gold2);background:rgba(227,176,102,.1)}.ws-row-menu svg,.ws-msg-more svg,.ws-menu-panel svg{width:20px;height:20px}
.ws-row-symbol{display:inline-flex;vertical-align:middle;margin-left:5px;color:var(--muted)}.ws-row-symbol svg{width:14px;height:14px}.ws-row-main .ws-draft{color:var(--gold2)}.ws-muted-unread{background:var(--muted)}
.ws-list-empty{padding:32px 20px;text-align:center;color:var(--muted);line-height:1.6}.ws-list-empty b{font:22px var(--serif);color:var(--ink)}
.ws-msg-more{width:36px;height:36px;flex-shrink:0;display:grid;place-items:center;border:0;background:none;color:var(--muted);border-radius:10px;cursor:pointer}
.ws-search-tools{display:none;align-items:center;gap:8px;padding:4px 12px;border-bottom:1px solid var(--line);font:12px var(--sans);color:var(--muted);min-height:44px;flex-shrink:0}.ws-searching .ws-search-tools{display:flex}.ws-search-tools #ws-search-results{flex:1}.ws-search-tools [hidden]{display:none}.ws-search-hit{color:var(--ink);background:rgba(227,176,102,.35);border-radius:3px}
.ws-chat[aria-busy=true] .ws-msgs{opacity:.55}.ws-root:not(.in-chat) .ws-chat-head,.ws-root:not(.in-chat) .ws-compose,.ws-root:not(.in-chat) .ws-search-tools,.ws-root:not(.in-chat) .ws-pin,.ws-root:not(.in-chat) .ws-reply{display:none}
.ws-menu-cover{position:fixed;inset:0;z-index:9200}.ws-menu-panel{position:absolute;min-width:245px;max-width:calc(100vw - 16px);max-height:calc(100dvh - 16px);overflow:auto;padding:8px;background:var(--bg2);border:1px solid var(--line);border-radius:16px;box-shadow:0 12px 36px rgba(0,0,0,.45);animation:ws-menu-in .14s ease-out}
.ws-menu-panel h3{font:18px var(--serif);color:var(--gold2);padding:8px;margin:0 0 4px}.ws-menu-panel button{width:100%;display:flex;align-items:center;gap:12px;min-height:44px;padding:10px 12px;background:none;color:var(--ink);border:0;border-radius:9px;text-align:left;font:14px var(--sans);cursor:pointer}.ws-menu-panel button:hover,.ws-menu-panel button:focus-visible{background:rgba(227,176,102,.1)}.ws-menu-panel button.danger{color:#f29a95}.ws-menu-panel .ws-menu-cancel{border-top:1px solid var(--line);border-radius:0;margin-top:6px;color:var(--muted)}
@keyframes ws-menu-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media(max-width:899px){.ws-menu-cover{background:rgba(0,0,0,.5)}.ws-menu-panel{bottom:env(safe-area-inset-bottom,0px);left:8px;right:8px;max-height:75dvh;margin-bottom:8px;border-radius:20px}.ws-menu-panel button{min-height:48px}.ws-msg-more{width:44px;height:44px}}
html.perf-low .ws-menu-panel{animation:none}@media(prefers-reduced-motion:reduce){.ws-menu-panel{animation:none}}

/* Полноэкранный раздел: без фирменного зазора #page-root.no-hero
   (14px сверху сдвигали низ композитора за край экрана). */
body.ws-body #page-root.no-hero { padding-top: var(--header-h); }
/* Окно Шёпота само держит свою высоту (включая плеер) — страница
   не должна скроллиться, иначе под окном выглядывает пустота/футер. */
body.ws-body { overflow: hidden; }
body.ws-body footer { display: none; }
.ws-root {
    display: flex;
    height: calc(100dvh - var(--ws-player, 0px) - var(--header-h) - var(--ws-kb, 0px) - 14px * var(--ui-scale));
    box-sizing: border-box;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--bg2);
    border: 1px solid var(--line);
    border-radius: calc(18px * var(--ui-scale));
    overflow: hidden;
    max-width: var(--maxw);
    margin: calc(14px * var(--ui-scale)) auto 0;
}

/* ── Список диалогов ────────────────────────────────────────── */
.ws-list {
    width: calc(340px * var(--ui-scale));
    flex-shrink: 0;
    display: flex; flex-direction: column;
    border-right: 1px solid var(--line);
    min-height: 0;
}
.ws-list-head { padding: calc(12px * var(--ui-scale)); border-bottom: 1px solid var(--line); }
.ws-search {
    width: 100%; box-sizing: border-box;
    background: var(--bg); color: var(--ink);
    border: 1px solid var(--line); border-radius: calc(22px * var(--ui-scale));
    padding: calc(10px * var(--ui-scale)) calc(16px * var(--ui-scale));
    font-family: var(--sans); font-size: max(12px, calc(14px * var(--ui-scale)));
    min-height: 44px;
}
.ws-search:focus { outline: none; border-color: rgba(227,176,102,.5); }
.ws-list-items { overflow-y: auto; flex: 1; padding: calc(6px * var(--ui-scale)); min-height: 0; }
.ws-row {
    display: flex; align-items: center; gap: calc(12px * var(--ui-scale));
    width: 100%; text-align: left; box-sizing: border-box;
    background: none; border: none; border-radius: calc(12px * var(--ui-scale));
    padding: calc(10px * var(--ui-scale)) calc(12px * var(--ui-scale));
    cursor: pointer; color: var(--ink); min-height: 60px;
    font-family: var(--sans);
}
.ws-row:hover, .ws-row.on { background: rgba(227,176,102,.07); }
.ws-av {
    width: calc(48px * var(--ui-scale)); height: calc(48px * var(--ui-scale));
    border-radius: 50%; object-fit: cover; flex-shrink: 0;
    border: 1px solid rgba(227,176,102,.3); background: var(--bg);
}
.ws-av.hs-avatar-letter { display: flex; align-items: center; justify-content: center; font-family: var(--serif); font-size: calc(22px * var(--ui-scale)); color: var(--gold2); }
.ws-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ws-row-name { font-weight: 600; font-size: max(12px, calc(14.5px * var(--ui-scale))); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ws-row-prev { color: var(--faint); font-size: max(11px, calc(13px * var(--ui-scale))); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ws-row-side { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex-shrink: 0; }
.ws-row-time { color: var(--faint); font-size: max(10px, calc(11.5px * var(--ui-scale))); }
.ws-unread {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: calc(20px * var(--ui-scale)); height: calc(20px * var(--ui-scale));
    border-radius: calc(10px * var(--ui-scale));
    background: var(--ember); color: #fff; font-weight: 700;
    font-size: max(10px, calc(11.5px * var(--ui-scale))); padding: 0 calc(6px * var(--ui-scale));
}

/* ── Чат ────────────────────────────────────────────────────── */
.ws-chat { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.ws-chat-head {
    display: flex; align-items: center; gap: calc(12px * var(--ui-scale));
    padding: calc(10px * var(--ui-scale)) calc(16px * var(--ui-scale));
    border-bottom: 1px solid var(--line); min-height: calc(60px * var(--ui-scale));
}
.ws-back {
    display: none; background: none; border: none; color: var(--gold);
    font-size: calc(30px * var(--ui-scale)); cursor: pointer; line-height: 1;
    width: 44px; height: 44px; flex-shrink: 0;
}
.ws-peer { display: flex; align-items: center; gap: calc(10px * var(--ui-scale)); flex: 1; min-width: 0; }
.ws-peer .ws-av { width: calc(40px * var(--ui-scale)); height: calc(40px * var(--ui-scale)); }
.ws-peer-name { color: var(--ink); font-weight: 600; font-size: max(12px, calc(15px * var(--ui-scale))); }
.ws-peer-name:hover { color: var(--gold2); }
.ws-typing { color: var(--gold); font-size: max(10px, calc(12.5px * var(--ui-scale))); margin-left: calc(8px * var(--ui-scale)); }
.ws-search-chat {
    width: calc(180px * var(--ui-scale)); flex-shrink: 0;
    background: var(--bg); color: var(--ink);
    border: 1px solid var(--line); border-radius: calc(18px * var(--ui-scale));
    padding: calc(8px * var(--ui-scale)) calc(14px * var(--ui-scale));
    font-family: var(--sans); font-size: max(11px, calc(13px * var(--ui-scale))); min-height: 40px;
}
.ws-search-chat:focus { outline: none; border-color: rgba(227,176,102,.5); }
.ws-search-chat{display:none}.ws-search-open .ws-search-chat{display:block}.ws-search-open{flex-wrap:wrap}.ws-search-open .ws-search-chat{flex:1 1 100%;width:100%}

.ws-msgs {
    flex: 1; overflow-y: auto; min-height: 0;
    padding: calc(16px * var(--ui-scale)) calc(16px * var(--ui-scale));
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}
.ws-day { text-align: center; margin: calc(14px * var(--ui-scale)) 0; }
.ws-day span {
    display: inline-block; padding: calc(3px * var(--ui-scale)) calc(14px * var(--ui-scale));
    border-radius: calc(12px * var(--ui-scale)); background: rgba(255,255,255,.05);
    color: var(--faint); font-size: max(10px, calc(12px * var(--ui-scale)));
    letter-spacing: .06em;
}
.ws-bubble-line { display: flex; margin: calc(3px * var(--ui-scale)) 0; }
.ws-bubble-line.mine-1 { justify-content: flex-end; }
.ws-bubble {
    max-width: min(76%, calc(560px * var(--ui-scale)));
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(16px * var(--ui-scale));
    padding: calc(9px * var(--ui-scale)) calc(13px * var(--ui-scale));
    position: relative;
}
.ws-bubble.mine { background: rgba(227,176,102,.10); border-color: rgba(227,176,102,.25); }
.ws-bubble.deleted { color: var(--faint); font-style: italic; font-size: max(11px, calc(13px * var(--ui-scale))); }
.ws-bubble-body { color: var(--ink); font-size: max(12px, calc(14.5px * var(--ui-scale))); line-height: 1.5; word-wrap: break-word; }
.ws-bubble .ws-img {
    display: block; max-width: 100%; height: auto;
    border-radius: calc(10px * var(--ui-scale));
    cursor: zoom-in; pointer-events: auto;
    margin: calc(2px * var(--ui-scale)) 0;
}
.ws-bubble-meta {
    display: flex; align-items: center; gap: calc(6px * var(--ui-scale));
    justify-content: flex-end; margin-top: calc(2px * var(--ui-scale));
    color: var(--faint); font-size: max(9px, calc(10.5px * var(--ui-scale)));
}
.ws-bubble:hover .ws-acts { opacity: 1; }
.ws-acts { opacity: 0; display: inline-flex; gap: calc(6px * var(--ui-scale)); transition: opacity .15s; }
.ws-acts .linklike { color: var(--faint); font-size: max(11px, calc(13px * var(--ui-scale))); min-height: 24px; }
.ws-acts .linklike:hover { color: var(--gold); }
/* Тап по пузырю (или наведение на десктопе) раскрывает действия. */
.ws-bubble-line.ws-open .ws-acts { opacity: 1; }
/* Аватар участника у чужого пузыря в группе (Telegram-ровность). */
.ws-who {
    display: flex; flex-direction: column; align-items: center;
    justify-content: flex-end; gap: calc(3px * var(--ui-scale));
    margin: 0 calc(6px * var(--ui-scale)) calc(2px * var(--ui-scale));
}
.ws-bname { font-size: max(10px, calc(11.5px * var(--ui-scale))); white-space: nowrap; }
.ws-bav {
    width: calc(28px * var(--ui-scale)); height: calc(28px * var(--ui-scale));
    border-radius: 50%; object-fit: cover; display: inline-block;
    border: 1px solid var(--line); flex-shrink: 0;
}
.ws-bav-letter {
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--bg2); color: var(--gold2);
    font-family: var(--serif); font-size: max(11px, calc(13px * var(--ui-scale)));
}
.ws-ticks { color: var(--faint); }
.ws-ticks.read { color: var(--cyan); }
.ws-edited { font-style: italic; }
.ws-quote {
    border-left: 2px solid var(--gold); padding: calc(2px * var(--ui-scale)) calc(8px * var(--ui-scale));
    margin-bottom: calc(6px * var(--ui-scale)); color: var(--muted);
    font-size: max(11px, calc(12.5px * var(--ui-scale)));
    background: rgba(227,176,102,.05); border-radius: 0 calc(6px * var(--ui-scale)) calc(6px * var(--ui-scale)) 0;
}
.ws-quote b { color: var(--gold2); font-weight: 600; }
.ws-edit {
    width: 100%; min-height: calc(56px * var(--ui-scale)); resize: vertical;
    background: var(--bg); color: var(--ink); border: 1px solid var(--line);
    border-radius: calc(8px * var(--ui-scale)); padding: calc(8px * var(--ui-scale));
    font-family: var(--sans); font-size: max(12px, calc(14px * var(--ui-scale))); box-sizing: border-box;
}
.ws-edit:focus { outline: none; border-color: rgba(227,176,102,.5); }
.ws-edit-acts { display: flex; gap: calc(8px * var(--ui-scale)); margin-top: calc(8px * var(--ui-scale)); }
.ws-delmenu {
    position: absolute; top: 100%; right: 0; z-index: 5;
    display: flex; flex-direction: column; gap: calc(4px * var(--ui-scale));
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(10px * var(--ui-scale)); padding: calc(8px * var(--ui-scale));
    box-shadow: 0 8px 24px rgba(0,0,0,.5);
}
.ws-down {
    position: absolute; right: calc(18px * var(--ui-scale)); bottom: calc(86px * var(--ui-scale));
    width: calc(44px * var(--ui-scale)); height: calc(44px * var(--ui-scale));
    border-radius: 50%; border: 1px solid var(--line);
    background: var(--panel); color: var(--gold); cursor: pointer;
    font-size: calc(20px * var(--ui-scale)); z-index: 4;
    box-shadow: 0 4px 16px rgba(0,0,0,.4);
}
.ws-chat { position: relative; }

/* ── Композитор ─────────────────────────────────────────────── */
.ws-reply {
    display: flex; align-items: center; gap: calc(8px * var(--ui-scale));
    padding: calc(6px * var(--ui-scale)) calc(16px * var(--ui-scale));
    border-top: 1px solid var(--line);
}
.ws-reply .ws-quote { margin: 0; flex: 1; }
.ws-compose {
    display: flex; align-items: flex-end; gap: calc(8px * var(--ui-scale));
    padding: calc(10px * var(--ui-scale)) calc(12px * var(--ui-scale)) calc(10px * var(--ui-scale));
    border-top: 1px solid var(--line);
}
.ws-attach {
    box-sizing: border-box; margin: 0; padding: 0; color: var(--ink); line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
    background: none; border: 1px solid var(--line); flex-shrink: 0;
    font-size: calc(18px * var(--ui-scale));
}
.ws-attach:hover { border-color: rgba(227,176,102,.5); }
#ws-text {
    min-width: 0; margin: 0;
    flex: 1; resize: none; max-height: 120px;
    background: var(--bg); color: var(--ink);
    border: 1px solid var(--line); border-radius: calc(20px * var(--ui-scale));
    padding: calc(11px * var(--ui-scale)) calc(16px * var(--ui-scale));
    font-family: var(--sans); font-size: max(12px, calc(14.5px * var(--ui-scale)));
    box-sizing: border-box; min-height: 44px;
}
#ws-text:focus { outline: none; border-color: rgba(227,176,102,.5); }
.ws-send {
    display: inline-flex; align-items: center; justify-content: center;
    box-sizing: border-box; margin: 0; padding: 0;
    width: 44px; height: 44px; border-radius: 50%; flex-shrink: 0;
    background: var(--gold); color: #14100a; border: none; cursor: pointer;
    font-size: calc(17px * var(--ui-scale)); line-height: 1;
}
.ws-send:active { transform: scale(.92); }

/* Гость: честная строка-обещание (решение владельца §2.1). */
.ws-guest { max-width: calc(560px * var(--ui-scale)); margin: 18vh auto 0; text-align: center; }
.ws-guest h1 { font-family: var(--serif); font-size: clamp(36px, 5vw, calc(52px * var(--ui-scale))); margin: calc(8px * var(--ui-scale)) 0; }
.ws-guest .pf-lead { color: var(--muted); margin-bottom: calc(18px * var(--ui-scale)); }

/* ── Телефон: один экран, свайпы, 100dvh (план §6) ──────────── */
@media (max-width: 899px) {
    body.ws-body #page-root { padding-bottom: 0; }
    .ws-root {
        margin: 0; border-radius: 0; border: none;
        height: calc(100dvh - var(--header-h) - var(--ws-kb, 0px)
            - var(--ws-player, 0px));
        max-width: none;
    }
    .ws-list { width: 100%; border-right: none; }
    .ws-chat { display: none; }
    .ws-root.in-chat .ws-list { display: none; }
    .ws-root.in-chat .ws-chat { display: flex; }
    .ws-back { display: block; }
    .ws-search-chat { width: calc(120px * var(--ui-scale)); }
    .ws-bubble { max-width: 86%; }
    .ws-down { bottom: calc(78px * var(--ui-scale)); }
}
@media (max-width: 899px) and (orientation: landscape) {
    /* Ландшафт телефона: панель диалогов уже, чат во весь остаток. */
    .ws-list { width: 40%; }
    .ws-root:not(.in-chat) .ws-list { width: 100%; }
    .ws-root.in-chat .ws-list { display: none; }
    .ws-root.in-chat .ws-chat { display: flex; }
}
@media (prefers-reduced-motion: reduce) {
    .ws-acts, .ws-send { transition: none; }
}

/* ── v2 (Э5): реакции ───────────────────────────────────────── */
.ws-reactbar {
    position: absolute; bottom: 100%; right: 0; z-index: 6;
    display: flex; gap: calc(2px * var(--ui-scale));
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(18px * var(--ui-scale)); padding: calc(3px * var(--ui-scale));
    box-shadow: 0 6px 18px rgba(0,0,0,.5); margin-bottom: calc(4px * var(--ui-scale));
}
.ws-reactbar button {
    background: none; border: none; cursor: pointer;
    font-size: calc(20px * var(--ui-scale)); line-height: 1;
    width: calc(36px * var(--ui-scale)); height: calc(36px * var(--ui-scale));
    border-radius: 50%;
}
.ws-reactbar button:hover { background: rgba(227,176,102,.15); transform: scale(1.15); }
.ws-rxrow { display: flex; gap: calc(4px * var(--ui-scale)); flex-wrap: wrap; margin-top: calc(4px * var(--ui-scale)); }
.ws-rx {
    background: rgba(255,255,255,.06); border: 1px solid var(--line);
    border-radius: calc(12px * var(--ui-scale)); cursor: pointer;
    padding: calc(2px * var(--ui-scale)) calc(8px * var(--ui-scale));
    font-size: max(11px, calc(13px * var(--ui-scale))); color: var(--ink);
    min-height: 26px;
}
.ws-rx.mine { border-color: rgba(227,176,102,.6); background: rgba(227,176,102,.12); }
.ws-who { font-size: max(10px, calc(11.5px * var(--ui-scale))); color: var(--gold); margin: 0 calc(8px * var(--ui-scale)) calc(2px * var(--ui-scale)); }
.ws-fwd { font-size: max(10px, calc(11.5px * var(--ui-scale))); color: var(--cyan); margin-bottom: calc(3px * var(--ui-scale)); }
.ws-bubble .flash { animation: ws-flash 1.2s ease; }
@keyframes ws-flash { 0%,60% { border-color: var(--gold); } 100% { border-color: var(--line); } }

/* ── Закреплённое ───────────────────────────────────────────── */
.ws-pin {
    display: flex; align-items: center; gap: calc(8px * var(--ui-scale));
    padding: calc(6px * var(--ui-scale)) calc(16px * var(--ui-scale));
    border-bottom: 1px solid var(--line); background: rgba(227,176,102,.05);
}
.ws-pin-ico { flex-shrink: 0; }
.ws-pin-text {
    flex: 1; min-width: 0; color: var(--gold2);
    font-size: max(11px, calc(13px * var(--ui-scale)));
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ws-bubble { position: relative; }

/* ── Пересылка / группа / стикеры: общий модальный каркас ───── */
.ws-fwdpick, .ws-gallery {
    position: fixed; inset: 0; z-index: var(--z-overlay);
    background: rgba(7,8,13,.9);
    display: flex; align-items: center; justify-content: center;
    opacity: 0; visibility: hidden;
    transition: opacity .25s ease, visibility 0s .25s;
}
.ws-fwdpick.open, .ws-gallery.open { opacity: 1; visibility: visible; transition: opacity .25s ease; }
.ws-fwdpick-box, .ws-gallery-box {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: calc(18px * var(--ui-scale));
    max-width: calc(480px * var(--ui-scale)); width: 94vw;
    max-height: 88dvh; overflow-y: auto;
    padding: calc(18px * var(--ui-scale));
}
.ws-fwdpick-box h3, .ws-gallery-box h3 {
    font-family: var(--serif); color: var(--gold2); font-size: calc(21px * var(--ui-scale));
    margin-bottom: calc(12px * var(--ui-scale));
}
.ws-fwdpick-list { display: flex; flex-direction: column; gap: calc(6px * var(--ui-scale)); margin-bottom: calc(10px * var(--ui-scale)); }
.ws-fwdpick-list button {
    background: var(--bg2); border: 1px solid var(--line); border-radius: calc(10px * var(--ui-scale));
    color: var(--ink); cursor: pointer; padding: calc(11px * var(--ui-scale)) calc(14px * var(--ui-scale));
    font-size: max(12px, calc(14px * var(--ui-scale))); text-align: left; min-height: 44px;
}
.ws-fwdpick-list button:hover { border-color: rgba(227,176,102,.5); }
.ws-fwdpick-box .ws-search { margin-bottom: calc(10px * var(--ui-scale)); }

/* Группа: чекбоксы Своих */
.wg-members { display: flex; flex-direction: column; gap: calc(4px * var(--ui-scale)); margin-bottom: calc(10px * var(--ui-scale)); max-height: 40dvh; overflow-y: auto; }
.wg-mem { display: flex; align-items: center; gap: calc(8px * var(--ui-scale)); padding: calc(8px * var(--ui-scale)); cursor: pointer; border-radius: calc(8px * var(--ui-scale)); font-size: max(12px, calc(14px * var(--ui-scale))); min-height: 40px; }
.wg-mem:hover { background: rgba(255,255,255,.04); }

/* Галерея чата */
.ws-gallery-grid {
    display: grid; gap: calc(8px * var(--ui-scale));
    grid-template-columns: repeat(auto-fill, minmax(calc(110px * var(--ui-scale)), 1fr));
    margin-bottom: calc(12px * var(--ui-scale));
}
.ws-gallery-grid img {
    width: 100%; aspect-ratio: 1/1; height: auto; object-fit: cover;
    border-radius: calc(10px * var(--ui-scale)); border: 1px solid var(--line);
    cursor: zoom-in; pointer-events: auto; display: block;
}

/* Стикеры */
.ws-st { background: none; border: none; padding: 0; cursor: pointer; }
.ws-st img { width: 100%; aspect-ratio: 1/1; object-fit: cover; border-radius: calc(10px * var(--ui-scale)); display: block; }
.ws-st:hover { outline: 2px solid rgba(227,176,102,.5); }
.ws-mem-ed { border-top: 1px solid var(--line); margin-top: calc(12px * var(--ui-scale)); padding-top: calc(12px * var(--ui-scale)); display: flex; flex-wrap: wrap; gap: calc(8px * var(--ui-scale)); }
.ws-mem-ed #ws-mem-canvas {
    width: 200px; height: 200px; border: 1px solid var(--line);
    border-radius: calc(10px * var(--ui-scale)); flex-shrink: 0;
}
.ws-mem-ed .ws-search { flex: 1; min-width: calc(140px * var(--ui-scale)); }
.ws-mem-ed .ws-edit-acts { width: 100%; }
.sticker-btn { font-size: calc(18px * var(--ui-scale)); }

/* Групповая строка участников */
.ws-list-acts { display: flex; gap: calc(8px * var(--ui-scale)); margin-top: calc(8px * var(--ui-scale)); }
.ws-hbtn {
    background: none; border: 1px solid var(--line); border-radius: calc(10px * var(--ui-scale));
    color: var(--muted); cursor: pointer; width: 44px; height: 44px; flex-shrink: 0;
    font-size: calc(16px * var(--ui-scale));
}
.ws-hbtn:hover { border-color: rgba(227,176,102,.5); color: var(--gold); }
@media (max-width: 899px) {
    .ws-reactbar button { width: 42px; height: 42px; font-size: 22px; }
}

/* ── Жалоба на переписку (Э6): режим выбора куска ───────────── */
.ws-reportbar {
    display: flex; align-items: center; gap: calc(10px * var(--ui-scale));
    padding: calc(8px * var(--ui-scale)) calc(16px * var(--ui-scale));
    border-bottom: 1px solid var(--line); background: rgba(226,96,58,.06);
    flex-wrap: wrap;
}
.ws-selmark {
    position: absolute; top: calc(-8px * var(--ui-scale)); left: calc(-6px * var(--ui-scale));
    z-index: 3; background: var(--ember); color: #fff;
    font-size: max(9px, calc(10px * var(--ui-scale))); font-weight: 700;
    padding: calc(1px * var(--ui-scale)) calc(7px * var(--ui-scale));
    border-radius: calc(8px * var(--ui-scale));
}
.ws-bubble-line.sel .ws-bubble { border-color: var(--ember); }
.ws-hbtn#ws-leave { color: var(--ember); }
.ws-hbtn#ws-leave:hover { border-color: rgba(226,96,58,.5); color: var(--ember); }

/* ── Реакции-стикеры и встроенный пак «Сага» (интеграция 07.10) ── */
.ws-rx-img,.ws-rx .saga-reaction {
    width: max(18px, calc(20px * var(--ui-scale)));
    height: max(18px, calc(20px * var(--ui-scale)));
    display: inline-block; vertical-align: middle; object-fit: contain;
}
.ws-rx { display: inline-flex; align-items: center; gap: 3px; }
.ws-reactbar button {
    display: inline-flex; align-items: center; justify-content: center;
    padding: calc(3px * var(--ui-scale));
}
.ws-reactbar .ws-rx-img,.ws-reactbar .saga-reaction {
    width: calc(32px * var(--ui-scale));
    height: calc(32px * var(--ui-scale));
}
.ws-fwdpick-box > .oc-meta:first-of-type { margin-top: 4px; }

/* ── Стикер-пузырь с действиями (решение владельца 07.10) ──
   Панель реакций и меню удаления позиционируются от .ws-sticker — он точка
   отсчёта (position:relative). Действия видны всегда: тап по самой картинке
   открывает лайтбокс, и «раскрыть по тапу», как у текста, тут не к чему
   привязать. */
.ws-sticker { position: relative; display: inline-block; max-width: min(76%, calc(560px * var(--ui-scale))); }
.ws-sticker > img { display: block; cursor: zoom-in; border-radius: calc(10px * var(--ui-scale)); }
.ws-sticker .ws-acts { opacity: 1; }
.ws-sticker .ws-bubble-meta { margin-top: calc(3px * var(--ui-scale)); }
.ws-sticker .ws-rxrow { margin-top: calc(2px * var(--ui-scale)); }
.ws-sticker .ws-delmenu { top: auto; bottom: calc(100% + 4px * var(--ui-scale)); }
/* Comfortable chat geometry independent of the artwork scale. */
.ws-root,.ws-chat,.ws-msgs,.ws-bubble-line,.ws-bubble,.ws-sticker { min-width:0; }
.ws-msgs { overflow-x:hidden; }
.ws-bubble-body { overflow-wrap:anywhere; }
.ws-sticker { width:min(240px,76%); }
.ws-sticker > img { max-width:100%; width:100%; height:auto; }
.ws-bubble-meta { flex-wrap:wrap; }
.ws-acts { opacity:1; display:flex; flex-wrap:wrap; gap:2px; }
.ws-acts .linklike { display:inline-flex;align-items:center;justify-content:center; min-width:44px;min-height:44px;font-size:20px;border-radius:12px; }
.ws-acts .linklike:hover,.ws-acts .linklike:focus-visible { background:rgba(227,176,102,.12); }
.ws-reactbar { position:relative;bottom:auto;right:auto;margin:8px 0 0;flex-wrap:wrap;width:fit-content;max-width:100%;box-sizing:border-box; }
.ws-reactbar button { width:44px;height:44px; }
.ws-editing .ws-compose,.ws-editing .ws-reply { display:none!important; }
.ws-edit-acts { flex-wrap:wrap; }
.ws-fwdpick,.ws-gallery { height:var(--visual-height,100dvh);box-sizing:border-box;padding:12px; }
.ws-fwdpick-box,.ws-gallery-box { box-sizing:border-box;min-width:0;width:min(100%,560px);max-width:100%;max-height:100%;overscroll-behavior:contain; }
.ws-mem-ed { display:grid;grid-template-columns:minmax(0,1fr); }
.ws-mem-ed #ws-mem-canvas { width:min(100%,240px);height:auto;aspect-ratio:1;justify-self:center; }
.ws-mem-ed .ws-search { min-width:0; }
.ws-peer > div { min-width:0; }
.ws-peer-name { overflow-wrap:anywhere; }
@media(max-width:600px) {
 body.ws-body .ws-root { height:calc(var(--visual-height,100dvh) - var(--header-h) - var(--ws-player,0px));padding-bottom:0; }
 body.ws-keyboard { --ws-player:0px!important; }
 .ws-chat-head { gap:6px;padding:8px;flex-wrap:wrap; }
 .ws-search-chat { flex:1 1 100%;width:100%;box-sizing:border-box; }
 .ws-peer-name,.ws-bubble-body,#ws-text,.ws-edit,.ws-search { font-size:16px; }
 .ws-bubble { max-width:92%;box-sizing:border-box; }
 .ws-compose { gap:6px;padding:8px;flex-shrink:0; }
 .ws-attach,.ws-send { width:44px;height:44px;font-size:20px; }
 .ws-msgs { padding:12px 8px; }
}
.ws-avatar { position:relative;display:inline-flex;align-items:center;justify-content:center;overflow:hidden;width:48px;height:48px;min-width:48px;font-family:var(--serif);font-size:24px;color:var(--gold);background:linear-gradient(135deg,var(--panel),var(--bg)); }
.ws-avatar img { position:absolute;inset:0;width:100%;height:100%;object-fit:cover; }
.ws-peer .ws-avatar { width:40px;height:40px;min-width:40px; }
body.ws-body .pwa-bar,body.drawer-open .pwa-bar { display:none; }
.ws-editing .ws-acts { display:none; }
.ws-dialog-head { position:sticky;top:0;z-index:2;display:flex;align-items:center;justify-content:space-between;gap:12px;background:var(--panel);padding-bottom:10px; }
.ws-dialog-head h3 { margin:0; }
.ws-gallery-grid > .oc-empty { grid-column:1 / -1; }
@media(max-width:600px) {
 .ws-dialog-head h3 { font-size:22px; }
 .ws-fwdpick-box .oc-meta,.ws-fwdpick-box .oc-empty { font-size:14px;line-height:1.5; }
}

/* Content-sized bubbles: timestamp never widens the action toolbar. */
.ws-bubble { width:fit-content; }
.ws-bubble-meta { display:flex;flex-direction:column;align-items:flex-end;gap:2px; }
.ws-acts { flex:0 0 auto;max-width:100%;width:max-content;justify-content:flex-end; }
.ws-acts .linklike { flex:0 0 44px;padding:0; }
body.ws-body #donateFab { display:none!important; }
/* The phone mini floats over the chat; only the expanded player needs a reserve. */
html[data-home-layout=phone] body.ws-body.player-floating { --ws-player:0px; }
html[data-home-layout=phone] body.ws-body .ws-root { height:calc(var(--visual-height,100dvh) - var(--header-h));margin:0;border:0;border-radius:0;padding-bottom:env(safe-area-inset-bottom,0px); }
html[data-home-layout=phone] body.ws-body:not(.player-floating):not(.ws-keyboard) .ws-root { height:calc(var(--visual-height,100dvh) - var(--header-h) - var(--ws-player,0px)); }

.ws-meta-info { display:inline-flex;align-items:center;gap:5px; }
.ws-acts{display:none}.ws-bubble-meta{flex-direction:row;align-items:center;gap:6px}.ws-msg-more{width:44px;height:44px}

/* ==============================================================
   DZS — workshop.css: «Мастерская» /workshop (план
   agents/design/PLAN-workshop-masterica.md, ЭW1).
   Одна золотая доминанта — кнопка «Скопировать промт» (закон 5).
   Анимации только transform/opacity; сетки ленивые (loading=lazy
   в JS). Герои/обёртки — классы Очага (.oc-hero/.oc-wrap).
   ============================================================== */

/* ── Шаги ─────────────────────────────────────────────────────── */
.ws-step { padding: calc(30px * var(--ui-scale)) 0 0; }
.ws-step-head {
    display: flex; align-items: baseline; gap: calc(16px * var(--ui-scale));
    margin-bottom: calc(18px * var(--ui-scale));
}
.ws-step-num {
    font-family: var(--serif); font-style: italic;
    font-size: calc(26px * var(--ui-scale)); color: var(--faint);
    min-width: calc(34px * var(--ui-scale)); text-align: right;
    border-bottom: 1px solid rgba(227, 176, 102, .25); padding-bottom: 2px;
}
.ws-step-head h2 {
    font-family: var(--serif); margin: 0;
    font-size: calc(24px * var(--ui-scale)); font-weight: 600; color: var(--gold2);
}
.ws-step-lead { margin: 2px 0 0; font-size: calc(13px * var(--ui-scale)); color: var(--muted); }

/* ── Плитки выбора (персонажи 2:3, места 1:1) ─────────────────── */
.ws-grid {
    display: grid; gap: calc(12px * var(--ui-scale));
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.ws-grid-ch { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); }
.ws-tile {
    position: relative; display: flex; flex-direction: column;
    padding: 0; border: 1px solid rgba(227, 176, 102, .16);
    border-radius: 10px; overflow: hidden; cursor: pointer;
    background: rgba(16, 19, 31, .5); color: var(--muted);
    font-family: var(--sans); text-align: center;
    transition: transform .18s ease, border-color .18s ease;
}
.ws-tile img { width: 100%; height: auto; display: block; }
.ws-tile:hover { transform: translateY(-2px); border-color: rgba(227, 176, 102, .45); }
.ws-tile.on { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold) inset; }
.ws-tile.on .ws-tile-name { color: var(--gold2); }
.ws-tile:active { transform: scale(.98); }
/* Порядковый бейдж фигуры (1..3) на выбранной плитке */
.ws-ord {
    position: absolute; top: calc(8px * var(--ui-scale)); left: calc(8px * var(--ui-scale));
    min-width: calc(22px * var(--ui-scale)); height: calc(22px * var(--ui-scale));
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%; background: linear-gradient(120deg, var(--gold), var(--gold2));
    color: #1a0f06; font-family: var(--sans); font-weight: 700;
    font-size: calc(12px * var(--ui-scale));
}
.ws-tile-name {
    display: block; padding: calc(6px * var(--ui-scale)) calc(6px * var(--ui-scale))
        calc(8px * var(--ui-scale));
    font-size: calc(12px * var(--ui-scale)); line-height: 1.25; color: var(--muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* «Без места»: тёмная плитка — тьма тоже материал */
.ws-tile-none { aspect-ratio: 1 / 1; justify-content: center; }
.ws-none-mark {
    display: block; font-family: var(--serif); color: var(--faint);
    font-size: calc(30px * var(--ui-scale)); line-height: 1;
}
.ws-tile-none.on .ws-none-mark { color: var(--gold); }

/* ── Чипы (свет, формат, платформа) ───────────────────────────── */
.ws-chips { display: flex; flex-wrap: wrap; gap: calc(8px * var(--ui-scale)); }
.ws-chip {
    padding: calc(9px * var(--ui-scale)) calc(14px * var(--ui-scale));
    border-radius: 999px; cursor: pointer;
    border: 1px solid rgba(227, 176, 102, .22);
    background: rgba(16, 19, 31, .5); color: var(--muted);
    font-family: var(--sans); font-size: calc(13px * var(--ui-scale));
    transition: transform .15s ease, border-color .15s ease, color .15s ease;
}
.ws-chip:hover { color: var(--gold2); border-color: rgba(227, 176, 102, .5); }
.ws-chip:active { transform: scale(.97); }
.ws-chip.on { color: var(--gold2); border-color: var(--gold); background: rgba(227, 176, 102, .1); }
/* Чипы сцен паспорта: компактная строка под сеткой места */
.ws-pscene {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: calc(6px * var(--ui-scale)); margin-bottom: calc(12px * var(--ui-scale));
}
.ws-pscene-label { font-size: calc(12px * var(--ui-scale)); color: var(--faint); margin-right: calc(4px * var(--ui-scale)); }
.ws-chip-sm { padding: calc(6px * var(--ui-scale)) calc(11px * var(--ui-scale)); font-size: calc(12px * var(--ui-scale)); }

/* ── Результат: приказ ────────────────────────────────────────── */
.ws-result {
    margin-top: calc(38px * var(--ui-scale));
    padding: calc(30px * var(--ui-scale)) 0 calc(60px * var(--ui-scale));
    border-top: 1px solid rgba(227, 176, 102, .14);
}
.ws-result h2 {
    font-family: var(--serif); margin: 0;
    font-size: calc(26px * var(--ui-scale)); font-weight: 600;
    color: var(--gold2);
}
.ws-result-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: calc(12px * var(--ui-scale)); flex-wrap: wrap;
    margin-bottom: calc(14px * var(--ui-scale));
}
.ws-switch { display: inline-flex; border: 1px solid rgba(227, 176, 102, .22); border-radius: 999px; overflow: hidden; }
.ws-switch-btn {
    padding: calc(7px * var(--ui-scale)) calc(16px * var(--ui-scale));
    background: transparent; border: 0; cursor: pointer; color: var(--muted);
    font-family: var(--sans); font-size: calc(12px * var(--ui-scale));
    transition: color .15s ease, background .15s ease;
}
.ws-switch-btn.on { color: var(--gold2); background: rgba(227, 176, 102, .1); box-shadow: 0 0 0 1px rgba(227, 176, 102, .4) inset; }
.ws-blocks { display: grid; gap: calc(20px * var(--ui-scale)); grid-template-columns: 1.6fr 1fr; }
.ws-prompt-wrap { min-width: 0; }
.ws-prompt, .ws-checklist {
    margin: 0; padding: calc(16px * var(--ui-scale));
    border: 1px solid rgba(227, 176, 102, .18); border-radius: 12px;
    background: rgba(10, 12, 20, .72);
    font-family: var(--sans); font-size: calc(12.5px * var(--ui-scale));
    line-height: 1.55; color: var(--ink);
    white-space: pre-wrap; word-break: break-word;
}
.ws-prompt { min-height: calc(220px * var(--ui-scale)); }
.ws-checklist { min-height: calc(150px * var(--ui-scale)); color: var(--muted); }
.ws-actions { display: flex; gap: calc(10px * var(--ui-scale)); margin-top: calc(12px * var(--ui-scale)); flex-wrap: wrap; }
/* Одна золотая доминанта страницы — «Скопировать промт» (.btn.primary);
   чеклист копируется тихой кнопкой */
.ws-actions .btn { min-height: 44px; }
.ws-refs h3 {
    font-family: var(--serif); margin: 0 0 calc(10px * var(--ui-scale));
    font-size: calc(18px * var(--ui-scale)); font-weight: 600; color: var(--gold2);
}
.ws-refs-pick { display: flex; flex-direction: column; gap: calc(8px * var(--ui-scale)); margin-bottom: calc(10px * var(--ui-scale)); }
.ws-ref-label {
    display: flex; align-items: center; justify-content: space-between; gap: calc(8px * var(--ui-scale));
    font-size: calc(12px * var(--ui-scale)); color: var(--faint);
}
.ws-ref-label select {
    max-width: 60%; padding: calc(6px * var(--ui-scale));
    border-radius: 8px; border: 1px solid rgba(227, 176, 102, .22);
    background: rgba(16, 19, 31, .7); color: var(--ink);
    font-family: var(--sans); font-size: calc(12px * var(--ui-scale));
}
.ws-platform { margin-top: calc(18px * var(--ui-scale)); }
.ws-plat-label { font-size: calc(12px * var(--ui-scale)); color: var(--faint); margin-right: calc(8px * var(--ui-scale)); }
.ws-note { margin: calc(10px * var(--ui-scale)) 0 0; font-size: calc(12.5px * var(--ui-scale)); color: var(--muted); }

/* ── Адаптив (768 / 480, тач-цели ≥44px) ──────────────────────── */
@media (max-width: 768px) {
    .ws-grid, .ws-grid-ch { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 8px; }
    .ws-tile-name { font-size: 11px; }
    .ws-chip { min-height: 44px; display: inline-flex; align-items: center; }
    .ws-blocks { grid-template-columns: 1fr; }
    .ws-step-num { display: none; }
    .ws-result-head { align-items: flex-start; flex-direction: column; }
}
@media (max-width: 480px) {
    .ws-grid, .ws-grid-ch { grid-template-columns: repeat(3, 1fr); }
    .ws-actions { flex-direction: column; }
    .ws-actions .btn { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
    .ws-tile, .ws-chip, .ws-switch-btn { transition: none; }
}

/* ── Заготовки (ЭW4, IndexedDB diz-whisper/workshop) ──────────── */
.ws-drafts-step {
    margin-top: calc(30px * var(--ui-scale));
    padding-bottom: calc(60px * var(--ui-scale));
}
.ws-drafts-title {
    font-family: var(--serif); margin: 0 0 4px;
    font-size: calc(22px * var(--ui-scale)); font-weight: 600; color: var(--gold2);
}
.ws-drafts-empty { font-size: calc(13px * var(--ui-scale)); color: var(--faint); }
.ws-draft {
    border: 1px solid rgba(227, 176, 102, .16); border-radius: 10px;
    background: rgba(16, 19, 31, .5);
    padding: calc(12px * var(--ui-scale)) calc(14px * var(--ui-scale));
    margin-bottom: calc(10px * var(--ui-scale));
}
.ws-draft-head { display: flex; align-items: baseline; gap: calc(10px * var(--ui-scale)); }
.ws-draft-head b { font-family: var(--serif); color: var(--gold2); font-weight: 600; font-size: calc(15px * var(--ui-scale)); }
.ws-draft-ts { font-size: calc(11px * var(--ui-scale)); color: var(--faint); }
.ws-draft-excerpt {
    margin: calc(6px * var(--ui-scale)) 0 calc(8px * var(--ui-scale));
    font-size: calc(12px * var(--ui-scale)); color: var(--muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ws-draft-acts { display: flex; gap: calc(6px * var(--ui-scale)); flex-wrap: wrap; }
.ws-draft-del:hover { color: var(--ember); border-color: rgba(226, 96, 58, .6); }

/* ── Хранилище (/storage) — порт DMN public/storage.html ─────────
   Стили страницы: карточки, полоса занятого места, переключатели,
   список альбомов. Классы st-* уникальны, с остальным сайтом не
   пересекаются. */

/* Hero */
body.storage-body .hero{min-height:34vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:100px 16px 32px}
body.storage-body .hero h1{font-family:var(--serif);font-size:clamp(32px,5vw,58px);font-weight:700;background:linear-gradient(180deg,var(--gold2),var(--gold),var(--ember));-webkit-background-clip:text;background-clip:text;color:transparent;margin-bottom:10px}
body.storage-body .hero .sub{font-size:clamp(14px,1.4vw,18px);color:var(--muted);max-width:560px;padding:0 12px}

/* Общая карточка */
.st-card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:calc(22px * var(--ui-scale));margin-bottom:18px}
.st-card h2{font-family:var(--serif);font-size:clamp(18px,2vw,24px);font-weight:600;color:var(--gold2);margin:0 0 6px}
.st-card .st-note{font-size:13px;color:var(--muted);line-height:1.55;margin:0 0 18px}

/* Занято на устройстве */
.st-usage-num{font-family:var(--serif);font-size:clamp(28px,4vw,40px);font-weight:700;color:var(--ink);line-height:1.1}
.st-usage-num small{font-family:var(--sans);font-size:14px;font-weight:400;color:var(--muted)}
.st-bar{height:8px;border-radius:6px;background:rgba(255,255,255,.07);overflow:hidden;margin:14px 0 10px}
.st-bar-fill{height:100%;border-radius:6px;background:linear-gradient(90deg,var(--gold),var(--ember));transition:width .4s}
.st-legend{display:flex;flex-wrap:wrap;gap:14px;font-size:12px;color:var(--muted)}
.st-legend i{display:inline-block;width:9px;height:9px;border-radius:3px;margin-right:6px;vertical-align:middle}

/* Переключатели */
.st-row{display:flex;align-items:center;gap:14px;padding:14px 0;border-top:1px solid var(--line)}
.st-row:first-of-type{border-top:0}
.st-row-txt{flex:1;min-width:0}
.st-row-txt b{display:block;font-size:15px;font-weight:600;color:var(--ink);margin-bottom:3px}
.st-row-txt span{font-size:12.5px;color:var(--muted);line-height:1.5}
.st-sw{position:relative;flex-shrink:0;width:46px;height:26px;border:0;padding:0;border-radius:14px;background:rgba(255,255,255,.12);cursor:pointer;transition:background .25s}
.st-sw::after{content:'';position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:50%;background:var(--ink);transition:transform .25s}
.st-sw[aria-checked="true"]{background:var(--gold)}
.st-sw[aria-checked="true"]::after{transform:translateX(20px);background:#0a0a0a}
.st-sw:disabled{opacity:.5;cursor:default}

/* Кнопки */
.st-btn{padding:10px 20px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--ink);font:inherit;font-size:14px;font-weight:600;cursor:pointer;transition:border-color .2s,background .2s,color .2s}
.st-btn:hover{border-color:var(--gold);color:var(--gold)}
.st-btn.gold{border-color:var(--gold);color:var(--gold)}
.st-btn.gold:hover{background:var(--gold);color:#0a0a0a}
.st-btn.danger:hover{border-color:var(--ember);color:var(--ember);background:transparent}
.st-btn:disabled{opacity:.45;cursor:default;border-color:var(--line);color:var(--muted)}
.st-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:4px}

/* Альбомы */
.st-alb{display:flex;align-items:center;gap:14px;padding:12px 0;border-top:1px solid var(--line)}
.st-alb:first-child{border-top:0}
.st-alb-cover{width:52px;height:52px;flex-shrink:0;border-radius:8px;object-fit:cover;background:var(--bg2);border:1px solid var(--line)}
.st-alb-body{flex:1;min-width:0}
.st-alb-title{font-size:14px;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.st-alb-meta{font-size:12px;color:var(--muted);margin-top:2px}
.st-alb-meta.ok{color:var(--gold)}
.st-alb-btn{flex-shrink:0;padding:8px 14px;font-size:13px}
.st-prog{height:4px;border-radius:3px;background:rgba(255,255,255,.08);overflow:hidden;margin-top:7px;display:none}
.st-prog-fill{height:100%;background:var(--gold);width:0;transition:width .2s}
.st-empty{font-size:13px;color:var(--muted);padding:10px 0}

@media(max-width:767px){
  .st-alb-cover{width:44px;height:44px}
  .st-alb-title{font-size:13px}
  .st-btn{padding:9px 14px;font-size:13px}
}

/* ==============================================================
   DiZ Saga — donate.css: кнопка поддержки и модальное окно.

   Кнопка живёт в правом верхнем углу, под «Войти». Она обязана быть
   заметной и при этом не раздражать человека, который пришёл читать: отсюда
   «дыхание» вместо мигания — цикл 4.8 с, как спокойный вдох-выдох, с почти
   незаметным изменением масштаба. Мигающий или прыгающий элемент в поле
   зрения читателя — это то, из-за чего закрывают вкладку.

   Размер и цвет задаются переменными --donate-size / --donate-color:
   владелец меняет их из панели, CSS править не нужно.
   ============================================================== */

:root{
  --donate-size:1;
  --donate-color:var(--gold);
  --donate-color2:var(--ember);
}

/* ── Кнопка ───────────────────────────────────────────────── */
/* position:absolute внутри header (он fixed) — так кнопка приклеена именно
   к нижней кромке шапки, ровно под «Войти», и не разъезжается при прокрутке
   и смене высоты окна. */
.donate-fab{
  position:absolute;
  top:calc(100% + 6px * var(--ui-scale));
  right:calc(20px * var(--ui-scale));
  z-index:calc(var(--z-header) - 1);
  width:calc(34px * var(--ui-scale) * var(--donate-size));
  height:calc(34px * var(--ui-scale) * var(--donate-size));
  display:flex;align-items:center;justify-content:center;
  padding:0;
  border:1px solid rgba(227,176,102,.35);
  border-radius:calc(9px * var(--ui-scale));
  /* Размытие подложки снято: кнопка висит в шапке на КАЖДОЙ странице и
     перечитывала фон за собой на каждом кадре — при её размере 34×34 это
     плата ни за что. Полупрозрачность заливки сохранена, фон под ней подмешан
     вручную: на глаз то же матовое стекло, только статичное. */
  background:linear-gradient(140deg,rgba(52,42,26,.92),rgba(48,28,20,.92));
  color:var(--donate-color);
  cursor:pointer;
  /* Появление: кнопка не должна «выпрыгивать» на загруженной странице. */
  animation:donateIn .5s cubic-bezier(.16,1,.3,1) both, donateBreath 4.8s ease-in-out 1s infinite;
  transition:transform .18s cubic-bezier(.16,1,.3,1),border-color .2s,background .2s,color .2s;
}
.donate-fab svg{
  width:calc(17px * var(--ui-scale) * var(--donate-size));
  height:calc(17px * var(--ui-scale) * var(--donate-size));
  display:block;
}
/* Зона нажатия. Кнопка по условию маленькая, а на телефоне масштаб
   интерфейса ужимает её примерно до 15 px — пальцем в такую не попасть.
   Растягиваем невидимую область до 44 px, не трогая внешний вид.
   Растём вниз и в стороны, но НЕ вверх: сверху шапка, и при центрировании
   зона поднималась на ~15 px внутрь неё, накрывая кнопку входа — тап по
   «Войти» открывал донат (ловилось на /storymap при 375 px). */
.donate-fab::after{
  content:'';position:absolute;left:50%;top:0;
  transform:translateX(-50%);
  width:max(100%,44px);height:max(100%,44px);
}
/* Раскрыто меню шапки — кнопку убираем: панель меню накрывает её сверху,
   получалась «полукнопка», по которой не нажать. Класс ставит core.js.
   animation:none обязателен: у donateIn fill-mode both, и оставленная им
   opacity:1 идёт от анимации — обычное объявление её не переспорит.
   Побочный эффект приятный: при закрытии меню кнопка проигрывает своё
   штатное появление заново. */
.nav.drop-open .donate-fab{
  animation:none;
  opacity:0;
  pointer-events:none;
}
.donate-fab:hover{
  transform:scale(1.1);
  border-color:var(--donate-color);
  background:linear-gradient(140deg,rgba(227,176,102,.28),rgba(226,96,58,.18));
  color:var(--gold2);
}
.donate-fab:active{transform:scale(.94);transition-duration:.08s}
.donate-fab:focus-visible{outline:2px solid var(--donate-color);outline-offset:3px}
/* Пока модалка открыта, дышать незачем — цель достигнута. */
.donate-fab.quiet{animation:donateIn .5s cubic-bezier(.16,1,.3,1) both}
.donate-fab.quiet::before{animation:none;opacity:0}

@keyframes donateIn{
  from{opacity:0;transform:translateY(calc(-6px * var(--ui-scale))) scale(.8)}
  to{opacity:1;transform:none}
}
/* Дыхание: масштаб почти не меняется (1 → 1.055), работает мягкая волна
   вокруг кнопки. Этого хватает, чтобы глаз заметил движение боковым зрением,
   и мало, чтобы оно мешало читать.
   Волну сняли с box-shadow: растущий spread — некомпозитное свойство, и
   кнопка вместе с полосой вокруг перекрашивалась каждый кадр, притом кнопка
   висит в шапке и живёт на КАЖДОЙ странице сайта. Теперь кольцо — отдельный
   слой ::before, которому анимируются только transform и opacity. */
@keyframes donateBreath{
  0%,100%{transform:scale(1)}
  50%{transform:scale(1.055)}
}
/* Кольцо: готовая тень постоянного размера, наружу его выносит масштаб.
   На нулевом кадре слой невидим — ровно как прежнее кольцо с нулевым spread. */
.donate-fab::before{
  content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:0 0 0 calc(8px * var(--ui-scale)) rgba(227,176,102,.26);
  opacity:0;transform:scale(.86);
  animation:donateRing 4.8s ease-in-out 1s infinite;
}
@keyframes donateRing{
  0%{transform:scale(.86);opacity:0}
  25%{opacity:.55}
  50%,100%{transform:scale(1.1);opacity:0}
}

/* Человек попросил систему не анимировать — просим не спорить.
   Кнопка остаётся, дыхание уходит. */
@media(prefers-reduced-motion:reduce){
  .donate-fab{animation:none}
  .donate-fab::before{animation:none;opacity:0}
  .donate-fab:hover{transform:none}
}

/* Телефоны: шапка теснее, кнопку чуть уменьшаем и поджимаем к краю. */
@media(max-width:767px){
  .donate-fab{
    right:calc(12px * var(--ui-scale));
    top:calc(100% + 4px * var(--ui-scale));
    width:calc(30px * var(--ui-scale) * var(--donate-size));
    height:calc(30px * var(--ui-scale) * var(--donate-size));
  }
  .donate-fab svg{
    width:calc(15px * var(--ui-scale) * var(--donate-size));
    height:calc(15px * var(--ui-scale) * var(--donate-size));
  }
}

/* Светлая тема. Сейчас сайт только тёмный, но кнопка не должна оказаться
   невидимой в тот день, когда светлая появится: прозрачные подложки и
   currentColor уже готовы, здесь только контраст. */
@media(prefers-color-scheme:light){
  :root:not([data-theme="dark"]) .donate-fab{
    border-color:rgba(180,120,40,.45);
    background:linear-gradient(140deg,rgba(227,176,102,.28),rgba(226,96,58,.16));
    color:#8a5a12;
  }
  :root:not([data-theme="dark"]) .donate-modal{background:rgba(20,18,14,.55)}
}

/* ── Модальное окно ───────────────────────────────────────── */
.donate-modal{
  position:fixed;inset:0;z-index:var(--z-auth,200);
  background:rgba(3,4,8,.82);backdrop-filter:blur(6px);
  display:none;align-items:center;justify-content:center;
  padding:calc(20px * var(--ui-scale));
  animation:donateFade .2s ease both;
}
.donate-modal.open{display:flex}
@keyframes donateFade{from{opacity:0}to{opacity:1}}

.donate-panel{
  background:var(--bg2);border:1px solid var(--line);
  border-radius:calc(16px * var(--ui-scale));
  width:min(calc(600px * var(--ui-scale)),100%);
  max-height:88vh;max-height:88svh;display:flex;flex-direction:column;overflow:hidden;
  box-shadow:0 24px 70px rgba(0,0,0,.6);
  animation:donateRise .32s cubic-bezier(.16,1,.3,1) both;
}
@keyframes donateRise{
  from{opacity:0;transform:translateY(calc(16px * var(--ui-scale))) scale(.97)}
  to{opacity:1;transform:none}
}
@media(prefers-reduced-motion:reduce){
  .donate-modal,.donate-panel{animation:none}
}

.donate-head{
  display:flex;align-items:flex-start;justify-content:space-between;gap:calc(12px * var(--ui-scale));
  padding:calc(20px * var(--ui-scale)) calc(22px * var(--ui-scale)) calc(14px * var(--ui-scale));
  border-bottom:1px solid var(--line);
}
.donate-head h2{
  font-family:var(--serif);font-size:calc(24px * var(--ui-scale));
  color:var(--gold2);margin:0;line-height:1.2;
}
.donate-head .close{
  background:none;border:0;color:var(--faint);font-size:calc(20px * var(--ui-scale));
  cursor:pointer;line-height:1;padding:calc(4px * var(--ui-scale));transition:color .2s;flex-shrink:0;
}
.donate-head .close:hover{color:var(--ember)}
.donate-body{padding:calc(18px * var(--ui-scale)) calc(22px * var(--ui-scale));overflow-y:auto}
.donate-desc{font-size:max(11px,calc(14px * var(--ui-scale)));color:var(--muted);line-height:1.65;white-space:pre-wrap}
.donate-thanks{
  margin-top:calc(14px * var(--ui-scale));padding:calc(12px * var(--ui-scale)) calc(14px * var(--ui-scale));
  border-radius:calc(10px * var(--ui-scale));
  background:rgba(227,176,102,.07);border:1px solid rgba(227,176,102,.2);
  font-family:var(--serif);font-size:max(11px,calc(16px * var(--ui-scale)));color:var(--gold2);
  line-height:1.5;white-space:pre-wrap;
}

/* Счётчики: сколько собрано и сколько людей поддержало. */
.donate-stats{display:flex;gap:calc(10px * var(--ui-scale));margin-top:calc(14px * var(--ui-scale));flex-wrap:wrap}
.donate-stat{
  flex:1;min-width:calc(120px * var(--ui-scale));
  background:var(--panel);border:1px solid var(--line);
  border-radius:calc(10px * var(--ui-scale));padding:calc(10px * var(--ui-scale)) calc(12px * var(--ui-scale));
}
.donate-stat b{display:block;font-family:var(--serif);font-size:calc(20px * var(--ui-scale));color:var(--gold2)}
.donate-stat span{font-size:max(11px,calc(11px * var(--ui-scale)));color:var(--faint)}

/* ── Способы оплаты ───────────────────────────────────────── */
.donate-section-title{
  font-size:max(11px,calc(12px * var(--ui-scale)));color:var(--faint);font-weight:600;
  text-transform:uppercase;letter-spacing:.06em;
  margin:calc(20px * var(--ui-scale)) 0 calc(10px * var(--ui-scale));
}
.donate-method{
  border:1px solid var(--line);border-radius:calc(12px * var(--ui-scale));
  background:var(--panel);padding:calc(14px * var(--ui-scale));
  margin-bottom:calc(10px * var(--ui-scale));transition:border-color .2s;
}
.donate-method:hover{border-color:rgba(227,176,102,.25)}
.donate-method.primary{border-color:rgba(227,176,102,.35);background:rgba(227,176,102,.04)}
.donate-method-head{display:flex;align-items:center;gap:calc(8px * var(--ui-scale));margin-bottom:calc(8px * var(--ui-scale))}
.donate-method-title{font-weight:600;font-size:max(11px,calc(14px * var(--ui-scale)));color:var(--ink)}
.donate-badge{
  padding:calc(1px * var(--ui-scale)) calc(7px * var(--ui-scale));border-radius:calc(4px * var(--ui-scale));
  font-size:max(10px,calc(10px * var(--ui-scale)));font-weight:600;
  background:rgba(227,176,102,.15);color:var(--gold2);
}
.donate-req{
  display:flex;align-items:center;gap:calc(8px * var(--ui-scale));
  background:var(--bg);border:1px solid var(--line);
  border-radius:calc(8px * var(--ui-scale));padding:calc(9px * var(--ui-scale)) calc(11px * var(--ui-scale));
}
.donate-req code{
  flex:1;font-family:monospace;font-size:max(11px,calc(13px * var(--ui-scale)));
  color:var(--ink);word-break:break-all;white-space:pre-wrap;user-select:all;
}
.donate-copy{
  flex-shrink:0;background:none;border:1px solid var(--line);color:var(--muted);
  padding:calc(4px * var(--ui-scale)) calc(9px * var(--ui-scale));
  border-radius:calc(5px * var(--ui-scale));font-size:max(11px,calc(11px * var(--ui-scale)));
  cursor:pointer;font-family:var(--sans);transition:.2s;
}
.donate-copy:hover{border-color:var(--gold);color:var(--gold2)}
.donate-copy.done{border-color:#4ade80;color:#4ade80}
.donate-hint{font-size:max(11px,calc(11px * var(--ui-scale)));color:var(--faint);margin-top:calc(6px * var(--ui-scale))}
.donate-qr{
  margin-top:calc(10px * var(--ui-scale));display:flex;justify-content:center;
}
.donate-qr img{
  width:calc(150px * var(--ui-scale));height:calc(150px * var(--ui-scale));
  border-radius:calc(8px * var(--ui-scale));background:#fff;padding:calc(6px * var(--ui-scale));
  pointer-events:auto;
}

/* ── Кнопки ───────────────────────────────────────────────── */
.donate-btn{
  padding:calc(9px * var(--ui-scale)) calc(18px * var(--ui-scale));
  border-radius:calc(8px * var(--ui-scale));font-size:max(11px,calc(13px * var(--ui-scale)));
  font-weight:600;cursor:pointer;font-family:var(--sans);transition:.2s;
  border:1px solid var(--line);background:transparent;color:var(--muted);
}
.donate-btn:hover{border-color:var(--gold);color:var(--gold2)}
.donate-btn.gold{border-color:var(--gold);background:var(--gold);color:#1a0f06}
.donate-btn.gold:hover{transform:translateY(-1px);box-shadow:0 4px 14px rgba(227,176,102,.3);color:#1a0f06}
.donate-btn:disabled{opacity:.45;cursor:not-allowed;transform:none;box-shadow:none}
.donate-actions{
  display:flex;gap:calc(10px * var(--ui-scale));flex-wrap:wrap;align-items:center;
  padding:calc(14px * var(--ui-scale)) calc(22px * var(--ui-scale));
  border-top:1px solid var(--line);
}

/* ── Форма «Я перевёл средства» ───────────────────────────── */
.donate-form{
  border-top:1px solid var(--line);margin-top:calc(18px * var(--ui-scale));
  padding-top:calc(16px * var(--ui-scale));display:none;
}
.donate-form.open{display:block;animation:donateRise .3s cubic-bezier(.16,1,.3,1) both}
.donate-fields{display:flex;flex-wrap:wrap;gap:calc(10px * var(--ui-scale))}
.donate-field{flex:1;min-width:calc(140px * var(--ui-scale))}
.donate-field.wide{flex-basis:100%}
.donate-field label{
  display:block;font-size:max(11px,calc(11px * var(--ui-scale)));color:var(--faint);
  margin-bottom:calc(4px * var(--ui-scale));
}
.donate-field input,.donate-field select,.donate-field textarea{
  width:100%;padding:calc(8px * var(--ui-scale)) calc(11px * var(--ui-scale));
  border-radius:calc(7px * var(--ui-scale));border:1px solid var(--line);
  background:var(--bg);color:var(--ink);font-size:max(11px,calc(13px * var(--ui-scale)));
  font-family:var(--sans);outline:0;transition:border-color .2s;
}
.donate-field textarea{resize:vertical;min-height:calc(62px * var(--ui-scale))}
.donate-field input:focus,.donate-field select:focus,.donate-field textarea:focus{border-color:var(--gold)}
.donate-field select{cursor:pointer}

/* Переключатель имени: два варианта рядом, чтобы выбор был очевиден и
   человек не гадал, что увидят другие. */
.donate-anon{
  display:flex;gap:calc(8px * var(--ui-scale));margin-top:calc(12px * var(--ui-scale));flex-wrap:wrap;
}
.donate-anon-opt{
  flex:1;min-width:calc(180px * var(--ui-scale));cursor:pointer;
  border:1px solid var(--line);border-radius:calc(9px * var(--ui-scale));
  padding:calc(10px * var(--ui-scale)) calc(12px * var(--ui-scale));
  transition:.2s;background:var(--bg);
}
.donate-anon-opt:hover{border-color:rgba(227,176,102,.3)}
.donate-anon-opt input{position:absolute;opacity:0;pointer-events:none}
.donate-anon-opt.sel{border-color:var(--gold);background:rgba(227,176,102,.07)}
.donate-anon-name{font-size:max(11px,calc(13px * var(--ui-scale)));font-weight:600;color:var(--ink)}
.donate-anon-desc{font-size:max(11px,calc(11px * var(--ui-scale)));color:var(--faint);margin-top:calc(2px * var(--ui-scale))}

.donate-msg{font-size:max(11px,calc(12px * var(--ui-scale)));min-height:calc(16px * var(--ui-scale))}
.donate-note{font-size:max(11px,calc(11px * var(--ui-scale)));color:var(--faint);line-height:1.5;margin-top:calc(10px * var(--ui-scale))}

/* Экран «спасибо» после отправки заявки. */
.donate-done{text-align:center;padding:calc(30px * var(--ui-scale)) calc(10px * var(--ui-scale))}
.donate-done .mark{
  width:calc(52px * var(--ui-scale));height:calc(52px * var(--ui-scale));margin:0 auto calc(14px * var(--ui-scale));
  border-radius:50%;display:flex;align-items:center;justify-content:center;
  background:rgba(74,222,128,.12);border:1px solid rgba(74,222,128,.4);
  color:#4ade80;font-size:calc(24px * var(--ui-scale));
}
.donate-done h3{font-family:var(--serif);font-size:calc(21px * var(--ui-scale));color:var(--gold2);margin-bottom:calc(8px * var(--ui-scale))}
.donate-done p{font-size:max(11px,calc(13px * var(--ui-scale)));color:var(--muted);line-height:1.6}

/* ── Рейтинг донатеров ────────────────────────────────────── */
.donate-periods{display:flex;gap:calc(4px * var(--ui-scale));flex-wrap:wrap;margin-bottom:calc(10px * var(--ui-scale))}
.donate-period{
  padding:calc(4px * var(--ui-scale)) calc(10px * var(--ui-scale));
  border-radius:calc(6px * var(--ui-scale));border:1px solid var(--line);
  background:transparent;color:var(--faint);font-size:max(11px,calc(11px * var(--ui-scale)));
  cursor:pointer;font-family:var(--sans);transition:.2s;
}
.donate-period:hover{border-color:rgba(227,176,102,.3);color:var(--muted)}
.donate-period.sel{border-color:var(--gold);background:rgba(227,176,102,.1);color:var(--gold2)}
.donate-top{border:1px solid var(--line);border-radius:calc(10px * var(--ui-scale));overflow:hidden}
.donate-top-row{
  display:flex;align-items:center;gap:calc(10px * var(--ui-scale));
  padding:calc(9px * var(--ui-scale)) calc(12px * var(--ui-scale));
  border-bottom:1px solid rgba(255,255,255,.04);font-size:max(11px,calc(13px * var(--ui-scale)));
}
.donate-top-row:last-child{border-bottom:none}
.donate-place{
  width:calc(24px * var(--ui-scale));text-align:center;font-family:var(--serif);
  font-size:max(11px,calc(15px * var(--ui-scale)));color:var(--faint);flex-shrink:0;
}
.donate-top-row.top1 .donate-place{color:var(--gold);font-size:max(11px,calc(17px * var(--ui-scale)))}
.donate-top-row.top2 .donate-place{color:#c8cddb}
.donate-top-row.top3 .donate-place{color:#cd7f32}
.donate-top-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.donate-top-name.anon{color:var(--faint);font-style:italic}
.donate-top-sum{font-weight:600;color:var(--gold2);white-space:nowrap}
.donate-top-date{font-size:max(10px,calc(10px * var(--ui-scale)));color:var(--faint);white-space:nowrap}
.donate-empty{padding:calc(22px * var(--ui-scale));text-align:center;color:var(--faint);font-size:max(11px,calc(13px * var(--ui-scale)))}

@media(max-width:767px){
  .donate-panel{max-height:92vh;border-radius:calc(14px * var(--ui-scale))}
  .donate-head{padding:calc(16px * var(--ui-scale))}
  .donate-head h2{font-size:calc(20px * var(--ui-scale))}
  .donate-body{padding:calc(14px * var(--ui-scale)) calc(16px * var(--ui-scale))}
  .donate-actions{padding:calc(12px * var(--ui-scale)) calc(16px * var(--ui-scale))}
  .donate-top-date{display:none}
}

/* ── Адаптивное качество (см. блок в app.css) ────────────────── */
html.perf-low .donate-fab{animation:donateIn .5s cubic-bezier(.16,1,.3,1) both;
  backdrop-filter:none;-webkit-backdrop-filter:none;
  background:linear-gradient(140deg,rgba(48,38,22,.96),rgba(44,25,16,.96))}
html.perf-low .donate-fab::before{animation:none;opacity:0}

.pc-banner,.pc-dialog{box-sizing:border-box;background:var(--bg2);border:1px solid var(--line);border-radius:18px;padding:20px;color:var(--ink);box-shadow:0 12px 40px rgba(0,0,0,.4);font:14px/1.55 var(--sans)}
/* Баннер согласия — по центру горизонтали, отступ над плеером тот же, что у
   плашек pwa-bar («Обновить сайт», «Установить как приложение»): одна формула
   bottom, --player-h считается player.js по факту (решение владельца
   14.10.2026). Появление — подъём из-под верхней грани плеера, уход — той же
   дорожкой вниз (.pc-leaving вешает privacy.ts). Свои keyframes: базовый
   translateX(-50%) обязан жить в каждом кадре анимации, иначе плитка прыгает
   к левому краю; общий pc-appear заканчивается transform:none и для баннера
   не годится. */
.pc-banner{position:fixed;z-index:9500;left:50%;bottom:calc(var(--player-h,0px) + 16px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);width:min(520px,calc(100vw - 32px));text-align:center;animation:pc-banner-in .32s cubic-bezier(.2,.7,.3,1)}
.pc-banner.pc-leaving{pointer-events:none;animation:pc-banner-out .3s ease-in forwards}
.pc-banner .pc-links{justify-content:center}
@keyframes pc-banner-in{from{opacity:0;transform:translate(-50%,28px)}to{opacity:1;transform:translate(-50%,0)}}
@keyframes pc-banner-out{from{opacity:1;transform:translate(-50%,0)}to{opacity:0;transform:translate(-50%,28px)}}
.pc-banner>strong{font:600 20px var(--serif);color:var(--gold2)}
.pc-links{display:flex;gap:12px;flex-wrap:wrap;margin:12px 0;font-size:13px}
.pc-links a{color:var(--gold2);text-decoration:underline;text-underline-offset:3px}
.pc-actions{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.pc-actions .btn{justify-content:center;white-space:normal;min-height:44px;padding:10px;font-size:13px}
.pc-status{font-size:13px;color:var(--muted);margin:10px 0 0}
.pc-status:empty{display:none}
.pc-overlay{position:fixed;inset:0;z-index:9600;display:grid;place-items:center;padding:16px;background:rgba(0,0,0,.7)}
.pc-dialog{width:min(560px,100%);max-height:85dvh;overflow:auto;animation:pc-appear .18s ease-out}
.pc-heading{display:flex;justify-content:space-between;align-items:center;gap:16px}
.pc-heading h2{margin:0;font:28px var(--serif)}
.pc-dialog .btn{min-height:44px}
.pc-option{display:flex;align-items:flex-start;gap:12px;padding:16px 0;cursor:pointer}
.pc-option input{width:22px;height:22px;flex-shrink:0;accent-color:var(--gold);margin:2px 0}
.pc-footer{display:flex;justify-content:center;gap:16px;flex-wrap:wrap;margin-top:16px;font-size:13px}
.pc-footer a,.pc-footer button{color:var(--muted)}
.pc-policy{max-width:800px;line-height:1.75}.pc-policy table{width:100%;border-collapse:collapse;font-size:14px}.pc-policy td,.pc-policy th{padding:10px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top;overflow-wrap:anywhere}.pc-policy a{color:var(--gold2)}
@keyframes pc-appear{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@media(max-width:600px){.pc-banner{width:calc(100vw - 20px);padding:16px;max-height:65dvh;overflow:auto}.pc-policy table{font-size:12px}.pc-policy td,.pc-policy th{padding:6px}}
html.perf-low .pc-banner,html.perf-low .pc-dialog{animation:none}
@media(prefers-reduced-motion:reduce){.pc-banner,.pc-dialog{animation:none}}

/* Shared handheld layout, loaded after all section styles. */
.menu-heading { display:block;padding:12px 14px 4px;color:var(--gold);font-size:11px;letter-spacing:.08em;text-transform:uppercase; }
.drop-panel { max-height:calc(100dvh - var(--header-h) - 24px);overflow-y:auto;overscroll-behavior:contain; }
.drop-panel a { display:flex;align-items:center;gap:8px;min-height:44px;box-sizing:border-box; }
.drop-panel .menu-badge { margin-left:auto; }
.admin-user-filters { display:grid;grid-template-columns:2fr 1fr 1fr;gap:12px; }
.admin-users tr[hidden] { display:none!important; }
.env-settings { margin-top:20px; }
.env-setting { border:1px solid var(--line);border-radius:14px;padding:16px;margin:12px 0; }
.env-setting summary { cursor:pointer;min-height:44px;font-size:16px; }
.env-setting p { color:var(--muted);line-height:1.6; }
.env-setting code { overflow-wrap:anywhere; }
.env-setting input { box-sizing:border-box; }
.mini-drag-ready { outline:2px solid var(--gold);outline-offset:5px; }
@media(max-width:1100px) {
  #page-root { font-size:16px;line-height:1.55; }
  #page-root :is(input:not([type=checkbox]):not([type=range]):not([type=radio]),textarea,select) { box-sizing:border-box;max-width:100%;font-size:16px;min-height:44px; }
  #page-root :is(.btn,.tab-btn,.an-btn,.st-btn,.admin-nav-item,.oc-tab,.ws-tab) { min-height:44px;font-size:14px;white-space:normal; }
  #page-root :is(.grid,.admin-main,.an-block,.vip-bible-layout,.mastery-layout,.ws-blocks)>* { min-width:0; }
  #page-root :is(.lead,.an-desc,.oc-text,.hs-post-body,.st-note,.st-row-txt span) { font-size:16px;line-height:1.65; }
  #page-root :is(.an-dim,.oc-meta,.hs-meta,.st-alb-meta,.st-legend) { font-size:13px; }
  .grid { grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr));gap:16px; }
  .wrap { padding-left:20px;padding-right:20px;box-sizing:border-box;max-width:100%; }
  .sec { padding-top:28px;padding-bottom:28px; }
  .tabs,.an-filters { gap:8px; }
  .admin-h1 { font-size:32px;line-height:1.2; }
  .admin-layout { flex-direction:column; }
  .admin-sidebar { width:100%;border-right:0; }
  .admin-nav { position:static;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px; }
  .admin-nav-item { padding:10px;min-width:0;width:auto; }
  .admin-main { width:100%;box-sizing:border-box;padding:16px 0; }
  .admin-main h2 { font-size:26px; }
  .an-block { padding:18px; }
  .admin-users .table-wrap { padding:8px; }
  .vip-bible-layout,.mastery-layout { grid-template-columns:1fr; }
  .ws-blocks { grid-template-columns:minmax(0,1fr); }
  .st-row { gap:12px; }
  .st-sw { border:9px solid transparent;background-clip:padding-box;box-sizing:content-box; }
  .st-sw::after { left:3px; }
  .drop-panel { min-width:250px; }
  .drop-btn { min-height:44px; }
  .rotate-hint { display:none!important; }
  .modal,.auth-card,.nt-box,.pf-charpick-box { box-sizing:border-box;max-width:calc(100vw - 24px);max-height:calc(100dvh - 24px);overflow-y:auto; }
}
@media(max-width:600px) {
  header.nav { height:60px;max-height:none;padding:0 8px;gap:6px; }
  header.nav .brand { width:34px; }
  header.nav .brand .sigil-img { width:30px;height:30px; }
  header.nav .drops { flex:2;gap:4px; }
  header.nav .drop-auth { flex:1; }
  header.nav .drop-btn { font-size:13px;min-height:44px;padding:0 6px; }
  header.nav .drop-label { overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
  header.nav .drop-panel { position:fixed;left:8px!important;right:8px!important;top:calc(var(--header-h) + 8px);width:auto;min-width:0;padding:12px;max-height:calc(100dvh - var(--header-h) - 24px);border-radius:20px; }
  header.nav .drop-panel a { font-size:16px;padding:10px 12px; }
  .menu-heading { font-size:12px; }
  .wrap { padding-left:12px;padding-right:12px; }
  .sec h1:not(.b-h1) { font-size:32px; }
  .sec h2:not(.b-h2) { font-size:27px; }
  .admin-h1,.admin-layout { padding-left:12px;padding-right:12px; }
  .admin-h1 { font-size:28px; }
  .admin-nav { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .admin-user-filters { grid-template-columns:1fr 1fr;gap:8px; }
  .admin-user-filters label:first-child { grid-column:1/-1; }
  .admin-users .table tbody { display:grid;gap:10px; }
  .admin-users .table tr { display:grid;grid-template-columns:auto minmax(0,1fr);gap:4px 10px;padding:12px;border:1px solid var(--line);border-radius:14px; }
  .admin-users .table td { padding:4px 0;min-width:0;display:flex;gap:8px;align-items:center;justify-content:space-between;border:0; }
  .admin-users .table td:nth-child(2) { font-weight:600; }
  .admin-users .table td:nth-child(n+3) { grid-column:1/-1; }
  .admin-users .table td:empty { display:none; }
  .admin-users .table td:nth-child(-n+2)::before { display:none; }
  .admin-users .table td:last-child { justify-content:flex-end;flex-wrap:wrap; }
  .admin-users .table { margin-top:0; }
  .admin-two { gap:12px; }
  .an-block { padding:14px; }
  .page-row { flex-wrap:wrap;gap:8px;padding:12px 0; }
  .page-name { flex:1 1 55%;font-size:15px; }
  .page-route { display:none; }
  .st-alb { flex-wrap:wrap; }
  .st-alb-btn { margin-left:auto; }
  .toast { box-sizing:border-box;max-width:calc(100vw - 24px);font-size:15px; }
  #toast-wrap { left:12px;right:12px;bottom:calc(var(--player-h,80px) + 16px); }
  .char-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .char-detail-stats { grid-template-columns:1fr; }
  .hs-post-head { gap:8px; }
  .ws-bubble { padding:9px 10px;max-width:100%; }
  .ws-bubble-meta { font-size:11px; }
  .ws-acts { gap:0; }
}
@media(min-width:601px) and (max-width:1100px) {
  .ws-list { width:280px; }
  .ws-chat-head { flex-wrap:wrap; }
  .ws-search-chat { flex:1 1 100%; }
  .ws-bubble { max-width:95%; }
  .album-top,.album-body { grid-template-columns:1fr; }
  .album-intro { font-size:16px; }
  .drop-btn { font-size:15px; }
}

.safe-avatar { position:relative;display:inline-flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0;background:var(--bg);color:var(--gold);font-family:var(--serif);border-radius:50%; }
.safe-avatar>img { position:absolute;inset:0;width:100%;height:100%;object-fit:cover; }

@media(min-width:601px) and (max-width:1100px) {
 .pd-album-desc,.pd-now-story,.pd-now-mood,.pd-album-desc .pd-motto { font-size:16px;line-height:1.55; }
 .pd-track { min-height:56px; }
 .pd-album-btn,.pd-book-link,.pd-video-link { min-height:44px; }
}

header.nav .drop-panel .phone-menu-only { display:none; }
@media(min-width:601px) {
 header.nav .drops { position:absolute;left:50%;transform:translateX(-50%);display:flex;gap:8px;width:auto; }
 header.nav .drop[data-drop="drop-participate"] { display:flex; }
 header.nav .drop-auth { margin-left:auto;flex:0 1 auto; }
 header.nav .drop-btn { width:auto;min-width:100px; }
}
@media(max-width:600px) {
 header.nav .drop[data-drop="drop-universe"] { display:none; }
 header.nav .drop[data-drop="drop-participate"] { display:flex; }
 header.nav .drop-panel .phone-menu-only { display:flex; }
}

@media(max-width:600px) {
 header.nav .drop.open .drop-panel { transform:none;animation:none; }
 body:has(.admin-layout) .pwa-bar { display:none; }
}
.safe-avatar.pf-avatar,.safe-avatar.hs-avatar { font-size:36px; }

@media(max-width:600px) {
 header.nav .drop .drop-panel { transform:translateY(-8px);transition:opacity .18s,transform .18s ease-out; }
 header.nav .drop.open .drop-panel { transform:none; }
}

@media(max-width:1100px) {
 #page-root button:not(.st-sw),#page-root [role=button],#page-root .pf-btn-file { min-height:44px;min-width:44px; }
 #page-root :is(.pf-btn,.btn-submit,.btn-auth,.ws-chip,.ws-chip-sm,.oc-btn,.hs-priv) { font-size:14px;line-height:1.4;padding:10px 14px; }
 #page-root :is(label,.pf-id-meta,.pf-id-user,.pf-role,.pf-err,.ws-draft-ts,.ws-pscene-label,.ws-plat-label) { font-size:13px;line-height:1.5; }
 #page-root :is(.pf-note,.pf-lead,.ws-step-lead,.ws-note,.ws-drafts-empty,.oc-empty,.hs-id-meta) { font-size:15px;line-height:1.6; }
 #page-root :is(.pf-h,.ws-step-title) { font-size:22px;line-height:1.25; }
 .pf-id-name { font-size:24px;overflow-wrap:anywhere; }
 .pf-wrap { padding-top:calc(var(--header-h) + 24px); }
 .pf-grid { grid-template-columns:minmax(0,240px) minmax(0,1fr);gap:20px; }
 .pf-card { padding:18px; }
 body.storage-body .hero { min-height:0;padding:48px 16px 32px; }
 #nav.drop-open #notificationBell { visibility:hidden; }
}
@media(max-width:600px) {
 .pf-grid { grid-template-columns:minmax(0,1fr);gap:16px; }
 .pf-id { display:grid;grid-template-columns:80px minmax(0,1fr);align-items:center;gap:4px 16px;text-align:left; }
 .pf-id .pf-avatar { width:80px;height:80px;grid-row:1 / 5;margin:0; }
 .pf-id-name { font-size:22px; }
 .pf-id-meta,.pf-id-user,.pf-role { margin:0; }
 .pf-role { width:fit-content; }
}

@media(max-width:1100px) {
 #page-root { --faint:#9da3b5;--muted:#b5b9c8; }
 #page-root .an-block h3 { font-size:21px;line-height:1.25; }
 #ss-url { display:block;width:100%;margin:8px 0 14px; }
}

/* Messenger type remains readable independently of the decorative UI scale. */
.ws-row-name,.ws-peer-name { font-size:16px; }
.ws-row-prev { font-size:14px; }
.ws-row-time,.ws-bubble-meta { font-size:12px; }
.ws-bubble-body { font-size:16px;line-height:1.55; }
.ws-meta-info { color:var(--muted); }
@media (max-width:600px) {
 .ws-bubble-body { font-size:17px; }
 .ws-bubble-meta,.ws-row-time { font-size:13px; }
}
@media (min-width:1101px) and (hover:hover) and (pointer:fine) {
 .ws-acts .linklike { min-width:36px;min-height:36px;flex-basis:36px;font-size:17px; }
}

/* Menus follow their button, with their natural content width and edge clamp. */
header.nav .drop .drop-panel {
 position:absolute;left:var(--menu-left,0px)!important;right:auto!important;
 top:calc(100% + 8px);width:max-content;min-width:0;max-width:calc(100vw - 16px);
 box-sizing:border-box;padding:10px 6px;overflow-x:hidden;
 transform:translateY(-8px);transform-origin:top center;
 transition:opacity .18s,transform .18s ease-out;
}
header.nav .drop.open .drop-panel { transform:none;animation:none; }
header.nav .drop-panel :is(a,button,.user-name) { white-space:normal;overflow-wrap:anywhere; }
@media(max-width:600px) {
 header.nav,header.nav.solid { border-bottom:0;box-shadow:none;background:linear-gradient(180deg,rgba(7,8,13,.92),rgba(7,8,13,0)); }
 header.nav .brand { flex:0 0 40px;width:40px;min-width:40px;margin-right:4px;align-items:center;transform:none; }
 header.nav .brand>a:first-child { justify-content:center;width:40px;height:34px;overflow:hidden; }
 header.nav .brand .sigil-img { transform:none;animation:none;max-width:30px; }
 header.nav .drops,header.nav .drop-auth { min-width:0; }
 header.nav .drop .drop-panel { padding:10px 6px;border-radius:20px; }
}

#ws-search-toggle { display:inline-flex;align-items:center;justify-content:center; }
#ws-search-toggle svg { width:22px;height:22px; }
@media(max-width:600px) {
 #ws-search-toggle { display:inline-flex;align-items:center;justify-content:center; }
 #ws-search-toggle svg { width:22px;height:22px; }
 .ws-chat-head .ws-search-chat { display:none; }
 .ws-chat-head.ws-search-open .ws-search-chat { display:block; }
}

@media(max-width:600px) {
 .ws-bubble-line .ws-acts { display:none; }
 .ws-bubble-line.ws-open .ws-acts { display:flex; }
 .ws-bubble-line :is(.ws-bubble,.ws-sticker) { -webkit-touch-callout:none; }
 .ws-bubble-line :is(.ws-bubble-body,.ws-sticker>img) { -webkit-user-select:none;user-select:none; }
}

/* Live text stays in the normal 2D paint tree; only moving copies use 3D. */
:root{--tile-surface:#100e0c}
/* Both live faces use the same rounded outline as their moving copies. */
.fl-flat>.fl-front{position:relative;grid-area:1/1;border-radius:inherit;overflow:hidden}
.ch-flip,.mc-flip{border-radius:inherit}
.ab-face,.ch-face,.hero-face,.pd-face{border-radius:inherit;overflow:hidden}
.sp-face,.mc-face{border-radius:inherit}
.sp-front{overflow:hidden}
.fl-flat{transform:none!important;transform-style:flat!important;perspective:none!important}
.fl-flat>.fl-face,.fl-flat>.ab-face,.fl-flat>.sp-face{transform:none!important;backface-visibility:visible!important}
.fl-flat>.fl-back,.fl-flat>.ab-back,.fl-flat>.sp-back{position:absolute;inset:0}
.fl-flat>.fl-face,.ab-face,.sp-face,.ch-face,.hero-face,.mc-face,.pd-face{background-color:var(--tile-surface)}
.fl-card,.ab-flip,.sp-card,.char-card,.hero-card,.pd-flip-inner,.map-panel{touch-action:pan-y pinch-zoom}
.fl-face,.ab-face,.sp-face,.ch-face,.hero-face,.mc-face,.pd-face,.ch-scroll,.hero-lyr-scroll,.pd-lyrics,.ab-toc,.ab-desc{touch-action:pan-y pinch-zoom}
.fl-flat.is-turning{overflow:visible!important}
.fl-prism-stage{position:absolute;inset:0;z-index:12;pointer-events:none;perspective:1100px;overflow:visible;border-radius:inherit}
.fl-shell{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}
.fl-prism-body{position:absolute;inset:0;transform-style:preserve-3d;border-radius:inherit}
.fl-prism-panel{position:absolute!important;inset:0!important;width:100%;height:100%;margin:0;box-sizing:border-box;overflow:hidden;border-radius:inherit;background-color:var(--tile-surface);backface-visibility:hidden;-webkit-backface-visibility:hidden;pointer-events:none}
.fl-prism-panel img{transform:none!important}
.fl-flat>.fl-edge,.fl-flat>.ab-edge,.fl-flat>.sp-edge{display:none}
.fl-prism-panel.sp-face::before,.fl-prism-panel.sp-face::after{display:none}
.f-flip .fl-back{overscroll-behavior:auto}
.ch-back{background:linear-gradient(180deg,#16120f,#20190f)}
.tri-card .fl-face,.ep-card .fl-face,.f-flip .fl-face{background-color:var(--tile-surface)}
.sp-card{border-radius:calc(20px * var(--ui-scale))}
.ab-flip-inner{border-radius:12px}
.shelf-panel{perspective:none}
.ab-flip,.ch-flip{perspective:none!important}
.sp-face::after{content:none}

/* Shared format row: both formats remain visible at every availability state. */
.pd-album-dl,.album-top-dl,.ab-back .ab-dl{display:grid!important;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;align-items:stretch}
.album-download{display:flex;align-items:center;justify-content:center;gap:5px;min-width:0;min-height:44px!important;width:100%;margin:0;padding:8px 5px;border:1px solid var(--line);border-radius:8px;background:var(--tile-surface);color:var(--gold);font:inherit;font-size:12px;line-height:1.3;white-space:nowrap;cursor:pointer}
.album-download svg{flex-shrink:0;width:16px;height:16px}
.album-download:disabled{opacity:.48;cursor:default}
.album-download.busy{opacity:.8}

/* Account controls share the site's type scale and touch targets. */
.auth-box{width:min(480px,calc(100vw - 24px));max-height:calc(100dvh - 24px);overflow:auto;padding:36px 28px 28px}
.auth-box input{font-size:16px;min-height:48px}.auth-tab{min-height:48px;font-size:20px}
.auth-box .btn-submit,.auth-box .btn-yandex{min-height:48px;font-size:16px}
.auth-box .close{width:44px;height:44px;top:4px;right:4px}
.auth-box .pass-wrap{margin-bottom:14px}.auth-box .pass-wrap input{margin-bottom:0;padding-right:52px}
#pass-eye{width:44px;height:44px;display:flex;align-items:center;justify-content:center;right:4px;padding:0}
.auth-box .auth-hint{margin:10px 0 18px;line-height:1.5;font-size:13px}
.auth-strength-t{margin:7px 0 0;font-size:13px;line-height:1.5}
.btn-yandex,.pf-card .btn-yandex{background:#17191f;color:var(--ink);border:1px solid var(--line);border-radius:12px}
.drop-panel .btn-auth{min-height:48px;font-size:16px}
.drop-panel .user-name{font:600 16px var(--sans);line-height:1.5}
.header-avatar{display:flex;align-items:center;margin-left:8px}.header-avatar[hidden]{display:none}
.header-avatar .safe-avatar{width:30px;height:30px;border-radius:50%;font-size:17px;flex-shrink:0}
#auth-label{max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* A common, bounded gradient shades captions without dimming the artwork. */
.tri-card .fl-front::after,.ep-card .fl-front::after{content:"";position:absolute;left:0;right:0;bottom:0;height:min(240px,100%);z-index:1;pointer-events:none;background:linear-gradient(0deg,rgba(4,5,8,.96) 0%,rgba(4,5,8,.83) 46%,rgba(4,5,8,.42) 75%,transparent 100%)}
.tri-card img,.ep-card img{transition:none}.tri-card:hover img,.ep-card:hover img,.char-card:hover:not(.is-turning) .ch-img{transform:none}
.pf-wrap{padding-top:90px}.pf-grid{grid-template-columns:minmax(0,240px) minmax(0,1fr)}
.pf-main{grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr))}.pf-tile{border-radius:calc(20px * var(--ui-scale));min-width:0;min-height:460px;position:relative;display:grid;grid-template:1fr/1fr}
/* Both faces contribute to height, so a turn never clips controls or resizes the tile. */
.pf-tile.fl-flat>.fl-face{position:relative;inset:auto;grid-area:1/1}
/* Moving copies keep the same layout; only their position and transform differ. */
.pf-tile .fl-face{border-radius:inherit;border:1px solid var(--line);padding:22px;overflow:hidden;background:var(--tile-surface);display:flex;flex-direction:column;gap:8px}
.pf-tile .fl-face>*{flex-shrink:0}.pf-tile h3{font-size:18px;margin:10px 0 0}.pf-tile .pf-note{margin:0}
.pf-tile h2{margin:0 0 6px;font-size:24px}.pf-tile .pf-label{margin:4px 0;font-size:14px}.pf-tile .pf-input{min-height:46px;font-size:16px}
.pf-tile .pf-btn{min-height:46px;margin-top:8px;padding:10px 16px;border:1px solid var(--line);border-radius:12px;background:linear-gradient(120deg,var(--gold),var(--ember));color:#1a0f06;font:600 15px var(--sans);cursor:pointer}.pf-tile .pf-turn{margin-top:auto;min-height:44px;background:transparent;color:var(--gold);border:1px solid var(--line);border-radius:10px;cursor:pointer}
.pf-tile .btn-yandex{background:#17191f;color:var(--ink)}.pf-tile .pf-btn-danger{min-height:46px}
.pf-tile .pf-av-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:stretch;gap:8px;margin-top:8px}#page-root .pf-tile .pf-av-row .pf-btn{display:flex;align-items:center;justify-content:center;min-width:0;min-height:46px;margin:0;font-size:14px;line-height:1.4;text-align:center}
.pf-tile .pf-danger{color:#ed9b88}.pf-tile .pf-note{font-size:14px;line-height:1.55}
.pf-charpick-box{width:min(860px,calc(100vw - 24px))}.pf-charpick-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:12px}
.pf-charpick-grid .pf-char{aspect-ratio:1/1;height:auto;min-width:0;border-radius:16px;overflow:hidden;position:relative;background:var(--panel);padding:0}
.pf-charpick-grid .pf-char img{width:100%;height:100%;object-fit:cover;object-position:center 25%}
.pf-charpick-grid .pf-char>span{position:absolute;bottom:0;left:0;right:0;padding:22px 8px 8px;background:linear-gradient(transparent,rgba(0,0,0,.9));font-size:13px}
.pf-crop{position:fixed;inset:0;z-index:10040;background:rgba(0,0,0,.8);display:grid;place-items:center;padding:12px}
.pf-crop-box{width:min(460px,100%);max-height:calc(100dvh - 24px);overflow:auto;border-radius:20px;border:1px solid var(--line);background:var(--bg2);padding:22px}
.pf-crop-stage{width:min(280px,100%);aspect-ratio:1;position:relative;margin:18px auto;overflow:hidden;background:#111;touch-action:none;border-radius:50%;outline:2px solid var(--gold)}
.pf-crop-stage img{position:absolute;max-width:none;pointer-events:none;user-select:none}.pf-crop-box input{width:100%;min-height:44px}.pf-crop-actions{display:flex;gap:12px;justify-content:flex-end;margin-top:18px}.pf-crop-actions button{min-height:44px}
.pf-crop-box input[type=range]{accent-color:var(--gold)}.pf-crop-box h2{font-family:var(--serif);color:var(--gold2)}
.vs-pagination{display:flex;justify-content:center;align-items:center;flex-wrap:wrap;gap:10px;margin:24px auto}.vs-pagination button,.vs-pagination select{min-height:44px;background:var(--panel);border:1px solid var(--line);color:var(--ink);border-radius:10px;padding:8px 14px;font:inherit}
.vs-section[hidden]{display:none}.vs-count{color:var(--muted);text-align:center}
.vs-pagination{display:grid;grid-template-columns:auto auto auto;width:fit-content;max-width:100%;gap:10px;box-sizing:border-box;padding:0 8px}
.vs-pagination label{display:grid;justify-items:center;gap:4px;min-width:0;font:14px var(--sans);text-align:center}
.vs-pagination select{width:76px;min-width:0;box-sizing:border-box;text-align:center;text-align-last:center}
.vs-pagination [data-more],.vs-pagination .vs-count{grid-column:1 / -1;justify-self:center}
.vs-pagination button{font-size:14px;white-space:nowrap}
.vs-current{scroll-margin-top:calc(var(--header-h) + 16px)}
@media(max-width:767px){.auth-box{padding:42px 20px 24px}.auth-box label{font-size:14px}.auth-box .sub{font-size:15px}.pf-grid,.pf-main{grid-template-columns:1fr}.pf-id .pf-role{justify-self:center}.pf-charpick-grid{grid-template-columns:repeat(2,minmax(0,1fr))}#auth-label{max-width:105px}.header-avatar{display:none}}

.ae-modal label{display:block;margin:12px 0;font-size:15px}.ae-modal input{display:block;min-height:48px;border:1px solid var(--line);border-radius:10px;background:var(--panel);color:var(--ink);padding:10px 12px;font:16px var(--sans)}.ae-modal [hidden]{display:none!important}.ae-modal [data-status]{font-size:14px;color:var(--muted);line-height:1.5;margin-top:12px}#auth-reset{display:block;margin:18px auto 0;min-height:44px}

/* ==============================================================
   DiZ Saga — fonts.css: свои шрифты (сгенерировано
   _scripts/fetch_fonts.py — правки руками не переживут перегенерацию)
   ============================================================== */

/* cyrillic-ext */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url(/assets/fonts/CormorantGaramond-300-700-italic-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* cyrillic */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url(/assets/fonts/CormorantGaramond-300-700-italic-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* latin-ext */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url(/assets/fonts/CormorantGaramond-300-700-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* latin */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url(/assets/fonts/CormorantGaramond-300-700-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* cyrillic-ext */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(/assets/fonts/CormorantGaramond-300-700-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* cyrillic */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(/assets/fonts/CormorantGaramond-300-700-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* latin-ext */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(/assets/fonts/CormorantGaramond-300-700-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* latin */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(/assets/fonts/CormorantGaramond-300-700-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* cyrillic-ext */
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url(/assets/fonts/Manrope-200-800-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* cyrillic */
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url(/assets/fonts/Manrope-200-800-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* latin-ext */
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url(/assets/fonts/Manrope-200-800-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* latin */
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url(/assets/fonts/Manrope-200-800-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Home has two authored compositions. Player state never selects a layout. */
:root{--home-scale:1;--player-h:96px}
body.player-floating{--player-handle-h:20px}
html[data-home-layout=desktop]{--ui-base:1;--maxw:1220px}
html[data-home-layout=desktop] :is(#page-root,footer){min-width:768px}
html[data-home-layout=desktop] header.nav{min-width:768px;padding:12px 20px;min-height:60px}
html[data-home-layout=desktop] header.nav .drop-btn{font-size:13px;min-height:36px;padding:8px 18px}
html[data-home-layout=desktop] header.nav .drops{position:absolute;left:50%;transform:translateX(-50%);width:auto;gap:8px}
html[data-home-layout=desktop] header.nav .brand-name{display:flex}
html[data-home-layout=desktop] header.nav .drop[data-drop=drop-universe]{display:block}
html[data-home-layout=desktop] header.nav .phone-menu-only{display:none}
body #page-root .st-note{font-size:16px;line-height:1.65}
body #page-root .map-hint{font-size:14px}
.tri-cap p{font-size:14px}
.oc-note{font-size:14px;line-height:1.6}
@media(pointer:coarse){
 html[data-home-layout=desktop]{--player-h:132px}
 html[data-home-layout=desktop] :is(#page-root,footer){min-width:0}
 html[data-home-layout=desktop] header.nav{min-width:600px}
}
@media(max-width:767px) and (pointer:coarse){
 html[data-home-layout=desktop] header.nav{padding:12px;gap:6px}
 html[data-home-layout=desktop] header.nav .drop-btn{min-width:84px;padding:8px 10px;font-size:12px}
 html[data-home-layout=desktop] #auth-label{max-width:100px}
}
body #page-root .home-hero{
  height:var(--home-height,calc(var(--home-offset,0px) + 1208px * var(--home-scale)));min-height:0;
  padding:0;display:block;overflow:clip;
}
.home-composition{
  position:absolute;top:var(--home-offset,0px);left:50%;width:1480px;height:1208px;
  margin-left:-740px;transform:scale(var(--home-scale));transform-origin:top center;
  display:flex;flex-direction:column;align-items:center;text-align:center;
  padding:120px 24px 0;--ui-scale:1;
}
body .home-hero .hero-emblem{width:420px;height:420px;margin-bottom:30px;flex:none}
body .home-hero .hero-logo{width:460px;height:259px}
body .home-hero .sub-gold{width:max(640px,calc(640px / var(--home-scale)));max-width:calc((100vw - 48px) / var(--home-scale));margin-top:22px;font-size:max(23px,calc(16px / var(--home-scale)));line-height:1.55}
body .home-hero .cta{width:auto;max-width:none;margin-top:42px;gap:16px;flex-flow:row nowrap}
body #page-root .home-hero .btn{width:auto;min-height:0;padding:14px 30px;font-size:max(15px,calc(13px / var(--home-scale)));line-height:1.6;white-space:nowrap}
body .home-hero .scroll-hint{top:var(--home-hint-top,1162px);bottom:auto;font-size:max(12px,calc(10px / var(--home-scale)));line-height:1.6;white-space:nowrap}

/* A tablet keeps the desktop grid, with the same proportions in either orientation. */
html[data-home-layout=desktop] .wrap{width:100%;max-width:1220px;padding:0 24px}
html[data-home-layout=desktop] #page-root:has(.home-hero) .triptych{grid-template-columns:repeat(3,1fr);gap:22px}
html[data-home-layout=desktop] #page-root:has(.home-hero) .epoch-grid{grid-template-columns:repeat(4,1fr);gap:18px}
html[data-home-layout=desktop] .ep-cap h4{font-size:clamp(16px,7.8cqw,23px)}
html[data-home-layout=desktop] .ep-cap .no{font-size:clamp(10px,4.3cqw,11px)}
html[data-home-layout=desktop] #page-root:has(.home-hero) .f-grid{grid-template-columns:repeat(3,1fr)}
html[data-home-layout=desktop] #page-root:has(.home-hero) .tri-card{height:auto;aspect-ratio:3/4}
html[data-home-layout=desktop] #page-root:has(.home-hero) .sec h2{font-size:min(56px,6.7cqw)}
html[data-home-layout=desktop] .album-top{grid-template-columns:1.7fr 1fr}
html[data-home-layout=desktop] .album-body{grid-template-columns:1.3fr 1.5fr}
html[data-home-layout=desktop] .album-hero{border-right:1px solid var(--line);border-bottom:0}
html[data-home-layout=desktop] .char-grid{grid-template-columns:repeat(4,minmax(0,1fr))}
html[data-home-layout=desktop] .album-intro{font-size:16px;line-height:1.65}
html[data-home-layout=desktop] .pf-grid{grid-template-columns:minmax(0,260px) minmax(0,1fr)}
html[data-home-layout=desktop] .ws-root .ws-list{display:flex;width:280px;border-right:1px solid var(--line)}
html[data-home-layout=desktop] .ws-root .ws-chat{display:flex;min-width:0}
html[data-home-layout=desktop] .ws-root .ws-back{display:none}
@media(max-height:800px){
 html[data-home-layout=desktop] .home-hero .hero-emblem{margin-bottom:16px}
 html[data-home-layout=desktop] .home-hero .sub-gold{margin-top:12px}
 html[data-home-layout=desktop] .home-hero .cta{margin-top:24px}
}

html[data-home-layout=phone]{--ui-base:.72;--player-h:122px}
html[data-home-layout=phone] body:has(.home-hero){--home-reserve:142px}
html[data-home-layout=phone] #page-root .home-hero{height:calc(100svh - 142px);min-height:calc(var(--header-h) + 360px)}
html[data-home-layout=phone] .home-composition{
  top:calc(var(--header-h) + 24px);width:min(390px,calc(100vw - 24px));
  height:calc(100svh - var(--header-h) - 24px - 142px);min-height:336px;max-height:730px;
  margin-left:0;transform:translateX(-50%);padding:0;
  display:grid;grid-template-rows:minmax(48px,1.3fr) minmax(36px,1fr) auto auto auto;
  justify-items:center;gap:8px;
}
html[data-home-layout=phone] .home-composition>picture{display:flex;align-items:center;justify-content:center;width:100%;height:100%;min-height:0}
html[data-home-layout=phone] .home-hero .hero-emblem{width:auto;height:100%;max-height:220px;max-width:100%;object-fit:contain;margin:0}
html[data-home-layout=phone] .home-hero .logo-h{height:100%;min-height:0;width:100%}
html[data-home-layout=phone] .home-hero .hero-logo{width:300px;max-width:100%;height:100%;max-height:169px;object-fit:contain}
html[data-home-layout=phone] .home-hero .sub-gold{width:100%;max-width:360px;margin:0;font-size:clamp(14px,4.35vw,17px);line-height:1.55}
html[data-home-layout=phone] .home-hero .scroll-hint{
  position:static;transform:none;animation:none;margin:0;font-size:10px;line-height:16px;
  order:5;
}
html[data-home-layout=phone] .home-hero .sub{order:3}
html[data-home-layout=phone] .home-hero .cta{order:4}
html[data-home-layout=phone] .hint-before-cta .scroll-hint{order:3}
html[data-home-layout=phone] .p-stone.mini{width:64px!important;height:64px!important;border-radius:50%;touch-action:none;user-select:none;-webkit-user-select:none}
html[data-home-layout=phone] .p-stone.mini .player-inner{display:flex;justify-content:center;padding:0!important;width:64px;height:64px;min-height:0}
html[data-home-layout=phone] .p-stone.mini :is(.p-right,.p-tab,.p-move-zone,.p-drag-zone,.p-btn:not(#player-play),.p-track-num){display:none!important}
html[data-home-layout=phone] .p-stone.mini .player-controls{gap:0}
html[data-home-layout=phone] .p-stone.mini #player-play{width:64px;height:64px;margin:0}
html[data-home-layout=phone] .home-hero .cta{flex-direction:column;width:260px;max-width:100%;margin:0;gap:8px}
html[data-home-layout=phone] #page-root .home-hero .btn{width:100%;height:clamp(36px,5.2svh,44px);min-height:0;justify-content:center;font-size:15px;padding:6px 20px}

