.mgr-draft-stat-controls { display: grid; grid-template-columns: 1.2fr 1fr 1fr; gap: 6px; margin: 6px 0; min-width: 0; }
.mgr-draft-stat-controls label { display: grid; gap: 3px; min-width: 0; font-size: var(--fs-2xs); color: var(--text-dim); }
.mgr-draft-stat-controls select { min-width: 0; width: 100%; padding: 6px 4px; font-size: var(--fs-xs); border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-alt); color: var(--text); }
.mgr-draft-stat-controls select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.mgr-player-stats > span { min-width: 0; text-align: right; padding-right: 3px; }
.mgr-player-stat-head > button { min-width: 0; text-align: right; padding-right: 3px; }

/* "Лёд и неон" (Fantiq brand palette) is the default theme since
   2026-08-19 - light is now the opt-in one, via [data-theme="light"] on
   <html>, toggled in the settings panel and remembered in localStorage
   (site.js's THEME_STORAGE_KEY). Bare :root carries the neon values so a
   pre-JS/no-JS paint still matches the new default rather than flashing
   the old light theme first. */
:root {
  color-scheme: dark;
  --bg: #0a1628;
  --surface: #111d32;
  --surface-alt: #16213b;
  --border: #1c2b45;
  --text: #e0f7ff;
  --text-dim: #7c93b0;
  --accent: #00e5ff;
  --accent-text: #0a1628;
  --ice: #13233d;
  --ice-line: #2e4a68;
  --ice-red: #ff4d6d;
  --ice-blue: #00e5ff;
  --success: #00e676;
  /* Motion tokens (2026-09-02) - one shared easing/duration vocabulary
     instead of each rule picking its own ms/curve by feel. --ease-out for
     anything entering/settling (fades, lifts), --ease-spring for a small
     playful overshoot (kept rare - badges/toasts, not everyday hovers). */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --dur-fast: 150ms;
  --dur-base: 220ms;
  --dur-slow: 380ms;
  --font-display: "Unbounded", "Segoe UI", sans-serif;
  --font-ui: "Inter", "Segoe UI", sans-serif;
  --radius: 16px;
  --radius-sm: 10px;
  --line: color-mix(in srgb, var(--ice-line) 65%, transparent);
  --shadow: 0 12px 32px color-mix(in srgb, var(--ice-blue) 18%, transparent);
  --shadow-glow: 0 0 18px color-mix(in srgb, var(--accent) 42%, transparent);
}

html[data-theme="light"] {
  color-scheme: light;
  --bg: #f4f6f9;
  --surface: #ffffff;
  --surface-alt: #eef1f6;
  --border: #dde2ea;
  --text: #14171c;
  --text-dim: #586172;   /* AA on bg / surface / surface-alt (was #667085: 4.39 on alt) */
  --accent: #1769b8;     /* AA as text on bg+surface and under white button text (was #1c7ed6: 3.9-4.2) */
  --accent-text: #ffffff;
  --ice: #cdeeff;
  --ice-line: #ffffff;
  --ice-red: #d64545;
  --ice-blue: #1769b8;
  --success: #127a46;    /* AA as text (was #1a9e5c: 3.2-3.5) */
}

* { box-sizing: border-box; }

/* Unlike <a>, a <button> (and anything else made clickable purely via a JS
   onclick/addEventListener - [onclick], [role="button"]) does NOT get a
   pointer cursor by default in any browser - it shows the plain arrow
   unless every clickable element opts in individually, which several had
   quietly missed (reported 2026-08-20, the "Удалить" swipe-confirm one
   among them). One global rule instead of chasing each class by hand. */
button, [onclick], [role="button"] { cursor: pointer; }
button:disabled { cursor: default; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  font-family: var(--font-ui);
  background: var(--bg);
  color: var(--text);
  position: relative;
}
/* Arena backdrop, dark theme only. The layer is fixed and taller than
   the screen; a scroll-driven transform (compositor, same frame as the
   scroll) slides it up a short distance across the whole page. A script
   that corrects an in-flow picture a frame later is what made it jump. */
html:not([data-theme="light"]) body::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 160vh;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(180deg, color-mix(in srgb, var(--bg) 28%, transparent), color-mix(in srgb, var(--bg) 48%, transparent) 62%, color-mix(in srgb, var(--bg) 82%, transparent)),
    url("/fantasy/static/img/arena_bg.webp?v=1");
  background-repeat: no-repeat;
  background-position: top center;
  background-size: cover;
}
@supports (animation-timeline: scroll()) {
  html:not([data-theme="light"]) body::before {
    animation: arena-drift linear both;
    animation-duration: auto;
    animation-timeline: scroll(root);
  }
}
@keyframes arena-drift {
  to { transform: translate3d(0, -40vh, 0); }
}
@media (prefers-reduced-motion: reduce) {
  html:not([data-theme="light"]) body::before {
    animation: none;
    height: 100vh;
    height: 100dvh;
  }
}

#app {
  max-width: 1200px;
  margin: 0 auto;
  padding: 1rem 1rem 4rem;
}

/* Outer page margin halved on phones - every row inside .workspace-col
   already fights for width (see the .player-card mobile rules further
   down), so the page's own left/right margin is worth shrinking too
   rather than only ever trimming space inside the cards. */
@media (max-width: 820px) {
  #app { padding-left: 0.5rem; padding-right: 0.5rem; }
}

.site-header { margin-bottom: 1rem; position: relative; }
.site-header-row { display: flex; align-items: center; justify-content: center; gap: 0.6rem; position: relative; }
.site-header h1.site-logo-wrap { margin: 0; line-height: 0; }
/* main_logo.webp is a transparent wordmark whose canvas is padded on the left
   so the WORD (not the puck trail) sits on the box centre: a centred box is a
   centred wordmark, no per-breakpoint nudging. width/height attributes in the
   markup reserve the box before the file arrives (R6). */
.site-logo {
  display: block;
  height: 7.5rem;
  width: auto;
  max-width: 100%;
  aspect-ratio: 814 / 250;
}
@media (max-width: 480px) {
  .site-logo { height: 4.6rem; }
}
/* Session identity + logout tucked into the header's own top-right corner
   (2026-08-31 redesign) instead of a full centered row under the tagline -
   the username was competing with the logo/tagline for the header's main
   visual weight when it's really just incidental account chrome. */
.session-corner {
  position: absolute;
  top: 0;
  right: 0;
  /* .site-header-row is a full-width flex box that comes later in the DOM and
     is position:relative, so without this it paints over the corner and
     swallows the logout click even though its visible content is centered. */
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.session-identity {
  font-size: var(--fs-sm);
  color: var(--text-dim);
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.session-identity strong { color: var(--text); font-weight: 600; }
/* Round icon-only logout - was a labeled "Выйти" button competing with the
   header's centered branding; an icon reads as account chrome at a glance
   without needing a text label (title/aria-label still carry it for
   tooltips and screen readers). */
.logout-icon-btn {
  flex: none;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
}
.logout-icon-btn svg { width: 16px; height: 16px; }
.logout-icon-btn:hover {
  background: color-mix(in srgb, var(--ice-red) 18%, transparent);
  color: var(--ice-red);
  border-color: color-mix(in srgb, var(--ice-red) 40%, var(--border));
}
/* Narrow phones: drop the name text, keep just the round icon so it never
   crowds the logo wordmark next to it. */
@media (max-width: 480px) {
  .session-identity { display: none; }
}
.login-landing {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 24rem);
  align-items: center;
  gap: var(--space-6);
  margin: var(--space-5) auto var(--space-6);
  max-width: 70rem;
}
.login-intro { min-width: 0; text-align: center; }
.hero-banner {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  max-width: 720px;
  margin: 0 auto;
  /* Login hero is still a JPEG, so it keeps the radial fade. The header
     wordmark is a transparent PNG and does not use this mask. */
  -webkit-mask-image: radial-gradient(ellipse 78% 75% at center, black 35%, transparent 92%);
  mask-image: radial-gradient(ellipse 78% 75% at center, black 35%, transparent 92%);
}
.login-eyebrow {
  margin: 0 0 var(--space-3);
  font-size: var(--fs-sm);
  color: var(--accent);
  letter-spacing: 0.06em;
}
.login-title {
  margin: 0;
  font: 500 clamp(1.5rem, 2.8vw, 2.5rem)/1.2 var(--font-display);
  letter-spacing: -0.03em;
  text-wrap: balance;
}
.login-description {
  max-width: 28rem;
  margin: var(--space-4) auto;
  color: var(--text-dim);
  font-size: var(--fs-base);
  line-height: var(--lh-prose);
  text-wrap: balance;
}
.login-preview-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  color: var(--text);
  font-size: var(--fs-md);
  text-decoration-color: var(--line);
  text-underline-offset: 0.3em;
}
.login-preview-link:hover { color: var(--accent); }
.login-preview-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.login-landing .login-gate {
  width: 100%;
  margin: 0;
  padding: var(--space-5);
  border-color: var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  backdrop-filter: blur(16px);
  box-shadow: var(--shadow);
}
.login-form-title {
  margin: 0 0 var(--space-3);
  font: 500 var(--fs-lg)/var(--lh-tight) var(--font-display);
  letter-spacing: -0.03em;
}
.login-landing .credentials-form input:not([type="checkbox"]) {
  min-height: var(--tap-min);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
}
.login-landing .credentials-form input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.login-landing .tab-btn, .login-landing .secondary-btn { min-height: var(--tap-min); }
@media (max-width: 900px) {
  .login-landing {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    gap: var(--space-5);
    margin-top: 0;
  }
  .login-intro { width: 100%; max-width: 30rem; }
  .login-landing .login-gate { max-width: 24rem; }
}
.credentials-form { display: flex; flex-direction: column; gap: 0.5rem; width: 100%; }
.credentials-form input {
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  padding: 0.45rem 0.6rem;
  font-size: var(--fs-body);
  width: 100%;
  box-sizing: border-box;
}
/* 4 pills (Вход/Регистрация/sports.ru/Почта, since email login graduated
   out of beta 2026-08-20) never fit one row - NOT just on a narrow
   viewport: .login-gate below is capped at max-width: 24rem (384px)
   regardless of screen size, so a single flex row overflowed that same
   fixed-width card on a 1920px desktop too (confirmed via a live
   scrollWidth measurement: 379px of content in a 334px box) - this isn't
   a responsive breakpoint issue, it's unconditional. Always a 2x2 grid. */
.auth-mode-toggle { display: grid; grid-template-columns: 1fr 1fr; gap: 0.3rem; width: 100%; }
.auth-mode-toggle .tab-btn { flex: none; }
.auth-status {
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--text-dim);
  margin: 0.4rem 0 0;
  line-height: 1.4;
}
.auth-status.error { color: #e05252; }
.auth-hint { margin: 0.2rem 0 0; font-size: var(--fs-xs); color: var(--text-dim); line-height: 1.35; }
.auth-consent-row {
  display: flex;
  align-items: flex-start;
  gap: 0.4rem;
  margin: 0.3rem 0 0;
  font-size: var(--fs-xs);
  color: var(--text-dim);
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
}
.auth-consent-row input {
  margin-top: 0.15rem;
  width: auto;
  padding: 0;
  border: none;
  background: none;
}
.link-btn {
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: var(--fs-sm);
  text-decoration: underline;
  cursor: pointer;
  padding: 0.2rem 0;
  align-self: center;
}
.link-btn:hover { color: var(--text); }
.email-status { font-size: var(--fs-sm); color: var(--text-dim); margin: 0 0 0.4rem; line-height: 1.4; }
.email-status.error { color: #e05252; }
.settings-hint-small { font-size: var(--fs-sm); color: var(--text-dim); margin: 0 0 0.9rem; line-height: 1.55; }
.settings-hint-small.error { color: #e05252; }
/* Lowercase Greek "β" (user's ask, 2026-08-24 - replaced the spelled-out
   "бета" text), pinned to the section button's own top-right corner
   instead of sitting inline after the label (which pushed it to its own
   line below the label on .section-btn's column layout - the button this
   sits in needs `position: relative`, see .section-btn/.nav-btn). No
   text-transform:uppercase here on purpose - it would render as "Β"
   (capital beta), the opposite of what was asked. */
.beta-badge {
  position: absolute;
  /* Inside the button's own box, not straddling its edge (real bug,
     2026-08-24: negative offsets pushed it past .section-nav's
     overflow-x: auto clip region on the last button in the row, so it
     visibly got cut off). */
  top: 2px;
  right: 2px;
  display: inline-block;
  font-size: var(--fs-2xs);
  font-weight: 700;
  line-height: 1;
  color: var(--accent);
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.1rem 0.25rem;
}
/* Same shape as .beta-badge, gold instead of accent-colored - matches the
   gold used for shared/player_detail.js's existing ".pd-pro-label" (the
   first Pro perk's own inline marker, docs/subscription_pro.md), so every
   Pro-only mark in the app reads as one consistent color regardless of
   which file renders it. */
.pro-badge {
  display: inline-block;
  font-size: var(--fs-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: #d4a017;
  background: var(--surface-alt);
  border-radius: 4px;
  padding: 0.1rem 0.35rem;
  margin-left: 0.35rem;
  vertical-align: middle;
}
.verify-status { font-size: var(--fs-md); margin: 0 0 0.4rem; color: var(--text-dim); }
.verify-status.verified { color: #2f9e58; font-weight: 700; }
.verify-status.pending { color: #a86400; font-weight: 700; }
/* Nickname/email input + a compact action button on the same line - Sports.ru
   and Почта's minimal "type it and go" shape (user's spec, 2026-08-18),
   replacing the old stacked label+input+full-width-button form. */
/* max-width caps it well short of #app's 1200px on a real desktop monitor
   (docs/ui_conventions.md's "don't stretch to fill the available width") -
   width isn't set here at all, so it still shrinks to fit a narrow
   container (a phone viewport) exactly as before. */
.inline-input-row { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; max-width: 22rem; }
.inline-input-row input {
  flex: 1;
  min-width: 0;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-body);
  box-sizing: border-box;
}
/* Unlabeled arrow button (Sports.ru's submit) - no "Отправить"-style text,
   just "→" (user's explicit "без надписи, типа вперёд"). */
.arrow-btn {
  flex: 0 0 auto;
  width: 2.2rem;
  height: 2.2rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-text, #fff);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
}
.email-verify-banner {
  max-width: 24rem;
  margin: 1rem auto 0;
  padding: 0.5rem 0.8rem;
  border-radius: 8px;
  background: var(--surface-alt);
  text-align: center;
  font-size: var(--fs-md);
}
.email-verify-banner.error { color: #e05252; }

.login-gate {
  max-width: 24rem;
  margin: 3rem auto;
  padding: 1.5rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  text-align: center;
}
.login-gate .gate-message {
  margin: 0 0 1rem;
  padding: 0.6rem 0.8rem;
  border-radius: 8px;
  background: var(--surface-alt);
  color: var(--text);
  font-size: var(--fs-body);
}
.login-gate .login-hint { margin: 0 0 1.2rem; color: var(--text-dim); font-size: var(--fs-body); }
.login-methods { display: flex; flex-direction: column; align-items: center; gap: 0.8rem; }
.telegram-login-widget-wrap { min-height: 40px; display: flex; justify-content: center; }
/* Reused inside Настройки -> Верификация (site.js's refreshVerificationSection)
   for the site-account "link Telegram" widget - same markup/mechanism as
   the login page's, just left-aligned instead of centered since it now
   sits inside a narrow settings column, not a full-width login screen. */
#verify-telegram-widget-wrap { justify-content: flex-start; }

/* Small round (?) button that toggles a collapsible explainer - Sports.ru's
   "как это работает" (user's spec, 2026-08-18): progressive disclosure so
   the instructions aren't always taking up space for a returning user. */
.help-toggle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  margin-left: 0.3rem;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  color: var(--text-dim);
  font-size: var(--fs-2xs);
  line-height: 1;
  cursor: pointer;
  vertical-align: middle;
}
.telegram-widget-fallback {
  margin: 0;
  padding: 0.6rem 0.8rem;
  border-radius: 8px;
  background: var(--surface-alt);
  color: var(--text-dim);
  font-size: var(--fs-sm);
  line-height: 1.4;
}
.login-divider { color: var(--text-dim); font-size: var(--fs-sm); }

.tab-row {
  display: flex;
  gap: 0.4rem;
  overflow-x: auto;
  /* overflow-x: auto forces overflow-y to auto too, so the hover lift
     clips the pill's top border. Same fix as .section-nav. */
  padding: 3px 0 0.5rem;
  margin-bottom: 0.6rem;
  align-items: center;
}

.tab-btn {
  position: relative;
  flex: 0 0 auto;
  padding: 0.5rem 1rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-dim);
  font-size: var(--fs-body);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease, transform 150ms ease;
}
.tab-btn:hover:not(.active):not(.disabled) { transform: translateY(-1px); border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }

.tab-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
}

.tab-btn.disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.tab-btn--beta {
  padding-right: 1.35rem;
}

/* ── Section nav (Профиль/Замены/Рейтинг/...) - deliberately NOT styled
   like .tab-btn (pill, single-line, no icon): a two-tier icon-over-label
   card, so it reads as a different kind of navigation (which section of
   the app) rather than another same-level tab row (view mode/league/
   format all just filter what's already on screen). */
.section-nav {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  /* overflow-x: auto with no overflow-y set computes overflow-y to auto too
     (CSS overflow spec) - without this top padding, .section-btn:hover's
     translateY(-2px) clips the button's own top border against that
     invisible y-scroll edge. */
  padding: 3px 0 0.6rem;
  margin-bottom: 0.8rem;
}
.section-btn {
  position: relative; /* anchors .beta-badge's top-right corner placement */
  flex: 1 1 0;
  min-width: 4.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  padding: 0.5rem 0.3rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text-dim);
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease,
    transform 150ms ease, box-shadow 150ms ease;
}
.section-btn:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
}
.section-btn.active {
  border-color: var(--accent);
  background: var(--surface-alt);
  color: var(--accent);
  box-shadow: var(--shadow-glow);
}
.section-icon { font-size: 1.2rem; line-height: 1; }
.section-label {
  /* Fills whatever is left under the icon (buttons stretch to the tallest
     label, so a one-line caption would otherwise sit glued to the icon
     with empty space below it) and centers the text in that space. */
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-2xs);
  font-weight: 600;
  text-align: center;
  line-height: 1.15;
  white-space: normal; /* e.g. "Статистика хоккеистов" wraps to two lines */
  min-height: 2.3em;   /* two lines reserved: a web-font swap re-wrapping the label never changes the button height */
}

/* Settings tab, pulled out of the .section-nav row into its own fixed
   round button, top-right, always on screen regardless of scroll (user's
   ask, 2026-08-20) - overrides the flex/column .section-btn base rules
   above rather than a whole separate class, so active/hover states stay
   identical to the other tabs. */
.settings-fab-btn {
  position: fixed;
  top: calc(14px + env(safe-area-inset-top));
  right: calc(14px + env(safe-area-inset-right));
  z-index: 500;
  flex: none;
  min-width: 0;
  width: 46px;
  height: 46px;
  padding: 0;
  border-radius: 50%;
  box-shadow: 0 2px 10px color-mix(in srgb, black 25%, transparent);
  /* Base .section-btn is a column flex with no justify-content, which
     top-aligns its (icon, label) pair - fine when there's a label below
     to fill the rest of the button, but this button has icon only, so
     without this it sits pinned to the top instead of centered in the
     circle. */
  justify-content: center;
}
.settings-fab-btn .section-icon { font-size: 1.3rem; }

/* Opens #pro-perks-modal - same fixed top-right positioning as
   .settings-fab-btn, immediately to its left (user's ask, 2026-08-24:
   "рядом с кнопкой настроек"). Gold, matching .pro-badge's color
   everywhere else Pro is marked, and a pill rather than a circle since
   "Pro" doesn't fit a 46px circle the way a single icon glyph does. */
/* Round, same 46px as .settings-fab-btn (user's ask, 2026-08-24 - was a
   pill, "кнопка Pro не круглая"). "PRO" still fits at this font-size. */
.pro-perks-fab-btn {
  position: fixed;
  top: calc(14px + env(safe-area-inset-top));
  right: calc(68px + env(safe-area-inset-right));
  z-index: 500;
  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid #d4a017;
  border-radius: 50%;
  background: var(--surface);
  color: #d4a017;
  font-weight: 700;
  font-size: var(--fs-2xs);
  letter-spacing: 0.02em;
  box-shadow: 0 2px 10px color-mix(in srgb, black 25%, transparent);
  cursor: pointer;
}
html.site-scrolled body:has(#section-manager-content:not(.hidden) #manager-room-detail-view:not(.hidden) .mgr-draft-room) .settings-fab-btn,
html.site-scrolled body:has(#section-manager-content:not(.hidden) #manager-room-detail-view:not(.hidden) .mgr-draft-room) .pro-perks-fab-btn {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-100%);
}
.pro-perks-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.7rem; }
.pro-perks-list li { padding-left: 1.4rem; position: relative; line-height: 1.4; }
.pro-perks-list li::before { content: "★"; position: absolute; left: 0; color: #d4a017; }

/* #pro-perks-modal overrides the generic centered .manager-modal shape
   (user's ask, 2026-08-24): anchored top-right, right under
   .pro-perks-fab-btn, instead of the screen center every other
   .manager-modal use (Manager Mode room create/join) still gets - and
   opens with a liquid-glass scale+blur-in from the button rather than
   the other modals' instant show/hide. Scoped by #pro-perks-modal on
   every rule so those other modals are untouched. */
.manager-modal#pro-perks-modal { align-items: flex-start; justify-content: flex-end; padding: 68px 16px 1rem 1rem; }
/* Backdrop stays near-transparent (a faint dimming, not the other modals'
   solid-ish scrim) - this reads as a popover anchored to the button, not
   a page-blocking dialog. */
#pro-perks-modal .manager-modal-backdrop {
  background: color-mix(in srgb, black 12%, transparent);
  opacity: 0;
  transition: opacity 0.22s ease;
}
#pro-perks-modal.open .manager-modal-backdrop { opacity: 1; }
/* The "liquid glass" look: a blurred, saturated, translucent pane rather
   than the other modals' flat opaque .surface background. */
#pro-perks-modal .manager-modal-panel {
  max-width: 340px;
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid color-mix(in srgb, white 25%, transparent);
  box-shadow: 0 12px 32px color-mix(in srgb, black 30%, transparent);
  /* Scale+fade in from the button's own top-right corner (transform-origin)
     rather than growing from the panel's own center - matches "открывается
     из кнопки". Pre-"open" state (scaled down, transparent, shifted up)
     is the rest state right after "hidden" is removed but before "open" is
     added - see wireProPerksModal's rAF-rAF double-frame for why that gap
     has to exist at all. */
  transform-origin: top right;
  transform: scale(0.82) translateY(-10px);
  opacity: 0;
  transition: transform 0.32s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s ease;
}
#pro-perks-modal.open .manager-modal-panel { transform: scale(1) translateY(0); opacity: 1; }
.pro-perks-hint { margin: 1rem 0 0; color: var(--text-dim); font-size: var(--fs-md); }

/* Priced CTAs (entitlements.FEATURE_ROLLOUT's "pro_purchase_buttons",
   experimental only) - real product/price for the acquiring-provider
   application, no payment integration yet so both link out to the same
   Telegram contact as .pro-perks-hint's fallback. */
.pro-perks-pricing { margin: 1rem 0 0; display: flex; flex-direction: column; gap: 0.4rem; }
.pro-perks-price { margin: 0.6rem 0 0; font-size: var(--fs-body); }
.pro-perks-pricing .primary-btn,
.pro-perks-pricing .secondary-btn {
  display: inline-block;
  text-align: center;
  text-decoration: none;
}
.pro-perks-more,
.pro-perks-manual {
  margin: 0.5rem 0 0;
  font-size: var(--fs-md);
}

/* /pro acquisition pages + payment result (docs/payment.md). Cards and
   pay controls cap at ~28rem so they don't stretch across #app. */
