.admin-icon {
  height: var(--icon-lg);
  width: var(--icon-lg);
}



.auth-notice {
  background: rgba(120, 157, 42, 0.1);
  border-radius: 0.5rem;
  border: 1px solid rgba(120, 157, 42, 0.3);
  margin-bottom: 1rem;
  padding: 0.75rem;
}

.auth-notice-text {
  color: #789d2a;
  font-size: 0.875rem;
  line-height: 1.4;
  text-align: center;
}

.avatar-loading {
  opacity: 0.2;
  transition: opacity 180ms ease-in-out;
}

.avatar-preview {
  height: 100%;
  object-fit: contain;
  width: 100%;
}

.avatar-preview-uploaded {
  border-radius: 0.5rem;
  height: 100%;
  object-fit: cover;
  width: 100%;
}

.banned-words-list {
  margin-top: 0.625rem;
  max-height: 12.5rem;
  overflow-y: auto;
}

.block {
  display: block !important;
}

.char-counter {
  color: #9aa;
  display: block;
  font-size: 0.8rem;
  text-align: right;
}

.clipboard-helper {
  left: -9999px;
  position: fixed;
}

.content-wrapper {
  box-sizing: border-box;
  margin: 1.5rem auto 0;
  max-width: 73rem;
  padding: 0 1rem 10rem;
  width: 100%;
}







.current-file-title {
  color: #e6e6e6;
  font-weight: 600;
}

.cursor-pointer {
  cursor: pointer;
}

.desc-placeholder {
  color: #4d4d4d;
  flex: 1;
  font-size: 1.25rem;
  font-style: italic;
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.5rem;
  max-width: 100%;
  min-width: 0;
  overflow-wrap: break-word;
  text-align: left;
  word-break: break-word;
  word-wrap: break-word;
}

.desc-text {
  color: #828282;
  flex: 1;
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.5rem;
  max-width: 100%;
  min-width: 0;
  overflow-wrap: break-word;
  text-align: left;
  word-break: break-word;
  word-wrap: break-word;
}

.drop-sub-text {
  color: #999;
  font-size: 0.75rem;
  margin-top: 0.25rem;
}









.edit-icon {
  flex-shrink: 0;
}



.edit-placeholder {
  color: #4d4d4d;
  flex: 1;
  font-size: 2.5rem;
  font-style: italic;
  font-weight: 800;
  letter-spacing: 0;
  line-height: 2.73125rem;
  max-width: 100%;
  min-width: 0;
  overflow-wrap: break-word;
  text-align: left;
  word-break: break-word;
  word-wrap: break-word;
}

.edit-text {
  color: #ebebeb;
  font-size: 2.5rem;
  font-weight: 800;
  letter-spacing: 0;
  line-height: 2.73125rem;
  max-width: 100%;
  min-width: 0;
  overflow-wrap: break-word;
  text-align: left;
  word-break: break-word;
  word-wrap: break-word;
}







.emote-ac {
  background: #1a1a1a;
  border-radius: 0.5rem;
  border: 1px solid #333;
  display: none;
  flex-direction: column;
  max-height: 14rem;
  overflow-y: auto;
  position: fixed;
  z-index: 100005;
}

.emote-ac-item {
  align-items: center;
  cursor: pointer;
  display: flex;
  gap: 0.5rem;
  padding: 0.35rem 0.625rem;
}

.emote-ac-item img {
  flex-shrink: 0;
  height: var(--icon-lg);
  width: auto;
}

.emote-ac-item span {
  color: #ccc;
  font-size: 0.9375rem;
}

.emote-ac-item.active,
.emote-ac-item:hover {
  background: #2a2a2a;
}

.emote-btn {
  align-items: center;
  background: none;
  border-radius: 0.375rem;
  border: none;
  color: #888;
  cursor: pointer;
  display: flex;
  flex-shrink: 0;
  padding: 0.3rem;
  transition: color 0.15s ease;
}

