/**
 * ZY MoonShine shell chrome — authenticated admin cockpit.
 * Load AFTER zy-tokens.css. Do NOT redeclare :root brand hex primitives here.
 *
 * Maps ZY chrome (slate/ink/cobalt, r2 redesign) onto MoonShine DOM:
 *   .layout-menu / .menu-*  → dark ink sidebar, cobalt active
 *   .layout-page / content  → cool slate surface
 *   .btn-primary            → ink fill (not purple)
 *
 * --zy-lime is now a cobalt blue, not lime — see zy-tokens.css header.
 * Any rule that fills a solid --zy-lime background pairs it with WHITE
 * text (not --zy-black): cobalt is a mid-dark hue, unlike the old neon
 * lime, so it needs light-on-accent instead of dark-on-accent.
 *
 * These overrides also kill purple chrome even if PurplePalette still
 * injects vars (see --color-purple-* remap below).
 */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

/*
 * Remap MoonShine ColorManager hooks → ZY tokens.
 * Stock PurplePalette paints --ms-cm-primary purple — override at source.
 */
html,
html.light,
html.dark,
:root {
  /*
   * Safety net: colors-definer may win on --ms-cm-* (injected later).
   * Still pin semantic aliases + green scale so washes never go olive.
   */
  --color-purple-100: var(--zy-gray-100);
  --color-purple-200: var(--zy-gray-200);
  --color-purple-300: var(--zy-gray-300);
  --color-purple-400: var(--zy-gray-400);
  --color-purple-500: var(--zy-gray-500);
  --color-purple-600: var(--zy-ink);
  --color-purple-700: var(--zy-gray-700);
  --color-purple-800: var(--zy-gray-800);
  --color-purple-900: var(--zy-gray-900);

  /* tokens.css accidentally mapped green-600..800 → lime — break that */
  --color-green-100: #DCFCE7;
  --color-green-300: #86EFAC;
  --color-green-500: #15803D;
  --color-green-600: #15803D;
  --color-green-700: #166534;
  --color-green-800: #14532D;

  --font-sans: var(--font);
  --font-mono: var(--mono);

  --ms-layout-body-bg-color: var(--zy-cream);
  --ms-layout-body-color: var(--zy-ink);
  --ms-layout-page-bg-color: var(--zy-white);
  --ms-layout-page-border-color: var(--color-border);
  --ms-menu-item-active-bg-color: var(--zy-lime);
  --ms-menu-item-active-color: var(--zy-white);
  --ms-menu-item-active-icon-color: var(--zy-white);
  --ms-menu-item-active-glow-color: transparent;
}

body {
  margin: 0;
  min-height: 100dvh;
  font-family: var(--font-sans);
  font-size: var(--font-size-base);
  line-height: var(--leading-normal);
  color: var(--zy-ink);
  background-color: var(--zy-cream) !important;
  background-image: none;
}

a {
  color: var(--zy-ink);
  text-decoration: none;
  transition: color var(--motion-fast) var(--ease-in-out);
}

a:hover,
a:focus {
  color: var(--zy-ink);
  text-decoration: underline;
  text-decoration-color: var(--zy-lime);
  text-underline-offset: 0.15em;
}

:focus {
  outline: none;
}

:focus-visible {
  outline: var(--ring-width) solid var(--color-ring);
  outline-offset: var(--ring-offset);
}

/* ——— Shell wrapper ——— */
.layout-wrapper {
  background: var(--zy-cream);
  color: var(--zy-ink);
  font-family: var(--font-sans);
  /* Room for the ink rail’s rounded corners to read as a floating panel. */
  --ms-layout-wrapper-spacing: 0.75rem;
  --ms-layout-wrapper-gap-x: 0.75rem;
  --ms-layout-wrapper-gap-y: 0.75rem;
}

@media (min-width: 1024px) {
  .layout-wrapper {
    padding: var(--ms-layout-wrapper-spacing) !important;
    column-gap: var(--ms-layout-wrapper-gap-x) !important;
  }
}

.layout-main {
  background: transparent;
  min-width: 0;
}

.layout-content {
  background: transparent;
  color: var(--zy-ink);
}

/*
 * The page surface and the body behind it are both slate, so the border +
 * shadow drew a large rounded outline around nothing — a frame with no
 * figure. Boxes and tables inside carry their own edges; the shell does not
 * need one too.
 */
.layout-page {
  background: transparent !important;
  border: 0 !important;
  color: var(--zy-ink);
  box-shadow: none !important;
  padding-inline: var(--space-6) !important;
}

.layout-page h1,
.layout-page .title,
.layout-navigation h1 {
  font-family: var(--font-display);
  font-weight: var(--font-weight-bold);
  letter-spacing: -0.03em;
  color: var(--zy-ink);
}

/* Form hints must wrap, not clip */
.form-hint,
.form-group .form-hint,
.field-hint,
.form-hint-text,
.form-group > .form-hint {
  white-space: normal !important;
  overflow: visible !important;
  text-overflow: unset !important;
  max-width: min(42rem, 100%);
  color: var(--zy-gray-600) !important;
  line-height: 1.45;
  font-size: 0.8125rem;
}

/* Soft UUID cells — mono short id, full on title hover */
.zy-uuid,
.zy-uuid-cell .zy-uuid {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.8125rem;
  color: var(--zy-gray-600);
  letter-spacing: 0.01em;
  cursor: help;
}
.breadcrumbs,
.breadcrumbs a,
.breadcrumbs-item,
.layout-navigation .breadcrumbs {
  color: var(--zy-gray-600) !important;
}

.breadcrumbs a:hover {
  color: var(--zy-ink) !important;
}

/*
 * Top bar: breadcrumb left, tools right, hairline under. Previously the
 * breadcrumb sat at body size directly above an <h1> repeating its last
 * crumb, so the same word appeared twice at nearly the same weight.
 * The crumb shrinks to a caption; the h1 keeps the voice.
 */
.layout-navigation {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 2.75rem;
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--color-border);
  background: transparent;
}

.layout-navigation .breadcrumbs,
.breadcrumbs {
  font-size: var(--font-size-xs);
  letter-spacing: 0.01em;
}

.layout-navigation .breadcrumbs a:hover {
  text-decoration: none !important;
}

/*
 * The avatar placeholder is the brand mark — a blue rounded square. Clipped
 * to a circle at 32px it lost its silhouette and read as a loading spinner.
 * Squircle + hairline gives it an edge and reads as an account, not a state.
 */
.profile > *,
.profile .avatar,
.profile img {
  border-radius: var(--radius-md) !important;
  border: 1px solid var(--color-border);
  background: var(--zy-white);
}

.profile img[src*="logo-mark"] {
  object-fit: contain !important;
  padding: 2px;
}

.menu-burger .btn,
.layout-collapse-btn,
.layout-collapse .btn {
  background: var(--zy-gray-900) !important;
  color: var(--zy-white) !important;
  border-color: transparent !important;
}

.menu-burger .btn:hover,
.layout-collapse-btn:hover {
  background: var(--zy-gray-800) !important;
  color: var(--zy-lime) !important;
}

/* ——— Sidebar (dark ink rail, cobalt active) ——— */
.layout-menu,
.layout-menu-mobile,
aside.layout-menu {
  background: var(--zy-gray-900) !important;
  color: var(--zy-white) !important;
  /* Floating panel: no hard seam with the content column */
  border: 0 !important;
  border-right: 0 !important;
  /* Hard px — do not depend on --radius-* cascade / late MoonShine tokens */
  border-radius: 16px !important;
  box-shadow: none !important;
}

/*
 * Desktop rail only. MoonShine's wrapper is `flex-flow: column wrap` below
 * 1024px and `flex-direction: row` at lg. Sticky + 100dvh on the menu must
 * NOT apply on small screens: sticky stays in-flow, so a column wrapper
 * parks a full-viewport sidebar above the page and stretches the admin down.
 * Internal column flex on the rail is fine — do not disable flex globally.
 */