.pro-page { max-width: 40rem; margin: 2rem auto; padding: 0 1rem 3rem; line-height: 1.45; }
.pro-page-nav { font-size: var(--fs-body); margin: 0 0 1rem; }
.pro-page h1 { margin: 0 0 0.6rem; font-size: 1.45rem; }
.pro-lead { color: var(--text-dim); margin: 0 0 1rem; }
.pro-method-grid { display: flex; flex-direction: column; gap: 0.7rem; }
.pro-method-card {
  display: block;
  text-decoration: none;
  color: inherit;
  background: var(--surface-alt);
  border-radius: 12px;
  padding: 0.9rem 1rem;
  max-width: 28rem;
}
.pro-method-card h2 { margin: 0 0 0.3rem; font-size: var(--fs-base); }
.pro-method-card p { margin: 0; color: var(--text-dim); font-size: var(--fs-md); }
.pro-tariff-list { display: flex; flex-direction: column; gap: 0.6rem; max-width: 28rem; }
.pro-tariff {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
  background: var(--surface-alt);
  border-radius: 12px;
  padding: 0.85rem 1rem;
  border: 1px solid transparent;
  cursor: pointer;
  text-align: left;
  color: inherit;
  font: inherit;
  width: 100%;
  max-width: 28rem;
}
.pro-tariff span { color: var(--text-dim); font-size: var(--fs-md); }
.pro-tariff.selected { border-color: var(--accent); }
.pro-legal-links { font-size: var(--fs-md); max-width: 28rem; }
.pro-pay-status { color: var(--text-dim); font-size: var(--fs-body); }
.pro-fallback { font-size: var(--fs-md); }
.pro-page .primary-btn { display: inline-block; text-decoration: none; }

/* Bot activation / Telegram link onboarding modals */
#bot-onboard-modal .manager-modal-panel,
#telegram-link-prompt-modal .manager-modal-panel {
  max-width: 22rem;
}
.bot-onboard-panel .squad-view-body {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.bot-onboard-panel .primary-btn,
.bot-onboard-panel .secondary-btn {
  display: block;
  width: 100%;
  text-align: center;
  text-decoration: none;
  box-sizing: border-box;
}
.telegram-link-warning {
  margin: 0;
  font-size: var(--fs-md);
  line-height: 1.4;
  color: var(--text-dim);
}

.primary-btn {
  border: none;
  border-radius: 8px;
  padding: 0.5rem 1rem;
  font-weight: 700;
  cursor: pointer;
  background: var(--accent);
  color: var(--accent-text);
}

/* ── Team info panel (Профиль section's right column) ────── */
.team-info { display: flex; flex-direction: column; gap: 0.5rem; }
.team-info-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.6rem 0.7rem;
  background: var(--surface-alt);
  border-radius: 8px;
  font-size: var(--fs-body);
}
.team-info-row span { color: var(--text-dim); }
.team-info-row strong { font-family: inherit; color: var(--text); }
.team-info-link-btn {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  font-size: var(--fs-sm);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.team-info-link-btn:hover { color: var(--text); }
/* Above the "Осталось замен" row (user's ask, 2026-08-31: was inline next
   to the count, moved to its own line above), self-aligned right since
   the row below it is space-between. */
.team-info-history-btn { align-self: flex-end; }

.transfers-history-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.55rem 0.7rem;
  background: var(--surface-alt);
  border-radius: 8px;
  font-size: var(--fs-md);
}
.transfers-history-row + .transfers-history-row { margin-top: 0.4rem; }
.transfers-history-ts { color: var(--text-dim); font-size: var(--fs-sm); white-space: nowrap; }
.transfers-history-players { display: flex; align-items: center; gap: 0.4rem; overflow: hidden; text-overflow: ellipsis; }
.transfers-history-out { color: var(--text-dim); text-decoration: line-through; }
.transfers-history-arrow { color: var(--text-dim); }
.transfers-history-in { color: var(--text); font-weight: 600; }

/* #tabs-format's own row (.tab-row already handles the format buttons'
   layout) plus, as a sibling, #rating-row-controls - the date range and
   "Привести друга" that used to live inside the rating tables themselves
   (user's ask, 2026-08-24: move both OUT of the table containers, up
   into this same row). margin-left: auto pushes the controls to the
   row's right edge, leaving Classic/Daily flush left as before; flex-wrap
   so a narrow viewport drops them to a 2nd line instead of squeezing
   .tab-row's own overflow-x scroll. */
.format-row { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 0.6rem; }
.format-row #tabs-format { min-width: 0; margin-bottom: 0; }
/* No margin-left: auto here any more - С/ДО sits in normal flow right
   after Classic/Daily (left half of the row), staying left of the
   boundary with the Влияние table/left of "Рейтинг очков"'s own right
   edge (user's ask, 2026-08-24: "левее правой границы Рейтинга очков").
   Only .influence-rating-header (the invite button) still gets pushed to
   the row's far right, via its own margin-left: auto below. */
/* flex-basis: 100% forces this onto its own line below #tabs-format
   (.format-row keeps flex-wrap), then the 1fr/1fr grid here mirrors
   .workspace-columns' own column split exactly (same gap, same
   minmax(0,1fr) pair) so the period-controls' right edge lands flush on
   the boundary between the Рейтинг очков and Влияние tables below (user's
   ask, 2026-08-24: "правее, к границе рейтинга очков"), instead of
   approximating it inside the Classic/Daily row's leftover flex space. */
.rating-row-controls { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1.25rem; align-items: center; flex: 1 1 100%; padding-bottom: 0.6rem; }
/* "с"/"до" date range - same small-caps header look/size as .rating-table
   th (font family, size, letter-spacing), but lowercase rather than
   uppercase (user's ask, 2026-08-24) and right-aligned to this column's
   own edge (justify-self: end) rather than sitting flush left. */
.rating-period-controls {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  justify-self: end;
  font-size: var(--fs-xs);
  text-transform: lowercase;
  letter-spacing: 0.03em;
  color: var(--text-dim);
}
.rating-period-controls input[type="date"] {
  margin-left: 0.3rem;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-sm);
  text-transform: none; /* the input's own value shouldn't be forced uppercase, just the С/ДО labels */
}
/* Native <input type="date"> renders its VALUE in the browser's own UI
   locale (often American mm/dd/yyyy, unrelated to this page's lang="ru")
   and there's no property that lets CSS/JS change that text directly - so
   the value is made invisible (color: transparent, both on the input
   itself and Chrome's ::-webkit-datetime-edit pseudo-element that
   actually renders the digits) and a "5 сен 2026"-formatted span is laid
   on top of it instead (user's ask, 2026-09-07: inside the field, not
   next to it). pointer-events: none on the overlay lets every click/tap
   still reach the real input underneath and open its native picker; the
   calendar icon itself isn't covered since the overlay is left-aligned
   text over a wider input. */
.date-field { position: relative; display: inline-flex; align-items: center; }
/* iOS WKWebView ignores `color: transparent` on a date input's value, so
   the raw locale-formatted value bled through under the overlay on mobile
   (user bug report, 2026-09-08). -webkit-text-fill-color handles iOS;
   opacity:0 on the edit part handles every WebKit/Blink build. */
.date-field input[type="date"] {
  color: transparent;
  -webkit-text-fill-color: transparent;
}
.date-field input[type="date"]::-webkit-datetime-edit {
  color: transparent;
  -webkit-text-fill-color: transparent;
  opacity: 0;
}
.date-field-overlay {
  position: absolute;
  left: 0.7rem;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--text);
  font-size: var(--fs-sm);
  white-space: nowrap;
  text-transform: none;
}

/* Онлайн section's date selector (user's 2026-08-25 ask) - same input
   look as .rating-period-controls above, own row above the friends bar. */
.online-date-row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.6rem; font-size: var(--fs-md); color: var(--text-dim); }
.online-date-row input[type="date"] {
  margin-left: 0.4rem;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-md);
}

/* ── Rating table (Рейтинг section) ───────────────────────── */
/* min-width: max-content - without it, a table this wide (7 columns in
   the Влияние table since the Топ-100 column landed, 2026-08-25) just gets
   squeezed to fit its 100%-width container instead of triggering the
   wrapper's overflow-x:auto scroll, clipping header text like "Топ-100"
   (real bug, same day). Forcing the table to never shrink below its own
   content width makes the scrollbar the thing that appears instead. */
.rating-table { width: 100%; min-width: max-content; border-collapse: collapse; font-size: var(--fs-body); }
/* Admin panel's "Профиль ↗" deep link (site.js's highlightAdminTargetUser) -
   a brief fading flash, not a persistent style, so it reads as "here's the
   row you jumped to" rather than a permanent marker. */
@keyframes admin-target-flash {
  from { background: color-mix(in srgb, var(--accent) 35%, transparent); }
  to { background: transparent; }
}
.admin-target-highlight td { animation: admin-target-flash 4s ease-out; }
.rating-table th {
  text-align: left;
  color: var(--text-dim);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: 0;
  padding: 0 0.3rem 0.5rem;
  border-bottom: 2px solid var(--border);
}
/* Grouped "Очки" header (colspan over the За период/Вчера/За всё время
   sub-columns, user's ask, 2026-08-24) - centered over its span, unlike
   every other (left-aligned) header cell, and its own bottom border
   separates it from the sub-header row instead of sitting flush against
   the table body. */
.rating-table thead tr:first-child th[colspan] { text-align: center; border-bottom: 1px solid var(--border); padding-bottom: 0.3rem; }
/* :nth-child(2), not :last-child - the Влияние table (same .rating-table
   class) has only ONE header row, and :last-child would then match that
   single row too, wrongly right-aligning its "Игрок"/"Статус" etc. This
   only ever matches when a 2nd header row actually exists (the
   tournament table's За период/Вчера/За всё время sub-columns). */
.rating-table thead tr:nth-child(2) th { padding-top: 0.3rem; text-align: right; }
/* "Общий зачёт" has a 2-row header (grouped "Очки" + 4 sub-labels), the
   Влияние table only one row - pad the Влияние header up to the same
   height and drop its labels to the bottom so rank #1 sits on the exact
   same line in both columns (user's ask, 2026-09-08). Scoped by the
   container id so the tournament table is untouched. */
.rating-table thead th { vertical-align: bottom; }
#influence-rating-table thead th { padding-top: 1.35rem; }
.rating-table td { padding: 0.4rem; border-bottom: 1px solid var(--border); }
.rating-table td.rt-rank { color: var(--text-dim); font-family: inherit; width: 2rem; }
/* Sticky #/Игрок columns (user's ask, 2026-08-29: "чтобы при прокрутке
   рейтинга вправо, колонка с никами была всегда видна") - the wrapper
   (#rating-table/.influence-rating-table-wrap) already scrolls
   horizontally rather than letting cells wrap (see that rule's comment),
   which on a phone-width table with 7+ columns means the name scrolls
   straight off screen with nothing left to say whose row you're looking
   at. Both columns need an opaque background (var(--bg), matching the
   page background behind the table - neither the table nor its wrapper
   sets one of its own) or the scrolled-past cells behind them would show
   through. left: 2rem on .rt-name matches .rt-rank's own fixed width
   exactly, so the two form one continuous frozen block with no gap. */
.rating-table td.rt-rank, .rating-table th.rt-rank {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--bg);
}
.rating-table td.rt-name, .rating-table th.rt-name {
  position: sticky;
  left: 2rem;
  z-index: 1;
  background: var(--bg);
  white-space: nowrap;
}
.rating-table.rt-clickable tbody tr:hover td.rt-rank,
.rating-table.rt-clickable tbody tr:hover td.rt-name {
  background: var(--surface-alt);
}
.rating-table td.rt-points { font-family: inherit; font-weight: 700; text-align: right; color: var(--success); position: relative; }
/* Daily captain-bonus breakout: small violet "+X" (already counted in the
   sum, just made visible). Absolutely positioned so the green number keeps
   its right-aligned column position - the "+X" hangs off the cell's right
   edge into the inter-column gap instead of shifting the digits left. */
.rating-table td.rt-points .rt-cap {
  position: absolute; right: 3px; top: 2px; transform: translateX(100%);
  color: #7c3aed; font-size: 0.68em; font-weight: 700;
}
/* Только на правой (Влияние) таблице - раскрашивается через inline
   color-mix() per-row in site.js (rank-based grey->green gradient, best in
   the list is pure --success). Font/weight stay static here; only the
   color itself is set inline. */
.rating-table td.rt-influence { text-align: right; font-family: inherit; font-weight: 700; white-space: nowrap; }
/* Друзья/Верифик./Поддержка/Топ-100 (Влияние table) - numbers centered
   under their header, not left-aligned like a name column (user's ask,
   2026-08-25). */
.rating-table th.rt-center, .rating-table td.rt-center { text-align: center; }
/* Fantiq Pro perk (docs/subscription_pro.md's 3rd) - a tournament-rating
   row opens the owner's squad read-only (site.js's openSquadView). Only
   the left (tournament) table's rows are clickable - the right-hand
   Влияние table isn't squad-scoped at all, nothing to open there. */
.rating-table.rt-clickable tbody tr { cursor: pointer; }
.rating-table.rt-clickable tbody tr:hover { background: var(--surface-alt); }

/* Squad-view modal - same centered-overlay-with-backdrop shape as
   shared/player_detail.js's #player-detail, own smaller namespace since
   reusing that component would mean threading a "squad mode" through code
   built entirely around one player. Lower z-index than #player-detail (30)
   on purpose - a card tapped from inside this modal opens on top of it. */
#squad-view-modal {
  position: fixed;
  inset: 0;
  z-index: 25;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
#squad-view-modal.hidden { display: none; }
/* #transfers-history-modal reuses .squad-view-* classes for its INNER
   elements (backdrop/panel/header/body) but is its own top-level id, so it
   needs this same fixed-overlay/centering block itself, not just the
   shared inner classes - real bug, 2026-08-31: without this it rendered
   inline in normal document flow (pushed down after the footer) instead
   of as a centered overlay, since only #squad-view-modal (not a shared
   class) ever had position:fixed. */
#transfers-history-modal {
  position: fixed;
  inset: 0;
  z-index: 25;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
#transfers-history-modal.hidden { display: none; }
.squad-view-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.5); }
.squad-view-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 560px;
  max-height: 85dvh;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
}
.squad-view-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 1rem 2.6rem 0.8rem 1rem;
  border-bottom: 1px solid var(--border);
}
.squad-view-header h3 { margin: 0; font-size: var(--fs-base); }
.squad-view-header .club-logo-toggle { margin-left: auto; }
.squad-view-close-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 50%;
  background: var(--surface-alt);
  color: var(--text-dim);
  font-size: var(--fs-body);
  cursor: pointer;
}
.squad-view-body { overflow-y: auto; padding: 0.8rem 1rem 1.2rem; }

.influence-rating-col { min-width: 0; }
/* "Привести друга" button/reveal - now up in .rating-row-controls (moved
   2026-08-24, see that class's own comment), not next to this table any
   more; kept as its own class since site.js's wireInfluenceRatingInvite
   still selects it by these ids. */
.influence-rating-header { display: flex; align-items: center; justify-content: flex-end; gap: 0.6rem; min-width: 0; margin-left: auto; }
.influence-rating-header #influence-invite-btn { flex-shrink: 0; }
/* min-width: 0 lets the revealed link box actually shrink (its own
   .invite-link-text is flex:1 + ellipsis) instead of forcing the header -
   and the button beside it - wider than the column (real bug that hit the
   old side-by-side layout, 2026-08-20; same fix, new layout). */
.influence-rating-header .invite-link-reveal { min-width: 0; flex: 1 1 auto; margin-top: 0; }
/* Table cells/headers never wrap to a second line (real bug, 2026-08-24 -
   two-word level names like "Зал Славы" wrapped when the column got
   squeezed); horizontal scroll on the wrapper is the fallback for a
   viewport too narrow to fit every column, per CLAUDE.md's wide-content
   rule, rather than letting cell text wrap and break row height. */
#rating-table, .influence-rating-table-wrap { overflow-x: auto; }
/* Общий зачёт always fits the viewport - no horizontal scroller (owner
   2026-09-30): fixed layout, name column takes the slack and truncates. */
#rating-table { overflow-x: visible; }
#rating-table .rating-table { min-width: 0; table-layout: fixed; }
#rating-table .rt-col-rank { width: 2rem; }
#rating-table .rt-col-pts { width: 3.6rem; }
#rating-table .rating-table th, #rating-table .rating-table td { padding-left: 0.25rem; padding-right: 0.25rem; }
#rating-table .rating-table td.rt-name { overflow: hidden; text-overflow: ellipsis; }
#rating-table .rating-table td.rt-points { padding-right: 0.9rem; }
#rating-table .rating-table td.rt-points .rt-cap { transform: none; right: 1px; }
#rating-table .rating-table td.rt-lineup-link { text-decoration: underline dotted; text-underline-offset: 3px; }
/* "Общий зачёт"/"Рейтинг влияния" headings above the two Рейтинг tables
   (user's ask, 2026-08-25) - static markup (templates/site/index.html),
   not JS-rendered, since the table containers below get their innerHTML
   fully replaced on every load. */
.rating-table-title { margin: 0 0 0.5rem; font-size: var(--fs-lg); color: var(--text); font-weight: 700; }
#today-rosters-title { margin: 2rem 0 0.9rem; }
.rating-table th, .rating-table td { white-space: nowrap; }
/* Level-thresholds legend is a hover tooltip now (native `title`, set in
   site.js's influenceLevelsTooltip) instead of a visible list, so the
   two tables split a plain, perfectly symmetric 1fr/1fr - the boundary
   between them lands exactly on this box's own horizontal center
   ("контейнеры двух рейтингов должны быть симметричны относительно
   центральной оси сайта", user's ask, 2026-08-24). That center is only
   the PAGE's own center, though, if the box itself spans the page's full
   content width - hence max-width: none below (2nd ask, same message:
   "расширь обе таблицы рейтингов до границ кнопок разделов"), unlike
   every other .workspace-columns pair (Squad/Picker etc.), which stay
   capped at 60rem and centered narrower than the section buttons above
   them. */
#section-rating-content.workspace-columns { max-width: none; }
/* 0-3 tightly-packed, overlapping green checkmarks - one per completed
   verification method, same overlap look as Telegram's own double-
   checkmark read receipt (user's own spec, 2026-08-20). */
.verify-checkmarks { display: inline-flex; margin-left: 0.3rem; }
.verify-checkmark {
  color: var(--success);
  font-size: var(--fs-sm);
  font-weight: 700;
  margin-left: -0.32rem;
}
.verify-checkmark:first-child { margin-left: 0; }
/* Founder badge (entitlements.FEATURE_ROLLOUT's "founder_badge", 2026-08-25) -
   plain text in a purple outline pill, same inline-after-name placement as
   the checkmarks above and the same shape as .pro-badge/.beta-badge, just
   outlined instead of filled (purple isn't used anywhere else in the
   palette, so an outline reads as its own distinct tier rather than
   competing with the filled gold PRO pill). Fixed hex regardless of theme,
   same convention .pro-badge already uses. */
.founder-badge {
  display: inline-block;
  font-size: var(--fs-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: #a855f7;
  background: transparent;
  border: 1px solid #a855f7;
  border-radius: 4px;
  padding: 0.05rem 0.32rem;
  margin-left: 0.35rem;
  vertical-align: middle;
  position: relative;
  top: -0.1em; /* the outline pill's own box otherwise sits visually lower than the name text next to it (user's ask, 2026-08-25) */
}

/* ── Статистика (Pro-only, docs/subscription_pro.md) ──────────────── */
.stats-table-controls { display: flex; gap: 0.6rem; margin-bottom: 0.8rem; flex-wrap: wrap; }
/* Same styled look as .picker-filters select (Замены's own filter row) -
   was a bare unstyled browser <select>, user's ask, 2026-08-24. */
.stats-table-controls select {
  max-width: 12rem;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  padding: 0.45rem 0.6rem;
  font-size: var(--fs-body);
  cursor: pointer;
}
/* The one place this table is allowed to make the PAGE-level content
   overflow sideways (CLAUDE.md's UI layout priorities: the page itself
   must never scroll horizontally, a component's own content can) - 23
   stat columns plus name/team/position never fit a normal viewport, and
   dropping columns would defeat the point of "вся доступная стата". */
.table-scroll { overflow-x: auto; }
.stats-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); white-space: nowrap; font-family: var(--font-ui); }
.stats-table th, .stats-table td { padding: 0.4rem 0.55rem; text-align: right; border-bottom: 1px solid var(--border); }
.stats-table th {
  color: var(--text-dim);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  border-bottom: 2px solid var(--border);
  position: sticky;
  top: 0;
  background: var(--surface);
}
.stats-table-name { text-align: left; font-weight: 600; }
.stats-table th.stats-table-sortable { cursor: pointer; user-select: none; }
.stats-table th.stats-table-sortable:hover { color: var(--text); }
/* Small direction glyph, not a separate column - added via ::after so it
   never shifts the header's own text out of alignment with its column's
   data cells. */
.stats-table th.sorted-asc::after { content: " ▲"; color: var(--accent); }
.stats-table th.sorted-desc::after { content: " ▼"; color: var(--accent); }
/* Grouped 2-row header (2026-09-04, "Грузи туда всё" - new columns landed
   under group headers like "Шайбы по ситуациям"/"ТОИ/игру по ситуациям") -
   row 1's group cells get centered text + their own bottom border, same
   pattern .rating-table's own grouped header (tournament "Очки") already
   uses. :nth-child(2) rather than :last-child - a goalie table has no 2nd
   header row at all (renderStatsTableBody only builds one when at least
   one column declares a `group`), so :last-child would wrongly re-target
   the single row there. */
.stats-table thead tr:first-child th[colspan] { text-align: center; border-bottom: 1px solid var(--border); }
.stats-table thead tr:nth-child(2) th { padding-top: 0.3rem; }
/* Zebra rows (readability across 20+ columns, same ask) - at the ROW level,
   not per-cell: a `bar`-marked cell (renderStatsTableBody) sets its own
   inline background, which needs to keep winning over this. */
.stats-table tbody tr:nth-child(even) { background: color-mix(in srgb, var(--surface-alt) 55%, transparent); }
.stats-table tbody tr:hover { background: var(--surface-alt); }
/* Sticky #/Игрок columns on horizontal scroll - same reasoning and exact
   left offsets as .rating-table's own (see that rule's comment): a wide
   stat table scrolled sideways otherwise loses whose row you're looking
   at. Sits above the sticky header (z-index: 2 vs. plain `position:
   sticky` on th, which defaults to auto/0) so the two corner cells layer
   correctly. */
.stats-table td.rt-rank, .stats-table th.rt-rank {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--bg);
  color: var(--text-dim);
  font-family: "JetBrains Mono", monospace;
}
.stats-table td.stats-table-name, .stats-table th.stats-table-name {
  position: sticky;
  left: 2rem;
  z-index: 1;
  background: var(--bg);
}
.stats-table thead th.rt-rank, .stats-table thead th.stats-table-name { z-index: 3; }
.stats-table tbody tr:hover td.rt-rank, .stats-table tbody tr:hover td.stats-table-name {
  background: var(--surface-alt);
}

/* ── Онлайн section (experimental) ─────────────────────────────────
   Card grid, not columns/a flat list (product decision) - auto-fit so
   4-6 cards fit across a normal desktop width without overflow, and it
   degrades to 1-2 per row on narrow/mobile widths automatically rather
   than needing a separate mobile layout. */