.emote-btn:hover {
  color: #789d2a;
}

.emote-inline {
  display: inline-block;
  height: var(--icon-xl);
  max-height: 60px;
  max-width: 180px;
  vertical-align: middle;
  width: auto;
  cursor: default;
}


.emote-inline.stv-sticker {
  height: 3rem;
  max-height: 3rem;
}

/* Плавающий тултип с названием эмоута при наведении (позиционируется из JS). */
.emote-tip {
  position: fixed;
  z-index: 9999;
  padding: 3px 8px;
  background: #000;
  color: #fff;
  font-size: 0.75rem;
  line-height: 1.2;
  border-radius: 5px;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}

.emote-picker {
  background: #1a1a1a;
  border-radius: 0.5rem;
  border: 1px solid #333;
  display: none;
  flex-direction: column;
  gap: 0.5rem;
  max-height: 22rem;
  padding: 0.625rem;
  position: fixed;
  width: 18rem;
  z-index: 100006;
}

.emote-picker-grid {
  align-content: flex-start;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  overflow-y: auto;
  padding: 0.125rem;
}

.emote-picker-item {
  align-items: center;
  background: none;
  border-radius: 0.25rem;
  border: none;
  cursor: pointer;
  display: flex;
  flex-shrink: 0;
  justify-content: center;
  padding: 0.25rem;
  transition: background 0.1s ease;
}

.emote-picker-item img {
  display: block;
  height: 32px;
  max-height: 32px;
  max-width: 96px;
  object-fit: contain;
  width: auto;
}

.emote-picker-item.just-added {
  background: rgba(120, 157, 42, 0.25);
}

.emote-picker-item.just-added,
.emote-picker-item:hover {
  background: #2e2e2e;
}

.emote-picker-search {
  background: #262626;
  border-radius: 0.375rem;
  border: 1px solid #333;
  box-sizing: border-box;
  color: #fff;
  font-family: inherit;
  font-size: 0.9375rem;
  outline: none;
  padding: 0.4rem 0.625rem;
  width: 100%;
}

.emote-picker-search:focus {
  border-color: #789d2a;
}





.file-count-danger {
  color: #ff6b6b;
}

.file-count-indicator {
  color: #789d2a;
  display: none;
  font-size: 0.875rem;
}

.file-count-success {
  color: #789d2a;
}

.file-stats {
  align-items: center;
  align-self: flex-start;
  color: #696969;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 0.625rem;
  max-width: 60rem;
  width: 100%;
}



.flex {
  display: flex !important;
}











/* ── Hashtag chip tokens ──────────────────────────────────────────
   Single source of truth for the hashtag tint ladder. Every value is
   derived from --brand / --ink (defined in static/style.css :root) via
   color-mix(), so changing the brand colour propagates here with no
   literal hex to keep in sync. Same chip idiom as .video-offer-cat
   (videos.css ~2803). The active tag filter is styled as a toolbar
   control instead — see .tag-filter-chip in feed.css.                */
:root {
  /* Fallback ladder for browsers without color-mix().
     !! These literals MUST stay in sync with style.css :root:
        --brand: #789d2a = rgb(120, 157, 42)
        --ink:   #ebebeb = rgb(235, 235, 235)
     They exist only because this project has no build step and no
     autoprefixer, so nothing backfills color-mix(). The @supports block
     below is the live definition on every modern browser. */
  --hashtag-bg:           rgba(120, 157, 42, 0.10);
  --hashtag-bg-hover:     rgba(120, 157, 42, 0.20);
  --hashtag-bg-active:    rgba(120, 157, 42, 0.30);
  --hashtag-border:       rgba(120, 157, 42, 0.32);
  --hashtag-border-hover: rgba(120, 157, 42, 0.60);
  --hashtag-ink-hover:    rgb(155, 180, 100);      /* 70% brand + 30% ink */
  --hashtag-radius:       999px;
  /* neutral hover wash for list rows, mirrors head.css .dropdown-item:hover */
  --hashtag-row-hover:    rgba(235, 235, 235, 0.06);
}