@media (min-width: 1024px) {
  .layout-wrapper {
    flex-direction: row !important;
    flex-wrap: nowrap !important;
  }

  .layout-menu,
  aside.layout-menu {
    position: sticky !important;
    top: var(--ms-layout-wrapper-spacing, 0.75rem) !important;
    align-self: start !important;
    flex: 0 0 auto;
    display: flex !important;
    flex-direction: column !important;
    height: calc(100dvh - (var(--ms-layout-wrapper-spacing, 0.75rem) * 2)) !important;
    max-height: calc(100svh - (var(--ms-layout-wrapper-spacing, 0.75rem) * 2)) !important;
    overflow: hidden !important;
    overscroll-behavior: contain;
  }
}

/* Brand block: match parent top radius so ink fill doesn’t square the corners */
.layout-menu .menu-header {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  background: var(--zy-gray-900);
  border-radius: 16px 16px 0 0;
  border-bottom-color: color-mix(in srgb, var(--zy-white) 10%, transparent) !important;
  /* Same horizontal inset as .menu-link so the mark lines up with icons below. */
  padding-inline: var(--ms-menu-item-padding-x, 0.625rem) !important;
  padding-top: 0.5rem;
  padding-bottom: 0.75rem !important;
}

.layout-menu .menu-logo,
.layout-menu .menu-logo a,
.layout-menu .menu-logo span {
  color: var(--zy-white) !important;
  font-family: var(--font-display);
  font-weight: var(--font-weight-semibold);
  letter-spacing: -0.02em;
  text-decoration: none !important;
}

.layout-menu .menu-logo,
.layout-menu .menu-logo a {
  display: inline-flex !important;
  align-items: center;
  gap: 0.5rem;
}

.layout-menu .menu--vertical,
.layout-menu .menu-list {
  background: transparent !important;
  min-height: 0;
  flex: 1 1 auto;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--zy-gray-700) transparent;
}

.layout-menu .menu-list::-webkit-scrollbar {
  width: 0.375rem;
}

.layout-menu .menu-list::-webkit-scrollbar-thumb {
  background: var(--zy-gray-700);
  border-radius: 999px;
}

.layout-menu .menu-divider,
.layout-menu .menu-divider span {
  color: color-mix(in srgb, var(--zy-white) 48%, transparent) !important;
  border-color: color-mix(in srgb, var(--zy-white) 10%, transparent) !important;
  font-size: 0.6875rem;
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.layout-menu .menu-link,
.layout-menu .menu-button,
.layout-menu .menu-item > a,
.layout-menu .menu-item > button {
  color: color-mix(in srgb, var(--zy-white) 78%, transparent) !important;
  background: transparent !important;
  border-radius: var(--radius-md);
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  text-decoration: none !important;
  box-shadow: none !important;
  /*
   * Eight top-level groups were spread over 400px at ~49px each — the rail
   * looked like a landing-page nav. At 34px all groups plus an expanded
   * submenu fit above the fold, which is what a rail is for.
   */
  min-height: 2.125rem;
  padding-block: 0.3125rem !important;
}

.layout-menu .menu-list > .menu-item + .menu-item {
  margin-top: 1px;
}

.layout-menu .menu-icon,
.layout-menu .menu-icon svg {
  width: 1rem;
  height: 1rem;
}

.layout-menu .menu-link:hover,
.layout-menu .menu-button:hover,
.layout-menu .menu-item > a:hover,
.layout-menu .menu-item > button:hover {
  background: color-mix(in srgb, var(--zy-white) 8%, transparent) !important;
  color: var(--zy-white) !important;
  text-decoration: none !important;
}

/*
 * Two DIFFERENT states, two different weights — do not merge them:
 *
 *   ._is-active         → the page you are on. Exactly one at a time.
 *                         Earns a solid accent fill.
 *   .menu-item--opened  → a group the operator expanded. Several can be open
 *                         at once, so a solid fill here paints the whole rail
 *                         blue and destroys the "you are here" signal.
 *                         Gets quiet emphasis: brighter label + accent arrow.
 */
.layout-menu > .menu .menu-list > .menu-item.menu-item--opened > .menu-link,
.layout-menu > .menu .menu-list > .menu-item.menu-item--opened > .menu-button,
.layout-menu .menu--vertical > .menu-list > .menu-item.menu-item--opened > .menu-link,
.layout-menu .menu--vertical > .menu-list > .menu-item.menu-item--opened > .menu-button {
  background: transparent !important;
  color: var(--zy-white) !important;
  font-weight: var(--font-weight-semibold) !important;
}

.layout-menu .menu--vertical > .menu-list > .menu-item.menu-item--opened > .menu-link:hover,
.layout-menu .menu--vertical > .menu-list > .menu-item.menu-item--opened > .menu-button:hover {
  background: color-mix(in srgb, var(--zy-white) 8%, transparent) !important;
  color: var(--zy-white) !important;
}

/* Current page — the only solid fill in the rail */
.layout-menu > .menu .menu-list > .menu-item._is-active > .menu-link,
.layout-menu > .menu .menu-list > .menu-item._is-active > .menu-button,
.layout-menu .menu--vertical > .menu-list > .menu-item._is-active > .menu-link,
.layout-menu .menu--vertical > .menu-list > .menu-item._is-active > .menu-button {
  background: var(--zy-lime) !important;
  color: var(--zy-white) !important;
  font-weight: var(--font-weight-semibold) !important;
}

.layout-menu .menu--vertical > .menu-list > .menu-item._is-active > .menu-link:hover,
.layout-menu .menu--vertical > .menu-list > .menu-item._is-active > .menu-button:hover {
  background: var(--zy-lime-bright) !important;
  color: var(--zy-white) !important;
}

.layout-menu .menu-icon,
.layout-menu .menu-arrow {
  color: inherit !important;
}

.layout-menu .menu--vertical > .menu-list > .menu-item._is-active .menu-icon,
.layout-menu .menu--vertical > .menu-list > .menu-item._is-active .menu-arrow {
  color: var(--zy-white) !important;
}

/* Opened group: accent lives in the arrow alone, not a full plate */
.layout-menu .menu--vertical > .menu-list > .menu-item.menu-item--opened > .menu-link .menu-arrow,
.layout-menu .menu--vertical > .menu-list > .menu-item.menu-item--opened > .menu-button .menu-arrow {
  color: var(--zy-lime-bright) !important;
}

.layout-menu .menu--vertical > .menu-list > .menu-item.menu-item--opened > .menu-link .menu-icon,
.layout-menu .menu--vertical > .menu-list > .menu-item.menu-item--opened > .menu-button .menu-icon {
  color: var(--zy-white) !important;
}

/*
 * Nested submenu — indentation + a hairline spine, NOT a bordered box.
 * Several groups can be open at once; boxes would stack into a ladder of
 * frames. The spine reads as "these belong to the group above" for free.
 * Active child: cobalt pill with white text.
 */
.layout-menu .menu-submenu {
  background: transparent !important;
  border: 0 !important;
  border-left: 1px solid color-mix(in srgb, var(--zy-white) 14%, transparent) !important;
  border-radius: 0 !important;
  margin-left: 1.35rem;
  padding-left: 0.5rem;
  color: var(--zy-white) !important;
}

.layout-menu .menu-submenu .menu-link,
.layout-menu .menu-submenu .menu-button,
.layout-menu .menu-submenu .menu-item > a,
.layout-menu .menu-submenu .menu-item > button,
.layout-menu .menu-submenu .menu-text {
  color: color-mix(in srgb, var(--zy-white) 82%, transparent) !important;
  background: transparent !important;
}

.layout-menu .menu-submenu .menu-link:hover,
.layout-menu .menu-submenu .menu-button:hover,
.layout-menu .menu-submenu .menu-item > a:hover,
.layout-menu .menu-submenu .menu-item > button:hover {
  background: color-mix(in srgb, var(--zy-white) 10%, transparent) !important;
  color: var(--zy-white) !important;
}

.layout-menu .menu-submenu .menu-item._is-active > .menu-link,
.layout-menu .menu-submenu .menu-item._is-active > .menu-button,
.layout-menu .menu-submenu a._is-active {
  background: var(--zy-lime) !important;
  color: var(--zy-white) !important;
  font-weight: var(--font-weight-semibold) !important;
}

.layout-menu .menu-submenu .menu-icon,
.layout-menu .menu-submenu .menu-arrow {
  color: color-mix(in srgb, var(--zy-white) 55%, transparent) !important;
}

.layout-menu .menu-submenu .menu-item._is-active .menu-icon,
.layout-menu .menu-submenu .menu-item._is-active .menu-arrow,
.layout-menu .menu-submenu .menu-item._is-active .menu-text {
  color: var(--zy-white) !important;
}

.layout-menu .menu-badge {
  background: var(--zy-lime) !important;
  color: var(--zy-white) !important;
}

.layout-menu .menu-footer,
.layout-menu .menu-actions {
  border-top-color: color-mix(in srgb, var(--zy-white) 10%, transparent);
  color: color-mix(in srgb, var(--zy-white) 70%, transparent);
}

.layout-menu .menu-actions .btn,
.layout-menu .theme-switcher {
  color: var(--zy-white) !important;
}

/* Kill purple glow under active menu links */
.layout-menu .menu-link:after,
.layout-menu .menu-button:after {
  box-shadow: none !important;
  background: transparent !important;
}

/* ——— Buttons ——— */
.btn,
button.btn,
a.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: var(--control-h);
  padding-inline: 0.875rem;
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-cta);
  border-radius: var(--radius-md);
  border-width: 1px;
  text-transform: none;
  box-shadow: none !important;
  transition:
    background-color var(--motion-fast) var(--ease-in-out),
    border-color var(--motion-fast) var(--ease-in-out),
    color var(--motion-fast) var(--ease-in-out),
    transform var(--motion-fast) var(--ease-out);
}