.online-friends-bar { position: relative; margin-bottom: 1rem; }
.online-friends-bar input {
  width: 100%;
  padding: 0.55rem 0.75rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-body);
}
.online-search-results {
  position: absolute;
  top: calc(100% + 0.25rem);
  left: 0;
  right: 0;
  z-index: 5;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  max-height: 280px;
  overflow-y: auto;
}
.online-search-result-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-md);
  gap: 0.5rem;
}
.online-search-result-row:last-child { border-bottom: none; }
.online-search-result-row button {
  border: none;
  background: var(--accent);
  color: #fff;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  font-size: var(--fs-base);
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.online-search-result-row button:disabled { opacity: 0.4; cursor: not-allowed; }
.online-search-result-label { display: flex; flex-direction: column; min-width: 0; }
.online-search-result-meta { font-size: var(--fs-xs); color: var(--text-dim); font-weight: 500; }
.online-search-quota {
  padding: 0.35rem 0.75rem;
  font-size: var(--fs-xs);
  color: var(--text-dim);
  border-bottom: 1px solid var(--border);
}
.online-sports-url-row {
  display: flex;
  gap: 0.4rem;
  padding: 0.4rem 0.75rem 0.5rem;
  align-items: center;
}
.online-sports-url-row input {
  flex: 1;
  min-width: 0;
  padding: 0.4rem 0.5rem;
  font-size: var(--fs-sm);
}
.online-sports-url-row button {
  width: auto;
  height: auto;
  border-radius: 8px;
  padding: 0.4rem 0.7rem;
  font-size: var(--fs-sm);
}
.online-sports-url-error {
  padding: 0 0.75rem 0.5rem;
  font-size: var(--fs-sm);
  color: var(--danger, #c0392b);
}
.online-unlinked-tag {
  font-weight: 500;
  font-size: var(--fs-2xs);
  color: var(--text-dim);
}
/* auto-fill (not auto-fit) + a fixed upper bound (not 1fr) - see
   docs/ui_conventions.md's "don't stretch to fill the available width":
   1fr would let 2-3 cards balloon to 400-600px each on a wide desktop
   monitor once the grid runs out of room for another column. Leftover row
   space stays empty instead of being force-fed into existing cards. */
.online-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 240px));
  gap: 0.75rem;
}
.online-card {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0.75rem 0.85rem;
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  transition: box-shadow 150ms ease, transform 150ms ease;
}
.online-card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  transform: translateY(-2px);
}
.online-card-self { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
.online-card-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 700;
  font-size: var(--fs-sm);
  margin-bottom: 0.5rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--border);
}
/* flex: 1 pushes .online-card-total/.online-remove-friend-btn to the
   header's right edge without needing justify-content: space-between
   (which would've put the total in the middle with a 3rd child, the
   remove button, present). */
.online-card-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Today's total next to the nickname (user's ask, 2026-08-24). */
.online-card-total { color: var(--success); font-family: inherit; }
/* Season-to-date total (user's ask, 2026-09-08) - violet, sits just left
   of the green today total on 5PORT5 cards. */
.online-card-season { color: #7c3aed; font-family: inherit; }
.online-remove-friend-btn {
  border: none;
  background: none;
  color: var(--text-dim);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
  padding: 0 0.25rem;
}
.online-card-empty { color: var(--text-dim); font-size: var(--fs-md); }
.online-card-locked { cursor: pointer; }
/* Uniform row height, no wrapping (CLAUDE.md's UI layout priorities) -
   name gets whatever width the meta column doesn't need, ellipsis rather
   than a second line. */
.online-player-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.3rem 0;
  font-size: var(--fs-md);
  border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
}
.online-player-row:last-child { border-bottom: none; }
.online-player-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1 1 auto;
  min-width: 0;
}
.online-player-meta {
  flex: 0 0 auto;
  font-family: inherit;
  font-size: var(--fs-sm);
  color: var(--text-dim);  /* grey = provisional / match still running */
  white-space: nowrap;
}
.online-player-meta.final { color: var(--success); }  /* green = protocol final */
.online-player-meta.time { color: var(--text); }      /* white = future kickoff time */
/* sports.ru-vs-ours per-player season Δ (5PORT5, own card / admin). */
.online-sports-delta {
  flex: 0 0 auto;
  font-family: inherit;
  font-size: var(--fs-2xs);
  font-weight: 700;
  padding: 0 0.3em;
  border-radius: 4px;
  cursor: help;
  white-space: nowrap;
}
.online-sports-delta.pos { color: #7c3aed; background: color-mix(in srgb, #7c3aed 12%, transparent); }
.online-sports-delta.neg { color: var(--text-dim); background: color-mix(in srgb, var(--text-dim) 14%, transparent); }

/* "По командам" - every real KHL team playing that date, below Друзья.
   One panel per match, the two teams side by side (home left) with a
   clear vertical divider between them so home's points never butt up
   against away's surnames. Panels themselves tile: two matches (four
   teams) per row on a wide desktop, one per row on a phone. Each team is
   a single readable column - вратари, защитники, then нападащие stacked
   (a dashed rule before the forwards). */
.today-rosters-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(24rem, 1fr));
  gap: 0.75rem;
  margin-top: 0.5rem;
  max-width: 66rem;
}
.today-roster-card { max-width: none; }
.today-match-panel { padding: 0.5rem 0.7rem; }
.today-match-time {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  text-align: center;
  margin-bottom: 0.35rem;
}
.today-match-teams {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 1.25rem;
  align-items: start;
}
.today-match-teams > .trt-team + .trt-team {
  border-left: 1px solid var(--border);
  padding-left: 1.25rem;
}
.trt-team { min-width: 0; }
.trt-team-name {
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  margin-bottom: 0.2rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.trt-zones { display: flex; flex-direction: column; }
.trt-zone { min-width: 0; }
.trt-zone + .trt-zone {
  margin-top: 0.2rem;
  padding-top: 0.2rem;
  border-top: 1px dashed color-mix(in srgb, var(--border) 70%, transparent);
}
/* Tighter rows than the Друзья cards - this view is scan-many-teams -
   but still a readable size (user's ask, 2026-09-08). */
.today-match-panel .online-player-row {
  padding: 0.12rem 0;
  font-size: var(--fs-sm);
  gap: 0.4rem;
}
.today-match-panel .p-today-points { font-size: var(--fs-sm); }
@media (max-width: 640px) {
  .today-rosters-grid { grid-template-columns: 1fr; max-width: 34rem; }
}
@media (max-width: 430px) {
  .today-match-teams { grid-template-columns: 1fr; row-gap: 0.4rem; }
  .today-match-teams > .trt-team + .trt-team {
    border-left: 0;
    border-top: 1px solid var(--border);
    padding-left: 0;
    padding-top: 0.4rem;
  }
}
/* ── Приведи друга card (top of Настройки) ────────────────────────── */
.invite-card {
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 12%, transparent), transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
  border-radius: 10px;
  padding: 1rem 1.1rem;
  margin-bottom: 1.2rem;
}
.invite-card h3 { margin: 0 0 0.4rem; font-family: var(--font-display); font-size: var(--fs-base); }
/* Plain counter, replacing the old progress bar (2026-08-20) - see
   site.js's refreshInviteSection for why the bar never actually
   communicated real progress. */
.invite-counter { font-size: 2rem; font-weight: 700; line-height: 1; margin: 0 0 0.3rem; color: var(--accent); }
.invite-level { font-weight: 700; margin: 0 0 0.5rem; }
.invite-explanation { margin: 0 0 0.6rem; font-size: var(--fs-sm); color: var(--text-dim); }
.invite-explanation summary { cursor: pointer; font-weight: 600; }
.invite-explanation ul { margin: 0.4rem 0 0; padding-left: 1.2rem; }
.invite-explanation li { margin-bottom: 0.2rem; }
/* Настройки' Влияние card: both links shown right away, each directly
   under its own button (user's ask, 2026-08-24 - was a single shared
   reveal-on-click box for both). */
/* 2x2: site button/link left column, Telegram button/link right column
   (user's ask, 2026-08-24) - replaces the old stacked
   .invite-actions-col/.invite-action-row (button+link per method,
   methods stacked vertically). */
.invite-actions-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem 0.75rem; margin-top: 0.7rem; }
.invite-actions-grid .secondary-btn { width: 100%; }
/* Both buttons' Russian text ("Приведи друга на сайт"/"...в Telegram")
   is too long for a half-width column on a phone - same 820px mobile
   breakpoint as .workspace-columns above, collapsing back to one column
   (stacked site-then-Telegram) rather than letting the button text wrap
   mid-word. */
@media (max-width: 480px) {
  .invite-actions-grid { grid-template-columns: 1fr; }
}
/* position: relative is the positioning context for
   .invite-generated-hint-small's absolute placement (see that rule) -
   only the Рейтинг/Влияние header's reveal uses the hint span, so it's
   harmless on the other .invite-link-reveal uses (Настройки' two link
   rows). */
.invite-link-reveal { margin-top: 0.7rem; position: relative; }
/* Positioning context for .copied-badge's absolute placement above it
   (see that rule) - every link (Настройки' two, Рейтинг's one) is
   wrapped in one of these. */
.invite-link-line { position: relative; display: flex; align-items: center; gap: 0.6rem; }
.invite-link-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: left;
  background: color-mix(in srgb, var(--text) 6%, transparent);
  border: 1px dashed color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 8px;
  padding: 0.5rem 0.6rem;
  font-family: inherit;
  font-size: var(--fs-sm);
  color: var(--accent);
  cursor: pointer;
}
/* Floats above the link instead of sitting inline next to it (user's
   ask, 2026-08-24: "не двигает кнопку и ссылку") - .invite-link-line's
   own position: relative anchors it. */
.copied-badge {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 0.3rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--success);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--success);
  white-space: nowrap;
  box-shadow: 0 2px 6px color-mix(in srgb, black 20%, transparent);
}
.invite-generated-hint { margin: 0.5rem 0 0; font-size: var(--fs-sm); color: var(--text-dim); }
/* Floats above the link box instead of sitting in normal flow above it
   (real bug, 2026-08-24: it used to push .invite-link-line, its row, and
   the whole interface down whenever the link got generated) - same
   float-without-shift trick as .copied-badge above. */
.invite-generated-hint-small {
  position: absolute;
  bottom: 100%;
  left: 0;
  margin-bottom: 0.2rem;
  font-size: var(--fs-xs);
  color: var(--text-dim);
  white-space: nowrap;
}

/* ── Rules text / stats placeholder (full-width content pages) ─── */
.rules-text h2 { font-family: var(--font-display); font-size: var(--fs-base); margin: 2.25rem 0 0.85rem; line-height: 1.25; }
.rules-text h2:first-child { margin-top: 0; }
.rules-text h3 { margin: 1.6rem 0 0.7rem; line-height: 1.3; }
.rules-text p { margin: 0 0 1.05rem; line-height: 1.65; color: var(--text); }
.rules-text ul { margin: 0.15rem 0 1.35rem; padding-left: 1.3rem; }
.rules-text li { margin: 0.4rem 0; line-height: 1.55; }
.placeholder-block { text-align: center; color: var(--text-dim); padding: 3rem 1rem; }

.soon-badge {
  font-size: var(--fs-2xs);
  font-weight: 700;
  background: var(--surface-alt);
  color: var(--text-dim);
  border-radius: 6px;
  padding: 0.1rem 0.35rem;
  margin-left: 0.3rem;
  vertical-align: middle;
}

/* Settings' three groups (Интерфейс/Аккаунт/Верификация), each a top-level
   <details> - collapsed by default EXCEPT Верификация (site.js never sets
   its `open` attribute, only the template does, once, since the point is
   nudging an unfinished action, not remembering a UI preference the user
   might collapse right back). */
.settings-group-collapsible {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.15rem 1.05rem 0.2rem;
  margin-bottom: 1.35rem;
}
.settings-group-collapsible > summary.settings-group-title {
  margin: 0;
  padding: 1rem 0;
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
  cursor: pointer;
  list-style: none;
}
.settings-group-collapsible > summary.settings-group-title::-webkit-details-marker { display: none; }
.settings-group-collapsible > summary.settings-group-title::before { content: "▸ "; }
.settings-group-collapsible[open] > summary.settings-group-title::before { content: "▾ "; }
.settings-group-collapsible[open] > summary.settings-group-title { border-bottom: 1px solid var(--border); }
.settings-group-collapsible > .settings-section:first-of-type { margin-top: 1.15rem; }
.settings-group-collapsible > .settings-section:last-of-type,
.settings-group-collapsible > details.settings-section:last-of-type { margin-bottom: 1.15rem; }

.settings-section { margin-bottom: 1.35rem; }
.settings-section:last-of-type { margin-bottom: 0.85rem; }
@keyframes settings-highlight-pulse {
  0%, 100% { box-shadow: 0 0 0 0 transparent; background: transparent; }
  35% { box-shadow: 0 0 0 3px var(--accent), 0 0 22px color-mix(in srgb, var(--accent) 55%, transparent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
}
.settings-section.settings-highlight {
  border-radius: 12px;
  padding: 0.7rem 0.75rem;
  margin-left: -0.75rem;
  margin-right: -0.75rem;
  animation: settings-highlight-pulse 1.15s var(--ease-out) 3;
}
.settings-hint { margin: 0.45rem 0 0.45rem; color: var(--text-dim); font-size: var(--fs-md); line-height: 1.45; }
/* Collapsed by default (details has no "open" attribute) - the drag-order
   list was taking up the same amount of vertical space as everything else
   in Settings combined for a control most people set once and never touch
   again. */
details.settings-section summary.settings-hint {
  cursor: pointer;
  margin-bottom: 0;
  list-style: none;
}
details.settings-section summary.settings-hint::-webkit-details-marker { display: none; }
details.settings-section summary.settings-hint::before { content: "▸ "; }
details.settings-section[open] summary.settings-hint::before { content: "▾ "; }
details.settings-section[open] summary.settings-hint { margin-bottom: 0.6rem; }
details.settings-section #format-order-list { margin-top: 0; }

/* Single compact toggle (was a two-button row) - shows the current
   theme's own label/icon, click flips to the other. */
.theme-toggle-btn {
  display: inline-block;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 20px;
  background: var(--surface-alt);
  color: var(--text);
  font-size: var(--fs-md);
  cursor: pointer;
}

#format-order-list, #bio-field-order-list { list-style: none; margin: 0; padding: 0; }
.bio-field-checkbox-label { display: flex; align-items: center; gap: 0.4rem; cursor: pointer; }

/* Drag-and-drop reorder, not up/down buttons (user's "перетаскиванием"
   spec, 2026-08-18) - one compact row per format with a drag handle. */
.order-drag-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.6rem;
  background: var(--surface-alt);
  border-radius: 8px;
  margin-bottom: 0.3rem;
  cursor: grab;
  user-select: none;
}
.order-drag-item.dragging { opacity: 0.4; }
.order-drag-handle { color: var(--text-dim); font-size: var(--fs-body); line-height: 1; }

/* Same max-width + centering as .workspace-columns below it, so its right
   edge lines up with the subs (picker) column's own right edge - this bar
   used to span the full (wider) page width on its own, which pushed
   .budget-bar-actions' buttons out past where the narrowed workspace
   actually ends. */
.player-card.skill-mismatch,
.face-card.skill-mismatch,
.jersey-card.skill-mismatch {
  outline: 2px solid #f8696b;
  outline-offset: 2px;
  border-radius: 0.4rem;
}

.transfer-schedule-banner {
  max-width: 60rem;
  margin: 0 auto 0.75rem;
  padding: 0.75rem 1rem;
  border-radius: 0.5rem;
  border: 1px solid var(--success);
  background: color-mix(in srgb, var(--success) 12%, transparent);
  color: var(--text);
  font-size: var(--fs-md);
}

.nav-notice-row { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.nav-notice-left { flex: 0 1 auto; min-width: 0; }
.daily-rules-banner {
  flex: 1 1 16rem;
  margin: 0 0 0.6rem;
  padding: 0.55rem 0.9rem;
  border-radius: 0.5rem;
  border: 1px solid var(--success);
  background: color-mix(in srgb, var(--success) 12%, transparent);
  color: var(--success);
  font-size: var(--fs-md);
  line-height: 1.45;
}
.daily-rules-banner.hidden { display: none; }

.rules-table-wrap { overflow-x: auto; margin: 0.4rem 0 1.4rem; }
.rules-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--fs-body);
  border: 1px solid var(--line);
  border-radius: 0.6rem;
  overflow: hidden;
}
.rules-table--narrow { max-width: 28rem; }
.rules-table th, .rules-table td {
  padding: 0.55rem 0.8rem;
  text-align: left;
  vertical-align: top;
  line-height: 1.4;
  border-bottom: 1px solid var(--line);
}
.rules-table thead th { background: var(--surface-alt); font-weight: 700; }
.rules-table tbody th { font-weight: 600; white-space: nowrap; color: var(--text-dim); }
.rules-table tbody tr:last-child th, .rules-table tbody tr:last-child td { border-bottom: 0; }
.rules-scoring td:nth-child(2) { font-weight: 700; white-space: nowrap; }
.rules-scoring td:nth-child(3) { color: var(--text-dim); }
.rules-table .rt-pos { color: var(--success); }
.rules-table .rt-neg { color: #e5484d; }

.squad-skill-mismatch-banner {
  max-width: 60rem;
  margin: 0 auto 1rem;
  padding: 0.75rem 1rem;
  border-radius: 0.5rem;
  border: 1px solid #f8696b;
  background: rgba(248, 105, 107, 0.12);
  color: var(--text);
  font-size: var(--fs-body);
}
.squad-skill-mismatch-banner .squad-skill-mismatch-title {
  font-weight: 700;
  margin-bottom: 0.3rem;
}
.squad-skill-mismatch-banner ul {
  margin: 0;
  padding-left: 1.2rem;
}
.squad-skill-mismatch-banner .squad-skill-mismatch-note {
  margin-top: 0.4rem;
  font-size: var(--fs-sm);
  opacity: 0.8;
}

.budget-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  max-width: 60rem;
  margin: 0 auto 1rem;
  font-family: inherit;
  color: var(--text-dim);
  font-size: var(--fs-body);
}

.budget-bar #budget-remaining { color: var(--success); font-weight: 700; }
/* Капитан sits with the budget text on the LEFT (user's ask, 2026-09-05 -
   it's not a squad-editing action like Сброс/Автосборка/Сохранить, so it
   doesn't belong in that right-aligned cluster). */
.budget-bar-left { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
/* Grouped together (not spread across the bar via .budget-bar's own
   space-between, which would leave a gap between them) - space-between
   only separates this group from .budget-bar-left on the left. */
.budget-bar-actions { display: flex; align-items: center; justify-content: flex-end; gap: 0.5rem; flex-wrap: wrap; }
#autofill-btn { font-family: var(--font-ui); }
#captain-btn.active {
  border-color: var(--success);
  color: var(--success);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--success) 35%, transparent);
}

/* Primary action (accent-filled, unlike #autofill-btn's neutral
   .secondary-btn) - nothing reaches the server until this is pressed (see
   site.js's docstring on the draft/save model), so it's deliberately the
   most visually prominent control in the bar. Disabled (see site.js's
   updateSaveButton) whenever there's nothing unsaved or the squad still
   has an empty slot - the backend rejects the latter outright. */
#save-btn {
  font-family: var(--font-ui);
  border: none;
  border-radius: 8px;
  padding: 0.5rem 1rem;
  font-weight: 700;
  cursor: pointer;
  background: var(--accent);
  color: var(--accent-text);
}
#save-btn:disabled {
  background: var(--surface-alt);
  color: var(--text-dim);
  cursor: default;
}

.secondary-btn {
  background: var(--surface-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.5rem 1rem;
  cursor: pointer;
  transition: border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease;
}
.secondary-btn:hover:not(:disabled) {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  transform: translateY(-1px);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--accent) 12%, transparent);
}
.danger-btn { color: var(--ice-red); border-color: color-mix(in srgb, var(--ice-red) 45%, var(--border)); }

.hidden, [hidden], [data-ui-pending] { display: none !important; }

/* Section-switch fade: replaying a CSS animation on an element that just
   went from display:none to visible needs no JS - the browser restarts
   animation state whenever an element re-enters layout, so this alone
   smooths section-btn.js's plain class-toggle without touching site.js. */
#section-squad-content:not(.hidden),
#section-online-content:not(.hidden),
#section-rating-content:not(.hidden),
#section-stats-content:not(.hidden),
#section-settings-content:not(.hidden),
#section-rules-content:not(.hidden),
#section-manager-content:not(.hidden),
#area-predict-content:not(.hidden),
#area-news-content:not(.hidden) {
  animation: section-fade-in 220ms ease;
}
@keyframes section-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  #section-squad-content:not(.hidden),
  #section-online-content:not(.hidden),
  #section-rating-content:not(.hidden),
  #section-stats-content:not(.hidden),
  #section-settings-content:not(.hidden),
  #section-rules-content:not(.hidden),
  #section-manager-content:not(.hidden),
  #area-predict-content:not(.hidden),
  #area-news-content:not(.hidden) {
    animation: none;
  }
  .section-btn:hover, .tab-btn:hover, .toplevel-btn:hover, .secondary-btn:hover { transform: none; }
  .online-card:hover { transform: none; }
}

/* ── Workspace: view-mode rail, squad, picker.
   The rail is deliberately OUT of the grid that centers squad/picker - see
   .workspace-columns below for why - so .workspace itself is just a
   positioning context (position: relative) for the rail to sit in via
   absolute positioning, "by leftover principle": it takes only the width
   its own (now vertical, narrow) text needs, wherever that lands in the
   space .workspace-columns doesn't use, rather than claiming a dedicated
   grid track that would push the centered pair off-axis. */
.workspace {
  position: relative;
}

/* squad/picker: equal width (1fr/1fr) AND, as a pair, narrower than the
   full workspace and centered on it via margin: 0 auto - so the boundary
   between them lands exactly on #app's own horizontal center, independent
   of the view-mode rail's width (see .workspace's comment). minmax(0, 1fr),
   not bare 1fr: a bare 1fr track still sizes to its content's min-content
   width first and only THEN distributes leftover space, so a wide child
   (e.g. .picker-filters' selects, or a long jersey/face rink row) could
   force that column wider than its fair half and drag the boundary off-
   center; minmax(0, 1fr) pins the track's minimum to 0, so the fr unit
   alone decides the split and content overflows *inside* the column
   instead of resizing it. */
.workspace-columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
  max-width: 60rem;
  margin: 0 auto;
}

@media (max-width: 820px) {
  .workspace-columns { grid-template-columns: 1fr; max-width: none; }
}

/* View-mode selector (Лица / Список) - a narrow rail pinned to the
   workspace's left edge, sized to its own (vertical, see .tab-btn below)
   content rather than a fixed box - see .workspace's comment for why this
   is position: absolute instead of a grid column. */
.view-tabs-vertical {
  position: absolute;
  left: 0;
  top: 0;
  flex-direction: column;
  overflow-x: visible;
  width: auto;
}
/* Rotated via writing-mode (not a transform: rotate, which keeps the
   original horizontal box and just repaints the glyphs inside it) so the
   button's own layout box actually becomes narrow-and-tall instead of
   staying wide-and-short with sideways text drawn inside it. */
.view-tabs-vertical .tab-btn {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  width: auto;
  padding: 0.7rem 0.4rem;
  white-space: nowrap;
}

@media (max-width: 820px) {
  .view-tabs-vertical {
    position: static;
    flex-direction: row;
    margin-bottom: 0.6rem;
  }
  .view-tabs-vertical .tab-btn {
    writing-mode: horizontal-tb;
    width: auto;
  }
}

.workspace-col {
  background: color-mix(in srgb, var(--surface) 90%, transparent);
  backdrop-filter: blur(14px);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1rem;
  min-width: 0;
}

/* Halved to match #app's own mobile margin reduction below - otherwise
   the white card container's OWN padding still leaves a wide unused strip
   around the rows even after the page's outer margin shrinks. */
@media (max-width: 820px) {
  .workspace-col { padding: 0.5rem; }
}

/* Jersey/faces: the ice itself has no card chrome. The subs column keeps
   a plate — its names and stats sit on the arena and need contrast. */
.workspace--rink #squad-container.workspace-col {
  background: transparent !important;
  background-color: transparent !important;
  backdrop-filter: none;
  border: none;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}
.workspace--rink #picker-panel.workspace-col {
  background: color-mix(in srgb, var(--surface) 90%, transparent);
  backdrop-filter: blur(14px);
}
.workspace--rink .team-info {
  background: transparent;
  border: none;
}
.workspace--rink .team-info-row {
  background: transparent;
  border-radius: 0;
  padding: 0.35rem 0;
}

.line-title {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-dim);
  /* Each title is the first child of its group wrapper, so a
     :first-child rule used to zero every heading and leave Звено 2
     flush against the row above. Only the first group sits flush. */
  margin: 0.75rem 0 0.35rem;
}
#squad-container > :first-child > .line-title,
#squad-view-body > :first-child > .line-title { margin-top: 0; }

