:root {
  --bg: var(--tg-theme-bg-color, #ffffff);
  --bg2: var(--tg-theme-secondary-bg-color, #f2f3f5);
  --text: var(--tg-theme-text-color, #111111);
  --hint: var(--tg-theme-hint-color, #7a7f87);
  --link: var(--tg-theme-link-color, #2a7bd5);
  --btn: var(--tg-theme-button-color, #2a7bd5);
  --btn-text: var(--tg-theme-button-text-color, #ffffff);
  --danger: #d64545;
  --ok: #2fa46b;
  --warn: #d9962b;
  --radius: 14px;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
/* Space for the scrollbar is always reserved: otherwise a short tab without scrolling is wider than a long one,
   and the screen jumps when switching. */
html { scrollbar-gutter: stable; }
body {
  background: var(--bg);
  color: var(--text);
  font: 16px/1.4 -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  padding-bottom: env(safe-area-inset-bottom);
}

.top {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px 10px;
  /* The header with the burger stays on scroll: the menu is reachable from anywhere in a long tab. */
  position: sticky; top: 0; z-index: 20;
  background: var(--bg);
  box-shadow: 0 1px 0 rgba(127, 127, 127, .18);
}
.brand {
  font-size: 20px; font-weight: 800; letter-spacing: .2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.who {
  margin-left: auto; color: var(--hint); font-size: 13px; text-align: right;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Wallet and stats in the header (feedback #296): you can see what is left while buying, crafting,
   sharpening. Sits on the right, the nickname after it; does not shrink, the tab title yields. Money is always shown,
   stats and nickname only while they fit: if the header does not fit, stats go first (.tw-nostats), then the nickname
   (.tw-nowho); on a phone the wallet is a second row (.tw-row). Decided by fitTop in app.js. */
.top-wallet { display: none; }
.top-wallet:not([hidden]) {
  display: flex; align-items: center; gap: 10px; flex: 0 0 auto; margin-left: auto;
  font-size: 13px; white-space: nowrap; font-variant-numeric: tabular-nums;
}
.top-wallet:not([hidden]) + .who { margin-left: 0; }
.top-wallet .tw-money, .top-wallet .tw-stats { display: flex; align-items: center; gap: 8px; }
.top-wallet .tw-stats { padding-left: 10px; border-left: 1px solid rgba(127, 127, 127, .3); color: var(--hint); }
.top-wallet .tw-i b { font-weight: 700; color: var(--text); }
.top-wallet .tw-stats .tw-i b { font-weight: 600; }
.top.tw-nostats .tw-stats, .top.tw-nowho .who { display: none; }
/* Phone: the wallet is a second header row, money on the left, stats on the right. The row overlays
   the header bottom padding rather than wrapping: the first row (title and nickname) lives as before. */
.top.tw-row { padding-bottom: 34px; }
.top.tw-row .top-wallet {
  position: absolute; left: 16px; right: 16px; bottom: 9px; margin: 0; gap: 8px; justify-content: space-between;
}
.top.tw-row .top-wallet + .who { margin-left: auto; }
.top.tw-row .tw-money, .top.tw-row .tw-stats { gap: 6px; }
.top.tw-row .tw-stats { padding-left: 0; border-left: 0; }

/* Square button with an SVG icon: the hamburger in the header and the close cross in the menu. */
.icon-btn {
  flex: 0 0 auto; width: 38px; height: 38px; padding: 0; border: 0; border-radius: 12px;
  background: var(--bg2); color: var(--text); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.icon-btn svg { width: 22px; height: 22px; }
.icon-btn:active { transform: scale(.94); }
.icon-btn[hidden] { display: none; }
/* «Вернуться» in the header: round, in the main button color, visible while a nested screen is open. */
.back-btn { border-radius: 50%; background: var(--btn); color: var(--btn-text); }
/* Fullscreen (Bot API 8.0): there is no Telegram header, its buttons lie over the app, so we push our
   header and menu below them. Safe areas are set by telegram-web-app.js (--tg-*). */
html.tg-fs .top {
  padding-top: calc(12px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
}
html.tg-fs .drawer {
  padding-top: calc(8px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
}

/* --- left menu: slides over the content, like in companion apps --- */
.drawer-overlay {
  position: fixed; inset: 0; z-index: 25; background: rgba(0, 0, 0, .5);
  opacity: 0; pointer-events: none; transition: opacity .25s ease;
}
.drawer-overlay.show { opacity: 1; pointer-events: auto; }

.drawer {
  position: fixed; top: 0; left: 0; bottom: 0; z-index: 30;
  width: min(304px, 85vw);
  display: flex; flex-direction: column;
  background: var(--bg); border-radius: 0 18px 18px 0;
  box-shadow: 0 0 40px rgba(0, 0, 0, .35);
  padding: calc(env(safe-area-inset-top) + 8px) 10px calc(env(safe-area-inset-bottom) + 12px);
  /* visibility hides the closed menu from keyboard focus and screen readers. */
  transform: translateX(-110%); visibility: hidden;
  transition: transform .26s cubic-bezier(.2, .7, .3, 1), visibility .26s;
}
.drawer.open { transform: none; visibility: visible; }

.drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 6px 6px 12px 14px; }
.drawer-brand {
  font-size: 20px; font-weight: 800; letter-spacing: .2px;
  /* Signature gradient, yellow-orange, like the asylum sign. */
  background: linear-gradient(100deg, #f0a132, #e5484d);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}

.drawer-nav { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
/* Bot version in the menu footer: the player cites it in feedback to say which build they have. */
.drawer-ver { padding: 10px 14px 2px; color: var(--hint); font-size: 12px; }
.drawer-nav button {
  display: flex; align-items: center; gap: 12px; width: 100%;
  border: 0; background: transparent; color: var(--text); font: inherit; font-weight: 600;
  font-size: 16px; padding: 10px 12px; border-radius: 14px; cursor: pointer; text-align: left;
}
.drawer-nav button[hidden] { display: none; }
.drawer-nav button:active { transform: scale(.98); }

/* Each section has its own color: it always paints the icon chip, and the row highlight
   only for the active one. The colors are ours, not from the Telegram theme, so they are the same day and night. */
.drawer-nav button[data-tab="profile"] { --c: #4d8ef7; }
.drawer-nav button[data-tab="energy"]  { --c: #f0a132; }
.drawer-nav button[data-tab="business"] { --c: #2f9e8f; }
.drawer-nav button[data-tab="dice"]    { --c: #8f6df0; }
.drawer-nav button[data-tab="mutagens"] { --c: #a24bcf; }
.drawer-nav button[data-tab="wardrobe"] { --c: #b0764a; }
.drawer-nav button[data-tab="friends"] { --c: #34b46b; }
.drawer-nav button[data-tab="bosses"]  { --c: #e5484d; }
.drawer-nav button[data-tab="fight"]   { --c: #22a8c4; }
.drawer-nav button[data-tab="raid"]    { --c: #f2703f; }
.drawer-nav button[data-tab="pvp"]     { --c: #c2413b; }
.drawer-nav button[data-tab="stats"]   { --c: #5b6ee1; }
.drawer-nav button[data-tab="changelog"] { --c: #4dc4a8; }
.drawer-nav button[data-tab="feedback"] { --c: #6f8cf5; }
.drawer-nav button[data-tab="notify"]  { --c: #e0559a; }
.drawer-nav button[data-tab="sub"]     { --c: #d99a1b; }
.drawer-nav button[data-tab="admin"]   { --c: #8a94a6; }

.nav-ico {
  flex: 0 0 32px; width: 32px; height: 32px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  background: var(--c, var(--hint)); color: #fff;
  box-shadow: inset 0 -6px 10px rgba(0, 0, 0, .14);
}
.nav-ico svg { width: 18px; height: 18px; }
.nav-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drawer-nav button.active {
  background: rgba(127, 127, 127, .14);                     /* fallback background without color-mix */
  background: color-mix(in srgb, var(--c) 16%, transparent);
  color: var(--c);
}

/* «How many are waiting» badge: a red pill, like Telegram unread counter. */
.nav-badge {
  flex: 0 0 auto; min-width: 22px; text-align: center;
  background: #e5484d; color: #fff; border-radius: 999px;
  font-size: 12px; font-weight: 700; padding: 3px 7px; line-height: 1.2;
}
/* State badges are not «waiting for a reply» but «what is going on now»: bar full is a green pill with ⚡;
   combat energy, auto-fight and raid get a quiet pill in their section color. */
.nav-badge.ok { background: var(--ok); }
.nav-badge.soft {
  background: rgba(127, 127, 127, .16);                       /* fallback background without color-mix */
  background: color-mix(in srgb, var(--c, var(--hint)) 18%, transparent);
  color: var(--c, var(--text)); font-weight: 700;
}

.drawer-sep { flex: 0 0 1px; height: 1px; background: rgba(127, 127, 127, .2); margin: 8px 14px; }

/* «Scroll down» arrow: more items than fit on screen, a semi-transparent circle
   on the right at the bottom edge of the list. The block itself has zero height, the arrow hangs over the list end.
   Tapping scrolls the menu down; an arrow faded at the end of the list does not catch taps. */
.drawer-more { position: relative; flex: 0 0 0; height: 0; pointer-events: none; }
.drawer-more-ico {
  position: absolute; right: 10px; bottom: 8px; width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  border: 0; padding: 0; font: inherit; pointer-events: none;
  background: rgba(127, 127, 127, .38); color: var(--text);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .18);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  opacity: 0; transition: opacity .2s ease;
}
.drawer-more-ico svg { width: 18px; height: 18px; }
.drawer-more.show .drawer-more-ico {
  opacity: .85; pointer-events: auto; cursor: pointer;
  animation: drawer-more-bob 1.6s ease-in-out infinite;
}
@keyframes drawer-more-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(3px); }
}

@media (prefers-reduced-motion: reduce) {
  .drawer, .drawer-overlay { transition: none; }
  .drawer-more.show .drawer-more-ico { animation: none; }
}

/* The «open via Telegram» screen: we show nothing else in a regular browser. */
.stub {
  min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; padding: 24px; text-align: center;
}
.stub-icon { font-size: 48px; }
.stub-title { font-size: 20px; font-weight: 700; }
.stub-sub { color: var(--hint); font-size: 14px; max-width: 30em; line-height: 1.45; }
.stub-link {
  margin-top: 8px; padding: 12px 20px; border-radius: 12px; text-decoration: none;
  background: var(--btn); color: var(--btn-text); font-weight: 600;
}

.asplayer {
  margin: 0 16px 10px; padding: 8px 12px; border-radius: 10px; font-size: 13px; line-height: 1.35;
  background: rgba(217,150,43,.16); color: var(--warn);
}

/* Top margin from the header: without it the first card stuck to its bottom edge. */
main { padding: 14px 16px 24px; }
.tab { display: none; }
.tab.active { display: block; }

.card {
  background: var(--bg2); border-radius: var(--radius); padding: 14px 16px; margin-bottom: 12px;
}
.card h2 { margin: 0 0 4px; font-size: 18px; }
.card .sub { color: var(--hint); font-size: 14px; }
.row { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 6px 0; }
.row + .row { border-top: 1px solid rgba(127,127,127,.15); }
.row .k { color: var(--hint); font-size: 14px; }
.row .v { font-weight: 600; text-align: right; }
.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 6px; }
.stat { background: var(--bg); border-radius: 10px; padding: 10px; text-align: center; }
/* A tile number on one line: if the row does not fit, fitNums in app.js shortens the longest
   («17,9М»); nothing left to shorten, so the icon moves a line up (.wrap). */
.stat .n { font-size: 18px; font-weight: 700; white-space: nowrap; }
.grid3.wrap .stat .n { white-space: normal; }
.stat .l { color: var(--hint); font-size: 12px; }

.bar { height: 10px; border-radius: 6px; background: rgba(127,127,127,.2); overflow: hidden; margin-top: 6px; }
.bar > i { display: block; height: 100%; background: var(--ok); transition: width .4s ease; }
.bar.boss > i { background: var(--danger); }
.bar.energy > i { background: var(--warn); }
.bar.combat > i { background: var(--link); }
.bar.exp > i { background: var(--link); }
.bar.big { height: 18px; border-radius: 9px; }
.exp-note { margin-top: 6px; font-size: 13px; color: var(--hint); font-variant-numeric: tabular-nums; }

/* --- energy: big bar, plan and work tasks --- */
.energy-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.energy-head h2 { margin: 0; }
.energy-big { font-size: 30px; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.energy-big span { font-size: 17px; font-weight: 600; color: var(--hint); }
.energy-big.small { font-size: 22px; }
.energy-note { margin-top: 8px; font-size: 14px; color: var(--hint); font-variant-numeric: tabular-nums; }
.energy-card.is-full .bar.big > i { background: var(--danger); }
.warn-text { color: var(--warn); }
.row .v.warn-text, .row .v.warn-text .sub { color: var(--warn); }

/* One row on any screen: if it does not fit, the row scrolls sideways instead of breaking downward. */
.chips {
  display: flex; flex-wrap: nowrap; gap: 8px; margin: 10px 0 4px;
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
  padding-bottom: 2px;
}
.chips::-webkit-scrollbar { display: none; }
.chip {
  border: 1px solid rgba(127,127,127,.3); background: var(--bg); color: var(--text);
  border-radius: 999px; padding: 8px 13px; font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
  flex: 0 0 auto; white-space: nowrap;
}
.chip.on { background: var(--btn); color: var(--btn-text); border-color: var(--btn); }
.chip.off { opacity: .45; cursor: default; }

/* Floor switch: exactly one is always selected, and under the code is a hint about the tasks there
   (best rate 💰/⚡, «не сканирован», «всё закрыто»). This shows where to go without switching. */
/* A grid rather than flex-wrap: buttons in the last row match the first and do not stretch. */
.floors { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 8px; margin: 10px 0 4px; }
.floorpick {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  border: 1px solid rgba(127,127,127,.3); background: var(--bg); color: var(--text);
  border-radius: 12px; padding: 8px 10px; font: inherit; cursor: pointer;
}
.floorpick b { font-size: 17px; font-weight: 800; line-height: 1.1; }
.floorpick span { font-size: 11px; color: var(--hint); white-space: nowrap; }
.floorpick.on { background: var(--btn); color: var(--btn-text); border-color: var(--btn); }
.floorpick.on span { color: var(--btn-text); opacity: .85; }
.floorpick.off { opacity: .45; cursor: default; }
.floor-note { margin-top: 8px; }

.plan-card { border: 1px solid rgba(127,127,127,.25); }
.plan-total { font-size: 17px; font-weight: 700; margin: 4px 0 6px; }

.tasks { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.task { background: var(--bg); border-radius: 12px; padding: 10px 12px; }
.task.dim { opacity: .5; }
.task-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.task-name { font-weight: 700; }
.task-rate { font-weight: 800; color: var(--ok); white-space: nowrap; font-variant-numeric: tabular-nums; }
.task-meta { color: var(--hint); font-size: 12px; margin-top: 2px; }
/* Price and reward got longer («4 × 8 ⚡ = 32 ⚡ → 79 золота»), so the row wraps,
   and the button hugs the right and, on wrapping, moves to its own row whole. */
.task-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-top: 8px; }
.task-cost { font-weight: 700; color: var(--warn); font-variant-numeric: tabular-nums; white-space: nowrap; }
.task-gain { font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.task-go { margin-left: auto; }
.task-go {
  border: 0; border-radius: 10px; padding: 8px 14px; background: var(--btn); color: var(--btn-text);
  font: inherit; font-weight: 600; cursor: pointer; white-space: nowrap;
}
.task-go:disabled { opacity: .45; cursor: default; }
.task-warn { color: var(--warn); font-size: 12px; margin-top: 6px; line-height: 1.3; }
/* A task the active quest asks for: gold outline and a soft glow.
   Neither color nor animation carries meaning on its own; there is always a «для квеста» label nearby. */
.task.quest {
  box-shadow: inset 0 0 0 2px #c9a227, 0 0 10px rgba(201, 162, 39, .35);
  animation: quest-glow 2.4s ease-in-out infinite;
}
.task-quest { color: #c9a227; font-size: 12px; font-weight: 700; margin-top: 6px; }
@keyframes quest-glow {
  0%, 100% { box-shadow: inset 0 0 0 2px #c9a227, 0 0 6px rgba(201, 162, 39, .25); }
  50%      { box-shadow: inset 0 0 0 2px #e7c35a, 0 0 16px rgba(231, 195, 90, .55); }
}
/* For those sensitive to animation the outline stays and the flicker is off. */
@media (prefers-reduced-motion: reduce) {
  .task.quest { animation: none; }
}

.task.done { opacity: .5; }
.task.done .task-rate { color: var(--hint); }

/* Grouping by work, as in the game window: the work title, its tasks in the original order. */
.work-group { margin-top: 12px; }
.work-head { font-weight: 700; font-size: 15px; margin-bottom: 6px; }
.work-head .sub { font-weight: 400; }
.work-foot { color: var(--hint); font-size: 12px; margin-top: 6px; line-height: 1.35; }
/* «G1» before the work name: the block players count works by (feedback #640). */
.work-block {
  display: inline-block; font-size: 12px; font-weight: 700; padding: 1px 7px; margin-right: 4px; border-radius: 6px;
  background: var(--btn); color: var(--btn-text); vertical-align: 1px;
}
/* «Фармить работу»: an expander under the work, rows like the stash work block. */
.farm-box { margin-top: 8px; border-radius: 12px; background: var(--bg); padding: 2px 12px; }
.farm-box > summary { cursor: pointer; font-weight: 600; font-size: 14px; padding: 9px 0; list-style: none; }
.farm-box > summary::-webkit-details-marker { display: none; }
.farm-box > summary::after { content: " ▸"; color: var(--hint); }
.farm-box[open] > summary::after { content: " ▾"; }
.farm-box > .btn { margin: 6px 0 10px; }

.seg { display: flex; gap: 4px; background: var(--bg); border-radius: 10px; padding: 3px; margin: 8px 0 4px; }
.seg-btn {
  flex: 1; border: 0; background: transparent; color: var(--hint); font: inherit; font-weight: 600;
  font-size: 13px; padding: 7px 4px; border-radius: 8px; cursor: pointer;
}
.seg-btn.on { background: var(--bg2); color: var(--text); }
/* Without a subscription the button stays live but looks like a lock: it leads to plans, not nowhere. */
.task-go.locked { background: var(--bg2); color: var(--hint); box-shadow: inset 0 0 0 1px rgba(127,127,127,.3); }

/* Businesses: a card ready to collect is highlighted, upgrade items use game icons. */
.task.biz-ready { box-shadow: inset 0 0 0 1px rgba(47, 164, 107, .55); }
.biz-head { align-items: center; gap: 10px; }
.biz-head .task-name { flex: 1 1 auto; min-width: 0; }
.biz-pic {
  flex: 0 0 42px; width: 42px; height: 42px; object-fit: cover; border-radius: 9px;
  background: rgba(127, 127, 127, .12);
}
.biz-up { margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(127, 127, 127, .15); }
.biz-up .task-line { margin-top: 6px; }
.biz-up .task-warn { margin-top: 0; flex: 1 1 auto; }
/* Upgrade items as tiles: a large image, «есть/нужно» and the boss the item
   drops from. The tile is a button into that boss card (2026-09-19). */
.biz-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 8px; margin-top: 8px; }
.biz-item {
  display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0;
  background: var(--bg2); border: 0; border-radius: 12px; padding: 8px 6px;
  font: inherit; color: var(--text); text-align: center;
}
button.biz-item { cursor: pointer; }
button.biz-item:active { opacity: .7; }
.biz-item-ico {
  width: 56px; height: 56px; object-fit: contain; border-radius: 10px;
  background: rgba(127, 127, 127, .12);
}
.biz-item-txt { display: flex; flex-direction: column; align-items: center; gap: 1px; min-width: 0; max-width: 100%; }
.biz-item-n { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
.biz-item.miss .biz-item-n { color: var(--warn); }
.biz-item-miss { font-size: 11px; color: var(--warn); line-height: 1.2; }
.biz-need-hint { margin: 2px 0 6px; }
/* A boss name can be long («Зузанна и Анита Болеславски»): two lines, then an ellipsis. */
.biz-item-src {
  font-size: 12px; line-height: 1.25; color: var(--link); overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* The boss ward is still closed to the player: dim name, below it the reason («с 30 ур.»). */
.biz-item-src.shut { color: var(--hint); }
.biz-item-lock { font-size: 11px; color: var(--warn); line-height: 1.2; }
.biz-src-list { font-size: 13px; color: var(--hint); margin-top: 8px; line-height: 1.5; }
.biz-src-list .link-btn.shut { color: var(--hint); text-decoration: none; }
.biz-hint { font-size: 12px; color: var(--hint); font-style: italic; margin-top: 6px; line-height: 1.3; }
/* A button that asks to confirm before spending currency or pills. */
.task-go.confirm, .btn.confirm { background: var(--warn); color: #fff; }

/* Stats: a row with the point price and a «+1» button. */
.statline { background: var(--bg); border-radius: 12px; padding: 10px 12px; margin-top: 8px; }
.statline-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.statline-name { font-size: 15px; }
.statline-name b { font-size: 17px; }
/* «По боссам» (feedback #447, owner 2026-10-06: «места много — босса слева, инфу справа»): the portrait
   of the boss, as on its card but smaller, on the left; name, count and drop on the right. */
.stat-boss-line { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0 14px; align-items: start; }
.stat-boss-body { min-width: 0; }
.stat-boss-body:first-child { grid-column: 1 / -1; }   /* no art: everything full width */
.stat-boss-line .stat-boss-face { width: 96px; }
.stat-boss-face .boss-face { width: 96px; height: 120px; border-radius: 12px; }
.stat-boss-face .boss-face-num { top: 6px; left: 6px; font-size: 11px; }
.stat-boss-body .statline-name { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.stat-boss-body .statline-head .badge { flex: none; white-space: nowrap; }
@media (max-width: 420px) {
  .stat-boss-line { gap: 0 10px; }
  .stat-boss-line .stat-boss-face, .stat-boss-face .boss-face { width: 72px; }
  .stat-boss-face .boss-face { height: 90px; }
}
.statline-left { color: var(--hint); font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* «+1» and «+MAX»: on a second row, evenly; on a phone they do not fit in the name row. */
.statline-btns { display: flex; gap: 8px; margin-top: 8px; }
.statline-btns .task-go { flex: 1 1 0; margin-left: 0; padding: 7px 10px; min-width: 0;
  white-space: normal; line-height: 1.2; font-variant-numeric: tabular-nums; }
.statline-btns .sg-n, .statline-btns .sg-c { display: block; white-space: nowrap; }
.statline-btns .sg-c { font-size: 12px; font-weight: 500; opacity: .85; margin-top: 2px; }
.statline-btns .sg-sep { display: none; }
.statline-meta { color: var(--hint); font-size: 13px; margin-top: 4px; font-variant-numeric: tabular-nums; }
.statline-note { color: var(--warn); font-size: 12px; margin-top: 6px; line-height: 1.3; }
/* «История» in stats: a fight card with the boss portrait, time, result as an icon and a colored
   stripe on the left, drops as chips with images. */
.hist { background: var(--bg2); border-left: 3px solid rgba(127,127,127,.35); padding: 10px 12px 11px; }
.hist-ok { border-left-color: var(--ok); }
.hist-danger { border-left-color: var(--danger); }
.hist-warn { border-left-color: var(--warn); }
.hist-head { display: flex; align-items: center; gap: 10px; }
.hist-head .boss-ava { flex-basis: 40px; width: 40px; height: 40px; }
.hist-who { flex: 1 1 auto; min-width: 0; }
.hist-name { font-size: 15px; line-height: 1.25; }
.hist-head .badge { flex: none; white-space: nowrap; }
/* time, mode and duration as separate words: if they do not fit they wrap whole, with no dangling dots */
.hist-meta { display: flex; flex-wrap: wrap; gap: 0 12px; margin-top: 2px; }
.hist-meta > * { white-space: nowrap; }
.hist-meta b { color: var(--text); font-weight: 600; }
.hist-line { font-size: 14px; margin-top: 8px; font-variant-numeric: tabular-nums; }
.hist-rewards { margin-top: 4px; }
.hist-drops { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.hist-drop {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 3px 10px 3px 4px; border-radius: 999px; background: var(--bg); font-size: 13px;
}
.hist-drop .ing-ico { flex: 0 0 22px; width: 22px; height: 22px; border-radius: 50%; }
.hist-drop .drop-name { flex: 0 1 auto; }
#histList .group-head { margin-top: 16px; color: var(--hint); }
#histMore .btn { margin-top: 12px; }

.btn {
  display: block; width: 100%; border: 0; border-radius: 12px; padding: 13px 16px; margin-top: 10px;
  background: var(--btn); color: var(--btn-text); font: inherit; font-weight: 600; cursor: pointer;
}
.btn.secondary { background: var(--bg); color: var(--text); border: 1px solid rgba(127,127,127,.25); }
.btn.danger { background: var(--danger); color: #fff; }
.btn:disabled { opacity: .5; cursor: default; }
.btn[hidden] { display: none; }   /* display: block above would otherwise override the hidden attribute */
/* A paid action without a subscription is a lock: a grey button with 🔒, tapping leads to plans,
   not into the game. «Выполнить» on the «Энергия» tab looks the same (.task-go.locked). */
.btn.locked, .fr-btn.locked {
  background: var(--bg); color: var(--hint); border: 1px solid rgba(127,127,127,.25);
}
.btn.locked::before, .fr-btn.locked::before { content: "🔒 "; }
/* Locked toggle: dim, the lock right on the knob. */
.switch.locked { opacity: .55; }
.switch.locked::after { content: "🔒"; font-size: 12px; line-height: 24px; text-align: center; }
.btn-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* A long batch is running (btnBusy in app.js): the pressed button shows a spinner, «Выполняю…» and how long
   it has been going. The button is locked but not dim, so it is clear the bot works rather than hangs. */
.btn.busy { cursor: progress; animation: busy-pulse 1.6s ease-in-out infinite; }
.btn.busy:disabled { opacity: 1; }
.busy-spin {
  display: inline-block; width: 1em; height: 1em; margin-right: 8px; vertical-align: -.16em;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  animation: busy-spin .8s linear infinite;
}
.busy-what, .busy-time { font-weight: 500; opacity: .75; }
.busy-time { font-variant-numeric: tabular-nums; }
@keyframes busy-spin { to { transform: rotate(360deg); } }
@keyframes busy-pulse { 50% { opacity: .72; } }
@media (prefers-reduced-motion: reduce) {
  .btn.busy { animation: none; }
  .busy-spin { animation-duration: 2.4s; }
}

/* friends: a «who, what, button» row */
.fr-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; }
.fr-row + .fr-row { border-top: 1px solid rgba(127,127,127,.15); }
.fr-who { flex: 1 1 auto; min-width: 0; }
.fr-who b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fr-meta { color: var(--hint); font-size: 12px; margin-top: 2px; }
.fr-btn {
  flex: 0 0 auto; border: 0; border-radius: 10px; padding: 9px 12px;
  background: var(--btn); color: var(--btn-text); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.fr-btn.done, .fr-btn:disabled { background: var(--bg); color: var(--hint); border: 1px solid rgba(127,127,127,.25); cursor: default; }
/* «Получено» is a label, not a button: a credited gift has nothing to press. */
.fr-got { flex: 0 0 auto; color: var(--hint); font-size: 12px; }
details.fr-log > summary { cursor: pointer; padding: 6px 0; color: var(--hint); font-size: 13px; }
/* Stashes: a «просит …» row under a friend and expanders of stash sets. */
.fr-stash-list { margin: 2px 0 8px; padding-left: 10px; border-left: 2px solid rgba(127,127,127,.25); }
.ing-ico {
  flex: 0 0 32px; width: 32px; height: 32px; object-fit: contain; border-radius: 8px;
  background: rgba(127, 127, 127, .12);
}
/* «Что нового»: update log rows. */
.cl-item { padding: 7px 0; line-height: 1.4; font-size: 15px; }
.cl-item + .cl-item { border-top: 1px solid rgba(127, 127, 127, .12); }
.cl-item.cl-warn { color: var(--danger); font-weight: 600; }
.fr-stash-note { margin: 2px 0 8px; }
details.stash-pick > summary, details.stash-set > summary,
details.fr-add > summary { cursor: pointer; padding: 6px 0; font-weight: 600; }
/* Invite by list is an expander under the search; friend stats are a second row under the level. */
details.fr-bulk { margin-top: 10px; padding-top: 4px; border-top: 1px solid rgba(127,127,127,.15); }
details.fr-bulk > summary { cursor: pointer; padding: 6px 0; font-weight: 600; }
details.fr-bulk textarea { margin-top: 6px; }
.fr-stats { font-variant-numeric: tabular-nums; white-space: nowrap; }
.chips-label { flex: 0 0 auto; align-self: center; color: var(--hint); font-size: 13px; }
/* Order and «Чистка» in several rows: in sideways scroll the selected button would be lost beyond the edge. */
.fr-sortbar { flex-wrap: wrap; overflow-x: visible; }
/* Player search: a wide input and a compact magnifier button, all in one row. */
.fr-search { display: flex; gap: 8px; margin-top: 8px; align-items: stretch; }
.fr-search input[type="search"] { flex: 1 1 auto; min-width: 0; width: auto; }
.fr-search .btn {
  flex: 0 0 auto; width: auto; margin-top: 0; padding: 0 16px;
  display: flex; align-items: center; justify-content: center;
}
.fr-search .btn svg { width: 18px; height: 18px; display: block; }
details.stash-set { margin-left: 8px; }
.btn-row .btn { margin-top: 10px; }
/* «Боссы»: fight buttons («Смотреть бой», «Стоп») do not stick to the queue card under them. */
#bossLiveHead .btn-row { margin-bottom: 12px; }

.hud { padding-bottom: 12px; }
.hud-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.hud-title { font-weight: 700; font-size: 16px; }
.switch-row, .count-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 0;
}
.count-row, .switch-row + .switch-row { border-top: 1px solid rgba(127,127,127,.15); }
.count-row.dim { opacity: .42; pointer-events: none; }
.switch {
  flex: 0 0 auto; width: 50px; height: 30px; border: 0; border-radius: 999px; padding: 0;
  background: rgba(127,127,127,.32); position: relative; cursor: pointer;
}
.switch::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 24px; height: 24px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.25);
  transition: transform .18s ease;
}
.switch.on { background: var(--ok); }
.switch.on::after { transform: translateX(20px); }
.stepper {
  display: flex; align-items: center; gap: 6px;
  background: var(--bg); border-radius: 999px; padding: 3px 4px 3px 3px;
  box-shadow: inset 0 0 0 1px rgba(127,127,127,.18);
}
.stepper .count {
  min-width: 2.4em; text-align: center; font-weight: 800; font-size: 22px;
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
}
/* The number is an input: a tap opens the numeric keyboard, and it looks the same as a label. */
input.count {
  width: 3.2em; border: 0; padding: 0; background: transparent; color: var(--text);
  outline: none; border-radius: 8px; -moz-appearance: textfield; appearance: textfield;
}
input.count:focus { background: var(--bg2); box-shadow: 0 0 0 2px var(--btn); }
.step {
  width: 34px; height: 34px; border: 0; border-radius: 50%;
  background: var(--bg2); color: var(--text); font-size: 20px; font-weight: 700; cursor: pointer;
  line-height: 1;
  /* Hold for auto-repeat: no text selection, zoom or long-press menu. */
  touch-action: manipulation; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.step:active { transform: scale(.96); }
/* Auto-heal steppers: a two-digit number, with a «%» sign next to the threshold; a long label on the left
   wraps while the stepper does not shrink; locked without a subscription */
.stepper[data-heal-key] { flex: 0 0 auto; }
input.count.n2 { width: 2.2em; min-width: 0; }
/* The number and «%» sit tight and together are as wide as a number without a sign, so «−» and «+» of both steppers line up */
input.count.n2.with-unit { width: 1.55em; text-align: right; }
.count-unit { width: 0.85em; margin-left: -6px; font-weight: 800; font-size: 17px; }
.stepper.locked { opacity: .55; }
.stepper.locked::before { content: "🔒"; font-size: 12px; margin-left: 6px; }
/* «Пополнить запас» is buying medkits and howl by hand (feedback #402): in the boss card and on «Рейд» */
.shop-toggle .pause-btn { width: auto; flex: 0 0 auto; margin-top: 0; padding: 8px 14px; font-size: 14px; }
.shop-panel { padding: 2px 0 6px; }
.shop-item { border-top: 1px solid rgba(127,127,127,.15); padding: 12px 0 4px; }
.shop-head { display: flex; align-items: center; gap: 10px; }
.shop-ico { width: 44px; height: 44px; object-fit: contain; flex: 0 0 auto; }
.shop-name { flex: 1 1 auto; min-width: 0; }
.shop-item .seg.shop-cur { margin: 0; flex: 0 0 auto; }
.shop-cur .seg-btn { padding: 6px 9px; font-size: 13px; white-space: nowrap; }
.shop-one { flex: 0 0 auto; white-space: nowrap; }
.shop-buy-row { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.shop-buy-row .stepper { flex: 0 0 auto; }
.shop-buy-row input.count { width: 2.6em; min-width: 0; }
.shop-buy-row .chip { margin: 0; }
.shop-buy { margin-top: 10px; }
.shop-refresh { margin-top: 12px; }
/* «Обойти все этажи»: the button, sweep progress and the previous result */
.biz-sweep { margin-top: 10px; }
.biz-sweep .bar { margin: 8px 0; }
.sweep-last { margin-top: 8px; }
.hud-progress { margin-top: 8px; }
.hud-frac {
  display: flex; justify-content: space-between; margin-top: 6px;
  font-weight: 800; font-variant-numeric: tabular-nums; font-size: 15px;
}
.hud-frac .muted { color: var(--hint); font-weight: 600; }

textarea, input[type="search"] {
  width: 100%; border: 1px solid rgba(127,127,127,.3); border-radius: 12px; padding: 12px;
  font: inherit; background: var(--bg); color: var(--text); outline: none;
}
textarea { min-height: 96px; resize: vertical; }
textarea:focus, input:focus { border-color: var(--btn); }

.guide { font-size: 14px; color: var(--hint); margin: 8px 0 0; }
.guide.warn-text { color: var(--warn); }
/* Dice for bills: a red notice about understated odds, more visible than a normal hint. */
.dice-odds-warn { margin: 8px 0; padding: 8px 10px; border-radius: 10px; font-weight: 700; font-size: 14px;
  color: var(--danger); background: rgba(214, 69, 69, .12); border: 1px solid rgba(214, 69, 69, .45);
  background: color-mix(in srgb, var(--danger) 12%, transparent);           /* above is a fallback without color-mix */
  border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
/* Dice: «Как часто выпадало», a combination and three share columns (immediate, fair dice, result). */
.df-table { margin-top: 10px; font-variant-numeric: tabular-nums; }
.df-row { display: grid; grid-template-columns: minmax(64px, 1.1fr) repeat(3, minmax(0, 1fr)); gap: 6px;
  align-items: center; padding: 5px 0; border-top: 1px solid rgba(127,127,127,.18); }
.df-head { border-top: 0; padding-top: 0; font-size: 12px; color: var(--hint); line-height: 1.2; }
.df-combo { font-weight: 700; font-size: 15px; letter-spacing: .06em; }
.df-triple .df-combo { color: var(--btn); }
.df-num { text-align: right; min-width: 0; }
.df-num b { display: block; font-weight: 600; font-size: 14px; }
.df-num small { display: block; font-size: 11px; color: var(--hint); }
.df-fair { color: var(--hint); font-size: 13px; }
.guide ol, .guide ul { padding-left: 20px; margin: 6px 0; }
.guide code { background: rgba(127,127,127,.15); padding: 1px 5px; border-radius: 5px; font-size: 13px; }
.guide .btn { margin: 6px 0; }
/* Bookmarklet code: one very long line; without wrapping it would stretch the whole screen. */
.bm-code, .ld-code { margin: 4px 0 8px; }
.bm-code > summary, .ld-code > summary { cursor: pointer; padding: 4px 0; }
.guide-other { margin: 8px 0; }
.guide-other > summary { cursor: pointer; padding: 6px 0; font-weight: 600; }
.bm-code code, .ld-code code { display: block; word-break: break-all; user-select: all; padding: 6px 8px; }

.list { display: flex; flex-direction: column; gap: 8px; }
.item {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  background: var(--bg2); border-radius: 12px; padding: 12px 14px; border: 0; width: 100%;
  font: inherit; color: var(--text); text-align: left; cursor: pointer;
}
.item .id { color: var(--hint); font-size: 13px; min-width: 40px; }
.item .name { flex: 1; font-weight: 600; }
.item .mark { font-size: 13px; color: var(--hint); }
.item.dim { opacity: .55; }

/* Bosses: round portraits in the list (like Telegram chats) and a card header with art */
.boss-item { padding: 8px 14px 8px 8px; }
.boss-main { flex: 1; min-width: 0; }
.boss-ava {
  flex: 0 0 44px; width: 44px; height: 44px; border-radius: 50%;
  object-fit: cover; object-position: top; background: rgba(127,127,127,.15);
}
.boss-num { color: var(--hint); font-weight: 400; font-size: 13px; }
.boss-place { display: block; color: var(--hint); font-weight: 400; font-size: 12px; margin-top: 1px; }
/* What dropped from the boss: «✓ всё собрано» as a green chip, «🎁 3/7» as a grey one (feedback 2026-09-29). */
.boss-got {
  display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 8px;
  font-size: 11px; line-height: 17px; font-weight: 600; white-space: nowrap; vertical-align: 1px;
  background: rgba(127, 127, 127, .15); color: var(--hint);
}
.boss-place > .boss-got:first-child { margin-left: 0; }
.boss-got.done { background: rgba(47, 164, 107, .16); background: color-mix(in srgb, var(--ok) 18%, transparent); color: var(--ok); }
/* Boss reward in the list row (feedback #425): «💰 235 💊 60»; exp is not shown, everyone gets it. */
.boss-rw {
  display: inline-block; margin-left: 6px; font-size: 12px; line-height: 17px; font-weight: 600;
  color: var(--hint); white-space: nowrap; vertical-align: 1px;
}
.boss-place > .boss-rw:first-child { margin-left: 0; }
/* Boss list by floors: a floor is an expander with an arrow on the right, like drop categories. */
.boss-seg { margin: 10px 0 0; }
/* «Старые боссы» switch (feedback 2026-10-08). */
.boss-old-row { padding: 8px 0 0; }
.boss-floor > summary { display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none; }
.boss-floor > summary::-webkit-details-marker { display: none; }
.boss-floor > summary .sub { font-weight: 400; font-size: 13px; }
.boss-floor > summary::after {
  content: "▸"; color: var(--hint); font-size: 14px; line-height: 1;
  margin-left: auto; transition: transform .15s ease;
}
.boss-floor[open] > summary::after { transform: rotate(90deg); }
.boss-floor:not([open]) > summary { margin-bottom: 10px; }
/* The raid skips a fully collected boss (toggle on the «Рейд → Боссы» tab). */
.boss-got.skip { background: transparent; box-shadow: inset 0 0 0 1px rgba(127, 127, 127, .45); }
.boss-collect { margin: 0 0 12px; }
.btn.compact { padding: 9px 14px; font-size: 14px; margin-top: 6px; }
/* Raid, «Боссы» tab: a smaller portrait than in the boss list; with selection off the rows
   are paler but the checkboxes are live (they can be set in advance). */
.raid-boss { display: flex; align-items: center; gap: 10px; min-width: 0; }
.raid-boss .boss-ava { flex-basis: 36px; width: 36px; height: 36px; }
/* «Зовут в бой»: boss portrait; the boss name wraps rather than being cut by an ellipsis; the row is */
.fr-row .boss-ava { flex-basis: 40px; width: 40px; height: 40px; }
.inv-row .fr-who b { white-space: normal; }
#raidBossList.raid-off .raid-boss { opacity: .55; }
/* Auto-fight queue («Боссы» section): number, portrait with name and floor, «↑» and «✕» */
.q-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-top: 1px solid rgba(127,127,127,.15); }
.q-row:first-child { border-top: 0; }
.q-num { flex: 0 0 1.4em; text-align: center; color: var(--hint); font-weight: 600; }
.q-open {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; padding: 0;
  background: none; border: 0; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.q-open .boss-ava { flex-basis: 36px; width: 36px; height: 36px; }
.q-open .name { min-width: 0; overflow-wrap: anywhere; }
.q-row .fr-btn { flex: none; min-width: 36px; }
/* Current boss is the first queue row: «▶» instead of a number and a mark on the right (feedback #430) */
/* «Как открыть босса» in a locked boss card (feedback #446, #450): floor and department conditions */
.unlock-how { margin-top: 10px; display: grid; gap: 10px; }
.unlock-group { padding: 10px 12px; border-radius: 12px; background: rgba(127,127,127,.08); }
.unlock-head { font-weight: 600; margin-bottom: 4px; }
.unlock-req { padding: 6px 0; border-top: 1px solid rgba(127,127,127,.15); }
.unlock-head + .unlock-req { border-top: 0; }
.unlock-req.met { opacity: .65; }
.unlock-line { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.unlock-line > span:first-child { min-width: 0; overflow-wrap: anywhere; }
.unlock-prog { flex: none; color: var(--hint); font-variant-numeric: tabular-nums; }
.unlock-go { margin-top: 8px; }
.unlock-biz { display: block; margin-top: 6px; font-size: 14px; }
.pace-note { margin: 10px 0 4px; }   /* «Темп удара»: the raid has its own (feedback #439) */
.q-now .q-num { color: var(--ok); }
.q-now-tag {
  flex: none; padding: 3px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; white-space: nowrap;
  color: var(--ok); background: rgba(127,127,127,.12);
}
/* Raid, «Боссы»: an order star next to the checkbox and a «Порядок боссов» block (feedback 2026-09-29) */
.raid-ctl { flex: none; display: flex; align-items: center; gap: 8px; }
.star-btn { min-width: 44px; padding: 6px 8px; font-size: 15px; line-height: 1.2; white-space: nowrap; }
.star-btn.locked::before { content: "🔒"; }
.raid-prio { margin-top: 10px; padding: 8px 12px 10px; border-radius: 12px; background: rgba(127,127,127,.08); }
.raid-prio-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 2px; }
.raid-prio .q-row .raid-boss { flex: 1; }
.raid-prio .name { min-width: 0; overflow-wrap: anywhere; }
.raid-prio > .sub { margin-top: 6px; font-size: 13px; }
/* Raid checklist and ★ order: portrait and name open the boss card (owner 2026-10-10), the row looks the same. */
.raid-open {
  flex: 1; padding: 0; background: none; border: 0; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.raid-open:active { opacity: .7; }
.raid-boss-txt { min-width: 0; }
.raid-boss-name, .raid-open .sub { display: block; }
/* Boss card header: portrait and name with medals in a row, diagnosis full width under
   them. Before, the diagnosis lived in the right column and on a phone it read as a narrow strip, while
   half of the card under the portrait stayed empty (owner complaint 2026-09-20). */
.boss-head { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0 14px; align-items: start; }
.boss-face-wrap { position: relative; width: 124px; }
.boss-face {
  display: block; width: 124px; height: 156px; border-radius: 16px;
  object-fit: cover; object-position: top; background: rgba(127,127,127,.15);
  box-shadow: 0 4px 14px rgba(0,0,0,.25);
}
.boss-face-num {
  position: absolute; top: 8px; left: 8px; padding: 2px 8px; border-radius: 8px;
  background: rgba(15,15,15,.72); color: #fff; font-size: 12px; font-weight: 700;
}
.boss-head-text { min-width: 0; }
/* The boss has no art (a new boss from the update): the name with medals takes the full width. */
.boss-head-text:first-child { grid-column: 1 / -1; }
.boss-diag { grid-column: 1 / -1; margin-top: 10px; font-size: 13px; color: var(--hint); line-height: 1.35; }
.boss-diag-label { display: block; font-weight: 700; color: var(--text); font-size: 12px; letter-spacing: .04em; }
.boss-killer {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 10px 0 4px; padding: 8px 10px; border-radius: 10px; background: rgba(127,127,127,.10);
  font-size: 13px;
}
.killer-ava { flex: 0 0 26px; width: 26px; height: 26px; border-radius: 50%; object-fit: cover; }
.killer-label { color: var(--hint); }
.killer-name { font-weight: 700; }
.killer-kills { color: var(--hint); margin-left: auto; }

/* SVG icons of boss card rows: drawn with currentColor, the class sets the color */
.ico { width: 16px; height: 16px; vertical-align: -3px; color: var(--hint); flex: 0 0 auto; }
.ico.red { color: var(--danger); }
.ico.warn { color: var(--warn); }
.ico.ok { color: var(--ok); }
.ico.fill { fill: currentColor; }

/* Generic drop breakdown by boss in stats: the row expands into a list */
.drop-sub summary { list-style: none; cursor: pointer; }
.drop-sub summary::-webkit-details-marker { display: none; }
.drop-sub .drop-parts { margin: 2px 0 6px 26px; display: flex; flex-direction: column; gap: 2px; }
.drop-sub[open] > summary .drop-tag { opacity: .7; }

/* Custom pause between auto-fight fights: two numbers and a button in the settings card */
.pause-block { padding: 10px 0 2px; border-top: 1px solid rgba(127,127,127,.15); margin-top: 4px; }
.pause-inputs { display: flex; align-items: center; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.pause-inputs input[type="number"] {
  width: 72px; padding: 8px 10px; border-radius: 10px; border: 1px solid rgba(127,127,127,.25);
  background: var(--bg); color: var(--text); font: inherit; text-align: center;
}
.pause-inputs .pause-btn { width: auto; padding: 8px 12px; margin-top: 0; font-size: 14px; }
/* Untouchable reserve (sharpening, dice for bills): the number can be long, so the field is full width */
.pause-inputs.wide { flex-wrap: nowrap; }
.pause-inputs.wide input[type="number"] { flex: 1 1 auto; width: auto; min-width: 0; text-align: left; }
/* Reroll goal for bill rolls: a normal phone list */
.goal-select {
  width: 100%; margin-top: 8px; padding: 10px 12px; border-radius: 10px; border: 1px solid rgba(127,127,127,.25);
  background: var(--bg); color: var(--text); font: inherit;
}
/* Dice goals checklist: checked ones on top with a number and a ⠿ handle to drag by (higher is more important). */
.goal-list { position: relative; margin-top: 8px; }
.goal-row {
  display: flex; align-items: center; gap: 8px; padding: 7px 0;
  border-top: 1px solid rgba(127,127,127,.15); background: var(--bg2);
}
.goal-row:first-child { border-top: 0; }
.goal-text { flex: 1 1 auto; min-width: 0; font-size: 14px; }
.goal-text .sub { font-size: 12px; }
.goal-row:not(.on) .goal-text { color: var(--hint); }
.goal-grip {
  flex: 0 0 30px; height: 36px; display: flex; align-items: center; justify-content: center;
  font-size: 20px; color: var(--hint); cursor: grab; touch-action: none; user-select: none;
  border-radius: 8px; background: rgba(127,127,127,.12);
}
.goal-grip.off { background: none; cursor: default; }
.goal-num { flex: 0 0 auto; min-width: 1.2em; font-weight: 700; color: var(--link); text-align: center; }
.goal-row .switch { flex: 0 0 auto; }
.goal-list.sorting { user-select: none; }
.goal-row.dragging {
  position: relative; z-index: 2; border-radius: 10px; border-top-color: transparent;
  box-shadow: 0 6px 18px rgba(0,0,0,.35); scale: 1.02;
}
.goal-row.dragging .goal-grip { cursor: grabbing; color: var(--text); }
.goal-slot {
  position: absolute; left: 0; right: 0; z-index: 1; pointer-events: none;
  border: 2px dashed var(--link); border-radius: 10px; background: rgba(127,127,127,.08);
}
.group-head { font-weight: 700; font-size: 14px; margin: 10px 2px 6px; }
.badge { display: inline-block; font-size: 12px; font-weight: 600; padding: 3px 8px; border-radius: 999px; background: rgba(127,127,127,.18); }
.badge.ok { background: rgba(47,164,107,.18); color: var(--ok); }
.badge.warn { background: rgba(217,150,43,.18); color: var(--warn); }
.badge.danger { background: rgba(214,69,69,.18); color: var(--danger); }

.loading, .empty { color: var(--hint); text-align: center; padding: 24px 0; }
.status-line { margin: 4px 0 10px; color: var(--hint); font-size: 14px; }

.toast {
  /* Above the menu (25/30): the «Безумие» switch phrase is visible in the menu while it is open. Below «Бот обновляется» (100). */
  position: fixed; left: 16px; right: 16px; bottom: 18px; z-index: 40;
  background: rgba(20,20,20,.92); color: #fff; padding: 12px 14px; border-radius: 12px; font-size: 14px;
  box-shadow: 0 6px 18px rgba(0,0,0,.25);
}
.toast.err { background: rgba(190,40,40,.95); }

/* Action queue: «бот занят» is a bar above the toast with a «Отменить» button.
   It lives over the tabs like a toast: screen redraws do not touch it. */
.queue-note {
  position: fixed; left: 16px; right: 16px; bottom: 72px; z-index: 40;
  display: flex; align-items: center; gap: 12px;
  background: rgba(20,20,20,.94); color: #fff; padding: 12px 14px; border-radius: 12px;
  font-size: 14px; box-shadow: 0 6px 18px rgba(0,0,0,.25);
}
/* display: flex beats the browser [hidden] rule, so we hide explicitly. */
.queue-note[hidden] { display: none; }
.queue-note .qn-text { flex: 1; min-width: 0; }
.queue-note .qn-text b { display: block; }
.queue-note .qn-sub { display: block; margin-top: 2px; font-size: 13px; opacity: .8; }
.queue-note .qn-cancel {
  flex: none; font: inherit; font-size: 14px; color: #fff; cursor: pointer;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.3);
  border-radius: 10px; padding: 8px 12px;
}
.queue-note .qn-cancel:disabled { opacity: .55; cursor: default; }

.plan {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
  background: var(--bg); border: 1px solid rgba(127,127,127,.25); border-radius: 12px;
  padding: 12px 14px; margin-top: 8px; font: inherit; color: var(--text); text-align: left; cursor: pointer;
}
.plan .price { font-weight: 800; font-size: 17px; white-space: nowrap; }
.plan .title { font-weight: 600; }
.plan .note { color: var(--hint); font-size: 13px; }
.plan.best { border-color: var(--btn); box-shadow: inset 0 0 0 1px var(--btn); }
.plan.locked { opacity: .55; cursor: default; }
.paywall { text-align: center; padding: 10px 0 4px; }
.paywall .lock { font-size: 34px; }

.admin-user {
  display: flex; justify-content: space-between; align-items: center; gap: 10px; width: 100%;
  background: var(--bg); border: 1px solid rgba(127,127,127,.22); border-radius: 12px;
  padding: 10px 12px; margin-top: 8px; font: inherit; color: var(--text); text-align: left; cursor: pointer;
}
.admin-user .who { font-weight: 600; }
.admin-user .meta { color: var(--hint); font-size: 12px; }
.admin-user .tag { font-size: 12px; white-space: nowrap; }
.mono { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 13px; }
.err-line { font-size: 12px; color: var(--hint); border-top: 1px solid rgba(127,127,127,.15); padding: 6px 0; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 8px; }
.field-row input { width: 100%; }

/* --- admin: feedback chats. Two panels on PC, like Telegram; on a phone, a list OR a chat. --- */
.fb-wrap { display: flex; gap: 10px; align-items: stretch; height: calc(100vh - 150px); min-height: 340px; }
.fb-side { flex: 0 0 260px; min-width: 0; display: flex; flex-direction: column; gap: 8px; overflow-y: auto; }
.fb-side .btn { margin-top: 0; }
.fb-main {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  background: var(--bg2); border-radius: var(--radius); overflow: hidden;
}
.fb-dialog {
  display: flex; justify-content: space-between; align-items: center; gap: 10px; width: 100%;
  background: var(--bg2); border: 1px solid rgba(127,127,127,.22); border-radius: 12px;
  padding: 10px 12px; font: inherit; color: var(--text); text-align: left; cursor: pointer; flex: 0 0 auto;
}
.fb-dialog.on { border-color: var(--btn); box-shadow: inset 0 0 0 1px var(--btn); }
.fb-dialog .who { font-weight: 600; }
.fb-d-body { flex: 1; min-width: 0; }
.fb-d-last { display: block; color: var(--hint); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fb-d-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.fb-d-meta .meta { color: var(--hint); font-size: 11px; white-space: nowrap; }
.fb-unread {
  display: inline-block; min-width: 20px; text-align: center; background: var(--btn); color: var(--btn-text);
  border-radius: 999px; font-size: 12px; font-weight: 700; padding: 2px 6px;
}
.fb-chat-head { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid rgba(127,127,127,.15); }
.fb-chat-head .who { font-weight: 700; }
.fb-chat-head .fb-close { margin-left: auto; flex: none; padding: 6px 12px; border-radius: 10px; border: 0;
  background: var(--bg); color: var(--link); font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; }
.fb-chat-head .fb-close:disabled { opacity: .5; }
.fb-msgs { flex: 1; overflow-y: auto; padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
/* When there are few messages we pin them to the bottom like Telegram; on overflow scrolling is normal. */
.fb-msgs > :first-child { margin-top: auto; }
.msg {
  /* On a wide PC window the bubble does not stretch full width: as in Telegram, there is a cap. */
  max-width: min(78%, 440px); padding: 8px 12px; border-radius: 14px; background: var(--bg);
  align-self: flex-start; word-break: break-word; font-size: 15px;
}
.msg.out { align-self: flex-end; background: var(--btn); color: var(--btn-text); }
/* We keep line breaks only for the player text; pre-wrap on the bubble itself would draw
   whitespace gaps from the HTML template between photo, text and time. */
.msg-text { white-space: pre-wrap; }
.msg-time { font-size: 11px; opacity: .65; margin-top: 3px; text-align: right; }
.msg-photo {
  display: block; max-width: 100%; max-height: 320px; border-radius: 10px; margin-bottom: 4px;
  min-width: 120px; min-height: 60px; background: rgba(127,127,127,.2); cursor: zoom-in;
}
.fb-compose { display: flex; gap: 8px; align-items: flex-end; padding: 10px; border-top: 1px solid rgba(127,127,127,.15); }
.fb-compose textarea { flex: 1; min-height: 44px; max-height: 140px; }
.fb-attach, .fb-send { flex: 0 0 44px; height: 44px; border: 0; border-radius: 12px; font-size: 18px; cursor: pointer; }
.fb-attach { background: var(--bg); color: var(--text); border: 1px solid rgba(127,127,127,.25); }
.fb-send { background: var(--btn); color: var(--btn-text); }
.fb-attach:disabled, .fb-send:disabled { opacity: .5; cursor: default; }
.fb-send.waiting { font-size: 13px; font-variant-numeric: tabular-nums; padding: 0; }   /* countdown to the next message */
/* «Обращения»: the same feed as in admin chats but from the player side, in a single card. */
/* The card fills the visible screen (height set by fitThreadCard), the feed takes what is left and scrolls.
   `.tab.active > .card` makes cards inline-block in the wide layout — this card must stay a flex column. */
.th-card, .tab.active > .card.th-card { display: flex; flex-direction: column; box-sizing: border-box; }
.th-card > * { flex: none; }
.th-msgs {
  flex: 1 1 auto; min-height: 140px; margin-top: 10px;
  background: var(--bg); border-radius: 12px;
}
.th-msgs .msg { background: var(--bg2); }
.th-msgs .msg.out { background: var(--btn); }
.th-msgs .msg.waits { opacity: .88; }
.th-msgs .msg.fresh { box-shadow: 0 0 0 2px var(--warn); }
.th-card .fb-compose { padding: 10px 0 0; border-top: 0; }
/* «Тебе ответили» in the profile feedback card: a developer reply came and the player has not opened it. */
.fb-answered {
  margin-top: 10px; padding: 9px 12px; border-radius: 10px; font-weight: 700;
  background: rgba(47, 164, 107, .16); color: var(--ok);
}
/* «Удалить мои данные» at the bottom of the profile: the entry is an unobtrusive link, the expanded card has a red
   edge. While a subscription is active, a notice replaces the red button, explaining why not yet. */
.wipe-entry { text-align: center; margin: 2px 0 14px; }
.wipe-link.link-btn { padding: 8px 12px; font-size: 13px; color: var(--hint); text-align: center; }
.wipe-card { border: 1px solid rgba(214, 69, 69, .45); border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
.wipe-card > h2 { color: var(--danger); }
.btn.danger.confirm { background: var(--warn); }   /* «Точно?» as with other confirm buttons */
.wipe-warn { margin-top: 10px; font-size: 14px; font-weight: 700; color: var(--danger); }
.wipe-lock {
  margin-top: 10px; padding: 9px 12px; border-radius: 10px; font-size: 14px; font-weight: 600;
  background: rgba(217, 150, 43, .16); color: var(--warn);
}
/* The attached screenshot is a chip above the field until the message is sent. */
.th-attached {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 10px; padding: 8px 10px 8px 12px; border-radius: 12px; font-size: 14px;
  background: var(--bg); border: 1px dashed rgba(127,127,127,.4);
}
.th-attached[hidden] { display: none; }
.th-attached > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The attached screenshot as a picture: small, so the feed keeps its room. */
.th-attached { justify-content: flex-start; }
.th-thumb { flex: none; max-height: 96px; max-width: 160px; border-radius: 8px; object-fit: contain; background: rgba(127,127,127,.15); }
.th-attached .th-thumb-cap { flex: 1; color: var(--hint); font-size: 13px; }
.th-attached #thDetach { flex: none; }
@media (max-width: 640px) {
  .fb-side { flex: 1; }
  .fb-wrap.has-chat .fb-side { display: none; }
  .fb-wrap:not(.has-chat) .fb-main { display: none; }
}

/* Round dice: three «faces» in a row, a reroll button under each. The one marked «лишняя» is
   the only one that can be rerolled without breaking an already collected pair. */
.dice-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 10px 0 4px; }
.die { background: var(--bg); border-radius: 12px; padding: 10px 6px; text-align: center; }
.die-face { font-size: 30px; font-weight: 800; line-height: 1.1; font-variant-numeric: tabular-nums; }
.die-best { outline: 2px solid var(--warn); }
.die-hint { color: var(--warn); font-size: 11px; margin-top: 4px; }
.die-btn { margin-top: 8px; padding: 8px 6px; font-size: 13px; }

/* Poker hand: five cards of the game's own art in a row; a tap picks the card to replace (outlined), the bot's
   advice wears a «совет» tag. Without the picture the card falls back to its rank and suit in text. */
.pk-hand { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; margin: 10px 0 6px; }
.pk-slot { position: relative; padding: 0; border: 0; background: none; color: inherit; font: inherit; cursor: default; }
.pk-slot:not(:disabled) { cursor: pointer; }
.pk-card { position: relative; display: block; aspect-ratio: 200 / 278; border-radius: 8px; overflow: hidden;
  background: #f1e9d8; box-shadow: 0 1px 3px rgba(0,0,0,.35); transition: transform .12s ease; }
.pk-card img { display: block; width: 100%; height: 100%; object-fit: cover; }
.pk-card img.off { display: none; }
.pk-face { display: none; position: absolute; inset: 0; align-items: center; justify-content: center;
  font-weight: 800; font-size: 18px; color: #1d1d1d; }
.pk-card.red .pk-face { color: #b3261e; }
.pk-card img.off + .pk-face { display: flex; }
.pk-card.pick { outline: 3px solid var(--warn); outline-offset: 1px; transform: translateY(-4px); }
.pk-tag { position: absolute; left: 50%; bottom: -8px; transform: translateX(-50%); padding: 1px 6px;
  border-radius: 8px; background: var(--warn); color: #1b1b1b; font-size: 10px; font-weight: 700; white-space: nowrap; }
.pk-combo { font-weight: 700; }
.df-combo.pk-df { font-size: 13px; letter-spacing: 0; }
#pkSwap { margin-top: 10px; }

/* Boss drop: expander categories, inside a list of items marked «уже открыта». */
.drop-cat { border-top: 1px solid rgba(127,127,127,.15); }
.drop-cat > summary {
  display: flex; gap: 10px; align-items: center;
  padding: 9px 0; cursor: pointer; list-style: none;
}
.drop-cat > summary::-webkit-details-marker { display: none; }
.drop-cat > summary .v {
  color: var(--hint); font-size: 13px; font-weight: 600; text-align: right; margin-left: auto;
}
/* Our own marker instead of the hidden system one: without an arrow the row does not look like a button. */
.drop-cat > summary::after {
  content: "▸"; color: var(--hint); font-size: 14px; line-height: 1;
  flex: 0 0 auto; transition: transform .15s ease;
}
.drop-cat[open] > summary::after { transform: rotate(90deg); }
.drop-list { padding: 2px 0 8px 6px; border-left: 2px solid rgba(127,127,127,.25); }
.drop-item { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 14px; }
.drop-item .ing-ico { flex: 0 0 22px; width: 22px; height: 22px; border-radius: 6px; }
.drop-item.have .drop-name { color: var(--ok); }
.drop-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drop-tag { color: var(--hint); font-size: 11px; white-space: nowrap; }

/* Mutagens: a grid of stock, like the ingredient window in the game: image, count, label.
   Dim tiles are empty; the selected craft target is outlined in the button color. */
.mut-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
  gap: 8px; margin: 10px 0 12px;
}
.mut-tile {
  position: relative; border: 0; border-radius: 12px; padding: 8px 4px 6px;
  background: var(--bg); color: var(--text); font: inherit; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
.mut-tile:active { transform: scale(.95); }
.mut-tile img { width: 46px; height: 46px; object-fit: contain; }
.mut-tile.dim img { opacity: .35; filter: grayscale(.6); }
.mut-tile.sel { outline: 2px solid var(--btn); }
.mut-count {
  position: absolute; top: 4px; right: 4px; min-width: 20px; text-align: center;
  background: var(--btn); color: var(--btn-text); border-radius: 999px;
  font-size: 11px; font-weight: 700; padding: 1px 5px;
}
.mut-lvl { color: var(--hint); font-size: 11px; font-weight: 600; }

/* Plan card: a target with a quantity stepper and a ladder of crafts from bottom to top. */
.mut-target { display: flex; align-items: center; gap: 10px; margin: 6px 0 4px; }
.mut-target img { flex: 0 0 52px; width: 52px; height: 52px; object-fit: contain; }
.mut-target-name { flex: 1 1 auto; min-width: 0; }
.mut-stepper { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.mut-step-btn {
  width: 34px; height: 34px; border: 0; border-radius: 10px;
  background: var(--bg); color: var(--text); font-size: 20px; font-weight: 700; cursor: pointer;
}
.mut-step-btn:disabled { opacity: .4; cursor: default; }
.mut-step-val { min-width: 40px; text-align: center; font-weight: 800; font-variant-numeric: tabular-nums; }
.mut-steps { margin: 8px 0 4px; }
.mut-step {
  display: flex; align-items: center; gap: 10px; padding: 7px 0;
  border-top: 1px solid rgba(127, 127, 127, .15);
}
.mut-step img { flex: 0 0 30px; width: 30px; height: 30px; object-fit: contain; }
.mut-step-txt { flex: 1 1 auto; min-width: 0; }
.mut-shortage { color: var(--danger); font-size: 14px; margin: 8px 0 2px; }
.mut-bonus { display: inline-flex; align-items: center; gap: 4px; margin-right: 10px; }
/* «Собрать сколько выйдет»: the quantity follows stock across the whole chain, no need to mash «+». */
.mut-max-btn {
  display: block; margin: 2px 0 0 auto; padding: 5px 12px; border: 1px solid rgba(127, 127, 127, .3);
  border-radius: 10px; background: transparent; color: var(--link); font: inherit; font-size: 13px;
  font-weight: 600; cursor: pointer;
}
.mut-max-btn:disabled { opacity: .5; cursor: default; }
/* While a new plan is computed, the old one stays in place dimmed: the page does not jump. */
.mut-plan-body.stale { opacity: .45; pointer-events: none; transition: opacity .15s ease; }

/* Mutagen chests (D1): the chest picture on the left, slot state next to it, the set and the button below.
   A ready chest is outlined so it is found at a glance among seven. */
.chest-row { display: flex; align-items: center; gap: 12px; }
.chest-img { flex: 0 0 64px; width: 64px; height: 64px; object-fit: contain; }
.chest-img.dim { opacity: .45; filter: grayscale(.7); }
.chest-body { flex: 1 1 auto; min-width: 0; }
.chest-title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.chest-line { font-size: 14px; color: var(--hint); margin-top: 4px; }
.chest-left { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }
.chest-card.ready { box-shadow: inset 0 0 0 2px var(--ok); }
.chest-set { font-size: 13px; color: var(--hint); margin-top: 8px; line-height: 1.35; }
.chest-last { margin-top: 10px; padding-top: 8px; border-top: 1px solid rgba(127, 127, 127, .15); }
.chest-rews { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 4px; }
.chest-rew { display: flex; align-items: center; gap: 6px; font-size: 14px; }
.chest-rew img { flex: 0 0 30px; width: 30px; height: 30px; object-fit: contain; }
.chest-auto { color: var(--link); font-size: 13px; margin-top: 3px; }
.chest-types { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 4px; }
.chest-stats .mut-tile { cursor: default; }

/* Mutagen exchange (D1): «first + second → result» in one row, then the price, the count and the button. */
/* W has 13 levels (8..20): they wrap to a second line instead of scrolling sideways — the hidden-scrollbar row
   was cut off at the card edge and could not be scrolled with a mouse (owner 2026-10-07). */
.exch-levels { margin-top: 2px; flex-wrap: wrap; overflow: visible; }
.exch-row { display: flex; align-items: flex-start; gap: 4px; margin-bottom: 4px; }
.exch-side {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center;
  gap: 2px; text-align: center; font-size: 13px;
}
.exch-side img { width: 48px; height: 48px; object-fit: contain; }
.exch-side .fr-meta { margin-top: 0; }
.exch-side.short b { color: var(--danger); }
.exch-side.result b { color: var(--link); }
.exch-op { flex: 0 0 auto; align-self: center; margin-top: -24px; color: var(--hint); font-weight: 800; font-size: 18px; }
.exch-bonus { text-align: center; margin: 2px 0 4px; }
.exch-count .k { line-height: 1.3; }
.exch-count .k .fr-meta { display: block; }

/* Exchange chain «хочу X-10 ×N»: variants one under another, the cheapest first; infeasible ones are dimmed and
   say what is missing. While a new plan is computed the old one stays dimmed (the page does not jump). */
.xc-card .mut-target .stepper { flex: 0 0 auto; }
.xc-vars.stale { opacity: .45; pointer-events: none; transition: opacity .15s ease; }
.xc-var { margin-top: 12px; padding-top: 10px; border-top: 1px solid rgba(127, 127, 127, .2); }
.xc-var.bad { opacity: .62; }
.xc-var-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.xc-uses { display: flex; flex-wrap: wrap; gap: 4px 12px; margin: 4px 0 2px; font-size: 13px; }
.xc-use { display: inline-flex; align-items: center; gap: 4px; }
.xc-use img { width: 24px; height: 24px; object-fit: contain; }
.xc-run { margin: 10px 0 2px; padding: 10px 12px; border-radius: 12px; background: var(--bg); }
.xc-run.live { box-shadow: inset 0 0 0 1px var(--link); }
.xc-run-text { white-space: pre-line; font-size: 14px; margin-bottom: 6px; }

/* «Заначки» tab: sets by floor, a set row expands into its parts, sources and «Собрать ×N». */
.sh-filters { flex-wrap: wrap; overflow-x: visible; }
.sh-set + .sh-set { border-top: 1px solid rgba(127,127,127,.15); }
.sh-head {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 0; border: 0; background: none;
  color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.sh-title { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.sh-title b { overflow-wrap: anywhere; }
.sh-head .badge { flex: none; white-space: nowrap; }
.sh-quest { margin: 2px 0 8px; font-size: 14px; color: var(--warn); }
/* An open set: sections on their own panel — parts, reward, «Собрать», «Работа за части» (owner 2026-10-11: tidy it). */
.sh-body { padding: 0 0 12px; container-type: inline-size; }
.sh-sec { margin-top: 10px; padding: 12px 14px; border-radius: 12px; background: var(--bg); }
.sh-sec-h { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.sh-sec > .sub { margin: 0 0 4px; }
.sh-note { font-size: 14px; color: var(--warn); line-height: 1.4; padding: 4px 0; }
/* Settings as rows: the label (with its hint) left, the control right — like the switches across the app. */
.sh-frow { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; padding: 9px 0; }
.sh-frow + .sh-frow, .sh-sec > .sub + .sh-frow { border-top: 1px solid rgba(127,127,127,.15); }
.sh-frow > .k { flex: 1 1 150px; min-width: 0; }   /* a control that does not fit next to it drops below */
.sh-frow > .k .sub { display: block; margin-top: 2px; font-size: 12px; }
.sh-frow > .v { flex: 0 1 auto; text-align: right; font-weight: 600; }
.sh-frow > .stepper, .sh-frow > .switch, .sh-frow > .seg { flex: 0 0 auto; margin-left: auto; }
.sh-frow > .sh-buy { flex: 1 1 auto; min-width: 0; }
.sh-frow > .seg { margin: 0; }
.sh-sec > .btn { margin-top: 10px; }
.sh-frow .sh-max { display: table; margin: 6px 0 0; }   /* under «Сколько раз», at the left edge */
.sh-part-txt .fr-meta { white-space: nowrap; }
.sh-goal .seg-btn { font-size: 13px; padding: 8px 10px; white-space: nowrap; }
/* A part = a row: picture, name and state; its buttons under it on a phone, to the right on a wide card. */
.sh-parts { display: block; }
.sh-part {
  display: grid; grid-template-columns: 40px minmax(0, 1fr); grid-template-areas: "img txt" "acts acts";
  column-gap: 10px; row-gap: 8px; align-items: center; padding: 8px 0;
}
.sh-part + .sh-part { border-top: 1px solid rgba(127,127,127,.15); }
.sh-part > img, .sh-noimg {
  grid-area: img; width: 40px; height: 40px; object-fit: contain; border-radius: 10px; background: rgba(127,127,127,.12);
}
.sh-part.locked > img { filter: grayscale(1); opacity: .55; }
.sh-part-txt { grid-area: txt; min-width: 0; }
.sh-none { color: var(--warn); }
.sh-acts { grid-area: acts; display: flex; flex-direction: column; gap: 8px; }
.sh-asked { font-size: 13px; color: var(--hint); }
@container (min-width: 520px) {
  .sh-part { grid-template-columns: 40px minmax(0, 1fr) auto; grid-template-areas: "img txt acts"; }
  .sh-acts { flex-direction: row; flex-wrap: wrap; justify-content: flex-end; align-items: center; }
  .sh-acts .sh-src { width: auto; }
}
/* Where a part comes from and «Попросить у друзей»: real buttons a finger hits (owner 2026-10-11: two text links
   one under the other were miss-taps) — at least 40 px high, 8 px apart. */
.sh-src {
  /* A plain block, not flex: the text is several nodes (two-voice spans), flex would break it into columns. */
  display: block; width: 100%; min-height: 40px; box-sizing: border-box;
  padding: 10px 12px; border: 1px solid rgba(127,127,127,.25); border-radius: 10px;
  background: rgba(127,127,127,.10); color: var(--link); font: inherit; font-size: 14px; line-height: 1.25;
  text-align: left; cursor: pointer; overflow-wrap: anywhere;
}
.sh-src:active { background: rgba(127,127,127,.22); }
.sh-src.locked { color: var(--hint); }
.sh-src.locked::before { content: "🔒 "; }
.sh-buy { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
/* Buying is a side action here: outlined, so «Работать за части» stays the one bright button of the section. */
.sh-buy .fr-btn { background: transparent; color: var(--link); box-shadow: inset 0 0 0 1px rgba(127,127,127,.35); }
/* «Энергия»: «Купить · N 💵» over «Использовать» at a booster (side by side they overflow a phone screen). */
.btn-pair { display: flex; flex-direction: column; align-items: stretch; gap: 6px; flex: 0 0 auto; }

/* Mutagens in items: a «item, mutagen, buttons» row, under the selected one a mutagen choice with price. */
.gear-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; }
.gear-row + .gear-row, .gear-pick + .gear-row { border-top: 1px solid rgba(127, 127, 127, .15); }
.gear-img { flex: 0 0 44px; width: 44px; height: 44px; object-fit: contain; }
.gear-row.sel .gear-img { outline: 2px solid var(--btn); border-radius: 8px; }
.gear-main { flex: 1 1 auto; min-width: 0; }
.gear-title { font-size: 15px; line-height: 1.25; overflow-wrap: anywhere; }
.gear-mut { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; font-size: 13px; margin-top: 3px; }
.gear-mut img { width: 22px; height: 22px; object-fit: contain; }
.gear-note { font-size: 12px; margin-top: 2px; }
.gear-btns { flex: 0 0 auto; display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
.gear-btns .fr-btn { white-space: nowrap; padding: 7px 10px; font-size: 12px; }
/* «Заточка» shop (2026-09-29): the price is a button on the right, locked items a collapsed list. */
.wp-buy { flex: 0 0 auto; white-space: nowrap; }
.wp-locked > summary { cursor: pointer; font-weight: 700; padding: 2px 0; }
.wp-locked .sub { margin: 6px 0 4px; }
.wp-locked .gear-img { opacity: .55; }
.gear-pick { background: var(--bg); border-radius: 12px; padding: 10px 12px; margin: 2px 0 10px; }
.gear-pick-head { font-weight: 600; font-size: 14px; }
.gear-pick .chips { margin-top: 8px; }
.gear-pick .mut-tile { background: var(--bg2); }
.gear-confirm { margin-top: 4px; }
.gear-hint { margin: 8px 0 2px; }
/* List sections are expanders (feedback #222): a header with the item count, arrow on the right. */
.gear-sec > summary, .inv-card > summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; cursor: pointer; list-style: none;
  font-size: 18px; font-weight: 700; padding: 2px 0;
}
.gear-sec-t { flex: 0 1 auto; min-width: 0; }
.gear-sec > summary::-webkit-details-marker, .inv-card > summary::-webkit-details-marker { display: none; }
.gear-sec > summary::after, .inv-card > summary::after {
  content: "▸"; color: var(--hint); font-size: 15px; line-height: 1;
  flex: 0 0 auto; margin-left: auto; transition: transform .15s ease;
}
.gear-sec[open] > summary::after, .inv-card[open] > summary::after { transform: rotate(90deg); }
.gear-sec[open] > summary, .inv-card[open] > summary { margin-bottom: 4px; }
/* «Зовут в бой» (feedback 2026-10-12): the same expander, floor chips sit right under the header. */
.inv-card > .chips { margin-top: 4px; }
/* Many floors: the chips wrap onto new rows instead of scrolling sideways — every floor stays in sight. */
.chips.inv-floors { flex-wrap: wrap; overflow-x: visible; }
.chips.inv-floors .chip { padding: 7px 11px; }
.gear-sec-n {
  flex: 0 0 auto; font-size: 13px; font-weight: 600; color: var(--hint);
  background: var(--bg); border-radius: 999px; padding: 2px 9px;
}
/* «N не работают» goes on a second header row: next to the title it does not fit on a phone. */
.gear-sec-idle { order: 1; flex: 0 0 100%; font-size: 12px; font-weight: 600; color: var(--warn); }
.gear-sec > .sub { margin-bottom: 2px; }
/* The item row is tappable as a whole; the selected one is highlighted; on a wide screen the choice is not under it. */
.gear-list .gear-row { cursor: pointer; }
.gear-list .gear-row.sel {
  margin: 0 -8px; padding-left: 8px; padding-right: 8px; border-radius: 10px;
  background: rgba(127, 127, 127, .12);
}
/* Choice card in the right panel: the item on top, the choice itself without its own backing. */
.gear-pick-card .gear-pick { background: none; padding: 0; margin: 4px 0 0; }
.gear-pick-card .gear-pick .mut-tile { background: var(--bg); }
.gear-pick-card > .gear-row { padding-top: 0; }
/* The second row button («Вытащить») is quieter than the main one; when locked it looks like all locks. */
.fr-btn.alt { background: var(--bg); color: var(--text); border: 1px solid rgba(127, 127, 127, .3); }
.fr-btn.alt.locked { color: var(--hint); }
.fr-btn.confirm { background: var(--warn); color: #fff; border-color: var(--warn); }

/* --- wardrobe: clothing and ornament sets ---
   On top «если купить всё открытое» (before -> after per stat), below the sets
   as item tiles: image, body slot, gain from this purchase and a button with a price. */
.wr-seg { background: var(--bg2); margin: 0 0 12px; }
.wr-seg .seg-btn { font-size: 14px; padding: 9px 4px; }
.wr-seg .seg-btn.on { background: var(--bg); }
.wr-sum-rows { margin: 8px 0 4px; }
.wr-sum-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 15px; }
.wr-sum-row + .wr-sum-row { border-top: 1px solid rgba(127,127,127,.15); }
.wr-sum-ico { flex: 0 0 24px; text-align: center; font-size: 18px; line-height: 1; }
.wr-sum-label { flex: 1 1 auto; min-width: 0; }
.wr-sum-change { color: var(--hint); font-size: 14px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.wr-sum-change b { color: var(--text); }
.wr-plus {
  min-width: 3.6em; text-align: right; font-weight: 800; color: var(--ok);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.wr-warn { color: var(--warn); font-size: 13px; margin-top: 6px; line-height: 1.35; }
.wr-both {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px;
  margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(127,127,127,.15); font-size: 14px;
}
.wr-both .k { color: var(--hint); }
.wr-fresh { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 6px; }
.wr-fresh .fr-btn { padding: 7px 10px; font-size: 12px; }

.wr-head { display: flex; align-items: center; gap: 10px; }
.wr-logo {
  flex: 0 0 44px; width: 44px; height: 44px; object-fit: contain; border-radius: 10px;
  background: rgba(127,127,127,.12);
}
.wr-title { flex: 1 1 auto; min-width: 0; }
/* A long set name wraps instead of being cut: the badge on the right stays on one line. */
.wr-title b { display: block; overflow-wrap: anywhere; }
.wr-head .badge { flex: none; white-space: nowrap; }
.wr-bonus { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin-top: 8px; font-size: 14px; }
.wr-bonus .k { color: var(--hint); }
.wr-stat { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.wr-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; margin-top: 10px; }
.wr-item {
  background: var(--bg); border-radius: 12px; padding: 8px 6px;
  display: flex; flex-direction: column; align-items: center; gap: 4px; text-align: center;
}
.wr-item > img { width: 72px; height: 72px; object-fit: contain; }
.wr-slot { font-size: 12px; color: var(--hint); line-height: 1.2; }
.wr-gain { min-height: 18px; font-size: 13px; display: flex; flex-wrap: wrap; justify-content: center; gap: 2px 6px; }
.wr-gain .muted { color: var(--hint); font-size: 12px; }
/* A slot label can take two lines («предплечье слева»), and price buttons still line up. */
.wr-item .fr-btn { width: 100%; padding: 8px 4px; white-space: nowrap; margin-top: auto; }
.wr-more { margin-top: 8px; padding: 10px 16px; font-size: 14px; }
.wr-after {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 4px 8px;
  margin-top: 6px; font-size: 13px; text-align: center; line-height: 1.35;
}
/* «До максимума» under the business upgrade row (W1) */
.biz-upmax { margin-top: 8px; }
.wr-line { font-size: 13px; color: var(--hint); margin-top: 8px; line-height: 1.35; }
.wr-set.compact .wr-line { margin-top: 6px; }
.link-btn {
  border: 0; background: none; padding: 0; font: inherit; color: var(--link);
  cursor: pointer; text-align: left; text-decoration: underline; text-underline-offset: 2px;
}
.fr-unhide { display: block; margin: 8px 0 2px; font-size: 13px; }   /* «Скрыто: N · вернуть» (feedback #457) */

/* --- wardrobe: rooms (feedback #288: «просто покупку, лёгкий интерфейс») ---
   The card is a landscape room image (1922x1082 from the game CDN), a name with a mark and one button.
   On a phone cards form a column, on a wide screen a grid (end of file). */
.wr-rooms { display: grid; gap: 12px; }
.wr-rooms > .card { margin: 0; }
.wr-room-art {
  width: 100%; aspect-ratio: 1922 / 1082; object-fit: cover; border-radius: 12px;
  background: rgba(127, 127, 127, .12); display: block;
}
/* Starting room: there is no static on the CDN, its background is the top of a Spine sprite sheet (the sheet is taller than the frame,
   object-position cuts the excess at the bottom). */
.wr-room-art.crop-top { object-position: top; }
/* A room whose art never loaded (new without static, CDN blinked): instead of an empty
   hole where the art should be, a door (2026-10-04, noticed by the owner on the test stand). */
.wr-room.noart::before {
  content: "🚪"; display: grid; place-items: center;
  width: 100%; aspect-ratio: 1922 / 1082; border-radius: 12px;
  background: rgba(127, 127, 127, .12); font-size: 44px; opacity: .6;
}
.wr-rooms-head h2 { display: flex; align-items: center; gap: 8px; }
.wr-room-row { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.wr-room-row b { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.wr-room-row .badge { flex: none; white-space: nowrap; }
.wr-room.sel { box-shadow: inset 0 0 0 2px var(--btn); }
.wr-room .btn { margin-top: 10px; }
.wr-room-sum .v.wr-bonus { margin-top: 0; justify-content: flex-end; font-size: 14px; }
.wr-room .wr-warn { text-align: center; }
.wr-room .wr-line { margin-top: 10px; }

/* Raid timer: «2 ч 30 мин» is longer than a number, so a smaller font to keep the stepper from bulging */
.raid-span { padding: 6px 0 2px; border-top: 0; }
.raid-span .count { font-size: 17px; min-width: 6.2em; }
#raidStatus .row .link-btn { margin-left: 6px; }
/* «Боевая энергия — на вкладке «ПвП»»: a link row instead of the combat bar card. */
.energy-pvp-link { margin: 4px 0 12px; text-align: center; }
.energy-pvp-link .link-btn { padding: 6px 4px; font-size: 14px; text-align: center; }

/* --- wardrobe: weapon sharpening ---
   Weapon card (level bar, stats) and under it the form «до какого уровня, чем точить,
   запас» with an estimate; while the bot sharpens, a progress card on top and a note about the fight pause. */
.wp-bar { margin-top: 10px; }
.wp-open { margin-top: 10px; }
.wp-plan { margin-top: 6px; }
.wp-plan .wr-sum-rows { margin-top: 10px; }
.wp-target { padding: 8px 0 2px; border-top: 0; }
.wp-target .k { color: var(--hint); font-weight: 700; font-variant-numeric: tabular-nums; }
.wp-cur .seg-btn { font-size: 13px; padding: 8px 2px; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* Auto-fight and raid pause during sharpening is a visible strip, not small text among others. */
.wp-pause { border-left: 3px solid var(--warn); padding: 2px 0 2px 8px; color: var(--text); }
/* «Заточить списком»: a tick on each card, ticked cards framed; the list form on top (on a wide screen — on the right). */
.wp-list-open { margin-bottom: 12px; }
.wp-multi .sub { margin-top: 4px; }
.wp-multi-pick { margin: 10px 0 2px; }
.wp-check { display: flex; align-items: center; gap: 10px; margin-top: 10px; padding: 8px 0 2px; cursor: pointer;
  font-weight: 700; border-top: 1px solid rgba(127,127,127,.15); }
.wp-check input { width: 22px; height: 22px; accent-color: var(--btn); margin: 0; }
.wp-card[data-wptick] { cursor: pointer; }
.wp-card.sel { box-shadow: inset 0 0 0 2px var(--btn); }

/* Red dot on the burger: something waits in the menu (gifts, «Что нового», admin feedback). */
.icon-btn { position: relative; }
.menu-dot {
  position: absolute; top: 3px; right: 3px; width: 13px; height: 13px;
  background: #e5484d; border-radius: 50%; border: 2px solid var(--bg);
}
.guide p { margin: 0 0 8px; }
.guide p:last-child { margin-bottom: 0; }

/* --- fight: card header with boss portrait, name and fight state --- */
.fight-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.fight-head .boss-ava { flex-basis: 52px; width: 52px; height: 52px; }
.fight-who { flex: 1; min-width: 0; }
.fight-who h2 { margin: 0 0 5px; overflow-wrap: anywhere; }
/* Portrait and name open the boss card (feedback #593). */
.fight-boss-link { cursor: pointer; }
span.fight-boss-link { text-decoration: underline dotted; text-underline-offset: 3px; }

/* --- fight: «Урон по боссу» and «Журнал боя», like the panels in the game fight itself --- */
.room-list { max-height: 260px; overflow-y: auto; margin-top: 6px; -webkit-overflow-scrolling: touch; }
.room-log { max-height: 320px; }
.room-row { display: flex; align-items: baseline; gap: 8px; padding: 6px 8px; border-radius: 8px; font-size: 14px; }
.room-row + .room-row { border-top: 1px solid rgba(127,127,127,.12); }
.room-row.me { background: rgba(214,69,69,.12); }
.room-row.gone { opacity: .55; }
.room-rank { color: var(--hint); min-width: 22px; }
.room-name { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.room-dmg { font-weight: 700; color: var(--danger); white-space: nowrap; }
.log-row { display: flex; align-items: baseline; gap: 6px; padding: 5px 2px; font-size: 13px; line-height: 1.35; }
.log-row + .log-row { border-top: 1px solid rgba(127,127,127,.12); }
.log-row.me .log-text { font-weight: 600; }
.log-time { color: var(--hint); font-variant-numeric: tabular-nums; white-space: nowrap; font-size: 12px; }
.log-icon { flex: 0 0 auto; }
.log-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }

/* --- «Бот обновляется» screen: fullscreen over the menu while the bot restarts ---
   The same image is in deploy/updating/index.html (the Caddy page, when the bot is fully down). */
.upd {
  position: fixed; inset: 0; z-index: 100; background: var(--bg); color: var(--text);
  display: flex; align-items: center; justify-content: center; text-align: center;
  padding: 24px 16px calc(24px + env(safe-area-inset-bottom));
  animation: upd-in .35s ease both;
}
.upd[hidden] { display: none; }
body.is-updating { overflow: hidden; }
.upd-card { width: 100%; max-width: 340px; }
.upd-art { width: min(240px, 68vw); height: auto; display: block; margin: 0 auto 8px; overflow: visible; }
.upd-title { font-size: 22px; font-weight: 800; margin: 6px 0 8px; }
.upd-text { margin: 0; color: var(--hint); font-size: 15px; }
.upd-bar { width: 180px; max-width: 70%; height: 4px; margin: 20px auto 0; border-radius: 2px;
  background: rgba(127,127,127,.22); overflow: hidden; }
.upd-bar > i { display: block; width: 40%; height: 100%; border-radius: 2px; background: var(--btn);
  animation: upd-slide 1.4s ease-in-out infinite; }
.upd-quip { margin: 12px 0 0; min-height: 20px; color: var(--hint); font-size: 13px; transition: opacity .3s ease; }
.upd-quip.fade { opacity: 0; }

.upd-shadow { fill: rgba(0,0,0,.18); transform-box: fill-box; transform-origin: center;
  animation: upd-shadow 3.2s ease-in-out infinite alternate; }
.upd-board { animation: upd-bob 3.2s ease-in-out infinite alternate; }
.upd-line { stroke-dasharray: 80; animation: upd-write 6s ease-in-out infinite both; }
.upd-line:nth-child(2) { animation-delay: .55s; }
.upd-line:nth-child(3) { animation-delay: 1.1s; }
.upd-line:nth-child(4) { animation-delay: 1.65s; }
.upd-check { stroke-dasharray: 80; animation: upd-write 6s ease-in-out 2.3s infinite both; }
.upd-gear { transform-box: fill-box; transform-origin: center; }
.upd-gear-big { animation: upd-spin 6s linear infinite; }
.upd-gear-small { animation: upd-spin 4.8s linear infinite reverse; }
.upd-teeth { fill: none; stroke: var(--btn); }
.upd-body { fill: var(--btn); }
.upd-gear-small .upd-teeth { stroke: var(--warn); }
.upd-gear-small .upd-body { fill: var(--warn); }
.upd-hole { fill: var(--bg); }

@keyframes upd-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes upd-slide { from { transform: translateX(-100%); } to { transform: translateX(250%); } }
@keyframes upd-bob { from { transform: translateY(0); } to { transform: translateY(-6px); } }
@keyframes upd-shadow { from { transform: scaleX(1); opacity: 1; } to { transform: scaleX(.88); opacity: .7; } }
@keyframes upd-spin { to { transform: rotate(360deg); } }
@keyframes upd-write {
  0% { stroke-dashoffset: 80; opacity: 1; }
  22% { stroke-dashoffset: 0; opacity: 1; }
  82% { stroke-dashoffset: 0; opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .upd, .upd-art *, .upd-bar > i { animation: none !important; }
}

/* «Ожидаем выхода игры…» screen (2026-09-23: the game was closed, the bot is frozen, see freeze.py). The same
   scene, but everything froze: gears stopped and iced over, lines are written, snow falls. The screen
   stays long, so tall content scrolls instead of being clipped. */
.upd-snow, .upd-note[hidden] { display: none; }
.upd.frozen { align-items: flex-start; overflow-y: auto; }
.upd.frozen .upd-card { margin: auto 0; }
.upd.frozen .upd-bar { display: none; }
.upd.frozen .upd-gear, .upd.frozen .upd-line, .upd.frozen .upd-check { animation: none; stroke-dashoffset: 0; }
.upd.frozen .upd-teeth { stroke: #8fc3e8; }
.upd.frozen .upd-body { fill: #8fc3e8; }
.upd.frozen .upd-gear-small .upd-teeth { stroke: #b9dcf2; }
.upd.frozen .upd-gear-small .upd-body { fill: #b9dcf2; }
.upd.frozen .upd-snow { display: inline; }
.upd-flake { fill: none; stroke: #8fc3e8; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  transform-box: fill-box; transform-origin: center; animation: upd-twinkle 5s ease-in-out infinite; }
.upd-snow g:nth-child(2) .upd-flake { animation-delay: -1.7s; }
.upd-snow g:nth-child(3) .upd-flake { animation-delay: -3.3s; }
.upd-note { margin: 16px auto 0; max-width: 320px; padding: 10px 12px; border-radius: 12px;
  background: rgba(127,127,127,.12); text-align: left; font-size: 14px; line-height: 1.45; }
.upd-note > div + div { margin-top: 6px; }
@keyframes upd-twinkle {
  0%, 100% { opacity: .45; transform: rotate(0deg) scale(.9); }
  50% { opacity: 1; transform: rotate(30deg) scale(1.05); }
}
@media (max-height: 640px) {
  .upd.frozen .upd-art { width: min(170px, 50vw); }
}

/* --- Quests: lines, reward waits, auto-claim ------------------------------------------- */
.qst-row { display: flex; align-items: flex-start; gap: 10px; padding: 10px 0; border-top: 1px solid rgba(127,127,127,.15); }
.qst-row:first-of-type { border-top: 0; }
.qst-row.ready { align-items: center; }
.qst-ava { flex: 0 0 44px; width: 44px; height: 44px; border-radius: 50%; object-fit: cover; object-position: top;
  background: rgba(127,127,127,.15); }
.qst-body { flex: 1; min-width: 0; }
.qst-name { font-weight: 700; line-height: 1.25; }
.qst-meta { color: var(--hint); font-size: 12px; margin-top: 2px; }
.qst-desc { color: var(--hint); font-size: 13px; line-height: 1.35; margin-top: 6px; font-style: italic; }
.qst-reward { font-size: 13px; margin-top: 4px; font-variant-numeric: tabular-nums; }
.rw { white-space: nowrap; margin-right: .7em; }
.rw:last-child { margin-right: 0; }
.qst-cond { margin-top: 6px; font-size: 13px; }
.qst-cond-head { display: flex; justify-content: space-between; gap: 8px; }
.qst-cond-head b { white-space: nowrap; font-variant-numeric: tabular-nums; }
.qst-cond.done { color: var(--ok); }
/* Boss condition: who the boss is now (quest texts keep the pre-rebrand names), a tap opens the card. */
.qst-boss {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; padding: 2px 8px 2px 2px;
  border: 0; border-radius: 999px; background: rgba(127,127,127,.15); color: var(--hint);
  font: inherit; font-size: 12px; cursor: pointer; max-width: 100%; text-align: left;
}
.qst-boss b { color: var(--text); font-weight: 600; }
.qst-boss-ava { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; object-position: top; flex: 0 0 22px; }
.qst-claim { box-shadow: inset 0 0 0 2px #c9a227; }
.qst-claim .btn { margin-top: 8px; }
.qst-total { margin: 2px 0 2px; }
.qst-sum { padding: 8px 0 2px; }
.qst-sum + .qst-sum { border-top: 1px solid rgba(127,127,127,.15); }
.qst-sum-k { color: var(--hint); font-size: 13px; }
.qst-sum-v { font-weight: 700; font-variant-numeric: tabular-nums; margin-top: 2px; line-height: 1.5; }
.qst-chips { display: flex; gap: 8px; overflow-x: auto; padding: 4px 2px 8px; margin: 0 -2px; scrollbar-width: none; }
.qst-chips::-webkit-scrollbar { display: none; }
.qst-chip { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 3px; width: 56px;
  border: 0; background: none; padding: 0; color: var(--text); font: inherit; font-size: 11px; cursor: pointer; }
.qst-chip img { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; object-position: top;
  background: rgba(127,127,127,.15); box-shadow: 0 0 0 2px transparent; }
.qst-chip.on img { box-shadow: 0 0 0 2px var(--btn); }
.qst-chip.locked img, .qst-chip.unknown img { filter: grayscale(1); opacity: .55; }
.qst-chip span { font-variant-numeric: tabular-nums; color: var(--hint); }
.qst-chip.on span { color: var(--text); font-weight: 700; }
.qst-line-head { margin: 4px 0 6px; }
.qst-line-name { font-weight: 700; font-size: 16px; }
.qst-done-btn { margin: 8px 0 4px; }
.qst-item { padding: 8px 10px; margin-top: 6px; border-radius: 10px; background: var(--bg); cursor: pointer; }
.qst-item-head { display: flex; align-items: baseline; gap: 8px; }
.qst-ico { flex: 0 0 1.2em; text-align: center; }
.qst-item-name { flex: 1; min-width: 0; font-weight: 600; }
.qst-floor { font-size: 11px; color: var(--hint); border: 1px solid rgba(127,127,127,.35); border-radius: 6px; padding: 0 5px; }
.qst-item .qst-reward { margin-left: calc(1.2em + 8px); color: var(--hint); }
.qst-item.claimed { opacity: .55; }
.qst-item.claimed .qst-ico { color: var(--ok); }
.qst-item.active { box-shadow: inset 0 0 0 1px var(--btn); }
.qst-item.completed { box-shadow: inset 0 0 0 2px #c9a227; }
.qst-item.locked { opacity: .75; }

/* --- Boss medals: three for 100 / 500 / 1000 kills, labeled right away ---------------------- */
/* Name and medals in one row while they fit; a long name («Бруно Цугенкнофф» in the font of
   «режим безумия») does not overlap the medals; they wrap under it. */
.boss-title-row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 6px 8px; }
.boss-title-row h2 { flex: 1 1 8em; margin: 0; overflow-wrap: break-word; }
.boss-medals {
  --medal: clamp(46px, 13.5vw, 64px);
  flex: 0 0 auto; margin-left: auto; display: flex; flex-direction: column; align-items: center;
}
.medal-row { display: flex; gap: 6px; }
.medal { display: flex; flex-direction: column; align-items: center; width: var(--medal); }
.medal[hidden] { display: none; }
.medal img { width: var(--medal); height: var(--medal); border-radius: 50%; filter: grayscale(1) brightness(.55); opacity: .6; }
.medal.on img { filter: none; opacity: 1; }
.medal-cap { font-size: 11px; line-height: 1.2; margin-top: 2px; color: var(--hint); font-variant-numeric: tabular-nums; }
.medal.on .medal-cap { color: var(--ok); font-weight: 700; }
.medal-prog { font-size: 12px; color: var(--hint); margin-top: 3px; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Promo plate (days gifted). Normal theme: a calm card over the app;
   in «режим безумия» mad.css restyles it as an order on paper with a stamp. */
.gift-wrap {
  position: fixed; inset: 0; z-index: 60; display: grid; place-items: center;
  padding: 24px 16px; background: rgba(0, 0, 0, .72); backdrop-filter: blur(2px);
}
.gift-wrap[hidden] { display: none; }
.gift-card {
  position: relative; width: 100%; max-width: 360px; padding: 26px 20px 20px;
  border-radius: 16px; background: var(--bg2); border: 1px solid rgba(127, 127, 127, .22);
  box-shadow: 0 18px 50px rgba(0, 0, 0, .55); text-align: center;
}
.gift-days {
  display: inline-flex; align-items: baseline; gap: 6px; margin: 4px 0 14px;
  font-size: 44px; font-weight: 700; line-height: 1; color: var(--warn);
}
.gift-days i { font-size: 18px; font-style: normal; font-weight: 600; }
.gift-card h2 { margin: 0; font-size: 20px; color: var(--text); }
.gift-card p { margin: 0 0 18px; white-space: pre-line; color: var(--text); opacity: .85; }
/* Close cross: the gift can be postponed, so the plate must not lock the app. */
.gift-close {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 32px; height: 32px; background: transparent; opacity: .6;
}
.gift-close svg { width: 18px; height: 18px; }
.gift-stamp { display: none; }

/* Daily game reward: the same card as the promo plate (.gift-card); in the center is the
   reward image from the game itself on a soft glow. «Режим безумия» restyles it in mad.css. */
.daily-day {
  margin: 6px 0 2px; font-size: 13px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--hint);
}
.daily-prize {
  position: relative; width: 136px; height: 136px; margin: 4px auto 2px;
  display: grid; place-items: center;
}
.daily-prize::before {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 196, 64, .42) 0%, rgba(255, 196, 64, .14) 42%, transparent 70%);
  animation: daily-glow 2.6s ease-in-out infinite;
}
.daily-prize img {
  position: relative; width: 112px; height: 112px; object-fit: contain;
  filter: drop-shadow(0 6px 10px rgba(0, 0, 0, .45)); transition: opacity .14s ease;
}
.daily-prize img.fade { opacity: 0; }
.daily-amount {
  font-size: 40px; font-weight: 700; line-height: 1.05; color: var(--warn);
  font-variant-numeric: tabular-nums;
}
.daily-amount:empty { display: none; }
.daily-name { margin: 2px 0 16px; font-size: 16px; color: var(--text); opacity: .85; }
.daily-done { margin: -6px 0 14px; font-weight: 600; color: var(--ok); }
.daily-done[hidden] { display: none; }
.daily-stamp { display: none; }
.daily-card.taken .daily-prize img { animation: daily-pop .5s cubic-bezier(.3, 1.6, .5, 1); }
@keyframes daily-glow {
  0%, 100% { transform: scale(.94); opacity: .8; }
  50% { transform: scale(1.05); opacity: 1; }
}
@keyframes daily-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.22); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .daily-prize::before, .daily-card.taken .daily-prize img { animation: none; }
}

/* --- profile: player doll (owner request 2026-10-01), the same character as on the main game screen
   with weapon and pet; stats over the scene on the left. Layers are same-size images stacked
   (doll.py, dollHtml in app.js). --- */
.doll-card { padding: 10px; }
.doll-stage {
  --doll-h: clamp(250px, 40vh, 340px);
  position: relative; display: flex; align-items: flex-end; min-height: 216px;
  padding: 16px 8px 0 10px; border-radius: 12px; overflow: hidden;
  background:
    radial-gradient(80% 60% at 62% 40%, rgba(255, 255, 255, .10), transparent 70%),
    linear-gradient(180deg, rgba(127, 127, 127, .14), rgba(127, 127, 127, .04));
}
/* Floor underfoot: a light strip at the bottom of the scene so the doll does not hang in the air. */
.doll-stage::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 12%;
  background: linear-gradient(180deg, transparent, rgba(127, 127, 127, .16)); pointer-events: none;
}
.doll-box { flex: 1; min-width: 0; display: flex; justify-content: flex-end; align-items: flex-end; }
.doll {
  position: relative; z-index: 1; width: min(100%, calc(var(--doll-h) * var(--ratio))); aspect-ratio: var(--ratio);
  transition: opacity .3s;
}
.doll.fresh { opacity: 0; }       /* the scene frame is still being computed; we will show the finished one */
.doll-space, .doll-l, .doll-g, .doll-pet { position: absolute; }
.doll-l { left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; -webkit-user-select: none; user-select: none; }
.doll-g { inset: 0; isolation: isolate; }
/* «Дышит»: grows from the floor like in the game (0.35% over 4.4 s there; the doll is smaller here, so a bit more visible). */
.doll-g, .doll-pet { transform-origin: 50% 100%; animation: doll-breath 4.4s ease-in-out infinite; }
.doll-pet { animation-delay: -3.5s; }     /* the pet lags by 0.9 s */
@keyframes doll-breath {
  0%, 100% { transform: scaleY(1); }
  50% { transform: scaleY(1.009); }
}
.doll-hair { isolation: isolate; }
.doll-tint {
  background: var(--tint); mix-blend-mode: multiply;
  -webkit-mask: var(--hair) center / 100% 100% no-repeat; mask: var(--hair) center / 100% 100% no-repeat;
}
.doll-keep { -webkit-mask: var(--keep) center / 100% 100% no-repeat; mask: var(--keep) center / 100% 100% no-repeat; }
/* An item hidden under another (a glove under a sleeve): visible everywhere except the mask. */
.doll-hide {
  -webkit-mask-image: linear-gradient(#000, #000), var(--hide); -webkit-mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; -webkit-mask-composite: xor;
  mask-image: linear-gradient(#000, #000), var(--hide); mask-size: 100% 100%;
  mask-repeat: no-repeat; mask-composite: exclude;
}
.doll-wait {
  width: 34%; height: calc(var(--doll-h) * .8); margin: 0 auto; border-radius: 45% 45% 14px 14px;
  background: rgba(127, 127, 127, .14); animation: doll-wait 1.4s ease-in-out infinite;
}
@keyframes doll-wait { 50% { opacity: .45; } }
/* Stats stand on the left in a column and slightly overlap the scene: the doll is to their right. */
.doll-stats {
  position: relative; z-index: 2; flex: 0 0 auto; align-self: center; margin-right: -22px;
  display: flex; flex-direction: column; gap: 8px;
}
.doll-stat {
  display: flex; align-items: center; gap: 8px; padding: 4px 13px 4px 4px; border-radius: 999px;
  background: rgba(0, 0, 0, .46); color: #fff; -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.doll-ico {
  flex: 0 0 34px; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  font-size: 18px; line-height: 1; background: rgba(255, 255, 255, .16);
}
.doll-val { display: flex; flex-direction: column; line-height: 1.12; }
.doll-val b { font-size: 15px; font-variant-numeric: tabular-nums; }
.doll-val i { font-style: normal; font-size: 11px; opacity: .72; }
.doll-lvl {
  position: absolute; right: 10px; top: 10px; z-index: 2; padding: 3px 10px; border-radius: 999px;
  background: rgba(0, 0, 0, .46); color: #fff; font-size: 13px; font-weight: 700;
}
@media (prefers-reduced-motion: reduce) {
  .doll-g, .doll-pet, .doll-wait { animation: none; }
}

/* --- Computer: wide screen (feedback #212, 2026-10-01: «на весь экран — полноценный десктоп,
   а не растянутая мобильная версия»). In a normal Telegram window the app is narrow like a phone and
   does not get here; once expanded fullscreen the menu becomes a permanent panel on the left and
   cards lay out in two columns. All CSS: tab markup is untouched. --- */
@media (min-width: 1000px) {
  :root { --side: 272px; }
  body { padding-left: var(--side); }
  /* Menu is a panel: always in place, with no dimming, burger or cross. */
  .drawer {
    transform: none; visibility: visible; transition: none;
    width: var(--side); border-radius: 0; box-shadow: none;
    border-right: 1px solid rgba(127, 127, 127, .18);
  }
  .drawer-overlay, #menuBtn, #drawerClose { display: none; }
  .drawer-head { padding: 10px 6px 14px 14px; }
  .top { padding: 16px 32px 14px; }
  html.tg-fs .top {
    padding-top: calc(16px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
  }
  .brand { font-size: 24px; }
  .who { font-size: 14px; }
  /* Wallet in the header (top of the file): roomier here; a long nickname does not stretch past 220 px. */
  .top-wallet:not([hidden]) { gap: 14px; font-size: 14px; }
  .top-wallet:not([hidden]) + .who { max-width: 220px; }
  .top-wallet .tw-money, .top-wallet .tw-stats { gap: 12px; }
  .top-wallet .tw-stats { padding-left: 14px; }
  .asplayer { margin: 12px 32px 0; }
  main { max-width: 1240px; margin: 0 auto; padding: 18px 32px 48px; }
  .toast { left: calc(var(--side) + 32px); right: 32px; max-width: 560px; margin: 0 auto; }
  .queue-note { left: calc(var(--side) + 32px); right: 32px; max-width: 560px; margin: 0 auto; }

  /* Two columns of cards. Everything that is not a card (buttons, hints, section switches,
     lists) goes full width and splits the tab into parts; cards above and below it
     lay out into columns by themselves. */
  .tab.active { column-count: 2; column-gap: 18px; }
  .tab.active > * { break-inside: avoid; }
  .tab.active > :not(.card) { column-span: all; }
  /* A card is an indivisible block (inline-block). A plain block is split at its top edge by columns, and what
     sticks out above it («режим безумия» tape) on the first card of a column slid to the bottom of
     the neighbor: under «Симптомами» in the profile hung tape that held nothing. */
  .tab.active > .card { display: inline-block; width: 100%; vertical-align: top; margin-bottom: 18px; }
  /* Row cards obey the rule «не меньше двух строк до и после разрыва колонки»: two
     or three cards then never spread across columns and stacked in the left one
     (boss card, «Рейд»). We allow a break after any card. */
  .tab.active { orphans: 1; widows: 1; }
  /* One column: tabs read top to bottom (log, stats, boss list), and those
     with a single card. All tabs share one width, so we do not make them narrower: the screen would jump wider/narrower
     on every tab or section switch. */
  #tab-changelog.active, #tab-stats.active, #tab-bosses.active:has(> .boss-main),
  .tab.active:not(:has(> .card ~ .card)) {
    column-count: 1;
  }
  /* Boss list: search and the list on the left, on the right a narrow «что сейчас» panel: fight, auto-fight,
     queue, how much is collected (owner complaint 2026-10-05: full-width buttons and everything «по разным
     углам»). The panel stays on scroll: fight buttons are always at hand. */
  #tab-bosses.active:has(> .boss-side) {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 420px); column-gap: 18px; align-items: start;
  }
  #tab-bosses.active > .boss-main { grid-column: 1; grid-row: 1; }
  /* Sticky offset = header (64 px) + main's top padding (18) − the panel's own 10 px pad: unscrolled, the panel's first
     card stands level with the left column's (owner 2026-10-09: with 86 px it stuck at once, 14 px lower). */
  #tab-bosses.active > .boss-side {
    grid-column: 2; grid-row: 1;
    position: sticky; top: 72px; max-height: calc(100vh - 90px); overflow-y: auto;
    margin: -10px -8px 0; padding: 10px 8px 0; scrollbar-width: thin;
  }
  html.tg-fs #tab-bosses.active > .boss-side {
    top: calc(72px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
    max-height: calc(100vh - 90px - var(--tg-safe-area-inset-top, 0px) - var(--tg-content-safe-area-inset-top, 0px));
  }
  /* There are 108 bosses: as many list columns as fit rows no narrower than 280 px. */
  #bossList .list { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
  /* Boss card: the boss and its drop on the left, everything you hit with on the right: fight button, auto-fight,
     settings. A full-width «В бой» button split the tab in half, and the right block could not rise
     under a short drop (owner complaint 2026-10-01). */
  #tab-bosses.active > #fightBtn, #tab-bosses.active > #watchBtn { column-span: none; margin: 0 0 12px; }
  #tab-bosses.active > #fightBtn, #tab-bosses.active > .card + #watchBtn { break-before: column; }
  /* Fight is two blocks side by side (owner request 2026-10-01: a full-width fight card looked
     stretched): the fight with buttons and queue on the left, who hits how on the right. The right block stays
     on scroll and fills the screen height: the log scrolls inside itself. If there is no damage or log
     (no fight and no hits), the left block is alone, full tab width. */
  #tab-fight.active:has(> .fight-side) {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-rows: auto 1fr;
    column-gap: 18px; align-items: start;
  }
  #tab-fight.active > .fight-main { grid-column: 1; grid-row: 1; }
  #tab-fight.active:has(> .fight-side) > #toBossesBtn { grid-column: 1; grid-row: 2; }
  /* 82 px = header (64) + main's top padding (18): the column starts level with its neighbour and sticks only on
     scroll (with 86 it stuck at once, 4 px lower — owner 2026-10-09 about «Боссы»). */
  #tab-fight.active > .fight-side {
    grid-column: 2; grid-row: 1 / span 2;
    position: sticky; top: 82px;
    display: flex; flex-direction: column; max-height: calc(100vh - 100px);
  }
  html.tg-fs #tab-fight.active > .fight-side {
    top: calc(82px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
    max-height: calc(100vh - 100px - var(--tg-safe-area-inset-top, 0px) - var(--tg-content-safe-area-inset-top, 0px));
  }
  .fight-main > .card, .fight-side > .card { margin-bottom: 18px; }
  .fight-head { gap: 16px; margin-bottom: 12px; }
  .fight-head .boss-ava { flex-basis: 68px; width: 68px; height: 68px; }
  .fight-who h2 { font-size: 21px; }
  /* Profile: the doll is on the left and stays on scroll, all data on the right. The doll gets half
     of the width and the full screen height (owner: «с ПК можно даже увеличить окно персонажа»). The last
     grid row (1fr) takes the doll height when the cards on the right are shorter: gaps do not
     grow between them. Columns have no pixel minimum: at a 1000 px window about
     630 px remain for both, and anything wider runs off the screen edge. */
  #tab-profile.active:has(> .doll-card) {
    display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    grid-template-rows: repeat(8, auto) 1fr; column-gap: 18px; align-items: start;
  }
  #tab-profile.active:has(> .doll-card) > * { grid-column: 2; }
  #tab-profile.active > .doll-card { grid-column: 1; grid-row: 1 / -1; position: sticky; top: 82px; }
  html.tg-fs #tab-profile.active > .doll-card {
    top: calc(82px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
  }
  .doll-stage { --doll-h: clamp(420px, calc(100vh - 176px), 900px); padding: 20px 14px 0; }
  .doll-box { justify-content: center; }
  .doll-stats { gap: 10px; margin-right: -10px; }
  .doll-lvl { right: 14px; top: 14px; font-size: 15px; padding: 4px 12px; }
  .doll-ico { flex-basis: 40px; width: 40px; height: 40px; font-size: 21px; }
  .doll-val b { font-size: 17px; }
  .doll-val i { font-size: 12px; }
  .fight-side > .card { flex: 0 0 auto; }
  .fight-side > .card:last-child { flex: 0 1 auto; min-height: 0; margin-bottom: 0; display: flex; flex-direction: column; }
  .fight-side > .card:last-child > .room-log { flex: 1 1 auto; min-height: 120px; max-height: none; }
  /* A crowded fight (feedback #682): the damage table scrolls inside itself and gives way to the log, otherwise
     the stuck column ran past the screen bottom and the log could never be reached. */
  .fight-side > .card:has(> #roomBoard):not(:last-child) { flex: 0 1 auto; min-height: 0; display: flex; flex-direction: column; }
  .fight-side #roomBoard { flex: 1 1 auto; min-height: 96px; max-height: 40vh; }
  /* Mutagens in items: the list of items by section on the left, on the right a panel that stays on
     scroll: mutagen choice for the pressed item, summary and «Заменить слабые» (feedback #222: the choice
     opened under the item at the very bottom, columns reshuffled and the item flew up). The panel
     taller than the screen scrolls inside itself. */
  #tab-mutagens.active:has(> .gear-side) {
    display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); grid-template-rows: auto auto 1fr;
    column-gap: 18px; align-items: start;
  }
  #tab-mutagens.active:has(> .gear-side) > .seg { grid-column: 1 / -1; grid-row: 1; }
  #tab-mutagens.active > .gear-list { grid-column: 1; grid-row: 2; }
  #tab-mutagens.active:has(> .gear-side) > .guide { grid-column: 1; grid-row: 3; }
  #tab-mutagens.active > .gear-side {
    grid-column: 2; grid-row: 2 / span 2;
    position: sticky; top: 86px; max-height: calc(100vh - 104px); overflow-y: auto;
    /* padding so scrolling does not clip card shadows and tape */
    margin: -10px -8px 0; padding: 10px 8px 0; scrollbar-width: thin;
  }
  html.tg-fs #tab-mutagens.active > .gear-side {
    top: calc(86px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
    max-height: calc(100vh - 104px - var(--tg-safe-area-inset-top, 0px) - var(--tg-content-safe-area-inset-top, 0px));
  }
  .gear-side > .card, .gear-list > .card { margin-bottom: 18px; }
  /* Rows are wider so item buttons line up, and the list is half as long. */
  .gear-list .gear-btns { flex-direction: row; align-items: center; }
  /* Wardrobe and «Заточка» (feedback 2026-10-04: «на компьютере неудобно»): sets, weapons
     or pets on the left, on the right a panel that stays on scroll: the «если купить всё открытое» summary
     with filters, and in «Заточка» the form of the selected item or the sharpening progress. As in «Мутагены в
     вещах»; if there is nothing to show on the right, the tab lays out as usual. The panel comes first in the markup
     (on a phone it is on top), «dense» places the list next to it in the same grid row. */
  /* «Заначки» (owner 2026-10-11: two columns of floors made the eyes run): one column of sets on the left, the
     summary with filters and the running batches as a sticky panel on the right — like «Гардероб». */
  #tab-stash.active:has(> .sh-side) {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 400px); column-gap: 18px; align-items: start;
  }
  #tab-stash.active > .sh-list { grid-column: 1; grid-row: 1; }
  /* The panel's own 10 px top padding (room for the card shadow) is taken back from `top`: its card starts on the
     same line as the list's first card and as the first card of every other tab (86 px). */
  #tab-stash.active > .sh-side {
    grid-column: 2; grid-row: 1;
    position: sticky; top: 76px; max-height: calc(100vh - 94px); overflow-y: auto;
    margin: -10px -8px 0; padding: 10px 8px 0; scrollbar-width: thin;
  }
  html.tg-fs #tab-stash.active > .sh-side {
    top: calc(76px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
    max-height: calc(100vh - 94px - var(--tg-safe-area-inset-top, 0px) - var(--tg-content-safe-area-inset-top, 0px));
  }
  .sh-side > .card, .sh-list > .card { margin-bottom: 18px; }
  #tab-wardrobe.active:has(> .wr-side) {
    display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); grid-auto-flow: row dense;
    column-gap: 18px; align-items: start;
  }
  #tab-wardrobe.active:has(> .wr-side) > .seg { grid-column: 1 / -1; }
  #tab-wardrobe.active > .wr-list { grid-column: 1; }
  #tab-wardrobe.active > .wr-side {
    grid-column: 2;
    position: sticky; top: 86px; max-height: calc(100vh - 104px); overflow-y: auto;
    margin: -10px -8px 0; padding: 10px 8px 0; scrollbar-width: thin;
  }
  html.tg-fs #tab-wardrobe.active > .wr-side {
    top: calc(86px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
    max-height: calc(100vh - 104px - var(--tg-safe-area-inset-top, 0px) - var(--tg-content-safe-area-inset-top, 0px));
  }
  .wr-side > .card, .wr-list > .card { margin-bottom: 18px; }
  /* The item whose form is on the right now is highlighted; a card in the list can be tapped as a whole. */
  .wr-list .wp-card[data-wppick] { cursor: pointer; }
  .wr-list .wp-card.sel { box-shadow: inset 0 0 0 2px var(--btn); }
  /* Rooms: the cards are landscape, in a two-column grid, not columns (columns would shuffle them). */
  #tab-wardrobe.active:has(> .wr-rooms) { column-count: 1; }
  #tab-wardrobe.active > .wr-rooms { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
}

/* Very wide screen: the hint that ends a tab (in the boss card, about auto, repair,
   medkits) becomes a separate panel on the right and stays on scroll; the space is empty there anyway
   (owner request 2026-10-01). Only for two-column tabs; narrower, the hint stays at the bottom as before.
   Room for the panel is reserved on every tab, even with no hint: all tabs share one width and the screen
   does not jump wider/narrower when switching. */
@media (min-width: 1700px) {
  main { max-width: 1600px; }
  .doll-stats { gap: 12px; }
  .doll-stat { padding: 5px 16px 5px 5px; gap: 10px; }
  .doll-ico { flex-basis: 46px; width: 46px; height: 46px; font-size: 24px; }
  .doll-val b { font-size: 20px; }
  .doll-val i { font-size: 13px; }
  .tab.active { padding-right: 348px; }
  .tab.active:has(> .card ~ .card):not(#tab-changelog, #tab-stats, #tab-admin) > .guide:last-child {
    position: fixed; top: 88px; width: 316px; margin: 0;
    right: max(32px, calc((100vw - var(--side) - 1600px) / 2 + 32px));
    max-height: calc(100vh - 120px); overflow-y: auto;
    padding: 14px 16px; border-radius: var(--radius); background: var(--bg2);
  }
  html.tg-fs .tab.active:has(> .card ~ .card):not(#tab-changelog, #tab-stats, #tab-admin) > .guide:last-child {
    top: calc(88px + var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px));
  }
  .tab.active:has(> .card ~ .card):not(#tab-changelog, #tab-stats, #tab-admin) > .guide:last-child::before {
    content: "Подсказки"; display: block; margin-bottom: 8px;
    font-size: 16px; font-weight: 700; color: var(--text);
  }
}

/* --- PvP: opponents with looks, the bot runs the fight (owner request 2026-10-03). The opponent scene is
   the same as the profile doll (.doll-stage); on a phone it is to the left of stats, on a wide screen
   three opponents in a row, scene on top. --- */
.pvp-list { display: grid; gap: 12px; margin-bottom: 12px; }
.pvp-cand { margin: 0; padding: 10px; display: grid; grid-template-columns: minmax(104px, 36%) minmax(0, 1fr); gap: 10px; }
.pvp-stage { --doll-h: 200px; min-height: 0; padding: 12px 4px 0; justify-content: center; }
.pvp-stage .doll-lvl { right: 6px; top: 6px; font-size: 12px; padding: 2px 8px; }
.pvp-stage .doll-wait { width: 56%; }
.pvp-ava { position: relative; z-index: 1; align-self: center; width: 76%; max-width: 132px; aspect-ratio: 1; margin-bottom: 12px; border-radius: 50%; object-fit: cover; }
/* Opponent without a look: a dark silhouette instead of a doll. */
.pvp-shade { position: relative; z-index: 1; height: var(--doll-h); max-width: 100%; object-fit: cover; object-position: 50% 62%; filter: brightness(0); opacity: .38; }
.pvp-noface { position: absolute; z-index: 2; left: 0; right: 0; bottom: 14px; text-align: center; font-size: 12px; color: #fff; opacity: .8; }
.pvp-info { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.pvp-name { font-size: 17px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pvp-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
.pvp-st { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 5px; align-items: center; padding: 4px 6px; border-radius: 10px; background: rgba(127, 127, 127, .12); min-width: 0; }
.pvp-st b, .pvp-st small { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pvp-st i { grid-row: 1 / 3; font-style: normal; font-size: 16px; line-height: 1; }
.pvp-st b { font-size: 14px; font-variant-numeric: tabular-nums; line-height: 1.15; }
.pvp-st small { font-size: 11px; color: var(--hint); line-height: 1.15; }
.pvp-rew { font-size: 14px; }
.pvp-info .btn { margin-top: auto; }
.pvp-run.live { border: 1px solid var(--link); }
.pvp-hp { margin-top: 8px; }
.pvp-hp .row { padding: 0; }
.pvp-log { margin: 8px 0 2px; font-size: 13px; color: var(--hint); display: grid; gap: 2px; }
.pvp-hit.me { color: var(--text); }
.pvp-me { margin-top: 4px; }
@media (min-width: 1000px) {
  .pvp-list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; margin-bottom: 18px; }
  .pvp-cand { grid-template-columns: minmax(0, 1fr); padding: 14px; }
  /* The scene has the same height for all three: names, stats and buttons line up. */
  .pvp-stage { --doll-h: clamp(240px, 38vh, 380px); padding-top: 16px; height: calc(var(--doll-h) + 16px); }
  .pvp-stage .doll { max-height: var(--doll-h); }
  .pvp-stage .doll-lvl { font-size: 14px; padding: 3px 10px; }
  /* Bar on the left, running fight or its result on the right; opponents and everything else full width. */
  #tab-pvp.active {
    column-count: 1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 18px; align-items: start;
  }
  #tab-pvp.active > * { grid-column: 1 / -1; }
  #tab-pvp.active > .pvp-head { grid-column: 1; }
  #tab-pvp.active > #pvpRun { grid-column: 2; }
  #tab-pvp.active > #pvpRun:empty { display: none; }
  /* No fight: the bar is full width, and rating and own stats sit to its right with no empty space. */
  #tab-pvp.active > .pvp-head:has(+ #pvpRun:empty) { grid-column: 1 / -1; }
  #tab-pvp.active > #pvpRefreshBtn { grid-column: 1; }
  #tab-pvp.active > #pvpReloadBtn { grid-column: 2; }
}

/* «Поддержать бота» on the subscription tab: amount chips above the own-amount input. */
.donate-chips { margin-top: 8px; }

/* Big friend checklists: filter chips wrap (five of them), tools as a row of links. */
.pick-chips { flex-wrap: wrap; overflow-x: visible; }
.pick-tools { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 6px 0 2px; }
.pick-tools .link-btn.locked::before { content: "🔒 "; }
.link-btn.pick-old { display: block; margin: 10px 0 2px; text-align: left; }

/* A boss drop that opens its card in «Гардероб» (player idea 08.10): the row is a button with a «›». */
.drop-link { width: 100%; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.drop-link .drop-go { margin-left: auto; color: var(--hint); font-size: 18px; line-height: 1; padding-left: 6px; }
.drop-link:active { opacity: .7; }
/* The item the drop led to lights up for a moment. */
.wr-target { animation: wr-target 2.4s ease-out; border-radius: 12px; }
@keyframes wr-target { 0%, 40% { box-shadow: 0 0 0 3px var(--btn), 0 0 18px 4px rgba(77, 142, 247, .45); } 100% { box-shadow: none; } }
@media (prefers-reduced-motion: reduce) { .wr-target { animation: none; outline: 3px solid var(--btn); } }

/* «Боссы»: one panel for both modes (2026-10-09) — the switch «Сам бью / Рейд», the timer and the one button. */
.hunt-panel .hunt-seg { margin-top: 0; }
.hunt-seg .seg-btn { font-size: 15px; padding: 9px 4px; }
.hunt-dot {
  display: inline-block; width: 8px; height: 8px; margin-left: 4px; border-radius: 50%; vertical-align: 1px;
  background: #34b46b; box-shadow: 0 0 0 0 rgba(52, 180, 107, .6); animation: hunt-dot 1.8s ease-out infinite;
}
@keyframes hunt-dot { 0% { box-shadow: 0 0 0 0 rgba(52, 180, 107, .6); } 100% { box-shadow: 0 0 0 7px rgba(52, 180, 107, 0); } }
.hunt-panel .pause-block { margin-top: 10px; }
/* «Сколько ходить»: all five choices stay in sight — the row wraps instead of hiding «8 ч» behind a sideways scroll
   (owner 2026-10-09: «8 ч» was cut off in the narrow right column). */
.hunt-panel .chips, #hudTimer .chips { flex-wrap: wrap; overflow-x: visible; }
.hunt-panel #huntGo { margin-top: 10px; }
/* The old «Рейд» menu item turning to dust: specks above everything but the tour, never catching taps. */
.snap-dust { position: fixed; inset: 0; z-index: 95; pointer-events: none; overflow: hidden; }
.snap-dust i { position: fixed; display: block; border-radius: 1px; will-change: transform, opacity; }
/* Tour note above its window (the target sits low): the arrow goes to the bottom edge. */
.mad-tut-note.above .mad-tut-arrow { top: auto; bottom: -9px; }
@media (prefers-reduced-motion: reduce) { .hunt-dot { animation: none; } }