.btn:active {
  transform: scale(0.98);
}

.btn-primary,
.btn.btn-primary {
  --ms-btn-bg-color: var(--zy-ink);
  --ms-btn-border-color: var(--zy-ink);
  --ms-btn-color: var(--zy-white);
  background: var(--zy-ink) !important;
  border-color: var(--zy-ink) !important;
  color: var(--zy-white) !important;
}

.btn-primary:hover,
.btn.btn-primary:hover {
  background: var(--zy-gray-800) !important;
  border-color: var(--zy-gray-800) !important;
  color: var(--zy-white) !important;
}

.btn-secondary,
.btn.btn-secondary {
  --ms-btn-bg-color: var(--zy-white);
  --ms-btn-border-color: var(--color-border);
  --ms-btn-color: var(--zy-ink);
  background: var(--zy-white) !important;
  border-color: var(--color-border) !important;
  color: var(--zy-ink) !important;
}

.btn-secondary:hover,
.btn.btn-secondary:hover {
  background: var(--zy-lime-wash) !important;
  border-color: color-mix(in srgb, var(--zy-lime) 45%, var(--color-border)) !important;
  color: var(--zy-ink) !important;
}

.btn-success {
  --ms-btn-bg-color: var(--zy-lime);
  --ms-btn-border-color: var(--zy-lime);
  --ms-btn-color: var(--zy-white);
  background: var(--zy-lime) !important;
  border-color: var(--zy-lime) !important;
  color: var(--zy-white) !important;
}

.btn-success:hover {
  background: var(--zy-lime-bright) !important;
  border-color: var(--zy-lime-bright) !important;
  color: var(--zy-white) !important;
}

.btn-error {
  background: var(--color-destructive) !important;
  border-color: var(--color-destructive) !important;
  color: var(--color-destructive-foreground) !important;
}

.btn:disabled,
.btn[disabled] {
  opacity: var(--opacity-disabled);
  cursor: not-allowed;
}

/* ——— Cards / boxes (content surface) ——— */
.box,
.card,
.layout-page .box {
  background: var(--zy-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  color: var(--zy-ink);
  box-shadow: none;
  padding: var(--space-5) !important;
}

.box + .box {
  margin-top: var(--space-4);
}

/*
 * Section titles sat at body size in full ink, the same treatment as a field
 * label — so "Карточка" and "Название" competed. A section is a signpost, not
 * content: caption size, tracked out, muted, with the rule doing the dividing.
 */
.box-title,
.card-title,
.box > h3:first-child,
.box > .box-header {
  margin: 0 0 var(--space-4) !important;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  font-family: var(--font-sans);
  font-size: 0.6875rem !important;
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--zy-gray-500) !important;
}

/* Nested boxes are sub-sections — no second frame, just an indent rule */
.box .box {
  border: 0;
  border-left: 2px solid var(--color-border);
  border-radius: 0;
  padding: 0 0 0 var(--space-4) !important;
  background: transparent;
}

/* ———————————————————————————————————————————————————————————————
 * Forms
 *
 * The task form ran full-bleed: a one-line "Название" input was 1180px wide,
 * so the eye had to travel back across the whole page to find the next label,
 * and a 40-character value looked lost in its own field. Fields now cap at a
 * readable measure; genuinely wide inputs opt out below.
 * ——————————————————————————————————————————————————————————————— */
.form-input,
.form-textarea,
.form-select,
input[type="text"],
input[type="password"],
input[type="email"],
input[type="number"],
input[type="search"],
input[type="datetime-local"],
input[type="date"],
textarea,
select {
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  color: var(--zy-ink);
  background: var(--zy-white);
  border-color: var(--color-input);
  border-radius: var(--radius-md);
}

.form-group .form-input,
.form-group .form-select,
.form-group input[type="text"],
.form-group input[type="password"],
.form-group input[type="email"],
.form-group input[type="number"],
.form-group input[type="datetime-local"],
.form-group input[type="date"],
.form-group select.form-select {
  max-width: var(--field-max);
  min-height: var(--control-h);
}

/* Long-form content earns the extra width */
.form-group textarea,
.form-group .form-textarea,
.form-group [data-field-selector="childRelations"] .form-input,
.form-group .ts-wrapper {
  max-width: var(--field-max-wide);
}

.form-label {
  display: block;
  margin-bottom: 0.3125rem;
  font-size: var(--font-size-label);
  font-weight: var(--font-weight-medium);
  color: var(--zy-gray-700);
}

.form-label .required {
  color: var(--color-destructive);
  margin-left: 0.125rem;
}

/*
 * Hints outnumber labels on this form and were set at label size in the same
 * dark grey, so every field read as two headings. They drop to caption size.
 */
.form-group .form-hint,
.form-hint {
  margin-top: 0.3125rem;
  font-size: 0.75rem !important;
  color: var(--zy-gray-500) !important;
}

.form-group + .form-group {
  margin-top: var(--space-4);
}

/* Search / codewords / ids read better in the mono face */
.form-group [name*="uuid"],
.form-group [name*="_id"],
.form-group [name="code"],
.form-group [name*="key"] {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
}

/* ———————————————————————————————————————————————————————————————
 * Switcher
 *
 * MoonShine's toggler inherited a cream track from the pre-cobalt palette
 * with a black knob riding it — off and on were nearly the same shape and it
 * read as an unstyled control. Slate track / cobalt when on / white knob.
 * ——————————————————————————————————————————————————————————————— */
.form-switcher .form-switcher-toggler {
  background: var(--zy-gray-300) !important;
  border: 0 !important;
  border-radius: 999px !important;
  transition: background-color var(--motion-fast) var(--ease-in-out);
}

.form-switcher .form-switcher-toggler::after,
.form-switcher .form-switcher-toggler::before {
  background: var(--zy-white) !important;
  border-color: var(--zy-white) !important;
  box-shadow: var(--shadow-sm);
}

.form-switcher .form-switcher-control:checked ~ .form-switcher-toggler {
  background: var(--zy-lime) !important;
}

.form-switcher .form-switcher-control:focus-visible ~ .form-switcher-toggler {
  outline: var(--ring-width) solid var(--color-ring);
  outline-offset: var(--ring-offset);
}

/* Inline switcher rows: control, label, hint on one baseline */
.form-group-inline {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  flex-wrap: wrap;
}

.form-group-inline .form-label {
  margin-bottom: 0;
}