/* ── List view ─────────────────────────────────────────── */
/* .slot-list's gap (blocks visually separated, squad window) vs
   .picker-list's zero gap (blocks flush against each other, substitution
   window) is one of the three deliberate squad<->subs differences (the
   other two: .slot-row's grey background, and squad-only slot-position/
   .slot-remove) - see the shared .slot-row, .picker-row rule below, which
   is otherwise the SAME block shape (border, radius, padding, gap) in
   both windows. */
.slot-list { display: flex; flex-direction: column; gap: 0.4rem; }

/* Shared block shape for both windows - border/radius identical.
   .picker-row keeps the flat single-box shape (border/radius/padding/gap
   all on itself, no inner wrapper - it has nothing to swipe). .slot-row is
   now a THIRD deliberate squad<->subs difference on top of the two below
   (grey background, squad-only slot-position/.slot-remove): its visible
   content lives in a nested .slot-row-inner that a left-swipe can slide
   independently of the outer box, which stays put as the clipping frame
   revealing .slot-swipe-delete underneath (see that rule + site.js's
   wireSwipeToDelete). Padding/gap/background that used to sit directly on
   .slot-row moved onto .slot-row-inner unchanged - the two together are
   still pixel-identical to a plain .picker-row from the outside. */
.picker-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.12rem 0.5rem;
  cursor: pointer;
  background: color-mix(in srgb, var(--surface-alt) 92%, transparent);
}
.picker-row .player-card { min-height: 0; }

.slot-row {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden; /* clips .slot-row-inner to this box as it slides left */
}

.slot-row-inner {
  position: relative; /* .slot-remove anchors to this, not the outer .slot-row */
  display: flex;
  align-items: center;
  gap: 0.4rem; /* was 0.7rem - the LW/C/RW label + this gap + .slot-position's own width read as excessive dead space on desktop before the surname, see .slot-position's comment */
  padding: 0.12rem 1.7rem 0.12rem 0.5rem;
  /* Opaque on purpose: the red "Удалить" panel sits behind this box, and a
     translucent fill let it show through the price until the row was slid. */
  background: color-mix(in srgb, var(--surface-alt) 92%, var(--bg));
  cursor: pointer;
  touch-action: pan-y; /* vertical scroll stays native; JS claims horizontal drags only past its own direction-lock threshold */
}

.slot-row .player-card { min-height: 0; }
.slot-row.empty { color: var(--text-dim); border-style: dashed; }
.slot-row.selected { outline: 2px solid var(--accent); outline-offset: -1px; }
.slot-row.locked { opacity: 0.7; }
.slot-row.mgr-slot-pending { pointer-events: none; }
.slot-row.mgr-slot-pending .slot-row-inner { opacity: 0.35; }
.slot-row.mgr-slot-pending::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1.4rem;
  height: 1.4rem;
  margin: -0.7rem 0 0 -0.7rem;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: mgr-slot-spin 0.7s linear infinite;
}
@keyframes mgr-slot-spin {
  to { transform: rotate(360deg); }
}
/* Профиль section: no .slot-remove ("x") button at all, so there's no
   corner reserve to keep - back to the plain 0.5rem every side, exactly
   matching .picker-row. The card itself stays clickable even here: a tap
   opens the player-detail window, which is read-only anyway (see site.js's
   openPlayerDetail tap matrix). */
.slot-row.readonly .slot-row-inner { padding-right: 0.5rem; cursor: default; }

/* Revealed behind .slot-row-inner as it slides left - see
   site.js's wireSwipeToDelete. Not rendered for empty/readonly/locked
   slots at all (no swipe listener attached, see that function), so
   there's nothing to accidentally reveal there. */
.slot-swipe-delete {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 84px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #c33;
  color: #fff;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  pointer-events: none; /* purely visual - touchend on .slot-row-inner decides the outcome */
  cursor: pointer;
}

.slot-position {
  font-family: inherit;
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--text-dim);
  /* Was 1.8rem - on desktop (where this box, unlike mobile, isn't already
     fighting for width) that plus .slot-row-inner's gap and the stripe/
     skill-reserve after it added up to a wide dead zone before the surname
     even started (~65px in a ~440px-wide card, see the .player-card
     comment for the full breakdown). 1.3rem still comfortably fits the
     longest label ("LD"/"RD") without clipping. */
  width: 1.3rem;
  flex: 0 0 auto;
  text-align: center; /* centered in the box between the row's left edge and the club stripe, not hugging either side (product decision, 2026-08-18 - was right-aligned to hug the stripe) */
}

.slot-empty-label { flex: 1; color: var(--text-dim); font-style: italic; padding: 0 0.4rem; cursor: pointer; }

.slot-remove {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 18px;
  height: 18px;
  border: none;
  border-radius: 50%;
  background: rgba(214, 69, 69, 0.15);
  color: #c33;
  font-size: 11px;
  line-height: 18px;
  padding: 0;
  cursor: pointer;
}
.slot-remove.captain-choice {
  visibility: visible;
  background: color-mix(in srgb, var(--success) 18%, transparent);
  color: var(--success);
  font-weight: 800;
}
.slot-remove.captain-choice.selected {
  background: var(--success);
  color: #fff;
}

/* Shared player-card layout, named grid areas - ONE constant template,
   now identical at EVERY viewport and on all three surfaces (desktop site,
   mobile site, Mini App - see frontend/styles.css's matching rule):
     row 1: [stripe][name, left            ][stats][price]
     row 2: [stripe][time+opponent, center ][stats][price]

   The two season averages are ONE cell (.p-stats), not two competing grid
   columns. That's the 2026-08-17 rework: as two columns they permanently
   fought the name for width - and one of them ("Текущий") renders "–" for
   every player in the league until the new season has match data (see
   routers/players.py's avg_points_this_season), so half that width bought
   nothing. Stacked inside one right-aligned cell they read as a pair
   (прошлый above, dim and small; текущий below, bold - a visual hierarchy
   instead of a header legend, which the squad window and the Mini App don't
   have) and free the difference for the surname. The full picture per
   season/per match now lives in the player-detail window a tap away
   (player_detail.py, site.js's openPlayerDetail).
   Skills (LW/C/RW/... badges) are NOT a grid column at all - they're an
   absolutely-positioned overlay (see .p-skills below) tucked between the
   stripe and where the name/time TEXT starts (name/meta reserve constant
   left padding for this, whether or not skills actually render). This is
   what makes the stripe/name/skills relationship identical in every
   format: Classic just leaves that reserved padding empty, Pro/Daily fill
   it - nothing else on the row ever shifts. Used by both a filled squad
   slot (.player-card in .slot-row, "окно состава" - the squad window,
   left column) and a picker row (.player-card in .picker-row, "окно
   замен" - the substitution window, right column) - mirrors the Mini
   App's .slot-main/.player-row (frontend/app.js's playerCardHtml) so a
   player reads identically wherever it appears. The column header
   (site.js's renderPickerColumnHeader) reuses this exact same grid so its
   controls land exactly above the matching data column, by construction
   rather than by matching pixel values by hand. */
.player-card {
  display: grid;
  grid-template-columns: 8px 1fr 40px 46px 52px;
  grid-template-rows: auto auto;
  grid-template-areas:
    "stripe name  points stats price"
    "stripe meta  points stats price";
  position: relative;
  align-items: center;
  align-content: start;
  gap: 2px 8px;
  min-height: 42px;
  flex: 1;
  min-width: 0;
}

.mono { font-family: inherit; font-variant-numeric: tabular-nums slashed-zero; font-feature-settings: "tnum" 1, "zero" 1; }

.p-stripe {
  grid-area: stripe;
  align-self: stretch;
  width: 8px;
  border-radius: 3px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.p-stripe-edge, .p-stripe-middle { flex: 1 1 33.333%; }

/* Overlay, not a grid participant (see .player-card's comment) - anchored
   at the fixed x-offset where the name/meta column begins (8px stripe +
   8px column-gap), sitting inside that column's own reserved left
   padding rather than pushing it. */
.p-skills {
  position: absolute;
  left: 16px;
  top: 0;
  bottom: 0;
  width: 20px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  font-size: 10px;
  color: var(--text-dim);
  line-height: 1.15;
  pointer-events: none;
}

/* Flex wrapper (added 2026-08-21 for .p-popularity) rather than a single
   text node - the name itself still needs to shrink+ellipsis, while the
   popularity badge next to it (top row, right up against the "points"
   column - user's spec) never should. */
.p-name {
  grid-area: name;
  padding-left: 24px; /* reserved for .p-skills - constant across formats, see .player-card's comment */
  display: flex;
  align-items: baseline;
  gap: 4px; /* was 6px - tighter around the lock badge (user's ask 2026-09-08) */
  min-width: 0;
}
.p-name-text {
  font-family: var(--font-ui); /* same face as Лица (.face-label), not Unbounded */
  font-weight: 600;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 15px;
  line-height: 1.3;
  color: var(--text);
  min-width: 0;
  flex: 1 1 auto;
}
/* Official club abbreviation on the match-time row (row 2), left-aligned
   under the surname. It used to be an absolute overlay in the padding
   under .p-name, which landed it in the gap between the two rows.
   Color is whichever of the player's two stripe colors (set as
   --club-abbr-light/-dark inline, sorted by luminance in site.js) reads
   better on the page background - the lighter one by default (dark theme
   is the unthemed :root default, see this file's top), the darker one
   once html[data-theme="light"] applies. */
.p-club-abbr {
  flex: 0 0 auto;
  max-width: 4.5rem;
  font-family: inherit;
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.03em;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--club-abbr-light, var(--text-dim));
}
html[data-theme="light"] .p-club-abbr { color: var(--club-abbr-dark, var(--text-dim)); }
/* Shown instead of a club abbreviation when the player has no active
   NHL/KHL contract - e.g. no two-way deal, no chance of NHL games. */
.p-club-abbr.p-free-agent {
  max-width: none;
  font-style: italic;
  font-weight: 500;
  color: var(--text-dim);
}
/* % of this format's saved squads that include the player - same
   font-size/weight as .p-today-points ("такого же шрифта как набранные
   очки"), same color as the name ("цвет как у фамилии") rather than
   .p-today-points' own gradient color, which only makes sense for a
   per-match outcome, not a static pool-wide stat. */
.p-popularity {
  flex: 0 0 auto;
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
}

/* Mobile (<=820px, the same breakpoint the workspace collapses to one
   column at): reclaim horizontal width from every column that isn't the
   name, rather than letting the name wrap. A wrapped second line grows
   THAT row's height only (grid rows are "auto") while every other row
   stays at its single-line height - rows visibly jumping in height as you
   scroll past whichever one happened to wrap is worse than a rare
   ellipsis, so this reverts to nowrap+ellipsis (the base .p-name rule,
   no override needed) and instead narrows everything around it:
   stat-last/stat-this stack into one column right next to price (already
   true before this comment existed), that column itself narrows further,
   grid/flex gaps tighten, and .slot-position shrinks - freeing enough
   width in practice that ellipsis should be the rare exception, not
   .p-name's earlier "always wraps instead" stance. */
@media (max-width: 820px) {
  /* The GRID is no longer overridden here - .player-card's template is one
     constant shape at every width now that the two season averages share a
     single cell (see that rule). All that's left to reclaim on a phone is
     spacing: tighter gaps and a slightly narrower stats/price pair. */
  .player-card {
    grid-template-columns: 8px 1fr 34px 40px 46px;
    gap: 2px 4px; /* tighter column-gap than desktop's 8px - hugs the stats block against price */
    min-height: 40px;
  }
  .p-stat-this { font-size: 14px; }

  /* padding-right stays at the same reserve as desktop (1.7rem, see the
     base .slot-row rule) - NOT shrunk here. A previous version of this
     rule used the "padding: 0.4rem" shorthand, which also zeroed
     padding-right and let .slot-remove's absolutely-positioned corner
     button sit directly on top of the price text. */
  /* Left edge -> stripe distance ~halved (was ~34px: 6.4px padding + 20.8px
     label box + 7.2px gap) by tightening the two purely-empty contributors
     (left padding, the label->stripe gap) and trimming the label box down
     to what its own text actually needs at the SAME font-size (0.72rem
     JetBrains Mono, 2 characters ≈ 14px) rather than shrinking that font -
     ~21px now. */
  .slot-row-inner { gap: 0.15rem; padding: 0.12rem 1.7rem 0.12rem 0.15rem; }
  /* Centered in the box, same as the base rule (product decision,
     2026-08-18) - was left-aligned here specifically since the base
     rule's right-align hugged the stripe and this mobile layout's
     left padding was thin enough that "hug the stripe" vs "hug the
     border" read as two visibly different positions; centering makes
     that distinction moot. */
  .slot-position { width: 1rem; font-size: var(--fs-xs); text-align: center; }

  /* Only the column WIDTHS still differ here - the stacked "двухэтажная"
     header shape moved into the base .picker-column-header rule, since the
     card it sits above now has that same shape at every width. Kept in
     lockstep with .player-card's mobile columns above, deliberately: the
     header is pinned to the data grid's template, so the two must change
     together or every label drifts off its column. */
  .picker-column-header {
    grid-template-columns: 8px 1fr 34px 40px 46px;
    gap: 2px 6px;
  }
}
/* Tablet: same card shape as the phone (one stacked stats cell), just less
   cramped once there is spare width. */
@media (min-width: 481px) and (max-width: 820px) {
  .player-card,
  .picker-column-header {
    grid-template-columns: 8px 1fr 48px 56px 64px;
    gap: 4px 12px;
  }
  .player-card { min-height: 48px; }
  .p-stat-this { font-size: 16px; }
  .slot-position { width: 1.35rem; }
}

/* Fixed 2-column grid (skull | time) instead of a centered flex row - a
   flex row re-centers its whole content whenever the skull span appears or
   disappears, which visibly shifted the time text horizontally at the
   exact moment it mattered most (a match starting). The skull column is
   reserved at a constant width whether or not its span is actually present
   (a plain length in grid-template-columns always reserves that track,
   empty or not), so the time column's position never moves regardless of
   what's around it. Today's points used to be a 3rd column HERE, sharing
   this one thin row (meta/row 2) with skull+time - on a narrow card the
   combined content could exceed the available width, and since the row is
   centered + overflow: hidden, BOTH ends (skull AND points) clipped
   symmetrically while only the time text in the middle survived (real bug
   on the Mini App, e.g. a goalie's "-25" - reported 2026-08-19, same
   underlying grid shared with .slot-main/.player-row there). Points now
   lives in its own full-height column (.p-today-points, "points" grid area
   below), vertically centered like .p-stats/.p-price, with no row-2 width
   competition at all. */
.p-meta {
  grid-area: meta;
  padding-left: 24px; /* same reserve as .p-name, see .player-card's comment */
  display: flex;
  align-items: center;
  column-gap: 6px;
  line-height: 1.3;
  overflow: hidden; /* skull sits alongside the time text now, not instead of it - clip rather than wrap, so a long combination never grows row 2's height and drags the whole card taller (see .player-card's comment on why wrapping is avoided) */
  white-space: nowrap;
}
/* Skull | time, centered in the space left of .p-club-abbr. The skull
   column stays reserved whether or not its span is present, so the time
   text does not shift when a skull or goalie icon appears. */
.p-meta-match {
  display: grid;
  grid-template-columns: 16px auto;
  justify-content: center;
  align-items: center;
  column-gap: 4px;
  flex: 1 1 auto;
  min-width: 0;
}
/* grid-row: 1 pins the icon to the first row: a second icon can never open a new row and stretch the card. */
.p-time-skull { grid-column: 1; grid-row: 1; justify-self: center; }
.p-time-value { grid-column: 2; font-size: 12px; color: var(--text-dim); justify-self: center; }
.p-time-value.soon { color: #a86400; }
.p-time-value.locked { color: #c33; }
.p-time-value.upcoming { color: var(--text-dim); }
.p-time-skull { font-size: 12px; }
/* Goalie net/bench icons share .p-time-skull's grid slot but read too
   small at the skull's own size (user's ask, 2026-08-27) - bumped up
   without touching the skull itself. */
.p-goalie-icon { font-size: 18px; }
/* Own column ("points" grid area), spans both rows, vertically centered -
   same treatment as .p-stats/.p-price. Bare number, no "очков" word. Color
   comes from site.js's todayPointsColor() as an inline style (a gradient
   can't be expressed as a fixed CSS color), not from here. */
.p-today-points {
  grid-area: points;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  font-weight: 700;
}

/* One cell, two stacked numbers, both flush right against price (see
   .player-card's comment). Which is which is carried by weight/size/colour
   rather than a label: прошлый сезон is the small dim one on top, текущий
   the bold one below - the same order the picker's stacked column header
   above them uses, and the same order the Mini App uses where there is no
   header at all. */
.p-stats {
  grid-area: stats;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  line-height: 1.2;
  min-width: 0; /* a flex item's default min-width is "auto" (content width), which let a wide number ignore the 40px stats track and spill into .p-price - real bug, reported 2026-08-20 */
  overflow: hidden;
}
.p-stat-last, .p-stat-this {
  max-width: 100%;
  overflow: hidden;
  text-overflow: clip; /* a numeric value truncated with "…" reads as a different, shorter number - clip (not ellipsis) is the honest failure mode if a value is ever still too wide */
  white-space: nowrap;
}
.p-stat-last { font-size: 11px; color: var(--text-dim); }
.p-stat-this { font-size: 15px; font-weight: 600; color: var(--text); }
/* A missing value drops back to the small dim treatment even in the bold
   "текущий" row - otherwise the largest, heaviest thing on the card is a
   dash carrying no information, which is exactly what every player looks
   like until the new season has match data (routers/players.py's
   avg_points_this_season). */
.p-stat-dash { font-size: 11px; font-weight: 400; color: var(--text-dim); }

/* Spans both grid rows (not just the "price" area's own row 2) and sized
   up - applies identically in .slot-row and .picker-row now that both
   render price inside .player-card the same way (site.js's renderSlotList
   docstring): neither row has anything else competing for that space
   (squad's .slot-remove is a corner overlay outside the card, not a grid
   participant), so price always gets the full row height. */
/* Dark gray, not green (user's own call, 2026-08-19) - price is
   informational, not a status to react to, unlike today's points'
   maroon-to-green/purple gradient, so it deliberately stays neutral rather
   than competing for attention or implying "good". */
.p-price {
  grid-area: price;
  font-family: var(--font-ui); /* same face and size as Лица .face-price */
  font-weight: 700;
  font-size: var(--fs-body);
  line-height: 1.05;
  text-align: right;
  color: var(--text-dim);
}
.picker-row .p-stripe,
.slot-row .p-stripe {
  align-self: center;
  height: 68%;
}

#squad-container.squad-on-ice {
  background: transparent !important;
  background-color: transparent !important;
  border: none !important;
  box-shadow: none !important;
  padding: 0 !important;
  border-radius: 0 !important;
}

/* Ice fills this box; the PNG's transparent frame is cropped (not the
   boards). Bbox of opaque pixels: 89,106 – 851,1566 in 941×1672. */
.rink {
  position: relative;
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 0;
  min-height: 0;
  overflow: visible;
  aspect-ratio: 762 / 1460;
}
.rink-art {
  position: absolute;
  width: calc(941 / 762 * 100%);
  height: calc(1672 / 1460 * 100%);
  left: calc(-89 / 762 * 100%);
  top: calc(-106 / 1460 * 100%);
  max-width: none;
  pointer-events: none;
  background: transparent;
}
/* Daily: bottom half only. PNG y=850 is the top of the red/white center
   line; ice ends at y=1566. */
.rink--daily {
  aspect-ratio: 762 / 717;
  overflow: hidden;
}
.rink--daily .rink-art {
  height: calc(1672 / 717 * 100%);
  top: calc(-850 / 717 * 100%);
}
.rink--daily .rink-slots {
  /* Line sits under the center stripe; G in the crease. */
  padding: 10% 7.5% 2%;
  --rink-daily-skaters-down: 24px;
}
.rink--daily .rink-lines {
  transform: translateY(var(--rink-daily-skaters-down));
}
.rink--daily .face-circle,
.rink--daily .jersey-shirt {
  width: 96px;
  height: 96px;
  font-size: 2.4rem;
}
.rink--daily .jersey-card,
.rink--daily .face-card {
  min-width: 126px;
}
.rink--daily .jersey-label,
.rink--daily .face-label {
  font-size: 1.16rem;
}
.rink--daily .jersey-price,
.rink--daily .face-price {
  font-size: var(--fs-base);
}
.rink--daily .rink-row--defense {
  min-height: 8.1rem;
}
.rink-slots {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 6.2% 7.5% 1%;
  box-sizing: border-box;
  /* Offsets from the rink midline. % here is of the ice row, NOT the
     card — do not put these inside transform:translate (that % is the
     card width, so a long name would shove the circle). */
  --rink-wing-from-center: 33%;
  --rink-d-from-center: 20%;
  --rink-wing-up: 30px;
  --rink-lower-lines-down: 30px;
}

.rink::before {
  display: none;
}

.rink-lines {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  gap: 1.15rem;
  min-height: 0;
}

.rink-line {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 0.38rem;
}
.rink-lines > :nth-child(n+2) {
  transform: translateY(var(--rink-lower-lines-down));
}

.rink-row {
  display: flex;
  justify-content: center;
  gap: 0.7rem;
}
/* C (and only C) lives in normal flow so the row height is the card.
   Wings are parked on the midline ± --rink-wing-from-center; translateX(-50%)
   is the CARD centre, which is the 64px circle because the label is
   centered under it — name length cannot shift the token. */
.rink-row--forwards {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: start;
  gap: 0;
  padding: 0;
}
.rink-row--forwards > :nth-child(1),
.rink-row--forwards > :nth-child(3) {
  position: absolute;
  top: calc(-1 * var(--rink-wing-up));
  z-index: 1;
  transform: translateX(-50%);
}
.rink-row--forwards > :nth-child(1) {
  left: calc(50% - var(--rink-wing-from-center));
}
.rink-row--forwards > :nth-child(3) {
  left: calc(50% + var(--rink-wing-from-center));
}

.rink-row--defense {
  position: relative;
  display: block;
  min-height: 5.4rem;
  padding: 0;
}
.rink-row--defense > :nth-child(1),
.rink-row--defense > :nth-child(2) {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
}
.rink-row--defense > :nth-child(1) {
  left: calc(50% - var(--rink-d-from-center));
}
.rink-row--defense > :nth-child(2) {
  left: calc(50% + var(--rink-d-from-center));
}

.rink-goalie-end {
  display: flex;
  justify-content: center;
}

/* ── Jersey cards ──────────────────────────────────────── */
.jersey-card {
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  min-width: 84px;
  width: max-content;
  overflow: visible;
}

.jersey-shirt {
  position: relative;
  width: 64px;
  height: 64px;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  border: 3px solid #12304a;
  box-sizing: border-box;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0,0,0,0.4);
}

.jersey-shirt.empty {
  background: rgba(18, 48, 74, 0.12);
  border-color: #3d5a73;
  border-style: dashed;
  color: #12304a;
  font-size: 1.6rem;
}

.jersey-shirt.selected { outline: 3px solid var(--accent); outline-offset: 2px; }

.jersey-label {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.22rem;
  font-size: var(--fs-base);
  font-weight: 700;
  color: #000;
  width: max-content;
  max-width: none;
  white-space: nowrap;
  padding: 0 0.1rem;
  line-height: 1.05;
  margin-top: calc(-0.12rem + 1px);
  text-shadow: 0 0 3px #fff, 0 0 6px #fff;
}
.jersey-label .rink-name {
  overflow: visible;
  white-space: nowrap;
  color: #000;
  font-weight: 700;
}
.jersey-price {
  font-family: inherit;
  font-size: var(--fs-body);
  color: #000;
  font-weight: 700;
  flex: none;
  text-shadow: 0 0 3px #fff, 0 0 6px #fff;
}

/* ── Face cards ────────────────────────────────────────── */
.face-card {
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  min-width: 84px;
  width: max-content;
  overflow: visible;
  position: relative;
}
.jersey-card { position: relative; }
/* Today-status badge on a rink card (💀 / ❌ / 🥅 / 🪑) - top-right of the
   circle/jersey, so it reads without hiding the crest. */
.rink-status {
  position: absolute;
  top: -2px;
  right: -2px;
  font-size: 14px;
  line-height: 1;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.5));
  pointer-events: none;
}
.rink-status:hover { pointer-events: auto; }