/* The derived definitions. Overriding the tokens (rather than each use
   site) keeps the fallback to a single block. NOTE: a plain
   "declare twice" fallback does NOT work for custom properties — a
   color-mix() token stream always parses, so the second declaration
   always wins and then becomes invalid-at-computed-value-time at the
   point of use, leaving background:transparent / border:currentColor.
   @supports is the only reliable gate without a build step. */
@supports (color: color-mix(in srgb, #000 10%, transparent)) {
  :root {
    --hashtag-bg:           color-mix(in srgb, var(--brand) 10%, transparent);
    --hashtag-bg-hover:     color-mix(in srgb, var(--brand) 20%, transparent);
    --hashtag-bg-active:    color-mix(in srgb, var(--brand) 30%, transparent);
    --hashtag-border:       color-mix(in srgb, var(--brand) 32%, transparent);
    --hashtag-border-hover: color-mix(in srgb, var(--brand) 60%, transparent);
    --hashtag-ink-hover:    color-mix(in srgb, var(--brand) 70%, var(--ink));
    --hashtag-row-hover:    color-mix(in srgb, var(--ink) 6%, transparent);
  }
}

/* Chip. Markup is <a class="hashtag hashtag-link"> — anchor defaults are
   reset here so no extra .hashtag-link rule is needed.
   NOTE: the "#" glyph is intentionally NOT dimmed. ::first-letter only
   applies to block containers, and this element is inline-flex, so there
   is no markup-free way to target it. Deliberately skipped. */
.hashtag {
  align-items: center;
  background: var(--hashtag-bg);
  border-radius: var(--hashtag-radius);
  border: 1px solid var(--hashtag-border);
  color: var(--brand);
  cursor: pointer;
  display: inline-flex;
  font-weight: 600;
  line-height: 1.15;
  padding: 1px 9px;
  text-decoration: none;
  /* Only the colour properties animate — never `all`. The 1px hover lift
     is deliberately instant so no layout-affecting property is tweened. */
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  white-space: nowrap;
}

.hashtag:hover {
  background: var(--hashtag-bg-hover);
  border-color: var(--hashtag-border-hover);
  color: var(--hashtag-ink-hover);
  transform: translateY(-1px);
}

/* Accessibility: these are keyboard-focusable links and had no focus
   style at all before. The outline is required, not decorative. */
.hashtag:focus-visible {
  background: var(--hashtag-bg-hover);
  border-color: var(--hashtag-border-hover);
  color: var(--hashtag-ink-hover);
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.hashtag:active {
  background: var(--hashtag-bg-active);
  border-color: var(--hashtag-border-hover);
  transform: translateY(0);
}

/* ── Hashtag autocomplete dropdown ───────────────────────────────
   Appearance owned entirely by CSS; the JS owns left/top/width only.
   `position: fixed` is mandatory and is declared here as well as inline:
   the dropdown is appended to <body> and positioned from viewport-relative
   getBoundingClientRect() coordinates, so `absolute` would misplace it
   the moment the inline style is absent. z-index matches AC_Z_INDEX
   (9999) in static/js/hashtags.js. Shape matches head.css .dropdown-menu. */
.hashtag-ac-dropdown {
  /* NO box-shadow anywhere in this file's hashtag styling — by request.
     Separation from the page comes from two things instead: a fully OPAQUE
     --surface fill, which is one step lighter than the --surface-ground page
     background so the panel reads as raised, and the --line-strong border
     (the codebase's existing "noticeable card border" token). */
  background: var(--surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line-strong);
  box-sizing: border-box;
  max-height: 264px;
  overflow-y: auto;
  /* keep a wheel/trackpad scroll inside the list from chaining to the page */
  overscroll-behavior: contain;
  padding: 5px;
  position: fixed;
  z-index: 9999;
}

/* Thin scrollbar, matching the .scrollable-list convention in admin.css */
.hashtag-ac-dropdown::-webkit-scrollbar {
  width: 6px;
}

.hashtag-ac-dropdown::-webkit-scrollbar-track {
  background: transparent;
}

.hashtag-ac-dropdown::-webkit-scrollbar-thumb {
  background: #222;
}

.hashtag-ac-dropdown::-webkit-scrollbar-thumb:hover {
  background: #333;
}

/* Rows stay deliberately compact: the transparent 1px border is always
   present so the keyboard-active state can colour it without ever shifting
   the text by a pixel. */
.hashtag-ac-item {
  align-items: baseline;
  border-radius: 9px;
  border: 1px solid transparent;
  cursor: pointer;
  display: flex;
  font-size: 0.95em;
  gap: 10px;
  justify-content: space-between;
  line-height: 1.25;
  padding: 5px 9px;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

/* Pointer hover: neutral wash, no border. */
.hashtag-ac-item:hover {
  background: var(--hashtag-row-hover);
}

/* Keyboard selection: brand tint + brand border + brightened text. Both
   states can be live at once (mouse resting on a different row than the one
   the arrow keys selected), so this is deliberately a different HUE, not
   just a stronger version of the hover wash. Declared after :hover so it
   wins on equal specificity — no !important needed.
   Colour-only difference: nothing here changes metrics, so arrowing through
   the list never reflows a row. */
.hashtag-ac-item--active,
.hashtag-ac-item--active:hover {
  background: var(--hashtag-bg-hover);
  border-color: var(--hashtag-border-hover);
}

.hashtag-ac-item--active .hashtag-ac-tag {
  color: var(--hashtag-ink-hover);
}

.hashtag-ac-item--active .hashtag-ac-count {
  color: var(--ink-2);
}

/* Long tags truncate instead of wrapping, so every row keeps the same
   height and the count column stays aligned. */
.hashtag-ac-tag {
  color: var(--brand);
  font-weight: 600;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* tabular-nums keeps the right-hand counts optically aligned down the list. */
.hashtag-ac-count {
  color: var(--ink-3);
  flex: none;
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .hashtag,
  .hashtag-ac-item {
    transition: none;
  }

  .hashtag:hover,
  .hashtag:focus-visible,
  .hashtag:active {
    transform: none;
  }
}

.hidden {
  display: none !important;
}



.loading-placeholder {
  color: #666;
  padding: 2.5rem 1.25rem;
  text-align: center;
}





.mb-16 {
  margin-bottom: 1rem;
}

.mb-20 {
  margin-bottom: 1.25rem;
}

.menu-icon {
  height: auto;
  max-height: 1.5rem;
  max-width: 1.5rem;
  width: auto;
}

.modal-content-narrow {
  max-width: 20rem !important;
  min-width: 20rem !important;
  width: 20rem !important;
}

.mt-15 {
  margin-top: 0.9375rem;
}

.mt-16 {
  margin-top: 1rem;
}

.mt-8 {
  margin-top: 0.5rem;
}

.no-decoration {
  text-decoration: none;
}





.preview-content {
  align-items: stretch;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  margin: 0 auto;
  max-width: 100%;
  overflow-x: hidden;
  padding: 0;
  position: relative;
  width: 100%;
}



.preview-wrapper {
  align-items: stretch;
  contain: layout;
  display: flex;
  justify-content: center;
  max-width: 100%;
  min-height: calc(100vh - 5rem);
  padding: 0;
  position: relative;
  width: 100%;
}

.profile-title {
  color: #fff;
  font-size: 1.5em;
  font-weight: 700;
  margin-bottom: 1.5rem;
}

.save-btn {
  align-items: center;
  background: #4caf50;
  border-radius: 0.5rem;
  border: none;
  cursor: pointer;
  display: none;
  flex-shrink: 0;
  height: 2.5rem;
  justify-content: center;
  width: 2.5rem;
}













.search-overlay {
  background: rgba(0, 0, 0, 0);
  bottom: 0;
  left: 0;
  position: fixed;
  right: 0;
  top: 0;
  z-index: 9998;
}





















.search-results-list {
  max-height: 60vh;
  overflow: auto;
  padding: 0.375rem 0;
}









.session-count {
  color: #999;
  font-size: 0.9rem;
  font-weight: 600;
}

.sessions-detail {
  flex: 1;
  overflow-y: auto;
  padding: 1rem;
}

.setting-desc {
  margin-top: 0.25rem;
}

.settings-grid {
  margin-bottom: 1.25rem;
}




















.stat-img {
  display: block;
  height: var(--icon-lg);
  object-fit: contain;
  width: var(--icon-lg);
}













.stat-value-accent {
  color: #789d2a;
}



.support-link {
  color: #789d2a;
  text-decoration: underline;
}






.text-muted {
  color: var(--ink-2);
}

.textarea-wrap {
  display: block;
  position: relative;
}

.textarea-wrap .char-counter {
  background: rgba(24, 24, 24, 0.75);
  border-radius: 4px;
  bottom: 8px;
  font-size: 0.75rem;
  margin: 0;
  padding: 1px 5px;
  pointer-events: none;
  position: absolute;
  right: 10px;
}



.warning-text {
  color: #ff6b6b;
  font-size: 0.9375rem;
  font-weight: 600;
  margin-bottom: 0.25rem;
}

/* --------------------------------------------------------------------------
   Scroll lock backstop — see window.lockScroll in static/js/header.js.

   The lock's freeze is inline (`position: fixed` + `top: -Npx` on <body>), and
   inline styles are exactly what a stray unlockScroll() used to strip. These
   rules make the `modal-open` CLASS sufficient on its own, so the locked state
   degrades to "cannot scroll" rather than "scrolls freely" if the inline half
   is ever lost again.

   Deliberately NOT restated here: `body.modal-open { overflow: hidden }`. It is
   already declared twice — style.css:1053 (with `!important`, alongside
   `height: 100%`) and, on profile pages only, profile.css:8. A third copy would
   just be another thing to keep in sync. What neither of those covers is <html>:
   `overflow: hidden` on <body> does not stop the root element scrolling, and
   style.css only ever gates that on the separate `no-scroll` class. This closes
   that gap for `modal-open` without touching either existing rule.

   :has() is the only way to reach <html> from a class the scripts put on <body>.
   Browsers without it (none in the supported matrix) simply fall back to the
   inline freeze, which is the current behaviour — no regression. -------------- */
html:has(> body.modal-open) {
  overflow: hidden;
}

body.modal-open {
  overscroll-behavior: none;
}

/* Freezing <body> removes the document scrollbar, so the viewport reflows 10px
   wider and the whole page slides sideways on open and back on close. Reserving
   the gutter keeps every box at the width it had while the scrollbar was there.

   This is deliberately NOT padding on <body>. `.main-header` is position:fixed,
   so it is laid out against the viewport and body padding cannot reach it — and
   its geometry is not even constant across breakpoints (measured: a centred
   1200px box at 1440 wide, but full-width at 1100), so per-element compensation
   was both incomplete and wrong at the second breakpoint. Reserving the gutter
   on the root fixes every fixed-position element at once, with no enumeration.

   Gated on the attribute, not on `.modal-open`, because the reservation is only
   correct when a scrollbar actually existed. A page too short to scroll has none,
   and reserving anyway shifts it the other way — measured at 1440x2400: <body>
   1440 -> 1430. header.js measures before freezing and sets the attribute only
   when the width is non-zero, so the measurement is the single source of truth
   and no scrollbar width is hardcoded here. */
html[data-scroll-lock-gutter] {
  scrollbar-gutter: stable;
}