.form-input:focus,
.form-textarea:focus,
.form-select:focus,
input:focus,
textarea:focus,
select:focus {
  border-color: var(--zy-lime) !important;
  outline: var(--ring-width) solid var(--zy-lime);
  outline-offset: var(--ring-offset);
}

input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--zy-lime);
}

/* Native selects (Task type etc.) — readable, full width */
select.form-select,
.form-group select.form-select {
  display: block;
  width: 100%;
  min-height: 2.5rem;
  padding: 0.5rem 2.25rem 0.5rem 0.75rem;
  appearance: auto;
  -webkit-appearance: menulist;
  cursor: pointer;
}

/*
 * Tom Select mounts dropdown on body (dropdownParent=body).
 * Without an explicit stack + ink colors, list can sit under the page
 * surface or look “empty” on cream/light chrome.
 *
 * The list itself has no max-height. In a create/edit modal the options
 * below the dialog fold are clipped; the inner thumb sits at the bottom
 * and wheel/drag cannot reach them. Cap the option pane so it scrolls.
 */
body > .ts-dropdown,
.ts-dropdown {
  z-index: 10050 !important;
  background: var(--zy-white) !important;
  color: var(--zy-ink) !important;
  border: 1px solid var(--color-border) !important;
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.12);
}

.ts-dropdown .ts-dropdown-content {
  max-height: min(42vh, 16rem) !important;
  overflow-y: auto !important;
  overscroll-behavior: contain;
}

body > .ts-dropdown .option,
.ts-dropdown .option,
body > .ts-dropdown .no-results {
  color: var(--zy-ink) !important;
  background: var(--zy-white) !important;
}

body > .ts-dropdown .option.active,
body > .ts-dropdown .option.selected,
.ts-dropdown .option.active,
.ts-dropdown .option.selected {
  background: var(--zy-gray-100, #f3f4f6) !important;
  color: var(--zy-ink) !important;
}

.ts-wrapper .ts-control {
  background: var(--zy-white) !important;
  color: var(--zy-ink) !important;
  border-color: var(--color-input) !important;
}

/* ———————————————————————————————————————————————————————————————
 * Tables — the primary work surface
 *
 * Rows were ~78px tall because badge padding pushed them open, so a screen
 * held nine records. Density here is a feature: chips shrank, cells tightened,
 * and the header sticks so column meaning survives a long scroll.
 * ——————————————————————————————————————————————————————————————— */
.table,
table {
  color: var(--zy-ink);
  font-size: var(--font-size-table);
  border-collapse: separate;
  border-spacing: 0;
}

.table thead th,
table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--zy-gray-50) !important;
  color: var(--zy-gray-600) !important;
  padding: var(--cell-pad-y) var(--cell-pad-x) !important;
  font-family: var(--font-sans);
  font-size: 0.6875rem;
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  border-bottom: 1px solid var(--zy-gray-200) !important;
}

.table tbody td,
table tbody td {
  padding: var(--cell-pad-y) var(--cell-pad-x) !important;
}

/*
 * Sort affordances sat at full ink on all nine columns, so the header read
 * as a row of arrows with words attached. They recede until hovered.
 */
.table thead th a .icon-wrapper,
.table thead th a svg {
  opacity: 0.35;
  transition: opacity var(--motion-fast) var(--ease-in-out);
}

.table thead th a:hover {
  color: var(--zy-ink) !important;
  text-decoration: none !important;
}

.table thead th a:hover .icon-wrapper,
.table thead th a:hover svg {
  opacity: 1;
}

/*
 * Row hover was a cobalt wash, which made a merely-hovered row look selected.
 * Neutral tint hovers; cobalt stays reserved for actual selection.
 */
.table tbody tr:hover,
table tbody tr:hover {
  background: var(--zy-gray-50) !important;
}

.table tbody tr:has(.form-checkbox:checked) {
  background: var(--chip-accent-bg) !important;
}

/*
 * Nine columns do not fit 1190px, and the old table clipped the last two at
 * the page edge with no hint that anything was missing. Scroll the table, and
 * fade the right edge so the cut is legible as "there is more".
 */