.face-circle {
  position: relative;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.6rem;
  color: #fff;
  border: 3px solid #12304a;
  box-sizing: border-box;
  /* was hidden - visible so the bottom-corner lock/points badges aren't
     clipped by the circle; the photo is rounded on the <img> itself. */
  overflow: visible;
}
.face-circle img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  border-radius: 50%;
}
.face-initials {
  pointer-events: none;
  line-height: 1;
}

.face-circle.empty {
  background: rgba(18, 48, 74, 0.12);
  border-color: #3d5a73;
  border-style: dashed;
  color: #12304a;
  font-size: 1.6rem;
}

.face-circle.selected { outline: 3px solid var(--accent); outline-offset: 2px; }

.face-label {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.22rem;
  font-size: var(--fs-base);
  font-weight: 700;
  color: #000;
  width: max-content;
  max-width: none;
  white-space: nowrap;
  padding: 0 0.1rem;
  line-height: 1.05;
  margin-top: calc(-0.12rem + 1px);
  text-shadow: 0 0 3px #fff, 0 0 6px #fff;
}
.face-label .rink-name {
  overflow: visible;
  white-space: nowrap;
  color: #000;
  font-weight: 700;
}
.face-price {
  font-family: inherit;
  font-size: var(--fs-body);
  color: #000;
  font-weight: 700;
  flex: none;
  text-shadow: 0 0 3px #fff, 0 0 6px #fff;
}

/* Замены: hover the rink token to get the same "Удалить" affordance the
   list row reveals with a swipe / ✕. Click is the confirm. */
.rink-remove {
  position: absolute;
  inset: 0;
  border: none;
  padding: 0;
  margin: 0;
  border-radius: 50%;
  background: rgba(196, 24, 32, 0.62);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
  z-index: 2;
}
.rink-remove::before,
.rink-remove::after {
  content: "";
  position: absolute;
  left: 22%;
  right: 22%;
  top: 50%;
  height: 4px;
  margin-top: -2px;
  background: #fff;
  border-radius: 1px;
}
.rink-remove::before { transform: rotate(45deg); }
.rink-remove::after { transform: rotate(-45deg); }
.rink-remove.captain-choice {
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--success) 78%, transparent);
  color: #fff;
  font: 600 1.35rem/1 var(--font-ui);
}
.rink-remove.captain-choice.selected {
  opacity: 1;
  pointer-events: auto;
  background: var(--success);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--success) 35%, transparent);
}
.rink-remove.captain-choice::before,
.rink-remove.captain-choice::after { display: none; }
.jersey-card:hover .rink-remove,
.face-card:hover .rink-remove {
  opacity: 1;
  pointer-events: auto;
}

/* ── Picker panel (always visible, right column) ──────────── */
.picker-panel-header {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 0.7rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--border);
}

.picker-slot-nav { display: flex; align-items: center; gap: 0.5rem; position: relative; }
.picker-slot-nav .club-logo-toggle {
  position: absolute;
  right: 1.65rem;
  top: 50%;
  transform: translateY(-50%);
}
/* Small "?" circle, absolutely positioned to the right edge (user's ask,
   2026-08-24) rather than a 3rd flex child - the row's arrows are hidden
   for everyone but beta testers (see .nav-btn below), so most of the time
   this would be the ONLY thing balancing .picker-slot-label's flex:1
   centering, and an extra flex child pushes that center point left. This
   overlays instead, so centering is exactly what it was before this
   button existed. */
.picker-hint-btn {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  flex: 0 0 auto;
  width: 1.3rem;
  height: 1.3rem;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  color: var(--text-dim);
  font-size: var(--fs-2xs);
  font-weight: 700;
  line-height: 1;
  cursor: help;
  padding: 0;
}
/* Hidden for everyone except beta testers (is_experimental_tester) - user's
   correction, 2026-08-20 ("убери для всех кроме beta-тестеров", the
   opposite of this rule's first version, which showed them to everyone
   BUT beta). See site.js's main(), docs/entitlements.md. */
.picker-slot-nav .picker-slot-label { flex: 1; text-align: center; }

.nav-btn {
  flex: 0 0 auto;
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  color: var(--text);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
}

.picker-slot-label {
  flex: 1;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-body);
  color: var(--text);
}

#picker-search {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  padding: 0.5rem 0.7rem;
}

/* Picker filter row: Поиск 40% / Статус 30% / Команда 30%. */
/* Position chips, multi-select. Daily is Нап/Защ/Вр; Classic and Elite
   are the six skills. None active = whole pool; tap a chip to narrow;
   × clears back to all. */
.picker-positions-row {
  display: flex;
  align-items: flex-start;
  gap: 0.35rem;
  margin-bottom: 0.45rem;
}
.picker-positions-row .picker-positions {
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
}
.picker-pos-clear {
  flex-shrink: 0;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface-alt);
  color: var(--text-dim);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
}
.picker-pos-clear:hover { border-color: var(--accent); color: var(--text); }
.picker-positions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.picker-pos {
  flex: 1 1 0;
  min-width: 2.4rem;
  padding: 0.28rem 0.4rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-alt);
  color: var(--text-dim);
  font: inherit;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
}
.picker-pos[aria-pressed="true"] {
  color: var(--text);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 22%, var(--surface));
}
.picker-filters {
  display: grid;
  grid-template-columns: 4fr 3fr 3fr;
  gap: 0.5rem;
}
.picker-filters #picker-search { min-width: 0; } /* background/border/padding already come from the general #picker-search rule above */
/* max-width caps it well short of #app's 1200px on a real desktop monitor
   (docs/ui_conventions.md's "don't stretch to fill the available width") -
   width: 100% is kept so it still shrinks to fit a narrow container. */
.settings-select {
  width: 100%;
  max-width: 22rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  padding: 0.45rem 0.6rem;
  font-size: var(--fs-body);
}
.inline-input-row .settings-select { flex: 1; min-width: 0; margin-bottom: 0; width: auto; max-width: none; }
.picker-filters select {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  padding: 0.4rem 0.5rem;
  min-width: 0;
}

.price-range {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding-top: 0.2rem;
}
.price-range-label { font-size: var(--fs-lg); font-weight: 700; color: var(--text); }
.price-range-track { position: relative; width: 100%; height: 24px; }
.price-range-track-bg {
  position: absolute;
  left: 0; right: 0; top: 50%;
  height: 4px;
  transform: translateY(-50%);
  background: var(--border);
  border-radius: 2px;
}
.price-range-track input[type="range"] {
  position: absolute;
  left: 0; right: 0; top: 50%;
  transform: translateY(-50%);
  width: 100%;
  margin: 0;
  background: none;
  pointer-events: none;
  -webkit-appearance: none;
  appearance: none;
}
.price-range-track input[type="range"]::-webkit-slider-runnable-track { background: transparent; }
.price-range-track input[type="range"]::-moz-range-track { background: transparent; }
.price-range-track input[type="range"]::-webkit-slider-thumb {
  pointer-events: auto;
  -webkit-appearance: none;
  appearance: none;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--surface);
  cursor: pointer;
}
.price-range-track input[type="range"]::-moz-range-thumb {
  pointer-events: auto;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--surface);
  cursor: pointer;
}

/* Column headers - pinned to the SAME grid template as .player-card (see
   that rule's comment) so each label sits directly above its actual data
   column instead of an independent equal-share row (an earlier version of
   this rule deliberately avoided that pinning - see git history - but the
   mismatch it caused between header and data columns was worse than the
   crowding that motivated it). "Играющие" has no data column of its own
   (it's a day filter, not a stat) - it shares the "name" column's cell
   with the "Игрок" sort button (see .col-header-name), same as the name
   text and skills share that column's space one row down. */
.picker-column-header {
  display: grid;
  grid-template-columns: 8px 1fr 40px 46px 52px;
  grid-template-rows: auto auto;
  align-items: center;
  gap: 2px 8px;
  /* Left/right 0.5rem matches .picker-row's own padding exactly (see the
     shared .slot-row, .picker-row rule) - .picker-row's .player-card sits
     inset by that padding, so without matching it here the header's grid
     columns started flush at x=0 while the data columns started 0.5rem to
     the right of it, throwing every column out of alignment by that
     amount despite using the identical column template. */
  padding: 0 0.5rem 0.5rem;
  border-bottom: 2px solid var(--border);
  margin-bottom: 0.3rem;
}

.col-header-name {
  grid-column: 2;
  grid-row: 1 / 3;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-width: 0;
}
.col-header-name > * { min-width: 0; }
.col-header-name .play-filter-select {
  flex: 1 1 0;
  min-width: 0;
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

.col-sort-btn {
  min-width: 0;
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: var(--fs-xs);
  color: var(--text-dim);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.col-sort-btn.col-name { text-align: left; }
/* Прошлый/Текущий stack in ONE column, directly above the two numbers of
   .p-stats they sort by (the mobile layout the user signed off on, now the
   layout at every width - see .player-card's comment). overflow: visible
   (not the base rule's hidden+ellipsis) is deliberate - the label must
   never be truncated, so it spills into the wide "name" gutter beside it
   instead of clipping. text-align: right (not center, despite stacking one
   per row) - .p-stats below it is right-aligned against price
   (align-items: flex-end), and a CENTERED label with symmetric overflow
   bleeds equally in both directions, which on a narrow 40px column reached
   past the 8px column-gap into "Цена" itself (real bug, found on every
   viewport, not just mobile - 2026-08-19). Right-aligning pins the label's
   right edge at the column's own right edge, so all of its overflow spills
   left into the wide gutter and none of it can ever touch the price
   column, matching where .p-stats' own numbers sit one row down. */
.col-sort-btn.col-stat-last,
.col-sort-btn.col-stat-this {
  grid-column: 4;
  text-align: right;
  /* Nudges the right-anchor point left of the column's true right edge
     (user's ask, 2026-08-24 - "подвинь левее") without giving up the
     right-align itself, which the comment above still needs (keeps
     overflow spilling away from "Цена", not into it). */
  margin-right: 0.35rem;
  overflow: visible;
  text-overflow: clip;
}
.col-sort-btn.col-stat-last { grid-row: 1; }
.col-sort-btn.col-stat-this { grid-row: 2; }
.col-sort-btn.col-price { grid-column: 5; grid-row: 1 / 3; text-align: right; }
.col-sort-btn:hover { color: var(--text); }
.col-sort-btn.active { color: var(--accent); }
.col-sort-btn .sort-arrow { font-size: var(--fs-2xs); margin-left: 1px; }

.play-filter-select {
  min-width: 0;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  padding: 0.15rem 0.2rem;
  cursor: pointer;
}
.play-filter-select.active { color: var(--accent); border-color: var(--accent); }

/* Zero gap between rows (the other deliberate squad<->subs difference,
   see .slot-list's comment) - .picker-row's own block shape is the shared
   .slot-row, .picker-row rule above. */
/* ~10 rows in view before scrolling (user's ask, 2026-08-24 - was 60vh,
   only fit ~7-8 on a typical laptop viewport). Vertical padding on
   .picker-row is tighter than the squad rows (2026-09-22) so more
   surnames fit; min() keeps the list from overflowing a short viewport. */
.picker-list { max-height: min(75dvh, 580px); overflow-y: auto; }

.picker-row:hover { background: var(--surface-alt); }
/* Dimmed, but still clickable: a locked player can't be picked (site.js's
   renderPickerList attaches no pick handler) yet must still open his
   detail window while browsing - pointer-events: none used to swallow that
   tap too. */
.picker-row.locked { opacity: 0.5; }
.picker-row.taken { opacity: 0.35; pointer-events: none; }

/* Locked player in Замены (deadline passed - can't sell/buy him): a big
   lock right before the surname, and the surname itself pushed toward the
   background colour (near-white in light theme, near-black in dark) so it
   reads as "not actionable". Hidden everywhere else - the badge markup is
   in every card (playerCardHtml), only these two contexts reveal it.
   .readonly ("Мои игроки") keeps its plain faded look, no lock. */
.p-lock-badge { display: none; }
.slot-row.locked:not(.readonly) .p-lock-badge,
.picker-row.locked .p-lock-badge {
  display: inline-flex;
  align-self: center;
  flex: 0 0 auto;
  font-size: 13px;
  line-height: 1;
  /* pull it tight against the skills column on its left and the surname
     on its right (user's ask 2026-09-08 - less air around it) */
  margin: 0 -1px 0 -3px;
}
.slot-row.locked:not(.readonly) .p-name-text,
.picker-row.locked .p-name-text {
  color: color-mix(in srgb, var(--text) 26%, var(--bg));
}
/* the lock + dimmed name carry the state now - don't also fade the whole
   editable row (a faded lock defeats the point). Мои игроки still fades. */
.slot-row.locked:not(.readonly),
.picker-row.locked { opacity: 1; }

/* Rink view (Джерси/Лица) counterpart of .p-lock-badge - see
   site.js's renderSlotVisual. */
.rink-lock {
  position: absolute;
  /* nudged out past the circle's arc (user's ask 2026-09-08) so it sits
     on the empty card corner, not on the photo where it was unreadable */
  left: -8px;
  bottom: -7px;
  font-size: 13px;
  line-height: 1;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
  pointer-events: none;
  z-index: 3;
}
/* Today's earned points ("+24" / "0" / "-3"), bottom-right of the face
   circle (user's ask 2026-09-08). Plain black, no background; a thin light
   shadow keeps it legible over a photo or a dark team colour. */
.rink-points {
  position: absolute;
  /* pushed out past the circle's arc (user's ask 2026-09-08) - reads
     against the empty card corner instead of the photo behind it */
  right: -10px;
  bottom: -7px;
  font-size: 19px;
  font-weight: 800;
  line-height: 1;
  color: #000;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.65);
  pointer-events: auto; /* tappable so a phone can open the breakdown tooltip */
  z-index: 3;
}

/* Tap-to-reveal tooltip - `title=` never shows on touch, so a tap on any
   points number (Онлайн, Замены, squad, rink) pops this instead (user's
   ask, 2026-09-08). Positioned by site.js's showTapTip. */
.tap-tip {
  position: fixed;
  z-index: 200;
  max-width: 16rem;
  padding: 0.4rem 0.6rem;
  border-radius: 8px;
  background: var(--surface-alt);
  color: var(--text);
  border: 1px solid var(--border);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.28);
  font-size: var(--fs-sm);
  line-height: 1.4;
  white-space: pre-line;
  pointer-events: none;
}

/* "Удалить команду" -> pick a tournament -> confirm. Built in JS
   (site.js's openDeleteTeamPicker), no template markup. */
.tournament-picker {
  position: fixed;
  inset: 0;
  z-index: 210;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(0, 0, 0, 0.5);
}
.tournament-picker-sheet {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1rem;
  width: 100%;
  max-width: 20rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.tournament-picker-title {
  margin: 0 0 0.3rem;
  font-weight: 700;
  text-align: center;
}
.tournament-picker-cancel {
  border: none;
  background: none;
  color: var(--text-dim);
  padding: 0.4rem;
  font-size: var(--fs-body);
}
.picker-empty { color: var(--text-dim); text-align: center; padding: 1.5rem 0; }
/* Loading skeletons (site.js loadingSkeleton / squadSkeleton, 2026-09-29):
   shimmering placeholders in the shape of what's coming. */
.skel {
  display: block; height: 12px; border-radius: 6px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--text) 7%, transparent) 25%,
    color-mix(in srgb, var(--text) 15%, transparent) 50%,
    color-mix(in srgb, var(--text) 7%, transparent) 75%);
  background-size: 200% 100%;
  animation: skel-shimmer 1.2s ease-in-out infinite;
}
.skel-circle { width: 32px; height: 32px; border-radius: 50%; flex: none; }
.skel-circle--lg { width: 48px; height: 48px; }
.skel-bar--short { width: 55%; }
.skel-list { display: flex; flex-direction: column; gap: 12px; padding: 0.8rem 0.4rem; }
.skel-row { display: flex; align-items: center; gap: 10px; }
.skel-lines { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.skel-squad { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 12px; padding: 1rem 0; }
.skel-card {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 12px 8px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
}
.skel-card .skel-bar { width: 80%; }
.skel-card .skel-bar--short { width: 50%; }
@keyframes skel-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
/* Loading rink: same ice + slot coordinates, players are shimmering silhouettes. */
.rink--skeleton { pointer-events: none; }
.rink--skeleton .face-label, .rink--skeleton .jersey-label {
  color: transparent; text-shadow: none; border-radius: 6px;
  background: rgba(120, 150, 190, 0.22); min-width: 3rem; min-height: 0.9rem;
}
.rink--skeleton .face-circle.empty,
.rink--skeleton .jersey-card .empty {
  border-style: solid;
  border-color: transparent;
  color: transparent;
  background: linear-gradient(100deg, rgba(120, 150, 190, 0.18) 30%, rgba(160, 190, 230, 0.34) 50%, rgba(120, 150, 190, 0.18) 70%);
  background-size: 200% 100%;
  animation: skel-shimmer 1.2s linear infinite;
}
.skel-reveal { animation: skel-reveal 0.25s ease-out; }
@keyframes skel-reveal { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .skel, .skel-reveal { animation: none; }
}
/* Squad-loading spinner (user's ask, 2026-08-24) - #squad-container shown
   while loadSquad's fetch is in flight, replaced by the real squad on
   completion (site.js's refreshSectionContent). */
.spinner-wrap { display: flex; justify-content: center; padding: 2.5rem 0; }
.spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spinner-spin 0.8s linear infinite;
}
@keyframes spinner-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; }
}

#error-banner {
  position: fixed;
  left: 50%;
  bottom: calc(1.2rem + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  background: #c33;
  color: #fff;
  padding: 0.7rem 1.1rem;
  border-radius: 10px;
  max-width: 90vw;
  z-index: 20;
  font-size: var(--fs-body);
}

footer {
  text-align: center;
  margin-top: 3rem;
  color: var(--text-dim);
  font-size: var(--fs-sm);
}
/* Plain text, no button chrome at all (user's ask, 2026-09-02) - the old
   .secondary-btn.danger-btn styling (bordered, red text) is exactly what
   you'd want for a button that SHOULD look pressable; buried here as
   small lowercase footer text is deliberately the opposite - findable if
   you're looking for it, nothing to catch a stray tap. */
.footer-delete-team-btn {
  display: block;
  margin: 0.6rem auto 0;
  background: none;
  border: none;
  padding: 0;
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  text-decoration: underline;
  cursor: pointer;
}
.footer-delete-team-btn:hover { color: var(--ice-red); }

/* ── Manager Mode section (experimental) ─────────────────────────────
   docs/manager_mode.md. Reuses .secondary-btn/.picker-empty/
   .squad-view-close-btn from elsewhere in this file - only genuinely new
   shapes get their own class here. */
.manager-list-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem; }
.manager-join-row { display: flex; gap: 0.5rem; margin-bottom: 1rem; max-width: 360px; }
.manager-join-row input {
  flex: 1;
  padding: 0.5rem 0.7rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
}
.manager-rooms-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 280px)); gap: 0.75rem; }
.manager-room-card {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0.75rem 0.85rem;
  background: var(--surface);
  cursor: pointer;
  transition: box-shadow 150ms ease, transform 150ms ease;
}
.manager-room-card:hover { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); transform: translateY(-2px); }
.manager-room-card-header { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.4rem; }
.manager-room-card-meta { color: var(--text-dim); font-size: var(--fs-md); }
#manager-join-requests { margin: 1rem 0; }
#manager-join-requests h3 { grid-column: 1 / -1; margin: 0; }
#manager-join-requests .manager-room-card { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; cursor: default; }
#manager-join-requests .manager-room-card span { flex-basis: 100%; }
.manager-status-badge {
  font-size: var(--fs-xs);
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.manager-detail-header { display: flex; align-items: center; gap: 0.75rem; margin: 0 0 1rem; min-height: 2rem; }
.manager-detail-header h2 { margin: 0; flex: 1; min-width: 0; }
.manager-visibility-row { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem 1rem; margin-bottom: 1rem; }
.manager-visibility-row .manager-tab-hint { margin: 0; }
.manager-visibility-row label { display: inline-flex; align-items: center; gap: 0.4rem; }
.mgr-league-nav {
  display: flex;
  gap: 0.2rem;
  margin: 0 0 0.85rem;
  padding: 0.28rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow-x: auto;
}
.mgr-league-nav button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  flex: 1 0 auto;
  min-height: 2.35rem;
  padding: 0.35rem 0.75rem;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  white-space: nowrap;
}
.mgr-league-nav button[data-section="home"] { flex: 0 0 auto; width: 2.5rem; padding: 0; }
.mgr-league-nav button.is-active {
  background: color-mix(in srgb, var(--accent) 16%, var(--surface-alt));
  color: var(--text);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, var(--border));
}
.mgr-home-icon { width: 1.05rem; height: 1.05rem; display: block; }
.mgr-league-panel { display: flex; flex-direction: column; gap: 0.45rem; }
.mgr-slate-day {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0.7rem 0.85rem;
}
.mgr-slate-day h3 { margin: 0 0 0.35rem; font-size: var(--fs-sm); color: var(--text-dim); font-weight: 600; }
.mgr-slate-games { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 0.4rem; }
.mgr-slate-game {
  font-size: var(--fs-md);
  padding: 0.4rem 0.6rem;
  background: var(--surface-alt);
  border-radius: 8px;
  text-align: center;
}
.mgr-league-panel .secondary-btn { align-self: flex-start; margin-top: 0.35rem; }
.mgr-league-panel input[type="search"] { width: 100%; margin-bottom: 0.35rem; }
/* Classic/Замены house style for Manager Mode (docs/ui.md §2): short facts
   as .team-info-row tiles 2–4 per row, members/metrics as chips, logs as
   .transfers-history-row, one accent primary action per view. */
.mgr-fact-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); gap: 0.5rem; margin: 0 0 0.6rem; }
.mgr-fact-grid .team-info-row strong { text-align: right; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mgr-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0 0 0.6rem; }
.mgr-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.3rem 0.65rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-alt);
  font-size: var(--fs-sm);
  white-space: nowrap;
}
.mgr-chip strong { font-variant-numeric: tabular-nums; }
.mgr-chip-dim { color: var(--text-dim); font-size: var(--fs-sm); font-weight: 400; }
.mgr-subhead { margin: 0.4rem 0 0.4rem; font-size: var(--fs-sm); color: var(--text-dim); font-weight: 600; }
.mgr-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; margin-bottom: 0.4rem; }
.mgr-panel-head h3 { margin: 0; font-size: var(--fs-body); }
.mgr-league-panel .mgr-panel-head .primary-btn { margin: 0; }
.mgr-action-bar { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.5rem; margin: 0 0 0.6rem; }

/* ── League office (docs/manager_mode.md section L, ESPN parity) ──────
   One top bar: plain entries + two grouped entries whose menu is a single
   fixed-position #mgr-nav-menu (the bar scrolls sideways on phones, so an
   in-bar dropdown would be clipped). Tables reuse .stats-table. */
.mgr-league-nav .mgr-nav-group-btn { gap: 0.25rem; }
.mgr-nav-caret { font-size: 0.7em; opacity: 0.7; transition: transform var(--dur-fast) var(--ease-out); }
.mgr-nav-group-btn[aria-expanded="true"] .mgr-nav-caret { transform: rotate(180deg); }
.mgr-nav-menu {
  position: fixed;
  z-index: 30;
  display: flex;
  flex-direction: column;
  min-width: 12rem;
  padding: 0.3rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  animation: mgr-menu-in var(--dur-fast) var(--ease-out);
}
.mgr-nav-menu.hidden { display: none; }
.mgr-nav-menu button {
  display: block;
  width: 100%;
  min-height: 2.35rem;
  padding: 0.45rem 0.75rem;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: var(--fs-md);
  font-weight: 600;
  text-align: left;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.mgr-nav-menu button:hover, .mgr-nav-menu button:focus-visible { background: var(--surface-alt); color: var(--text); outline: none; }
.mgr-nav-menu button.is-active { color: var(--text); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, var(--border)); }
@keyframes mgr-menu-in { from { opacity: 0; } to { opacity: 1; } }

.mgr-league-panel { min-height: 50dvh; } /* R1: reserve the section's box before its fetch lands */
.mgr-table-wrap { border: 1px solid var(--border); border-radius: var(--radius-sm); margin: 0 0 0.6rem; background: var(--surface); }
.mgr-table-wrap .stats-table { font-size: var(--fs-md); }
.mgr-table-wrap .stats-table td { font-variant-numeric: tabular-nums slashed-zero; font-feature-settings: "tnum" 1, "zero" 1; }
.mgr-table-wrap .stats-table td.stats-table-name, .mgr-table-wrap .stats-table th.stats-table-name {
  max-width: 16rem; overflow: hidden; text-overflow: ellipsis; text-align: left;
  position: static; background: var(--surface);
}
/* Identity column sticks only where it is the row's first column or follows
   the rank (docs/ui.md §5); elsewhere a fixed 2rem offset would cover a cell. */
.mgr-table-wrap .stats-table .rt-rank { background: var(--surface); font-family: inherit; }
.mgr-table-wrap .stats-table tbody tr:hover td { background: var(--surface-alt); }
.mgr-table-wrap .stats-table .rt-rank + .stats-table-name { position: sticky; left: 2rem; }
.mgr-table-wrap .stats-table .stats-table-name:first-child { position: sticky; left: 0; }
.mgr-fa-table .player-card { min-width: 9.5rem; }
.mgr-table-wrap .stats-table tbody tr.is-me td { background: color-mix(in srgb, var(--accent) 9%, var(--surface)); }
.mgr-table-wrap .stats-table tr.is-bench td { color: var(--text-dim); }
.stats-table th.mgr-td-text, .stats-table td.mgr-td-text { text-align: left; }
.stats-table td.mgr-td-actions { display: flex; align-items: center; justify-content: flex-end; gap: 0.35rem; }
.stats-table td.mgr-td-actions .primary-btn { margin: 0; padding: 0.3rem 0.8rem; font-size: var(--fs-sm); }
.mgr-fa-table .player-card { cursor: pointer; }
.mgr-pos { color: var(--success); }
.mgr-neg { color: var(--ice-red); }

.mgr-status-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  color: var(--text-dim);
}
.mgr-status-pill.is-live { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); }
.mgr-status-pill.is-final { color: var(--success); border-color: color-mix(in srgb, var(--success) 45%, var(--border)); background: color-mix(in srgb, var(--success) 10%, var(--surface)); }
.mgr-status-pill.is-upcoming { color: var(--text-dim); }

.mgr-filter-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin: 0 0 0.6rem; }
.mgr-filter-sep { width: 1px; height: 1.4rem; background: var(--border); margin: 0 0.15rem; }
.mgr-filter-chip {
  min-height: 2.1rem;
  padding: 0.3rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.mgr-filter-chip:hover { color: var(--text); background: var(--surface-alt); }
.mgr-filter-chip.is-active { color: var(--text); background: color-mix(in srgb, var(--accent) 14%, var(--surface-alt)); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.mgr-select, .mgr-search-input, .mgr-num-input, .mgr-note-input {
  max-width: 14rem;
  padding: 0.42rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-alt);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-md);
  transition: box-shadow var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.mgr-search-input { width: 100%; max-width: 22rem; }
.mgr-num-input { width: 4.2rem; text-align: right; font-variant-numeric: tabular-nums; }
.mgr-note-input { width: 100%; max-width: 40rem; resize: vertical; }
.mgr-select:focus, .mgr-search-input:focus, .mgr-num-input:focus, .mgr-note-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 35%, transparent);
}
.mgr-check, .mgr-date-field { display: inline-flex; align-items: center; gap: 0.35rem; font-size: var(--fs-sm); color: var(--text-dim); }
.mgr-panel-head .mgr-select { margin-left: auto; }
.mgr-panel-head .mgr-filter-row { margin: 0; }

.mgr-week {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0.6rem 0.8rem;
}
.mgr-week.is-live { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.mgr-week h4 { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.4rem; font-size: var(--fs-sm); color: var(--text-dim); font-weight: 600; }
.mgr-matchup {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 0.6rem;
  min-height: 2.2rem;
  padding: 0.3rem 0.6rem;
  border-radius: 8px;
  background: var(--surface-alt);
  font-size: var(--fs-md);
}
.mgr-matchup + .mgr-matchup { margin-top: 0.3rem; }
.mgr-matchup.is-me { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, var(--border)); }
.mgr-matchup-team { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mgr-matchup-team.is-away { text-align: right; }
.mgr-matchup-score { font-weight: 700; font-variant-numeric: tabular-nums slashed-zero; white-space: nowrap; }

.mgr-bracket { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 18rem)); gap: 0.5rem; margin: 0 0 0.8rem; }
.mgr-bracket-round { display: flex; flex-direction: column; justify-content: center; gap: 0.4rem; }
.mgr-bracket-game { display: flex; flex-direction: column; gap: 0.2rem; padding: 0.45rem 0.65rem; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-alt); font-size: var(--fs-md); }
.mgr-bracket-game.is-final { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); min-height: 2.6rem; justify-content: center; }
.mgr-seed { display: flex; gap: 0.4rem; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mgr-seed strong { color: var(--text-dim); font-variant-numeric: tabular-nums; }

.mgr-news-list, .mgr-activity-list { display: flex; flex-direction: column; gap: 0.35rem; }
.mgr-news-row {
  display: grid;
  grid-template-columns: 1.6rem minmax(9rem, 14rem) minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.6rem;
  min-height: 3rem;
  padding: 0.35rem 0.7rem;
  background: var(--surface-alt);
  border-radius: 8px;
  font-size: var(--fs-md);
}
.mgr-news-row.is-injury { box-shadow: inset 3px 0 0 var(--ice-red); }
.mgr-news-icon { text-align: center; }
.mgr-news-row .player-card { cursor: pointer; min-width: 0; }
.mgr-news-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mgr-news-owner { white-space: nowrap; }
@media (max-width: 820px) {
  .mgr-news-row { grid-template-columns: 1.4rem minmax(0, 1fr); }
  .mgr-news-text, .mgr-news-owner { grid-column: 2; }
}

.mgr-tool-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 18rem)); gap: 0.5rem; margin: 0 0 0.4rem; }
.mgr-tool-card {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  text-align: left;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.mgr-tool-card strong { font-size: var(--fs-body); }
.mgr-tool-card span { color: var(--text-dim); font-size: var(--fs-sm); }
.mgr-tool-card:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
#mgr-lm-delete { border-color: color-mix(in srgb, var(--ice-red) 45%, var(--border)); }
#mgr-lm-delete strong { color: var(--ice-red); }
.secondary-btn.is-danger { color: var(--ice-red); border-color: var(--ice-red); }

.mgr-note {
  margin: 0 0 0.6rem;
  padding: 0.55rem 0.8rem;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 8%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  font-size: var(--fs-md);
  max-width: 70ch;
}
.mgr-clubhouse { min-height: 3.2rem; } /* R1: tiles arrive after two fetches */

.mgr-strategy-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 0.6rem; }
.mgr-pref-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); gap: 0.35rem 0.6rem; max-height: 22rem; overflow-y: auto; }
.mgr-pref-row { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr); align-items: center; gap: 0.4rem; font-size: var(--fs-sm); }
.mgr-limits-table td { text-align: right; }

.mgr-star-btn {
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font-size: var(--fs-base);
  line-height: 1;
  transition: color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.mgr-star-btn.is-on { color: var(--accent); box-shadow: var(--shadow-glow); }
.mgr-day-nav {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 0.6rem;
}
.mgr-day-nav-btn {
  flex: 0 0 auto;
  width: 1.8rem;
  height: 1.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  color: var(--text);
  font-size: var(--fs-sm);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
.mgr-day-nav-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 14%, var(--surface-alt)); }
.mgr-day-nav-btn:disabled { opacity: 0.35; cursor: default; }
.mgr-day-nav-label { font-weight: 600; color: var(--text); }
@media (prefers-reduced-motion: reduce) {
  .mgr-nav-menu { animation: none; }
  .mgr-tool-card:hover { transform: none; }
}

/* Same centered-overlay-with-backdrop shape as #squad-view-modal, own
   namespace since two modals (create room / pick player) share it here. */
.manager-modal { position: fixed; inset: 0; z-index: 25; display: flex; align-items: center; justify-content: center; padding: 1rem; }
.manager-modal.hidden { display: none; }
.manager-modal-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.5); }
.manager-modal-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 480px;
  max-height: 85dvh;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
}
.manager-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 1rem 2.6rem 0.8rem 1rem;
  border-bottom: 1px solid var(--border);
}
.manager-modal-header h3 { margin: 0; font-size: var(--fs-base); }
.manager-modal-body { padding: 1rem; overflow-y: auto; display: flex; flex-direction: column; gap: 0.65rem; }
.manager-modal-body label { display: flex; flex-direction: column; gap: 0.25rem; font-size: var(--fs-md); color: var(--text-dim); }
.manager-modal-body input, .manager-modal-body select {
  padding: 0.5rem 0.6rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  color: var(--text);
  font-size: var(--fs-body);
}
.manager-modal-body button#manager-create-room-submit-btn,
.manager-modal-body button#manager-pick-confirm-btn { margin-top: 0.25rem; }
.manager-advanced-fields { border: 1px solid var(--border); border-radius: 8px; padding: 0.5rem 0.7rem; }
.manager-advanced-fields summary { cursor: pointer; color: var(--text-dim); font-size: var(--fs-md); }
.manager-advanced-fields label { margin-top: 0.5rem; }

.manager-joining-panel p { margin: 0.3rem 0; }
.manager-invite-row { margin-top: 0.6rem; }
.manager-invite-row code {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.15rem 0.5rem;
}

.manager-turn-banner {
  padding: 0.65rem 0.9rem;
  border-radius: 10px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  margin-bottom: 1rem;
  font-weight: 600;
}
.manager-turn-banner.my-turn { background: color-mix(in srgb, var(--accent) 18%, var(--surface-alt)); border-color: var(--accent); }


.manager-pick-results, .manager-wishlist-panel #manager-wishlist-results {
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.manager-pick-result-row { padding: 0.5rem 0.7rem; border-bottom: 1px solid var(--border); cursor: pointer; font-size: var(--fs-md); }
.manager-pick-result-row:last-child { border-bottom: none; }
.manager-pick-result-row:hover { background: var(--surface-alt); }
.manager-pick-result-row.selected { background: color-mix(in srgb, var(--accent) 20%, var(--surface)); }
.manager-pick-terms { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.5rem; }

.manager-wishlist-panel input { width: 100%; margin-bottom: 0.5rem; }
.manager-wishlist-remove-btn {
  border: none;
  background: none;
  color: var(--text-dim);
  cursor: pointer;
  margin-left: 0.4rem;
}

.manager-picks-log { display: flex; flex-direction: column; gap: 0.3rem; max-height: 320px; overflow-y: auto; }
.manager-pick-log-row {
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  font-size: var(--fs-md);
}

.manager-join-type-field { border: 0; padding: 0; margin: 0.25rem 0; }
.manager-join-type-field label { display: block; margin: 0.2rem 0; font-size: var(--fs-body); }
.manager-setup-panel { display: flex; flex-direction: column; gap: 1.75rem; }
#manager-setup-tabs { margin-bottom: 0; }
.mgr-field-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 0.6rem 0.8rem; }
.mgr-field-grid-3 { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
.mgr-field-wide { display: block; margin-bottom: 0.6rem; }
.mgr-settings-block {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1rem 1.15rem 1.15rem;
}
.mgr-settings-block h3 { margin: 0 0 0.55rem; font-size: var(--fs-body); }
.mgr-setup-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.35rem; }
.mgr-top50 { margin-top: 1rem; max-height: 22rem; overflow: auto; }
.mgr-top50 h4 { margin: 0 0 0.4rem; font-size: var(--fs-body); }
.mgr-top50-row { display: flex; gap: 0.6rem; padding: 0.2rem 0; font-size: var(--fs-md); }
.mgr-top50-row span:last-child { margin-left: auto; font-variant-numeric: tabular-nums; }
.manager-setup-panel label { display: flex; flex-direction: column; gap: 0.25rem; margin-bottom: 0.5rem; font-size: var(--fs-md); color: var(--text-dim); }
.manager-setup-panel .mgr-select { width: 100%; max-width: 100%; }
#manager-room-switcher { margin-bottom: 0.5rem; max-width: 640px; }
.manager-open-join-btn { margin-top: 0.4rem; }

#manager-active-shell { margin-top: 0.25rem; }
#manager-active-tabs { margin-bottom: 0.85rem; max-width: 640px; }
.manager-tab-hint { color: var(--text-dim); font-size: var(--fs-md); margin: 0 0 0.6rem; }
.manager-roster-list { max-width: 420px; }
.manager-roster-group { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.75rem; }
.manager-slot-meta { font-size: var(--fs-xs); color: var(--text-dim); margin-left: 0.4rem; white-space: nowrap; }
.manager-trade-row {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
}
.manager-other-picker { display: flex; flex-direction: column; gap: 0.25rem; max-width: 280px; margin-bottom: 0.75rem; font-size: var(--fs-md); color: var(--text-dim); }
.manager-trade-propose { display: flex; flex-direction: column; gap: 0.6rem; max-width: 720px; }
.manager-trade-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
.manager-trade-cols h4 { margin: 0 0 0.35rem; font-size: var(--fs-md); }
.manager-trade-check { display: block; font-size: var(--fs-md); margin: 0.2rem 0; }
.manager-trade-actions { display: flex; gap: 0.4rem; flex-shrink: 0; margin-top: 0.4rem; }
.manager-trade-row { margin-bottom: 0.4rem; }
.manager-trade-row .transfers-history-row { flex-wrap: wrap; }
.mgr-market-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-bottom: 0.5rem; }
#manager-market-search { max-width: 320px; margin: 0; }
.mgr-segmented { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.mgr-segmented-btn {
  border: none;
  background: var(--surface-alt);
  color: var(--text-dim);
  padding: 0.3rem 0.7rem;
  font-size: var(--fs-sm);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.mgr-segmented-btn + .mgr-segmented-btn { border-left: 1px solid var(--border); }
.mgr-segmented-btn.active { background: color-mix(in srgb, var(--accent) 16%, var(--surface-alt)); color: var(--text); }
/* Was capped at 320px via .manager-picks-log (~5 rows visible); rows here
   are compact stat lines, not full player-cards, so a taller viewport-
   relative cap fits far more without an inner-scroll trap. */
.mgr-market-results { display: flex; flex-direction: column; gap: 0.25rem; max-height: 70vh; overflow-y: auto; }
.mgr-market-row-compact {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--surface-alt) 92%, var(--bg));
}
.mgr-market-identity { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.mgr-market-name { font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mgr-market-stats { flex: 1 1 auto; font-size: var(--fs-md); text-align: right; }
.mgr-market-metric-list { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.4rem; font-size: var(--fs-xs); color: var(--text-dim); }
.mgr-market-metric-list strong { color: var(--text); }

@media (max-width: 640px) {
  .manager-trade-cols { grid-template-columns: 1fr; }
}

/* ── Top-level areas (Фэнтези / Прогнозы / Новости) + Прогнозы section ──
   #toplevel-nav sits above the section nav as a bolder segmented control
   so it reads as a different navigation tier. Hidden entirely for
   non-testers (site.js). */
.toplevel-nav {
  display: flex;
  width: fit-content;          /* a 2-3 option switch is compact, not a full-width bar */
  min-width: min(100%, 18rem);
  max-width: 100%;
  gap: 0.4rem;
  margin: 0 auto 0.9rem;
  padding: 0.3rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
}
.toplevel-btn {
  flex: 1 1 auto;
  min-width: 7rem;
  padding: 0.55rem 0.6rem;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--text-dim);
  font-size: var(--fs-body);
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  transition: background 150ms ease, color 150ms ease;
}
.toplevel-btn:hover:not(.active) { color: var(--text); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.toplevel-btn.active {
  background: var(--accent);
  color: var(--accent-text);
}

#area-predict-content { margin: 0 auto; }
.predict-intro h2 { margin: 0 0 0.4rem; font-size: var(--fs-lg); }
.predict-rules { color: var(--text-dim); font-size: var(--fs-md); line-height: 1.45; margin: 0 0 0.4rem; }
.predict-deadline { font-size: var(--fs-md); margin: 0 0 0.8rem; }
.predict-deadline em { color: var(--text-dim); font-style: normal; }
.predict-hint { font-size: var(--fs-sm); color: var(--text-dim); margin: 0.5rem 0 0; }
.predict-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.8rem 1.2rem; margin-bottom: 0.6rem; }
.predict-save-status { font-size: var(--fs-md); margin: 0.5rem 0 0; color: var(--accent); }
.predict-save-status.error { color: #ff6b6b; }
#predict-standings-save, [id$="-save"].secondary-btn { margin-top: 0.7rem; }

/* discrete: ordered list */
.predict-discrete { display: flex; flex-direction: column; gap: 0.25rem; }
.predict-disc-row {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.45rem 0.7rem;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface);
}
.predict-place { font-weight: 700; color: var(--accent); min-width: 1.5rem; text-align: center; }
.predict-team { flex: 1; }
.predict-disc-arrows button {
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
  border-radius: 6px; width: 1.8rem; height: 1.8rem; cursor: pointer; font-size: var(--fs-sm);
}
.predict-disc-arrows button:disabled { opacity: 0.3; cursor: default; }

/* differentiated grid */
/* Two 11x11 grids sit side by side inside .predict-standings-cols, so the
   table must fit its half-column with no horizontal scroll - fixed layout,
   equal-width cells, abbreviations (not full names) in the row labels. The
   wrap keeps overflow-x only as a last-resort safety on very narrow phones
   (below the 900px breakpoint the two grids already stack vertically). */
.predict-grid-wrap { overflow-x: auto; }
.predict-grid { border-collapse: collapse; table-layout: fixed; width: 100%; font-size: var(--fs-sm); }
/* Single big group (ВХЛ, 30 places). Desktop: the whole matrix is meant to
   be visible with no scrolling - a plain fixed-layout table at 100% width
   so every place/weight column is exactly equal and 30+ of them still fit.
   Mobile: the wrap turns into a sticky scroll pane (min-width forces the
   horizontal scroll) with the header row + name column pinned. */
.predict-standings-col-wide .predict-grid {
  table-layout: fixed; width: 100%;
  /* separate + spacing 0 so `position: sticky` cells hold on iOS Safari
     (sticky <td> is broken under border-collapse there) */
  border-collapse: separate; border-spacing: 0;
}
.predict-standings-col-wide .predict-grid th,
.predict-standings-col-wide .predict-grid td {
  border: 0;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-2xs); padding: 0;
}
/* name column: the one non-equal column, fixed (table-layout:fixed reads
   the first row) so the remaining place columns split evenly */
.predict-standings-col-wide .predict-grid thead th:first-child { width: 6.5rem; }
/* neutralise the narrow-grid's per-column width rules so every place column
   splits evenly under table-layout: fixed */