.table-responsive {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.table-container {
  position: relative;
  background: var(--zy-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

/*
 * Edge shadow via background-attachment: the `local` layers sit on the
 * scrolled content and cover the `scroll` shadow layers when there is
 * nothing more to scroll to — so the hint appears only when the table is
 * genuinely cut off, which a fixed overlay could never do.
 */
.table-responsive {
  background:
    linear-gradient(to right, var(--zy-white) 30%, transparent) left center,
    linear-gradient(to left, var(--zy-white) 30%, transparent) right center,
    radial-gradient(farthest-side at 0 50%, color-mix(in srgb, var(--zy-ink) 12%, transparent), transparent) left center,
    radial-gradient(farthest-side at 100% 50%, color-mix(in srgb, var(--zy-ink) 12%, transparent), transparent) right center;
  background-repeat: no-repeat;
  background-size: 2.5rem 100%, 2.5rem 100%, 0.75rem 100%, 0.75rem 100%;
  background-attachment: local, local, scroll, scroll;
}

.table-responsive::-webkit-scrollbar {
  height: 0.5rem;
}

.table-responsive::-webkit-scrollbar-thumb {
  background: var(--zy-gray-300);
  border-radius: 999px;
}

/* Mono for IDs / numbers */
.font-mono,
code,
.table td[data-column-selection*="id"],
.table .field-id {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ———————————————————————————————————————————————————————————————
 * Badges → status chips
 *
 * Previously every badge was a solid fill: ink for type, cobalt for
 * "Активна", cobalt for the activity window. A task row therefore carried
 * three saturated blocks — including one wrapping a 40-character date range —
 * and the table read as confetti with no scannable hierarchy.
 *
 * Chips are tinted instead: hue in the background, ink in the text. A whole
 * column of them still reads as text, and the one place a solid accent
 * survives (current nav item, primary submit) keeps its meaning.
 * ——————————————————————————————————————————————————————————————— */
.badge,
[class*="badge-"] {
  display: inline-flex !important;
  align-items: center;
  gap: 0.3em;
  min-height: 1.375rem;
  padding: 0.0625rem 0.4375rem !important;
  border: 1px solid transparent;
  border-radius: var(--radius-sm) !important;
  font-size: 0.75rem !important;
  font-weight: var(--font-weight-medium) !important;
  line-height: 1.35;
  letter-spacing: 0;
  white-space: nowrap;
  vertical-align: baseline;
}

/* Default / primary / gray / secondary → neutral chip */
.badge,
.badge-primary,
.badge-gray,
.badge-secondary {
  background: var(--chip-neutral-bg) !important;
  color: var(--chip-neutral-fg) !important;
  border-color: var(--chip-neutral-bd) !important;
}

.badge-purple,
.badge-blue {
  background: var(--chip-accent-bg) !important;
  color: var(--chip-accent-fg) !important;
  border-color: var(--chip-accent-bd) !important;
}

.badge-success,
.badge-green {
  background: var(--chip-success-bg) !important;
  color: var(--chip-success-fg) !important;
  border-color: var(--chip-success-bd) !important;
}

.badge-warning,
.badge-yellow,
.badge-orange {
  background: var(--chip-warning-bg) !important;
  color: var(--chip-warning-fg) !important;
  border-color: var(--chip-warning-bd) !important;
}

.badge-error,
.badge-danger,
.badge-red {
  background: var(--chip-danger-bg) !important;
  color: var(--chip-danger-fg) !important;
  border-color: var(--chip-danger-bd) !important;
}

.badge-info,
.badge-teal,
.badge-cyan {
  background: var(--chip-info-bg) !important;
  color: var(--chip-info-fg) !important;
  border-color: var(--chip-info-bd) !important;
}

/*
 * A leading dot gives the chip a fixed-width anchor, so a column of chips of
 * different label lengths still lines up on one vertical edge — and it keeps
 * the status legible for anyone who can't separate the tints by hue alone.
 */
.table .badge::before,
.table [class*="badge-"]::before {
  content: "";
  flex: 0 0 auto;
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.75;
}

/* The nav counter badge is chrome, not data — it keeps its solid fill. */
.layout-menu .menu-badge::before {
  content: none;
}

/* ——— Alerts — never dark-on-olive; soft wash + readable ink ——— */
.alert,
.alert-default {
  background: var(--zy-cream-deep) !important;
  color: var(--zy-ink) !important;
  border-color: var(--color-border) !important;
}

.alert-primary {
  background: var(--zy-ink) !important;
  color: var(--zy-white) !important;
  border-color: var(--zy-ink) !important;
}

.alert-success {
  color: #14532D !important;
  background: color-mix(in srgb, #15803D 12%, var(--zy-white)) !important;
  border-color: color-mix(in srgb, #15803D 35%, var(--color-border)) !important;
}

.alert-warning {
  color: #92400E !important;
  background: color-mix(in srgb, var(--color-warning) 14%, var(--zy-white)) !important;
  border-color: color-mix(in srgb, var(--color-warning) 35%, var(--color-border)) !important;
}

.alert-error,
.alert-danger {
  color: #991B1B !important;
  background: color-mix(in srgb, var(--color-error) 12%, var(--zy-white)) !important;
  border-color: color-mix(in srgb, var(--color-error) 35%, var(--color-border)) !important;
}

.alert-info {
  color: #115E59 !important;
  background: color-mix(in srgb, var(--color-info) 12%, var(--zy-white)) !important;
  border-color: color-mix(in srgb, var(--color-info) 35%, var(--color-border)) !important;
}

/* Profile dropdown — ink logout, readable on cream */
.profile .dropdown-body,
.dropdown-body {
  background: var(--zy-white) !important;
  color: var(--zy-ink) !important;
  border-color: var(--color-border) !important;
}

.profile .dropdown-footer {
  background: var(--zy-cream-deep) !important;
  border-top-color: var(--color-border) !important;
}

.profile .dropdown-footer .btn,
.profile .dropdown-footer button.btn {
  background: var(--zy-ink) !important;
  color: var(--zy-white) !important;
  border: 0 !important;
  font-weight: var(--font-weight-semibold) !important;
}

.profile .dropdown-footer .btn:hover,
.profile .dropdown-footer button.btn:hover {
  background: var(--zy-gray-800) !important;
  color: var(--zy-white) !important;
}

.profile .name,
.profile .email,
.dropdown-menu-item,
.dropdown-menu-item a {
  color: var(--zy-ink) !important;
}

/* ——— Footer ——— */
.layout-footer {
  color: var(--color-muted-foreground);
  font-size: var(--font-size-xs);
  border-top: 1px solid var(--color-border);
  background: transparent;
}

/* ——— Overlay / mobile menu ——— */
.layout-overlay {
  background: var(--color-scrim);
}

/* ——— Responsive ——— */
@media (max-width: 1023px) {
  /*
   * Restore MoonShine off-canvas: fixed + translate, out of the column flow.
   * Class is `_is-opened` (vendor), not `_is-open`.
   */
  .layout-menu,
  aside.layout-menu,
  .layout-menu-mobile {
    position: fixed !important;
    inset-block: 0 !important;
    left: 0 !important;
    top: 0 !important;
    height: 100dvh !important;
    max-height: 100dvh !important;
    width: min(16rem, 85vw) !important;
    max-width: min(16rem, 85vw);
    z-index: 40;
    border-radius: 0 !important;
    display: flex !important;
    flex-direction: column !important;
  }

  .layout-menu:not(._is-opened),
  aside.layout-menu:not(._is-opened) {
    transform: translateX(-100%) !important;
  }

  .layout-menu._is-opened,
  aside.layout-menu._is-opened {
    transform: none !important;
  }
}

@media (max-width: 767px) {
  .layout-content,
  .layout-page {
    padding-inline: var(--space-3);
  }

  body {
    font-size: var(--font-size-base);
  }
}

/* ——— Reduced motion ——— */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .btn:active {
    transform: none;
  }
}

/* ———————————————————————————————————————————————————————————————
 * Login (MoonShine .authentication)
 *
 * Three separate faults met on this screen:
 *  1. `place-items: center` centred the logo row and the card row
 *     *independently*, parking the mark ~200px above the card with dead slate
 *     between them. `align-content` + a gap centres them as one stack.
 *  2. logo.svg's wordmark is filled #F3F5F7 for the dark rail — on the slate
 *     login background it was invisible, so the mark alone floated with no
 *     name attached. The card heading already says "Золотое Яблоко", so the
 *     login shows the mark only and the wordmark stays a sidebar asset.
 *  3. MoonShine's stock `.authentication-content::before` paints a 53×4 ink
 *     bar at top-left of the card. Landing on our cobalt top border it read
 *     as a black notch chipped out of the card edge.
 * ——————————————————————————————————————————————————————————————— */
.authentication {
  min-height: 100dvh !important;
  background: var(--zy-cream) !important;
  color: var(--zy-ink) !important;
  font-family: var(--font-sans) !important;
  display: grid !important;
  grid-auto-rows: min-content !important;
  justify-items: center !important;
  align-content: center !important;
  gap: var(--space-6) !important;
  padding: var(--space-6) !important;
}

.authentication .authentication-logo,
.authentication-logo {
  display: flex;
  justify-content: center;
  width: 100%;
}

/* Wordmark is light-on-dark — sidebar only. Login gets the mark. */
.authentication-logo img[src*="logo.svg"] {
  display: none !important;
}

.authentication-logo img[src*="logo-mark"] {
  display: block !important;
  height: 2.75rem !important;
  width: auto;
}

.authentication .authentication-content,
.authentication-content {
  position: relative;
  width: min(23rem, 100%) !important;
  margin: 0 auto !important;
  padding: var(--space-8) var(--space-6) !important;
  background: var(--zy-white) !important;
  border: 1px solid var(--color-border) !important;
  border-top: 3px solid var(--zy-lime) !important;
  border-radius: var(--radius-lg, 0.5rem) !important;
  box-shadow: var(--shadow-md, 0 4px 16px color-mix(in srgb, var(--zy-ink) 8%, transparent));
}

/* Kill the stock decorative bar (see note 3 above) */
.authentication .authentication-content::before,
.authentication-content::before,
.authentication .authentication-content::after,
.authentication-content::after {
  content: none !important;
  display: none !important;
}

.authentication .authentication-header,
.authentication-header {
  margin-bottom: var(--space-6);
  text-align: center;
}

.authentication .authentication-header .heading,
.authentication-header .heading {
  color: var(--zy-ink) !important;
  font-family: var(--font-display, var(--font-sans)) !important;
  font-size: var(--font-size-xl) !important;
  font-weight: 700 !important;
  letter-spacing: -0.02em;
}

.authentication .authentication-header .heading h1 {
  font-size: inherit !important;
  margin: 0;
}

.authentication .authentication-header .description,
.authentication-header .description {
  margin-top: 0.25rem;
  font-size: var(--font-size-sm) !important;
  color: var(--zy-gray-500) !important;
}

/* Submit is the only action here — let it own the full width */
.authentication .btn-primary,
.authentication-form .btn-primary {
  width: 100%;
  min-height: 2.5rem;
  margin-top: var(--space-2);
}

.authentication .form-group .form-input,
.authentication .form-group input {
  max-width: 100% !important;
}

.authentication .authentication-form,
.authentication-form.zy-login-form {
  margin-top: var(--space-4);
}

.authentication .btn-primary,
.authentication-form .btn-primary {
  background: var(--zy-ink) !important;
  color: var(--zy-white) !important;
  border-color: var(--zy-ink) !important;
}

.authentication .btn-primary:hover,
.authentication-form .btn-primary:hover {
  background: var(--zy-gray-800) !important;
  border-color: var(--zy-gray-800) !important;
}

.authentication .form-input:focus {
  outline: 2px solid var(--zy-lime) !important;
  outline-offset: 1px;
  border-color: var(--zy-ink) !important;
}

/* ——— Layout page: cream cockpit, not stark white ——— */
.layout-page,
.layout-content,
main.layout-page,
.layout .layout-page {
  background: var(--zy-cream) !important;
}

.layout-menu {
  background: var(--zy-ink) !important;
}

.layout-menu .logo img,
.layout-menu .menu-logo img,
aside img[src*="images/zy"] {
  max-height: 2rem;
  width: auto;
}

/*
 * Footer — a hairline and a caption, nothing more. The filled grey strip read
 * as a UI band with a job to do; it only holds a copyright line.
 */
.layout-footer,
footer.layout-footer,
.layout .footer {
  background: transparent !important;
  border-top: 1px solid var(--color-border) !important;
  color: var(--zy-gray-400) !important;
  font-size: 0.6875rem;
  padding-block: var(--space-4) var(--space-2) !important;
  margin-top: var(--space-8);
}

/* Table cells: readable titles; only UUID soft-shortens */
.table-list td,
.table td {
  vertical-align: middle;
  max-width: none;
  white-space: normal;
  overflow: visible;
}

/*
 * Title columns carry the only human-readable value in the row — they get the
 * ink weight. Everything else (ids, timestamps, counts) is reference data and
 * sits back a step so the eye lands on the name first.
 */
.table td:nth-child(3) {
  font-weight: var(--font-weight-medium);
}

.table td time,
.table td .zy-datetime {
  color: var(--zy-gray-600);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.table-list td .zy-uuid,
.table td .zy-uuid {
  display: inline-block;
  max-width: 7.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

.table-list .badge,
.table .badge {
  white-space: nowrap;
}
.table,
table.table,
.table-list,
.layout-page table {
  width: 100%;
  background: var(--zy-white) !important;
  color: var(--zy-ink) !important;
}

/* Timestamps read as one token — never let them wrap to two lines */
.table td:has(> time) {
  white-space: nowrap;
}

.table tbody td,
.table tbody th,
table.table tbody td {
  color: var(--zy-ink) !important;
  background: transparent !important;
}

.table tbody tr,
table.table tbody tr {
  background: var(--zy-white) !important;
  color: var(--zy-ink) !important;
  border-color: var(--color-border) !important;
}

.theme-switcher {
  display: none !important;
}

/* ———————————————————————————————————————————————————————————————
 * Dashboard cockpit
 *
 * `auto-fit, minmax(140px, 1fr)` packed seven tiles into row one and left the
 * eighth stranded on row two — a ragged edge that read as a rendering fault.
 * A fixed four-column grid gives 8 tiles two clean rows at any width the
 * sidebar leaves us.
 * ——————————————————————————————————————————————————————————————— */
.zy-dash {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.zy-dash-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-3);
}

@media (max-width: 1180px) {
  .zy-dash-stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/*
 * The old tiles carried a 3px coloured cap each — ink, cobalt, red, green,
 * green, cobalt — with no rule behind the assignment, so "Пользователи" and
 * "Failed BT" looked equally urgent. Colour now marks one thing only: a
 * number an operator has to act on. Everything else is a plain count.
 */
.zy-stat {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: var(--space-4);
  background: var(--zy-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  color: inherit;
  text-decoration: none !important;
  transition:
    border-color var(--motion-fast) var(--ease-in-out),
    background-color var(--motion-fast) var(--ease-in-out);
}

a.zy-stat:hover {
  border-color: var(--zy-gray-300);
  background: var(--zy-gray-50);
  text-decoration: none !important;
}

.zy-stat__label {
  order: -1;
  font-size: 0.6875rem;
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--zy-gray-500);
  /* Two-word labels wrapped unevenly and knocked the numbers off a shared
     baseline — reserve both lines so every tile is the same height. */
  min-height: 2.1em;
  line-height: 1.35;
  text-wrap: balance;
}

.zy-stat__value {
  font-family: var(--font-mono);
  font-size: 1.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--zy-ink);
  letter-spacing: -0.04em;
  line-height: 1.05;
}

/*
 * Attention states — an inset rule rather than a thicker border, so the
 * tile's geometry is identical to its neighbours. Widening border-left
 * pushed the content 2px right and left the marker sitting outside the
 * rounded corner, which read as a rendering seam.
 */
.zy-stat--warn {
  box-shadow: inset 3px 0 0 var(--color-warning);
}

.zy-stat--danger {
  box-shadow: inset 3px 0 0 var(--color-destructive);
}

.zy-stat--warn .zy-stat__value {
  color: #8A4B04;
}

.zy-stat--danger .zy-stat__value {
  color: var(--color-destructive);
}

/* ——— Dashboard panels ——— */
.zy-dash-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 1024px) {
  .zy-dash-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/*
 * Both panels wore a cobalt cap and a drop shadow, so a link list and a
 * three-line how-to floated like the most important things on the page.
 * They are reference material: flat surface, hairline border, quiet title.
 */
.zy-dash-card {
  padding: var(--space-4) var(--space-5) var(--space-5);
  background: var(--zy-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.zy-dash-card__title {
  margin: 0 0 var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  font-size: 0.6875rem;
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--zy-gray-500);
}

/*
 * Three shortcuts rendered as three filled buttons — two ink, one cobalt for
 * no stated reason — which claimed more emphasis than the KPI row above.
 * They are navigation, so they look like navigation: a list of rows that
 * light up on hover, with the accent left for the one queue that needs work.
 */
.zy-dash-actions {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.zy-dash-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 0.5rem 0.625rem;
  margin-inline: -0.625rem;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--zy-ink) !important;
  text-decoration: none !important;
  font-weight: var(--font-weight-medium);
  font-size: var(--font-size-sm);
  transition: background-color var(--motion-fast) var(--ease-in-out);
}

.zy-dash-btn::after {
  content: "→";
  color: var(--zy-gray-400);
  font-size: 0.875rem;
}

.zy-dash-btn:hover {
  background: var(--zy-gray-50);
  color: var(--zy-ink) !important;
  text-decoration: none !important;
}

.zy-dash-btn:hover::after {
  color: var(--zy-lime);
}

.zy-dash-btn--accent {
  color: var(--chip-accent-fg) !important;
  font-weight: var(--font-weight-semibold);
}

.zy-dash-btn--accent:hover {
  background: var(--chip-accent-bg);
  color: var(--chip-accent-fg) !important;
}

.zy-dash-steps {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: zy-step;
  color: var(--zy-gray-600);
  font-size: var(--font-size-sm);
  line-height: 1.5;
}

.zy-dash-steps li {
  display: grid;
  grid-template-columns: 1.25rem minmax(0, 1fr);
  gap: 0.625rem;
  counter-increment: zy-step;
}

.zy-dash-steps li::before {
  content: counter(zy-step);
  display: grid;
  place-items: center;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: var(--zy-gray-100);
  color: var(--zy-gray-600);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: var(--font-weight-semibold);
}

.zy-dash-steps li + li {
  margin-top: 0.625rem;
}

/* ———————————————————————————————————————————————————————————————
 * Page header + toolbar
 *
 * Every page opened with a breadcrumb, then an <h1> repeating the last
 * crumb, then 40px of slate, then the toolbar — three separate bands of
 * chrome before any data. Tighten the run-in so records start higher.
 * ——————————————————————————————————————————————————————————————— */
.layout-content {
  padding-block: var(--space-4) 0 !important;
}

.layout-page h1,
.layout-content > h1 {
  margin: 0 0 var(--space-4) !important;
  font-size: var(--font-size-2xl) !important;
  line-height: var(--leading-tight);
}

/* Index toolbar: create left, filters/search right, on one line */
.layout-content form + .table-container,
.layout-content .table-container {
  margin-top: var(--space-4);
}

/* ———————————————————————————————————————————————————————————————
 * MoonShine metric cards (Метрики заданий)
 *
 * Stock metrics render an icon, then a 2rem number, then a label inside
 * ~1.5rem of padding — five KPIs filled a full screen and the fifth wrapped
 * alone onto a third row. Same tile language as the dashboard, three up.
 * ——————————————————————————————————————————————————————————————— */
.zy-metrics-cards {
  gap: var(--space-3) !important;
}

/* Stock markup hard-codes xl:col-span-6 → two per row, so five KPIs left a
   stranded fifth on its own row. Three up closes the ragged edge. */
.zy-metrics-cards > [class*="col-span"] {
  grid-column: span 4 / span 4 !important;
}

@media (max-width: 1180px) {
  .zy-metrics-cards > [class*="col-span"] {
    grid-column: span 6 / span 6 !important;
  }
}

.zy-metrics-cards .box {
  padding: 0 !important;
}

/*
 * Each KPI was ~190px tall for one number: a 24px icon on its own line, a
 * 2rem value, then the label, inside 1.5rem of padding. Same tile language as
 * the dashboard — label first as a caption, number underneath.
 *
 * Markup is .report-card > [.report-card-heading (icon only)]
 *                        > .report-card-body > [.report-card-value, h5.report-card-title]
 */
.report-card {
  padding: var(--space-4) !important;
}

/* The icon is the same generic outline glyph on every tile — it distinguishes
   nothing and cost a full line of height. */
.report-card-heading {
  display: none !important;
}

.report-card-body {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

/*
 * Sentence case here, unlike the dashboard's uppercase KPI captions: these
 * labels are full clauses ("Пользователи с ≥1 выполненным заданием"), and
 * uppercase at that length costs the word-shape cues that make them readable.
 * Short labels get small-caps; sentences get sentences.
 */
.report-card-title,
.report-card h5.report-card-title {
  order: -1;
  margin: 0 !important;
  font-size: 0.75rem !important;
  font-weight: var(--font-weight-medium) !important;
  letter-spacing: 0;
  text-transform: none;
  color: var(--zy-gray-500) !important;
  line-height: 1.4;
  /* Reserve two lines so the numbers across a row share one baseline
     instead of floating at three different heights. */
  min-height: 2.3em;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.report-card-value,
.report-card .report-card-value {
  font-family: var(--font-mono);
  font-size: 1.75rem !important;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--zy-ink);
  letter-spacing: -0.04em;
  line-height: 1.05;
}

.report-card-footer {
  font-size: var(--font-size-sm);
  color: var(--zy-gray-600);
}

/* The pending KPI is the one tile you can click — mark it, don't inflate it */
.zy-metric-pending-card {
  position: relative;
  text-decoration: none !important;
  transition:
    border-color var(--motion-fast) var(--ease-in-out),
    background-color var(--motion-fast) var(--ease-in-out);
}

.zy-metric-pending-card:hover {
  border-color: var(--zy-gray-300) !important;
  background: var(--zy-gray-50) !important;
}

.zy-metric-pending-card__cue {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  color: var(--zy-gray-400);
  font-size: 0.875rem;
}

.zy-metric-pending-card:hover .zy-metric-pending-card__cue {
  color: var(--zy-lime);
}

/*
 * Обрезка текста в таблицах — глобально.
 *
 * MoonShine оборачивает содержимое ячеек в .text-ellipsis с ограничением
 * max-width:24ch, white-space:nowrap и overflow:hidden. Правила про form-hint
 * выше эти ячейки не затрагивают, поэтому подписи резались на 24 символах:
 * шесть строк «Завершили страну на 100%: …» выглядели одинаково, потому что
 * сам префикс длиннее лимита и название страны не попадало в видимую часть.
 *
 * Здесь правило снято для всех таблиц админки, а не для одной: текст
 * переносится по словам вместо обрезки. Ограничение по ширине оставлено,
 * чтобы длинные значения (например, текст ошибки доставки) переносились
 * в несколько строк, а не растягивали таблицу по горизонтали.
 */
.table .text-ellipsis,
.table-list .text-ellipsis,
table .text-ellipsis {
  max-width: min(32rem, 100%) !important;
  white-space: normal !important;
  overflow: visible !important;
  text-overflow: unset !important;
  overflow-wrap: anywhere;
  line-height: 1.4;
}

/*
 * Таблицы метрик дополнительно: колонка названия шире, колонка значения
 * по содержимому.
 */
.zy-metrics-table.table,
.zy-metrics-table.table-list,
table.zy-metrics-table {
  table-layout: auto;
}

.zy-metrics-table td:first-child,
.zy-metrics-table th:first-child,
.zy-metrics-table .text-ellipsis {
  min-width: min(18rem, 100%);
  max-width: none !important;
  white-space: normal !important;
  overflow: visible !important;
  text-overflow: unset !important;
  overflow-wrap: break-word;
  word-break: normal;
  line-height: 1.4;
}

.zy-metrics-table td:last-child,
.zy-metrics-table th:last-child {
  width: 1%;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-family: var(--font-mono, ui-monospace, monospace);
  vertical-align: top;
}

/* Period legend used to live in a 1-col TableBuilder → same 24ch clip. */
.zy-metrics-period-legend {
  max-width: 48rem;
  color: var(--zy-gray-600);
  font-size: 0.875rem;
  line-height: 1.5;
}

.zy-metrics-period-legend p {
  margin: 0 0 0.65rem;
  white-space: normal;
  overflow: visible;
  text-overflow: unset;
}

.zy-metrics-period-legend p:last-child {
  margin-bottom: 0;
}

/* Мелкая сноска под карточками метрик: суммы — баллы, не люди. */
.zy-metrics-cards-note {
  margin: -0.35rem 0 0.25rem;
  color: var(--zy-gray-600);
  font-size: 0.78rem;
  line-height: 1.4;
}

/* ———————————————————————————————————————————————————————————————
 * Query tags (Pending / In transit / Failed / …)
 *
 * These are a filter control, not seven primary actions — they were rendered
 * as seven full buttons with the selected one filled ink. Segmented row: one
 * shared frame, the current tag carries the accent.
 * ——————————————————————————————————————————————————————————————— */
.layout-content > div:has(> .js-query-tag-button) {
  display: inline-flex !important;
  flex-wrap: wrap;
  gap: 1px !important;
  width: auto;
  padding: 2px;
  background: var(--zy-gray-100);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.js-query-tag-button {
  min-height: var(--control-h-sm) !important;
  padding-inline: 0.75rem !important;
  border: 0 !important;
  border-radius: var(--radius-sm) !important;
  background: transparent !important;
  color: var(--zy-gray-600) !important;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium) !important;
}

.js-query-tag-button:hover {
  background: color-mix(in srgb, var(--zy-white) 70%, transparent) !important;
  color: var(--zy-ink) !important;
  text-decoration: none !important;
}

/* MoonShine marks the current tag by swapping in .btn-primary */
.js-query-tag-button.btn-primary {
  background: var(--zy-white) !important;
  color: var(--zy-ink) !important;
  font-weight: var(--font-weight-semibold) !important;
  box-shadow: var(--shadow-sm) !important;
}

/* ———————————————————————————————————————————————————————————————
 * Empty + pagination
 * ——————————————————————————————————————————————————————————————— */
.table tbody tr:only-child td:only-child,
.table-container .empty,
.layout-content .empty {
  padding: var(--space-10) var(--space-4) !important;
  text-align: center;
  color: var(--zy-gray-500);
  font-size: var(--font-size-sm);
}

.pagination {
  margin-top: var(--space-4);
  font-size: var(--font-size-sm);
}

.pagination .btn,
.pagination a {
  min-height: var(--control-h-sm);
  color: var(--zy-gray-600) !important;
}

/* Skeletons were near-black bars on white and read as filled rows */
.skeleton {
  background: var(--zy-gray-100) !important;
  border-radius: var(--radius-sm);
  height: 0.875rem;
}

/* ———————————————————————————————————————————————————————————————
 * Row actions
 *
 * With the table no longer clipping its right edge these became visible —
 * and they were an ink square and a saturated red square on every single
 * row. Fifteen delete buttons at full alarm red is fifteen invitations to
 * destroy a record, and the red stops meaning "danger" by the third row.
 * Ghost icon buttons: the colour arrives on hover, at the moment it is
 * actually about to matter.
 * ——————————————————————————————————————————————————————————————— */
.table td .btn-square,
.table td .btn.btn-square {
  width: var(--control-h-sm) !important;
  height: var(--control-h-sm) !important;
  min-height: 0 !important;
  padding: 0 !important;
  background: transparent !important;
  border-color: transparent !important;
  color: var(--zy-gray-500) !important;
}

.table td .btn-square svg {
  width: 1rem;
  height: 1rem;
}

.table td .btn-square:hover {
  background: var(--zy-gray-100) !important;
  color: var(--zy-ink) !important;
}

.table td .btn-square.btn-error:hover,
.table td .btn-square.btn-secondary.btn-error:hover,
.table td [class*="delete"]:hover {
  background: var(--chip-danger-bg) !important;
  color: var(--color-destructive) !important;
}

.table td .flex.gap-4 {
  gap: 0.125rem !important;
}

/* ———————————————————————————————————————————————————————————————
 * Form width
 *
 * Section boxes stretched the full 1550px while the fields inside capped at
 * 34rem, so each box was a mostly-empty white slab with a control tucked in
 * the left third. Cap the box to a column the fields can actually fill.
 * ——————————————————————————————————————————————————————————————— */
/*
 * Cap the whole CRUD form column, not each box. Capping boxes individually
 * left pages that mix a filter card with a data table (users-stats) showing
 * two stacked panels at two different widths — a ragged right edge that read
 * as a layout bug. One constraint on the form keeps every section flush.
 */
.layout-content form:has(> .box) {
  max-width: 58rem;
}

/* The condition builder is a table of selects — it needs the full column. */
.layout-content form:has(.zy-condition-builder) {
  max-width: none;
}

/*
 * Form actions row.
 *
 * NOT sticky: MoonShine renders this row as the form's last child, so a
 * `position: sticky; bottom: 0` has zero travel — the container's bottom
 * edge is already the row's own bottom edge, and it would never pin. Pinning
 * the submit to the viewport needs the actions hoisted into a wrapper that
 * spans the form, which is markup MoonShine owns. Give it a rule and breathing
 * room instead, so it reads as the end of the form rather than a stray button.
 */
.layout-content form div.flex:has(> .btn-lg) {
  margin-top: var(--space-6) !important;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

/* ———————————————————————————————————————————————————————————————
 * Condition builder toolbar — align with the button scale above
 * ——————————————————————————————————————————————————————————————— */
.zy-condition-builder .zy-cb-toolbar button {
  min-height: var(--control-h-sm);
  padding: 0 0.75rem !important;
}

/* ———————————————————————————————————————————————————————————————
 * Support user card
 * ——————————————————————————————————————————————————————————————— */
.zy-card-hint {
  margin: 1rem 0;
  color: var(--zy-ink-muted, #5b6572);
}

.zy-card-hint--warn {
  color: #9a3412;
}

.zy-user-card {
  display: grid;
  gap: 0.75rem;
  margin-top: 0.75rem;
}

.zy-user-card__hero {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--color-border, #d7dde5);
  background: #fff;
}

.zy-user-card__phone {
  font-size: 1.5rem;
  font-weight: 650;
  letter-spacing: 0.01em;
}

.zy-user-card__id code {
  font-size: 0.85rem;
}

.zy-user-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  margin: 0;
}

.zy-user-card__meta dt {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--zy-ink-muted, #5b6572);
}

.zy-user-card__meta dd {
  margin: 0.15rem 0 0;
  font-weight: 600;
}

.zy-user-card__section {
  padding: 0.85rem 1.25rem 1rem;
  border: 1px solid var(--color-border, #d7dde5);
  background: #fff;
}

/* Filled sections must stand out — operator scans for "why no points". */
.zy-user-card__section--filled {
  padding-bottom: 1.25rem;
  border-color: #c5ced8;
  box-shadow: inset 3px 0 0 #0f172a;
}

.zy-user-card__section--filled + .zy-user-card__section--filled {
  margin-top: 0.35rem;
}

.zy-user-card__section--empty {
  padding-top: 0.65rem;
  padding-bottom: 0.65rem;
  background: #f8fafc;
  border-style: dashed;
}

.zy-user-card__section--empty .zy-user-card__title {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.5rem;
  font-size: 0.95rem;
}

.zy-user-card__title {
  margin: 0 0 0.35rem;
  font-size: 1.1rem;
}

.zy-user-card__subtitle {
  margin: 0 0 0.5rem;
  font-size: 0.95rem;
}

.zy-user-card__count {
  display: inline-block;
  margin-left: 0.35rem;
  padding: 0.05rem 0.45rem;
  border-radius: 999px;
  background: #eef2f7;
  font-size: 0.8rem;
  font-weight: 600;
}

.zy-user-card__none {
  margin-left: auto;
  color: var(--zy-ink-muted, #5b6572);
  font-size: 0.85rem;
  font-weight: 500;
}

.zy-user-card__lead,
.zy-user-card__empty {
  margin: 0 0 0.75rem;
  color: var(--zy-ink-muted, #5b6572);
  font-size: 0.9rem;
}

/* «Выдать награду»: ручное боевое начисление из карточки. */
.zy-user-card__award-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: flex-end;
  margin: 0 0 0.75rem;
}

.zy-user-card__award-field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 16rem;
  font-size: 0.85rem;
  color: var(--zy-ink-muted, #5b6572);
}

.zy-user-card__award-field select,
.zy-user-card__award-field input {
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--color-border, #d7dde5);
  border-radius: 0.375rem;
  background: #fff;
  color: var(--zy-ink, #0f172a);
  font-size: 0.9rem;
}

/* ZYN-422: сброс прогресса тестового пользователя с карточки. */
.zy-user-card__reset-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 0.25rem;
}

.zy-user-card__reset-form {
  margin: 0;
}

.zy-user-card__reset-btn--all {
  background: #14171c;
  color: #fff;
}

.zy-user-card__notes {
  margin: 0.25rem 0 0;
  padding: 0.65rem 1rem;
  border: 1px solid var(--color-border, #d7dde5);
  background: #fafbfc;
  color: var(--zy-ink-muted, #5b6572);
  font-size: 0.8rem;
}

.zy-user-card__notes summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--zy-ink, #0f172a);
  list-style: none;
}

.zy-user-card__notes summary::-webkit-details-marker {
  display: none;
}

.zy-user-card__notes summary::before {
  content: "▸";
  display: inline-block;
  margin-right: 0.4rem;
  color: var(--zy-ink-muted, #5b6572);
}

.zy-user-card__notes[open] summary::before {
  content: "▾";
}

.zy-user-card__notes p {
  margin: 0.55rem 0 0;
  line-height: 1.45;
}

.zy-user-card__table-wrap {
  overflow-x: auto;
}

.zy-user-card__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.zy-user-card__table th,
.zy-user-card__table td {
  padding: 0.45rem 0.55rem;
  border-bottom: 1px solid #e8edf3;
  text-align: left;
  vertical-align: top;
}

.zy-user-card__table th {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--zy-ink-muted, #5b6572);
  white-space: nowrap;
}

.zy-user-card__row--failed {
  background: #fff7ed;
}

.zy-user-card__error {
  max-width: 22rem;
  word-break: break-word;
  color: #9a3412;
}

.zy-user-card__group {
  margin-top: 0.75rem;
  padding: 0.75rem;
  border: 1px solid #e8edf3;
  background: #fafbfc;
}

.zy-user-card__group-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin-bottom: 0.5rem;
}

.zy-user-card__progress {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.zy-user-card__split {
  display: grid;
  gap: 1rem;
}

@media (min-width: 960px) {
  .zy-user-card__split {
    grid-template-columns: 1fr 1fr;
  }
}

.zy-pill {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: #eef2f7;
  font-size: 0.75rem;
  font-weight: 650;
  white-space: nowrap;
}

.zy-pill--pending,
.zy-pill--in_transit {
  background: #fff7ed;
  color: #9a3412;
}

.zy-pill--awarded {
  background: #ecfdf3;
  color: #166534;
}

.zy-pill--failed {
  background: #fef2f2;
  color: #991b1b;
}

.zy-pill--muted {
  background: #f1f5f9;
  color: #475569;
}

.zy-pill--warn {
  background: #fef3c7;
  color: #92400e;
}