.predict-standings-col-wide .predict-grid th:nth-child(2),
.predict-standings-col-wide .predict-grid td:nth-child(2),
.predict-standings-col-wide .predict-grid th:last-child,
.predict-standings-col-wide .predict-grid td:last-child { width: auto; }
.predict-standings-col-wide .predict-grid td.predict-grid-team,
.predict-standings-col-wide .predict-grid thead th:first-child,
.predict-standings-col-wide .predict-grid .predict-grid-foot td:first-child {
  text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 0 0.35rem; font-size: var(--fs-xs);
}
.predict-standings-col-wide .predict-grid .predict-cell { height: 1.65rem; }
.predict-standings-col-wide .predict-grid-wrap {
  overflow: visible; border: 1px solid var(--border); border-radius: 8px;
}
@media (max-width: 900px) {
  .predict-standings-col-wide .predict-grid { table-layout: auto; width: max-content; min-width: 100%; }
  .predict-standings-col-wide .predict-grid thead th:first-child { width: auto; }
  .predict-standings-col-wide .predict-grid td.predict-cell,
  .predict-standings-col-wide .predict-grid td.predict-grid-sum,
  .predict-standings-col-wide .predict-grid thead th:not(:first-child),
  .predict-standings-col-wide .predict-grid .predict-grid-foot td:not(:first-child) {
    width: 1.9rem; min-width: 1.9rem; max-width: 1.9rem; box-sizing: border-box;
    font-size: var(--fs-2xs);
  }
  .predict-standings-col-wide .predict-grid td.predict-grid-team,
  .predict-standings-col-wide .predict-grid thead th:first-child,
  .predict-standings-col-wide .predict-grid .predict-grid-foot td:first-child {
    max-width: 7.5rem; font-size: var(--fs-xs);
  }
  .predict-standings-col-wide .predict-grid-wrap {
    max-height: 68dvh; overflow: auto; -webkit-overflow-scrolling: touch;
  }
  .predict-standings-col-wide .predict-grid thead th {
    position: sticky; top: 0; z-index: 3;
    background: var(--surface, var(--bg)); box-shadow: inset 0 -1px 0 var(--border);
  }
  .predict-standings-col-wide .predict-grid td.predict-grid-team,
  .predict-standings-col-wide .predict-grid thead th:first-child,
  .predict-standings-col-wide .predict-grid .predict-grid-foot td:first-child {
    position: sticky; left: 0; z-index: 2;
    background: var(--surface, var(--bg)); box-shadow: inset -1px 0 0 var(--border);
  }
  .predict-standings-col-wide .predict-grid thead th:first-child { z-index: 4; }
}
/* city after a team name in ВХЛ/МХЛ «По местам» */
.predict-disc-city { color: var(--text-dim); font-weight: 400; font-size: 0.88em; }
.predict-wildcards { grid-column: 1 / -1; padding: 0.8rem 0.9rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.predict-wildcards h3 { margin: 0 0 0.6rem; font-size: var(--fs-body); }
.predict-wildcards h3 small { color: var(--text-dim); font-weight: 400; }
.predict-wildcard-opts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.5rem; }
.predict-wildcard-opt { display: grid; grid-template-columns: auto 1fr; column-gap: 0.5rem; align-items: center; padding: 0.5rem 0.6rem; border: 1px solid var(--border); border-radius: 8px; background: var(--bg); cursor: pointer; min-width: 0; }
.predict-wildcard-opt small { grid-column: 2; color: var(--text-dim); font-size: var(--fs-xs); }
.predict-wildcard-opt.checked { border-color: var(--accent, #00e5ff); }
.predict-wildcard-opt.disabled { opacity: 0.4; cursor: not-allowed; }
@media (max-width: 640px) { .predict-wildcard-opts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* МХЛ probability grid: real club names in the left column (Latin abbrs are
   made up), auto-sized to content so nothing is clipped, kept tight. */
.predict-grid-names { table-layout: auto; }
.predict-grid-names td.predict-grid-team,
.predict-grid-names th:nth-child(2) {
  width: auto; white-space: nowrap; text-align: left;
  padding: 0 0.45rem; overflow: visible; font-size: var(--fs-xs);
}
.predict-grid-names td.predict-cell,
.predict-grid-names td.predict-grid-sum,
.predict-grid-names thead th:not(:nth-child(1)):not(:nth-child(2)) {
  width: 1.9rem; min-width: 1.9rem; max-width: 1.9rem; box-sizing: border-box;
}
.predict-grid th, .predict-grid td {
  border: 1px solid var(--border); text-align: center; padding: 0.15rem 0;
  height: 2.2rem; overflow: hidden;
}
.predict-grid th:first-child, .predict-grid td:first-child { width: 2.3rem; }   /* ож. (avg place) */
.predict-grid th:nth-child(2), .predict-grid td:nth-child(2) { width: 3.4rem; } /* abbr */
.predict-grid th:last-child, .predict-grid td:last-child { width: 2rem; }
.predict-grid-avg {
  text-align: right; color: var(--text-dim); font-variant-numeric: tabular-nums;
  font-size: var(--fs-xs); padding: 0 0.25rem;
}
.predict-grid-team {
  text-align: left; white-space: nowrap; font-weight: 600;
  padding: 0 0.25rem; text-overflow: ellipsis; overflow: hidden;
}
.predict-grid-foot td:first-child { text-align: left; white-space: nowrap; padding: 0 0.25rem; }
.predict-cell { cursor: pointer; user-select: none; -webkit-touch-callout: none; }
.predict-cell:hover { outline: 2px solid var(--accent); outline-offset: -2px; }
.predict-grid-sum { font-weight: 700; }
.predict-grid-foot td { color: var(--text-dim); font-size: var(--fs-sm); }

/* stat nominations - one card per metric, cards flow left->right; inside
   each, a vertical 1..5 ranked list (empty ranks shown as placeholder
   rows, "как рейтинг"). */
.predict-nom-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
  gap: 0.9rem; margin-bottom: 1rem;
}
/* Main individual-stats block: a fixed 4 across (the other groups stay
   auto-fill ~5). Drops to 2 on a phone. */
.predict-nom-grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 720px) {
  .predict-nom-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Player nominations are shown in groups (main / оборона / вратари), each
   in its own grid so goalie cards break to a fresh row after a gap. */
.predict-nom-group { margin-top: 1.6rem; }
.predict-nom-group-h {
  margin: 0 0 0.6rem; font-size: var(--fs-sm); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim);
}
.predict-nom-card {
  border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
  padding: 0.7rem 0.8rem; display: flex; flex-direction: column; gap: 0.5rem;
}
.predict-nom-card .predict-nom-title {
  font-weight: 700; font-size: var(--fs-md); margin: 0; color: var(--accent);
}
.predict-rank-list { display: flex; flex-direction: column; gap: 0.25rem; }
.predict-rank-row {
  display: flex; align-items: center; gap: 0.5rem; cursor: pointer;
  padding: 0.3rem 0.45rem; border: 1px solid var(--border); border-radius: 7px;
  background: var(--bg); font-size: var(--fs-md);
}
.predict-rank-row:hover { border-color: var(--accent); }
.predict-rank-row.filled { border-color: var(--accent); }
/* pre-filled from last-season top-5, not yet claimed by the user */
.predict-rank-row.prefilled { border-style: dashed; border-color: var(--border); }
.predict-rank-row.prefilled .predict-rank-name { color: var(--text-dim); font-style: italic; }
.predict-prefill-hint { margin: 0 0 1rem; }
.predict-nom-card.partial { border-color: #d97706; }
.predict-nom-warn {
  font-size: var(--fs-xs); font-weight: 600; color: #d97706; margin: -0.15rem 0 0.1rem;
}
.predict-rank-num { font-weight: 700; color: var(--text-dim); min-width: 1.1rem; text-align: center; }
.predict-rank-row.filled .predict-rank-num { color: var(--accent); }
.predict-rank-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.predict-rank-row:not(.filled) .predict-rank-name { color: var(--text-dim); }
.predict-rank-add { font-size: var(--fs-2xs); color: var(--text-dim); }
.predict-rank-x {
  border: none; background: none; color: #ff6b6b; font-size: var(--fs-base); line-height: 1;
  padding: 0 0.15rem; cursor: pointer;
}

/* subject picker overlay */
.predict-picker-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.5);
  display: flex; align-items: center; justify-content: center; z-index: 2000;
}
.predict-picker {
  background: var(--bg); border: 1px solid var(--border); border-radius: 12px;
  padding: 1rem; width: min(420px, 92vw); max-height: 80dvh; display: flex; flex-direction: column; gap: 0.6rem;
}
.predict-picker-input {
  padding: 0.6rem; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text); font-size: var(--fs-body);
}
.predict-picker-list {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; gap: 0.2rem;
}
.predict-picker-list button, .predict-picker-current button {
  /* extra right padding so every value sits the same distance from the edge */
  text-align: left; padding: 0.28rem 1.4rem 0.28rem 0.6rem; border: 1px solid var(--border);
  border-radius: 7px; background: var(--surface); color: var(--text); cursor: pointer;
  display: flex; flex-direction: row; align-items: center; justify-content: space-between;
  gap: 0.6rem; font-size: var(--fs-body); width: 100%;
}
.predict-picker-list button:hover, .predict-picker-current button:hover { border-color: var(--accent); }
.predict-pick-line { display: flex; align-items: baseline; gap: 0.35rem; min-width: 0; flex: 1 1 auto; }
.predict-pick-rank {
  font-family: inherit; font-size: var(--fs-2xs); color: var(--accent);
  border: 1px solid var(--accent); border-radius: 4px; padding: 0 0.3rem; flex: 0 0 auto;
  min-width: 1.1rem; text-align: center; align-self: center;
}
.predict-pick-pos { color: var(--text-dim); font-size: var(--fs-xs); flex: 0 0 auto; text-transform: uppercase; }
.predict-pick-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* the metric value, styled like a fantasy player-card price (.p-price) */
.predict-pick-val {
  flex: 0 0 auto; font-weight: 700; font-size: var(--fs-base); line-height: 1.2;
  color: var(--text-dim); font-variant-numeric: tabular-nums; text-align: right;
  white-space: nowrap;
}
/* the "ср. X.X" per-game figure trailing a player's season total */
.predict-pick-avg { font-weight: 400; font-size: var(--fs-xs); color: var(--text-dim); }
.predict-statmode { margin-bottom: 0.8rem; }
/* On/off switch (replaced the two-tab row) - "За сезон" | "За игру" */
.predict-switch {
  display: inline-flex; align-items: center; gap: 0.45rem; cursor: pointer;
  font-size: var(--fs-sm); color: var(--text-dim); user-select: none;
}
.predict-switch input { position: absolute; opacity: 0; pointer-events: none; }
.predict-switch span.on { color: var(--text); font-weight: 600; }
.predict-switch-track {
  position: relative; width: 2.1rem; height: 1.1rem; border-radius: 0.55rem;
  background: var(--border); transition: background 0.15s;
}
.predict-switch-knob {
  position: absolute; top: 0.15rem; left: 0.15rem; width: 0.8rem; height: 0.8rem;
  border-radius: 50%; background: #fff; transition: transform 0.15s;
}
.predict-switch input:checked ~ .predict-switch-track { background: var(--accent); }
.predict-switch input:checked ~ .predict-switch-track .predict-switch-knob { transform: translateX(1rem); }
.predict-sub { font-size: var(--fs-xs); color: var(--text-dim); }
/* the current pick, pinned above the scrollable list while swapping it */
.predict-picker-current:empty { display: none; }
.predict-picker-current {
  flex: 0 0 auto; padding-bottom: 0.5rem; border-bottom: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 0.2rem;
}
.predict-picker-current-label {
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim);
}
.predict-picker-current button { border-color: var(--accent); }

/* leaderboards */
.predict-boards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
.predict-board h3 { margin: 0 0 0.4rem; font-size: var(--fs-body); }
.predict-board table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.predict-board th, .predict-board td { border-bottom: 1px solid var(--border); padding: 0.3rem 0.4rem; text-align: left; }
.predict-board tr.me { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.predict-board tr.late { color: var(--text-dim); }

/* Прогнозы - additions (drag reorder, countdown, grid meta) */
.predict-countdown { color: var(--text-dim); font-style: italic; }
.predict-disc-row { cursor: grab; }
/* The row left behind while dragging - a dashed hole, not a dimmed copy;
   the drag image itself stays fully opaque (see renderDiscrete). */
.predict-disc-row.predict-disc-ghost {
  opacity: 0.35; border-style: dashed; background: transparent;
}
.predict-disc-row.drop-before { box-shadow: inset 0 3px 0 #38bdf8; }
.predict-disc-row.drop-after { box-shadow: inset 0 -3px 0 #38bdf8; }
.predict-disc-handle { color: var(--text-dim); cursor: grab; font-size: var(--fs-base); line-height: 1; }
/* playoff watershed line after the 8th place - deliberately thin/tight */
.predict-playoff-line {
  display: flex; align-items: center; gap: 0.4rem; margin: -0.15rem 0;
  color: #38bdf8; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.08em;
}
.predict-playoff-line::before, .predict-playoff-line::after {
  content: ""; flex: 1; height: 1px; background: #38bdf8; opacity: 0.7;
}
.predict-step { display: flex; align-items: center; gap: 0.4rem; font-size: var(--fs-sm); color: var(--text-dim); }
.predict-step input { width: 7rem; }
.predict-step span { font-weight: 700; color: var(--accent); min-width: 0.9rem; text-align: center; }
.predict-grid-meta { margin-top: 0.6rem; font-size: var(--fs-md); }
.predict-grid .predict-cell { height: 2.2rem; font-size: var(--fs-sm); }

/* Прогнозы - the standings groups side by side (KHL: 2 conferences;
   МХЛ/НХЛ: 4 divisions wrap 2x2; ВХЛ: one full-width group). */
.predict-standings-cols { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: flex-start; }
.predict-standings-col { flex: 1 1 320px; min-width: 0; }
.predict-standings-col-wide { flex-basis: 100%; }
.predict-standings-col > h3 { margin: 0 0 0.5rem; font-size: var(--fs-body); color: var(--text-dim); }
/* 4 groups (МХЛ / НХЛ) -> a fixed 2x2 grid. Seed order puts the gold МХЛ
   divisions first, so they land on the top row. */
.predict-standings-cols-2x2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 900px) {
  .predict-standings-cols { flex-direction: column; }
  .predict-standings-col { flex-basis: auto; }
  .predict-standings-cols-2x2 { grid-template-columns: 1fr; }
}

/* Прогнозы - league switcher (КХЛ / ВХЛ / МХЛ / НХЛ) */
.predict-league-tabs { margin-bottom: 1rem; }
.predict-league-tabs[hidden] { display: none; }
.predict-beta-tag {
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.04em;
  padding: 0.05rem 0.3rem; border-radius: 4px; margin-left: 0.3rem;
  background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent);
  vertical-align: middle;
}
#predict-tabs[hidden] { display: none; }

/* Прогнозы - feed of other participants' latest standings predictions
   (reuses .online-grid/.online-card from the Онлайн section) */
.predict-feed { margin-top: 1.5rem; }
.predict-feed-title { font-size: var(--fs-body); margin: 0 0 0.6rem; }
.predict-feed-metric {
  display: inline-flex; align-items: center; gap: 0.5rem; margin-bottom: 0.8rem;
  font-size: var(--fs-sm); color: var(--text-dim);
}
.predict-feed-metric select {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  background: var(--surface-alt);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='none' stroke='%23888' stroke-width='1.5' d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.6rem center;
  border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); font: inherit; font-size: var(--fs-sm);
  padding: 0.35rem 1.7rem 0.35rem 0.6rem;
  cursor: pointer;
}
.predict-feed-metric select:hover { border-color: var(--accent); }
.predict-feed-metric select:focus { outline: none; border-color: var(--accent); }
.predict-feed-picks {
  margin: 0.35rem 0 0; padding-left: 1.3rem; font-size: var(--fs-sm);
  display: flex; flex-direction: column; gap: 0.1rem;
}
.predict-feed-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
/* No timestamp column in the header anymore (it's the card's title tooltip),
   so let a long nickname wrap and show in full instead of ellipsing. */
.predict-feed-grid .online-card-name { overflow: visible; white-space: normal; text-overflow: clip; }
/* Both conferences stacked as narrow vertical lists, side by side, so the
   participant card is tall and slim rather than wide. */
.predict-feed-confs { display: flex; gap: 0.6rem; margin-top: 0.4rem; }
.predict-feed-conf { display: flex; flex-direction: column; gap: 0.15rem; flex: 1; min-width: 0; }
.predict-feed-conf-label { font-weight: 700; color: var(--text-dim); font-size: var(--fs-2xs); margin-bottom: 0.1rem; }
/* Plain coloured abbreviation - no box, no rank number. The colour is a
   5-step gradient by how far the participant moved the club from the
   default (last-season) prediction. */
.predict-feed-abbr {
  font-family: inherit; font-size: var(--fs-xs);
  font-weight: 600; line-height: 1.45; white-space: nowrap;
}
.predict-feed-abbr.d-g2 { color: var(--success); }
.predict-feed-abbr.d-g1 { color: color-mix(in srgb, var(--success) 55%, var(--text-dim)); }
.predict-feed-abbr.d-n  { color: var(--text-dim); font-weight: 500; }
.predict-feed-abbr.d-r1 { color: color-mix(in srgb, #e05252 55%, var(--text-dim)); }
.predict-feed-abbr.d-r2 { color: #e05252; }

/* ── UI 3.0 (docs/ui.md) ──────────────────────────────────────────── */
h1, h2, h3, .line-title, .section-label {
  font-family: var(--font-display);
  font-weight: 500;
  letter-spacing: -0.03em;
}
button, input, select, textarea { font: inherit; }
::selection { background: color-mix(in srgb, var(--accent) 38%, transparent); color: var(--text); }
.p-stats, .p-price, .p-today-points, .p-stat-last, .p-stat-this,
td, .budget-num, #budget-remaining, #budget-total, .online-card-total {
  font-variant-numeric: tabular-nums slashed-zero;
  font-feature-settings: "tnum" 1, "zero" 1;
}
button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 35%, transparent);
}
#save-btn:not(:disabled) { box-shadow: var(--shadow-glow); }
/* Header scrolls away with the page. A sticky wordmark covered the
   section nav and the first rows. */
.site-header {
  position: relative;
  padding: 0.15rem 0 0.35rem;
  background: transparent;
}
/* The two fixed FABs (PRO + settings) own the viewport's top-right. On a
   phone the centered wordmark and the logout chip sit in that same band
   and overlap them. Keep the logo in the gap between a left-side logout
   and the FABs. */
@media (max-width: 720px) {
  .site-header {
    --hdr-inset-l: 2.6rem;   /* logout chip */
    --hdr-inset-r: 7.4rem;   /* PRO + settings FABs */
    padding-left: var(--hdr-inset-l);
    padding-right: var(--hdr-inset-r);
  }
  /* The insets above are unequal, which shoves the centring box left. Put the
     word back on the window centre by exactly half the imbalance - derived from
     the two insets, so changing either one keeps the logo centred. */
  .site-logo { height: 4.4rem; transform: translateX(calc((var(--hdr-inset-r) - var(--hdr-inset-l)) / 2)); }
  .session-identity { display: none; }
  .session-corner { right: auto; left: 0.35rem; top: 0.35rem; }
}
.slot-row, .picker-row, .online-card {
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.online-card:hover, .manager-room-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow);
}
.slot-row.just-filled { animation: slot-settle var(--dur-base) var(--ease-spring); }
@keyframes slot-settle {
  from { transform: scale(0.97); }
  to { transform: scale(1); }
}
.online-player-row.clickable { cursor: pointer; }
.online-player-row.clickable:hover { background: var(--surface-alt); }
#section-squad-content,
#section-online-content,
#section-rating-content,
#section-stats-content,
#section-manager-content { min-height: 60dvh; }
.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--surface-alt);
  border-radius: var(--radius-sm);
  min-height: 1.1rem;
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 20%, color-mix(in srgb, var(--accent) 28%, transparent) 50%, transparent 80%);
  animation: skeleton-sweep 1.4s ease-in-out infinite;
}
@keyframes skeleton-sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
html:not([data-theme="light"]) body::after {
  content: "";
  position: fixed;
  width: 46vw;
  height: 46vw;
  right: -10vw;
  top: 8vh;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(circle, color-mix(in srgb, var(--accent) 22%, transparent), transparent 68%);
  filter: blur(6px);
  animation: blob-drift 18s var(--ease-out) infinite alternate;
}
@keyframes blob-drift {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-4vw, 3vh, 0); }
}
.reveal { animation: reveal-in var(--dur-slow) var(--ease-out) both; }
.reveal-2 { animation-delay: 0.12s; }
.reveal-3 { animation-delay: 0.22s; }
@keyframes reveal-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Manager draft room — layout from docs/espn-fantasy-hockey-draft-ui.md */
.mgr-draft-room {
  --draft-cyan: var(--accent);
  --draft-ink: rgb(6, 43, 44);
  display: flex;
  flex-direction: column;
  width: 100%;
  min-width: 0;
  min-height: calc(100dvh - 9.5rem);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: visible;
}
.mgr-draft-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 45px;
  padding: 0 16px;
  background: var(--draft-cyan);
  color: #111;
}
.mgr-draft-header h1 { margin: 0; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--fs-base); font-weight: 600; }
.mgr-draft-help {
  width: 22px; height: 22px; flex: none; border-radius: 50%;
  border: 1px solid #111; display: grid; place-items: center; font-size: var(--fs-xs);
}
.mgr-pick-train {
  display: flex; align-items: stretch; height: 80px;
  background: var(--draft-ink); color: #fff;
}
.mgr-clock {
  width: 110px; flex: 0 0 auto; padding: 8px 10px;
  display: flex; flex-direction: column; justify-content: center;
}
.mgr-clock.my-turn { background: color-mix(in srgb, var(--draft-cyan) 22%, var(--draft-ink)); }
.mgr-clock-label { font-size: 10px; letter-spacing: 0.08em; opacity: 0.8; }
.mgr-clock-digits {
  font-variant-numeric: tabular-nums;
  font-size: 24px; color: var(--draft-cyan); line-height: 1.1;
}
.mgr-clock-bar { height: 4px; background: rgba(255,255,255,0.15); margin-top: 4px; }
.mgr-clock-bar > div { height: 100%; background: var(--draft-cyan); transform-origin: left center; }
.mgr-pick-train-scroller {
  display: flex; gap: 6px; align-items: center;
  min-width: 0; overflow-x: auto; padding: 8px; flex: 1;
}
.mgr-train-current {
  flex: none; align-self: center; display: inline-flex; align-items: center; justify-content: center;
  gap: 0.35rem; min-height: 40px; margin: 0 0.5rem;
  padding: 0.45rem 0.75rem; border: 1px solid var(--accent);
  border-radius: 999px; background: var(--draft-ink); color: var(--accent);
  font-size: var(--fs-xs); font-weight: 700; white-space: nowrap; cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.mgr-train-current:hover { background: color-mix(in srgb, var(--accent) 15%, var(--draft-ink)); }
.mgr-train-current:active { transform: scale(0.95); }
.mgr-mobile-nav { display: none; }
.mgr-round-chip { font-size: 10px; letter-spacing: 0.08em; opacity: 0.7; padding: 0 6px; white-space: nowrap; }
.mgr-train-pick {
  flex: 0 0 auto; width: max-content; min-width: 88px; max-width: min(10rem, 100%); padding: 6px 8px; border-radius: 6px;
  background: rgb(8, 60, 62); font-size: 11px;
}
.mgr-train-pick.is-mine { background: var(--draft-cyan); color: #111; }
.mgr-train-pick.is-current { outline: 2px solid #fff; }
.mgr-train-num { font-size: 10px; letter-spacing: 0.06em; opacity: 0.75; }
.mgr-train-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mgr-draft-room {
  display: flex;
  flex-direction: column;
  height: calc(100dvh - 11rem);
  min-height: 28rem;
  overflow: hidden;
}
.mgr-draft-columns {
  display: grid;
  grid-template-columns: minmax(200px, 16rem) minmax(320px, 26rem) minmax(0, 1fr);
  align-items: stretch;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.mgr-draft-queue { min-width: 0; }
.mgr-draft-rink-col { background: var(--bg); min-width: 0; overflow: auto; }
.mgr-draft-center {
  background: var(--surface);
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.mgr-draft-tab:not(.is-hidden) {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.mgr-player-table {
  flex: 1;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
}
.mgr-draft-col { overflow: auto; padding: 10px 12px; border-right: 1px solid var(--line); }
.mgr-draft-center { border-right: 0; overflow: hidden; }
.mgr-commish-tools { display: flex; gap: 0.5rem; margin: 0 0 0.8rem; }
.mgr-league-home p { margin: 0.2rem 0 0.6rem; }
.mgr-draft-queue .manager-wishlist-panel .manager-pick-log-row,
.mgr-draft-queue #manager-wishlist-items {
  max-height: none;
  overflow: visible;
  white-space: normal;
}
.mgr-player-row {
  display: grid; grid-template-columns: 20px minmax(0, 1fr) auto auto;
  grid-template-rows: 26px 18px; column-gap: 6px; align-items: center;
  min-height: 50px; padding: 3px 0; border-bottom: 1px solid var(--line); font-size: 13px;
}
.mgr-player-row > .mgr-rk { grid-column: 1; grid-row: 1 / 3; color: var(--text-dim); font-size: var(--fs-xs); }
.mgr-player-row > .mgr-identity { grid-column: 2; grid-row: 1; }
.mgr-player-row > .mgr-queue-btn { grid-column: 3; grid-row: 1; }
.mgr-player-row > .mgr-draft-btn { grid-column: 4; grid-row: 1; }
.mgr-player-row .mgr-identity { display: flex; align-items: center; gap: 6px; min-width: 0; width: auto; height: 24px; overflow: hidden; }
.mgr-player-row .mgr-identity .p-stripe { flex: none; height: 24px; }
.mgr-player-row .mgr-identity .p-meta, .mgr-player-row .mgr-identity .p-skills { display: none; }
.mgr-player-row .mgr-identity .p-name, .mgr-player-row .mgr-identity .p-name-text { min-width: 0; max-width: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-left: 0; }
.mgr-player-stats, .mgr-player-stat-head { display: grid; grid-template-columns: repeat(var(--mgr-stat-count, 3), minmax(0, 1fr)); text-align: center; }
.mgr-player-stats { grid-column: 2 / -1; grid-row: 2; color: var(--text-dim); }
.mgr-ppg { font-variant-numeric: tabular-nums; font-size: var(--fs-xs); }
.mgr-player-head { display: grid; grid-template-columns: 20px minmax(0, 1fr); column-gap: 6px; padding: 0.35rem 0; border-bottom: 1px solid var(--line); color: var(--text-dim); font-size: var(--fs-2xs); }
.mgr-player-head > button { text-align: left; }
.mgr-player-stat-head { grid-column: 2 / -1; gap: 3px; }
.mgr-player-stat-head button { text-align: right; }
.mgr-player-head button { border: 0; background: transparent; color: inherit; padding: 0.15rem 0; cursor: pointer; font-size: inherit; }
.mgr-player-head button.is-active { color: var(--accent); font-weight: 700; }
.mgr-player-head button.is-active::after { content: " ↓"; }
.mgr-player-head button.is-active[data-direction="asc"]::after { content: " ↑"; }
.mgr-roster-slots { display: flex; flex-direction: column; gap: 0.25rem; }
.mgr-roster-group { margin: 0.65rem 0 0.25rem; color: var(--text-dim); font-size: var(--fs-xs); font-weight: 600; }
.mgr-roster-group:first-child { margin-top: 0; }
.mgr-roster-slot {
  display: grid; grid-template-columns: 2.8rem minmax(0, 1fr) auto;
  align-items: center; gap: 0.5rem; width: 100%; min-height: 44px;
  padding: 0.35rem 0.6rem; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); text-align: left;
}
.mgr-roster-slot:hover:not(:disabled) { background: var(--surface-alt); border-color: var(--accent); }
.mgr-roster-slot:disabled { cursor: default; }
.mgr-roster-slot-key { font-size: var(--fs-xs); font-weight: 700; color: var(--accent); }
.mgr-roster-slot-name { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--fs-md); }
.mgr-roster-slot:not(.is-filled) .mgr-roster-slot-name { color: var(--text-dim); }
.mgr-roster-slot-action { color: var(--text-dim); font-size: var(--fs-2xs); white-space: nowrap; }
.mgr-roster-slot:not(.is-filled):not(:disabled) .mgr-roster-slot-action { color: var(--accent); }
.mgr-draft-tabs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px; width: 100%; padding: 4px; border: 1px solid var(--line); border-radius: 12px; background: var(--bg); margin-bottom: 8px; }
.mgr-draft-tabs button {
  min-height: 38px; background: transparent; border: 0; border-radius: 8px;
  padding: 6px 8px; font-size: 14px; color: var(--text-dim); cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.mgr-draft-tabs button:hover { background: var(--surface-alt); color: var(--text); }
.mgr-draft-tabs button.is-active { color: var(--draft-ink); background: var(--draft-cyan); font-weight: 700; }
.mgr-draft-tabs button:active, .mgr-mobile-nav button:active, .mgr-queue-btn:active, .mgr-draft-btn:active { transform: scale(0.96); }
.mgr-draft-tab.is-hidden { display: none; }
.mgr-scenario h3 { margin: 0 0 2px; font-size: var(--fs-base); }
.mgr-scenario p { margin: 0 0 8px; color: var(--text-dim); font-size: var(--fs-md); }
.mgr-player-filters { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.mgr-player-filters input[type="search"] { flex: 1; min-width: 140px; }
.mgr-show-drafted { display: inline-flex; align-items: center; gap: 0.35rem; align-self: flex-start; margin: -2px 0 4px; color: var(--text-dim); font-size: var(--fs-xs); opacity: 0.72; cursor: pointer; }
.mgr-show-drafted:hover, .mgr-show-drafted:focus-within { opacity: 1; }
.mgr-show-drafted input { width: 13px; height: 13px; margin: 0; accent-color: var(--accent); }
.mgr-identity {
  flex: none;
  grid-template-columns: 8px max-content;
  grid-template-areas:
    "stripe name"
    "stripe meta";
  width: max-content;
  min-width: max-content;
}
.mgr-identity .p-stats,
.mgr-identity .p-today-points,
.mgr-identity .p-price { display: none; }
.mgr-copies-left {
  margin-left: 0.35rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--text-dim);
}
.mgr-identity .p-name,
.mgr-identity .p-name-text,
.manager-roster-list .p-name,
.manager-roster-list .p-name-text {
  overflow: visible;
  text-overflow: clip;
  min-width: max-content;
  flex: none;
}
.manager-roster-list .slot-row { overflow: visible; }
.mgr-pos { font-size: 10px; border-radius: 16px; padding: 0 4px; text-align: center; }
.mgr-pos.pos-f, .mgr-pos.pos-d { background: rgb(255, 144, 0); }
.mgr-pos.pos-g { background: rgb(48, 239, 221); }
.mgr-queue-btn, .mgr-draft-btn {
  border-radius: 8px; min-height: 26px; font-size: var(--fs-2xs); padding: 0 7px; cursor: pointer; white-space: nowrap;
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.mgr-queue-btn { background: transparent; border: 1px solid var(--line); color: var(--text); }
.mgr-queue-btn:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--surface)); }
.mgr-draft-btn:hover { filter: brightness(0.9); }
.mgr-draft-queue .manager-pick-log-row { display: flex; align-items: center; justify-content: space-between; gap: 0.35rem; }
.mgr-queue-player { display: flex; align-items: center; gap: 0.4rem; min-width: 0; flex: 1; }
.mgr-stripe { display: flex; width: 0.42rem; height: 1.45rem; flex: none; border-radius: 2px; overflow: hidden; }
.mgr-stripe span { flex: 1 1 33%; }
.mgr-queue-name { font-weight: 700; }
.mgr-queue-skills { margin-left: auto; color: var(--text-dim); font-size: var(--fs-xs); letter-spacing: 0.03em; white-space: nowrap; }
.mgr-queue-btn.is-on { background: color-mix(in srgb, var(--accent) 18%, var(--surface)); border-color: var(--accent); color: var(--accent); }
.mgr-draft-btn { background: var(--text); color: var(--bg); border: 0; }
.mgr-roster-limits { position: sticky; bottom: 0; background: var(--surface); padding: 8px 0; font-size: 12px; color: var(--text-dim); }
.mgr-rule-row { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
.mgr-rule-row span:last-child { color: var(--text-dim); }
.mgr-pre-draft-strategy { margin-top: 1rem; }
.mgr-wishlist-label { display: block; margin-bottom: 0.35rem; font-weight: 600; }
.mgr-wishlist-item { display: flex; align-items: center; gap: 0.45rem; min-width: 0; }
.mgr-wishlist-item .mgr-identity { flex: 1; min-width: 0; width: auto; grid-template-columns: 8px minmax(0, 1fr); overflow: hidden; }
.mgr-wishlist-item .p-name, .mgr-wishlist-item .p-name-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.mgr-wishlist-item.is-dragging { opacity: 0.5; }
.mgr-wishlist-item.is-unavailable .mgr-identity { opacity: 0.55; }
.mgr-wishlist-status { grid-column: 2; font-size: var(--fs-2xs); color: var(--text-dim); }
.mgr-wishlist-number { flex: none; width: 1.5rem; text-align: right; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.mgr-wishlist-controls { display: flex; align-items: center; gap: 0.15rem; flex: none; }
.mgr-wishlist-controls button { min-width: 36px; min-height: 40px; padding: 0.2rem; border: 1px solid var(--line); border-radius: 999px; background: var(--surface-alt); color: var(--text); }
.mgr-wishlist-controls button:disabled { opacity: 0.35; }
.mgr-more-players { display: block; margin: 0.8rem auto; }
.manager-invite-picked { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0; }
@media (max-width: 1100px) {
  .mgr-mobile-nav { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); background: var(--surface); border-bottom: 1px solid var(--line); }
  .mgr-mobile-nav button { min-height: 44px; border: 0; border-bottom: 2px solid transparent; background: transparent; color: var(--text-dim); }
  .mgr-mobile-nav button.is-active { border-bottom-color: var(--accent); color: var(--text); }
  .mgr-draft-columns { grid-template-columns: 1fr; }
  .mgr-draft-room { height: auto; max-height: none; overflow: visible; }
  .mgr-draft-columns { overflow: visible; }
  .mgr-draft-center { order: -1; overflow: visible; }
  .mgr-draft-col, .mgr-draft-tab:not(.is-hidden), .mgr-player-table { overflow: visible; }
  .mgr-player-table { max-height: none; }
  .mgr-pick-train { position: sticky; top: 0; z-index: 5; }
  .mgr-draft-room[data-mobile-view="players"] .mgr-draft-queue,
  .mgr-draft-room[data-mobile-view="players"] .mgr-draft-rink-col,
  .mgr-draft-room[data-mobile-view="queue"] .mgr-draft-center,
  .mgr-draft-room[data-mobile-view="queue"] .mgr-draft-rink-col,
  .mgr-draft-room[data-mobile-view="roster"] .mgr-draft-center,
  .mgr-draft-room[data-mobile-view="roster"] .mgr-draft-queue { display: none; }
}
@media (max-width: 600px) {
  .manager-detail-header { flex-wrap: wrap; }
  .mgr-draft-header { padding: 0 0.75rem; }
  .mgr-pick-train { height: 70px; }
  .mgr-clock { width: 92px; padding: 6px; }
  .mgr-clock-digits { font-size: 20px; }
  .mgr-train-current { width: 40px; height: 40px; min-height: 40px; padding: 0; margin: 0 0.35rem; font-size: 1.2rem; }
  .mgr-train-current-label { display: none; }
  .mgr-player-row { grid-template-columns: 16px minmax(0, 1fr) auto auto; column-gap: 4px; }
  .mgr-player-head { grid-template-columns: 16px minmax(0, 1fr); column-gap: 4px; }
  .mgr-queue-btn, .mgr-draft-btn { font-size: var(--fs-2xs); padding: 0 5px; }
  .mgr-wishlist-item .mgr-identity .p-name-text { max-width: 7rem; }
  #section-manager-content input:not([type="checkbox"]):not([type="range"]),
  #section-manager-content select,
  .manager-modal input:not([type="checkbox"]):not([type="range"]),
  .manager-modal select { font-size: 16px; }
}
.espn-board-wrap {
  overflow: auto;
  max-height: 70dvh;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg) 70%, transparent);
}
.espn-board { border-collapse: separate; border-spacing: 4px; min-width: 100%; }
.espn-board th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-align: left;
  padding: 0.35rem 0.4rem;
  white-space: nowrap;
}
.espn-board th.espn-round,
.espn-board td.espn-round {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface);
  width: 2.2rem;
  text-align: center;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.espn-board th.espn-round { z-index: 3; }
.espn-board th.is-me { color: var(--accent); }
.espn-pick {
  min-width: max-content;
  min-height: 52px;
  padding: 0.35rem 0.45rem;
  border-radius: 8px;
  background: var(--surface-alt);
  border-left: 3px solid transparent;
  cursor: default;
}
.espn-pick.pos-f { border-left-color: var(--ice-blue); }
.espn-pick.pos-d { border-left-color: #ffb020; }
.espn-pick.pos-g { border-left-color: var(--ice-red); }
.espn-pick.on-clock {
  outline: 1px solid var(--accent);
  box-shadow: var(--shadow-glow);
}
.espn-pick-name { font-weight: 600; font-size: var(--fs-sm); white-space: nowrap; overflow: visible; text-overflow: clip; }
.espn-pick-meta { font-size: var(--fs-2xs); color: var(--text-dim); font-variant-numeric: tabular-nums; }
.espn-pick-empty { color: var(--text-dim); font-size: var(--fs-xs); }
.espn-draft-side h3 { margin: 0.2rem 0 0.45rem; font-size: var(--fs-body); }
@media (max-width: 820px) {
  .espn-draft { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .reveal, .slot-row.just-filled { opacity: 1; transform: none; }
}

/* Manager forms: one select/input language, not the browser's native chrome. */
#section-manager-content select,
.manager-modal select,
.mgr-draft-room select,
#section-manager-content input,
.manager-modal input,
.manager-setup-panel input {
  font: inherit;
  color: var(--text);
  background-color: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.5rem 0.7rem;
}
#section-manager-content select,
.manager-modal select,
.mgr-draft-room select {
  appearance: none;
  padding-right: 2rem;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
    linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
  background-position: calc(100% - 14px) 55%, calc(100% - 8px) 55%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
#section-manager-content select:focus,
.manager-modal select:focus,
.mgr-draft-room select:focus,
#section-manager-content input:focus,
.manager-modal input:focus,
.manager-setup-panel input:focus {
  outline: none;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 35%, transparent);
}
.mgr-metric-groups { display: flex; flex-direction: column; gap: 0.85rem; }
.mgr-metric-block {
  background: var(--surface-alt);
  border-radius: 8px;
  padding: 0.55rem 0.7rem 0.7rem;
}
.mgr-metric-block h4 {
  margin: 0 0 0.45rem;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 650;
}
.mgr-metrics {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.7rem;
  margin: 0;
}
.mgr-metric {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  flex: 0 0 auto;
}
.mgr-metric:not(.is-on) .mgr-metric-label,
.mgr-metric:not(.is-on) .mgr-metric-x { color: var(--text-dim); }
.mgr-metric-label {
  font-size: var(--fs-xs);
  font-weight: 650;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.mgr-metric-x {
  width: 0.9rem;
  height: 0.9rem;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  padding: 0;
  line-height: 1;
  font-size: var(--fs-md);
}
.mgr-metric.is-on .mgr-metric-x { color: var(--text); }
.mgr-metric-num {
  display: flex;
  align-items: stretch;
  flex: 0 0 3.9rem;
  width: 3.9rem;
  height: 1.7rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  overflow: hidden;
}
.mgr-metric-num:focus-within {
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 35%, transparent);
}
#section-manager-content .mgr-metric input,
.manager-modal .mgr-metric input {
  width: 100%;
  min-width: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  padding: 0 0.1rem 0 0.2rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
  box-shadow: none;
  -moz-appearance: textfield;
  appearance: textfield;
}
#section-manager-content .mgr-metric input:focus,
.manager-modal .mgr-metric input:focus { outline: none; box-shadow: none; }
.mgr-metric input::-webkit-outer-spin-button,
.mgr-metric input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.mgr-metric-spin {
  display: flex;
  flex-direction: column;
  flex: 0 0 1.15rem;
  border-left: 1px solid var(--line);
}
.mgr-metric-step {
  flex: 1;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  position: relative;
}
.mgr-metric-step + .mgr-metric-step { border-top: 1px solid var(--line); }
.mgr-metric-step::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
}
.mgr-metric-step[data-dir="1"]::before {
  border-bottom: 6px solid var(--text);
  transform: translate(-50%, -40%);
}
.mgr-metric-step[data-dir="-1"]::before {
  border-top: 6px solid var(--text);
  transform: translate(-50%, -60%);
}

/* PlayerOut.roster_status badge next to the surname: 🚑 injured / bold F farm-reserve */
.p-roster-badge { margin-left: 4px; font-size: 0.85em; flex: none; }
.p-roster-farm, .p-roster-unsure {
  display: inline-block; min-width: 1.15em; padding: 0 0.25em; border-radius: 4px;
  font-weight: 800; font-size: 0.75em; line-height: 1.4; text-align: center;
}
.p-roster-farm { color: #fff; background: #c0392b; }
.p-roster-unsure { color: #1a1a1a; background: #e0a526; }

/* Draft room: statuses, chat, queue snipes, personal ranking (docs/draft_room.md) */
.mgr-autopilot { display: inline-flex; align-items: center; gap: 0.3rem; margin-left: auto; font-size: var(--fs-sm); color: var(--text-dim); white-space: nowrap; cursor: pointer; }
.mgr-train-pick.is-online .mgr-train-num::after { content: ""; display: inline-block; width: 6px; height: 6px; margin-left: 4px; border-radius: 50%; background: #3ecf6e; vertical-align: middle; }
.mgr-train-pick.is-auto .mgr-train-name::after { content: " · авто"; font-size: 0.7em; opacity: 0.75; }
.mgr-member-chip { display: inline-flex; align-items: center; gap: 0.3rem; padding: 2px 8px; border: 1px solid var(--line); border-radius: 999px; font-size: var(--fs-sm); color: var(--text-dim); }
.mgr-member-chip.is-me { border-color: var(--draft-cyan); }
.mgr-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--line); flex: none; }
.mgr-member-chip.is-online { color: var(--text); }
.mgr-member-chip.is-online .mgr-dot { background: #3ecf6e; }
.mgr-auto-badge { padding: 0 5px; border-radius: 6px; background: var(--surface-alt); font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; }
.mgr-chat-members { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px; }
.mgr-chat-log { display: flex; flex-direction: column; gap: 6px; height: min(55vh, 460px); overflow-y: auto; padding: 6px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg); }
.mgr-chat-msg { max-width: 85%; padding: 6px 9px; border-radius: 10px; background: var(--surface-alt); align-self: flex-start; overflow-wrap: anywhere; }
.mgr-chat-msg.is-mine { align-self: flex-end; }
.mgr-chat-meta { display: flex; align-items: center; gap: 6px; font-size: var(--fs-xs); color: var(--text-dim); }
.mgr-chat-author { font-weight: 600; color: var(--text); }
.mgr-chat-react-add { margin-left: auto; padding: 0 4px; border: 0; background: none; color: var(--text-dim); cursor: pointer; font-size: var(--fs-body); }
.mgr-chat-body { font-size: var(--fs-md); white-space: pre-wrap; }
.mgr-chat-reactions, .mgr-chat-picker { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 3px; }
.mgr-chat-reactions:empty { display: none; }
.mgr-chat-reaction, .mgr-chat-picker button { padding: 1px 6px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg); color: var(--text); font-size: var(--fs-sm); cursor: pointer; }
.mgr-chat-reaction.is-mine { border-color: var(--draft-cyan); background: color-mix(in srgb, var(--draft-cyan) 18%, var(--bg)); }
.mgr-chat-form { display: flex; gap: 6px; margin-top: 8px; }
.mgr-chat-form input { flex: 1; min-width: 0; }
.mgr-chat-unread { display: inline-block; min-width: 1.2em; margin-left: 4px; padding: 0 4px; border-radius: 999px; background: var(--danger); color: #fff; font-size: var(--fs-2xs); line-height: 1.4; }
.mgr-wishlist-item.is-sniped { animation: mgr-snipe 4s ease-out forwards; }
.mgr-wishlist-item.is-sniped .mgr-wishlist-status { color: var(--danger); font-weight: 700; }
@keyframes mgr-snipe {
  0%, 60% { background: color-mix(in srgb, var(--danger) 28%, transparent); }
  100% { background: transparent; opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) { .mgr-wishlist-item.is-sniped { animation: none; background: color-mix(in srgb, var(--danger) 20%, transparent); } }
.mgr-seed-lead { display: inline-flex; align-items: center; gap: 0.35rem; }
.mgr-seed-lead input { width: 4.5rem; }
.mgr-ranking-tools { display: flex; gap: 6px; margin-bottom: 6px; }
.mgr-ranking-tools input { flex: 1; min-width: 0; }
.mgr-ranking-list { max-height: 60vh; overflow-y: auto; }
.mgr-ranking-item { display: flex; align-items: center; gap: 0.45rem; min-width: 0; }
.mgr-ranking-item .mgr-identity { flex: 1; min-width: 0; width: auto; overflow: hidden; }
.mgr-ranking-number { width: 3.6rem; flex: none; text-align: right; }
.mgr-ranking-more { width: 100%; margin-top: 6px; }

/* Waiver policy (docs/manager_mode.md D): vote banner + reset-time field. */
.mgr-vote-note { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem 0.8rem; }
#mgr-lm-waiver-reset { width: 7rem; text-align: left; }

/* Categories mode (docs/manager_mode.md): LM picklist + per-matchup W/L/T grid. */
.mgr-cat-picker { display: grid; grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr)); gap: 0.25rem 0.6rem; margin: 0.5rem 0; }
.mgr-cat-group { grid-column: 1 / -1; margin: 0.4rem 0 0; font-size: var(--fs-sm); color: var(--text-dim); font-weight: 600; }
.mgr-cat-details { margin: 0.15rem 0 0.4rem; font-size: var(--fs-sm); }
.mgr-cat-details summary { cursor: pointer; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.mgr-cat-table { max-width: 28rem; }

/* Salary Cap (auction) draft panel (docs/manager_mode.md "Аукционный драфт"). */
.mgr-auction { margin: 0 0 0.6rem; padding: 0.6rem 0.8rem; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); }
.mgr-auction-lot { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 0.5rem; }
.mgr-auction-price .mono { font-size: 1.4rem; font-weight: 700; color: var(--accent); }
.mgr-auction-form { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.mgr-auction-form .mgr-num-input { width: 5.5rem; }
.mgr-auction-bids { margin: 0; padding-left: 1.2rem; font-size: var(--fs-sm); color: var(--text-dim); max-height: 6rem; overflow-y: auto; }
.mgr-chip.is-me { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }

/* Scoring preview: dense season statistics with a contained horizontal scroll. */
.mgr-metrics-scroll { overflow: auto; max-width: 100%; }
.mgr-metrics-table { width: 100%; border-collapse: collapse; font-size: 12px; font-variant-numeric: tabular-nums; }
.mgr-metrics-table th, .mgr-metrics-table td { padding: 7px 9px; text-align: right; white-space: nowrap; border-bottom: 1px solid var(--line); }
.mgr-metrics-table th:first-child, .mgr-metrics-table td:first-child { text-align: left; position: sticky; left: 0; background: var(--surface-alt); min-width: 130px; }
.mgr-metrics-table small { display: block; color: var(--text-dim); font-size: 10px; line-height: 1.3; }
.mgr-metrics-table tbody tr { cursor: pointer; }
.mgr-metrics-table tbody tr:hover { background: var(--surface-alt); }

/* Initial area visibility comes from server-rendered hidden classes. */

/* No prices (Daily): the price track disappears and the two numeric cells
   (today's points, season averages) get the freed width, flush right.
   Header shares the template so labels stay above their columns. */
body.no-prices #squad-container .player-card,
body.no-prices #picker-panel .player-card,
body.no-prices #squad-view-modal .player-card {
  grid-template-columns: 8px 1fr 44px 64px;
  grid-template-areas:
    "stripe name  points stats"
    "stripe meta  points stats";
}
body.no-prices #picker-panel .picker-column-header { grid-template-columns: 8px 1fr 44px 64px; }
@media (max-width: 820px) {
  body.no-prices #squad-container .player-card,
  body.no-prices #picker-panel .player-card,
  body.no-prices #squad-view-modal .player-card,
  body.no-prices #picker-panel .picker-column-header { grid-template-columns: 8px 1fr 38px 54px; }
}
@media (min-width: 481px) and (max-width: 820px) {
  body.no-prices #squad-container .player-card,
  body.no-prices #picker-panel .player-card,
  body.no-prices #squad-view-modal .player-card,
  body.no-prices #picker-panel .picker-column-header { grid-template-columns: 8px 1fr 52px 76px; }
}

/* Daily captain mark, top-right of a card / face / jersey. */
.p-captain {
  position: absolute; top: 2px; right: 2px; z-index: 2;
  width: 16px; height: 16px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: #7c3aed; color: #fff; font-size: 10px; font-weight: 800; line-height: 1;
  pointer-events: none;
}
.p-captain.rink-captain { top: -4px; right: -4px; }

/* Squad view (Рейтинг): compact - the card only needs name + a few numbers. */
#squad-view-modal .squad-view-panel { max-width: 400px; }

/* ── UI audit 2026-10-02 (docs/ui.md section 10) ─────────────────────────
   Reserved space (R1): these regions are filled after a fetch; without a floor
   the page collapsed and snapped back (CLS 0.05-0.16 on Рейтинг / Онлайн /
   Статистика). The floor is the skeleton's own height. */
#rating-table { min-height: 12rem; }
#online-grid { min-height: 9rem; }
#stats-table-panel .table-scroll { min-height: 60dvh; }

/* Wide screens: the section's single-line controls follow --control-max
   (shared_static/ui_base.css); these two were full-width by id. */
.online-friends-bar, #settings-invite-site-text, #settings-invite-tg-text,
#settings-invite-site-btn, #settings-invite-tg-btn { max-width: var(--control-max); }

/* Daily key-rules notice: three short lines never need more than a card's
   width, and on a phone it must not eat a third of the first screen. */
.daily-rules-banner { max-width: 26rem; }
@media (max-width: 820px) {
  .daily-rules-banner { flex-basis: 100%; max-width: none; font-size: var(--fs-sm); line-height: 1.35; padding: 0.4rem 0.7rem; }
}

/* Онлайн, team rosters on a phone (owner, 2026-10-02: "мелковат шрифт и большое
   расстояние между хоккеистом и очками"). Single-column rows are as wide as the
   screen, which parked the points ~300px from the surname. Cap the roster
   column at a readable measure, centre it, and size the text for a thumb-held
   screen (15px names, bold 14px points, 36px rows). */
@media (max-width: 640px) {
  .today-match-teams { max-width: 22rem; margin-inline: auto; }
  .today-match-panel .online-player-row {
    font-size: var(--fs-body);
    min-height: 2.25rem;
    padding: 0.2rem 0;
  }
  .today-match-panel .p-today-points { font-size: var(--fs-md); font-weight: 700; }
  .trt-team-name { font-size: var(--fs-sm); }
  .today-match-time { font-size: var(--fs-sm); }
}

/* 360px phones: five-six nav buttons with min-widths summed past the viewport (+3px
   horizontal scroll). Let them shrink; the labels already wrap. */
@media (max-width: 400px) {
  .section-btn { min-width: 0; }
  .toplevel-btn { min-width: 0; }
  #tabs-section { gap: 0.25rem; }  /* 7 buttons across 344px: keeps each >= 44px wide */
}

/* Touch: action buttons and tabs are 40px bodies with a 44px hit area (ui_base.css). */
@media (pointer: coarse) {
  #save-btn, #autofill-btn, #reset-btn, #captain-btn, #delete-team-btn,
  #manager-join-code-btn, #tab-online-btn, #tab-manager-btn,
  #settings-invite-site-btn, #settings-invite-tg-btn {
    min-height: var(--control-h);
    min-width: var(--control-h);
  }
  /* The commit / primary actions get the full 44px body. */
  #save-btn, #autofill-btn, #reset-btn, #captain-btn, #delete-team-btn {
    min-height: var(--tap-min);
    min-width: var(--tap-min);
  }
  #settings-invite-site-text, #settings-invite-tg-text { min-height: var(--control-h); }
  #password-submit, #delete-account-btn, #custom-nickname-submit,
  #settings-invite-site-btn, #settings-invite-tg-btn { min-height: var(--tap-min); }
  #custom-nickname-submit { min-width: var(--tap-min); }
  #verify-sportsru-help-btn { position: relative; }
  #verify-sportsru-help-btn::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: var(--tap-min); height: var(--tap-min); transform: translate(-50%, -50%);
  }
}

/* Statistics keeps the league and entity controls on one responsive row. */
.stats-view-controls { display: flex; align-items: center; gap: .6rem; margin-left: auto; color: var(--text-dim); font-size: var(--fs-sm); }
.stats-view-controls .tab-row { margin: 0; }
