/* UPP shell + components. Everything reads tokens.css — no literal colours here.
 * A CI check (Phase 8) fails the build on a raw hex outside tokens.css. */

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

html { color-scheme: light dark; }

body {
  margin: 0;
  min-height: 100vh;
  /* Soft wing-tinted corner glows + the base fill give every page some
     atmosphere instead of a flat field. Fixed so they don't scroll. */
  background-color: var(--bg);
  background-image:
    radial-gradient(1100px 560px at 84% -8%,
      color-mix(in srgb, var(--accent) 9%, transparent), transparent 60%),
    radial-gradient(820px 500px at 2% 106%,
      color-mix(in srgb, var(--wing-links) 7%, transparent), transparent 56%);
  background-attachment: fixed;
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
/* A whisper of film grain over the glows — texture without noise. Sits above
   the page fill but below all content (z-index:-1). */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 170px 170px;
}

/* ---- Time-of-day ambient backdrop (data-daypart on <html>, set in base.html)
   Cozy, low-attention temporal wash: warm at dawn/dusk, cool by day, deep at
   night, with a soft sun/moon glow that drifts corner + hue with the hour. The
   rgba() literals are intentional ambient tints, not panel tokens. */
.daysky {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: var(--daysky-mul, 1);
  transition: background 1.6s ease, opacity 1.6s ease;
  background:
    radial-gradient(135% 105% at 12% 116%, var(--sky-a, transparent), transparent 60%),
    radial-gradient(115% 95% at 90% -14%, var(--sky-b, transparent), transparent 56%);
}
html[data-bg-intensity="subtle"] { --daysky-mul: .5; }
html[data-bg-intensity="vivid"]  { --daysky-mul: 1.7; }
html[data-bg="off"] .daysky { display: none; }
.daysky::after {
  content: ""; position: absolute; width: min(48vw, 540px); aspect-ratio: 1;
  left: var(--orb-x, 82%); top: var(--orb-y, 14%); translate: -50% -50%;
  border-radius: 50%; opacity: .55;
  background: radial-gradient(circle, var(--orb, transparent) 0%, transparent 66%);
  transition: left 1.6s ease, top 1.6s ease, background 1.6s ease;
}
:root[data-daypart="dawn"]  { --sky-a: rgba(255,170,128,.17); --sky-b: rgba(255,122,150,.12); --orb: rgba(255,178,120,.22); --orb-x: 76%; --orb-y: 26%; }
:root[data-daypart="day"]   { --sky-a: rgba(120,178,255,.10); --sky-b: rgba(150,205,255,.08); --orb: rgba(255,241,205,.16); --orb-x: 84%; --orb-y: 12%; }
:root[data-daypart="dusk"]  { --sky-a: rgba(255,146,88,.17);  --sky-b: rgba(150,110,224,.15); --orb: rgba(255,150,108,.22); --orb-x: 20%; --orb-y: 22%; }
:root[data-daypart="night"] { --sky-a: rgba(96,116,208,.15);  --sky-b: rgba(70,92,172,.12);  --orb: rgba(184,202,255,.15); --orb-x: 80%; --orb-y: 15%; }
@media (prefers-reduced-motion: reduce) { .daysky, .daysky::after { transition: none; } }

/* ---- Surface tint (data-surface): a constant warm/cool cast over the app,
   distinct from the time-tracking daysky. Very low alpha — a cast, not a fog. */
.skin-tint {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: 0; transition: opacity .8s ease, background .8s ease;
}
html[data-surface="warm"] .skin-tint {
  opacity: 1;
  background:
    radial-gradient(150% 130% at 50% -20%, rgba(255,176,110,.06), transparent 72%),
    linear-gradient(rgba(255,168,104,.045), rgba(255,146,86,.05));
}
html[data-surface="cool"] .skin-tint {
  opacity: 1;
  background:
    radial-gradient(150% 130% at 50% -20%, rgba(120,176,255,.055), transparent 72%),
    linear-gradient(rgba(122,168,255,.045), rgba(92,140,232,.05));
}

/* ---- Secondary colour (--accent2): defaults to the primary accent, overridden
   by the injected skin when the operator sets one. Used for secondary accents. */
:root { --accent2: var(--accent); --accent2-hover: var(--accent-hover); --accent2-weak: var(--accent-weak); }
.btn-ghost:hover { color: var(--accent2); border-color: color-mix(in srgb, var(--accent2) 45%, var(--border)); }
.bell-count { background: var(--accent2); }

/* ---- Contrast (data-contrast=high): push secondary text further from the
   ground. References --text and --bg only (no self-reference). */
html[data-contrast="high"] {
  --text-dim: color-mix(in srgb, var(--text) 82%, var(--bg));
  --text-faint: color-mix(in srgb, var(--text) 62%, var(--bg));
}

/* ---- Density (data-density=compact): tighten the busiest components. */
html[data-density="compact"] .card-pad { padding: var(--sp-4); }
html[data-density="compact"] .btn { min-height: 32px; }
html[data-density="compact"] .btn-sm { min-height: 26px; }
html[data-density="compact"] .wingtab { padding-top: .45rem; padding-bottom: .45rem; }
html[data-density="compact"] .hub__grid { gap: 12px; }
html[data-density="compact"] .hub-card { min-height: 130px; padding: 18px 20px 16px; }

/* ---- Custom dropdown (shared/js/dropdown.js enhances a wrapped <select>) --- */
.dropdown { position: relative; display: inline-block; min-width: 180px; }
/* native select: an invisible overlay over the button — still the source of
   truth (form-submitting) AND focusable, so a `required` select's validation
   can scroll to + focus it (a clip-hidden control errors "not focusable"). */
.dropdown__native {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; padding: 0; border: 0; opacity: 0; pointer-events: none;
}
.dropdown__btn {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  width: 100%; min-height: 38px; padding: 0 .7rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text);
  font: inherit; font-size: var(--fs-sm); text-align: left; cursor: pointer;
  transition: border-color .12s, box-shadow .12s;
}
.dropdown__btn:hover { border-color: var(--border-strong); }
.dropdown.is-open .dropdown__btn,
.dropdown__btn:focus-visible { border-color: var(--accent); box-shadow: var(--ring); outline: none; }
/* When the menu is open below the button, flatten the button's bottom corners
   so the two shapes read as one "field with a drawer". When flipped above,
   flatten the top corners instead. Set by dropdown.js. */
.dropdown.is-open:not(.dropdown--flipped) .dropdown__btn {
  border-bottom-left-radius: 0; border-bottom-right-radius: 0;
}
.dropdown.is-open.dropdown--flipped .dropdown__btn {
  border-top-left-radius: 0; border-top-right-radius: 0;
}
.dropdown__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dropdown__caret { width: 14px; height: 14px; color: var(--text-dim); flex: none; transition: transform .15s ease; }
.dropdown.is-open .dropdown__caret { transform: rotate(180deg); }
.dropdown__menu {
  /* Position/coords are set inline by dropdown.js (position: fixed) —
     these are the base styles the menu inherits. z-index sits above the
     wingbar (~30) and cards; well below full-page modals if any exist. */
  position: absolute; z-index: 1000; top: calc(100% + 2px); left: 0; right: 0;
  margin: 0; padding: 4px; list-style: none;
  border: 1px solid var(--accent); border-radius: var(--radius-sm);
  background: var(--surface); box-shadow: 0 14px 34px -14px rgba(0,0,0,.55);
  max-height: 300px; overflow-y: auto; overflow-x: hidden;
}
/* When the menu is BELOW its trigger, flatten its top corners so the two
   share a seam. Flipped (above) → flatten the bottom. Also set by
   dropdown.js via the wrap's `.dropdown--flipped` class, but a paired
   :has() selector avoids needing markup for the default case. */
.dropdown.is-open:not(.dropdown--flipped) .dropdown__menu {
  border-top-left-radius: 0; border-top-right-radius: 0;
}
.dropdown.is-open.dropdown--flipped .dropdown__menu {
  border-bottom-left-radius: 0; border-bottom-right-radius: 0;
}
.dropdown__opt {
  padding: .42rem .7rem; border-radius: calc(var(--radius-sm) - 2px);
  font-size: var(--fs-sm); color: var(--text); cursor: pointer;
  /* Options never wrap or clip — the menu itself is width:auto in JS so
     long labels ("Генерация · Drop", full GIDs) fit end-to-end. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dropdown__opt:hover { background: var(--surface-2); }
.dropdown__opt.is-selected { color: var(--accent); font-weight: 600; background: var(--accent-weak); }
/* block: fill the container (form fields). sm: compact for table cells/inline. */
.dropdown--block { display: block; width: 100%; min-width: 0; }
.dropdown--sm { min-width: 0; }
.dropdown--sm .dropdown__btn { min-height: 30px; padding: 0 .55rem; font-size: var(--fs-sm); }
.dropdown--sm .dropdown__caret { width: 12px; height: 12px; }
/* Options in a compact trigger stay legible — same font-size as the normal
   variant so long entries don't turn into microscopic text. */
.dropdown--sm .dropdown__opt { padding: .38rem .6rem; font-size: var(--fs-sm); }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); }

:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--radius-sm);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* ---- Top bar ---------------------------------------------------------- */
.topbar {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  height: 56px;
  padding: 0 var(--sp-5);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--fs-md);
  letter-spacing: 0.14em;
  color: var(--text);
  text-decoration: none;
  border-radius: var(--radius-sm);
  transition: color .18s ease;
}
.wordmark .dot { color: inherit; }
/* Hover simply tints the letters a single colour: WHITE/base on the hub, and the
   current section's wing colour on a wing page (driven by body[data-wing]). The
   hexmark wheel still spins on hover (see .hexmark). No gradient/glint — the
   multicolour sweep was tried through several rounds and dropped. */
:root                      { --wordmark-hover: var(--text); }
body[data-wing="emd"]      { --wordmark-hover: var(--wing-emd); }
body[data-wing="pbn"]      { --wordmark-hover: var(--wing-pbn); }
body[data-wing="drop"]     { --wordmark-hover: var(--wing-drop); }
body[data-wing="links"]    { --wordmark-hover: var(--wing-links); }
body[data-wing="hreflang"] { --wordmark-hover: var(--wing-hreflang); }
body[data-wing="chain"]    { --wordmark-hover: var(--wing-chain); }
body[data-wing="admin"]    { --wordmark-hover: var(--wing-admin); }
a.wordmark:hover, .hub__base:hover .nm { color: var(--wordmark-hover); }
/* ⬡ Outpost/Pinwheel mark — doubles as the "back to hub" affordance when the
   wordmark is a link. See .hexmark below for the segmented ring. */
.wordmark-hub {
  font-size: 1.15em; line-height: 1; letter-spacing: 0;
  display: inline-flex; align-items: center;
}
/* The pinwheel hexagon mark (src/templates/_hexmark.html) is a colour WHEEL seen
   through a FIXED hollow-hexagon ring mask: `.hexmark` carries the ring mask (the
   outline never moves); `.hexmark__wheel` inside it is a conic-gradient of the six
   wing hues in hard 60° sectors. `from 90deg` starts emd at 3 o'clock so the
   sectors line up exactly with the old six trapeziums (their boundaries are radial
   lines through the hexagon's vertices). Static & colourful at rest. */
.hexmark {
  display: inline-block; width: 0.95em; height: 0.95em;
  vertical-align: -0.12em; flex: none;
  --hexring: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20fill='%23fff'%20fill-rule='evenodd'%20d='M22.5%2012%2017.25%2021.09%206.75%2021.09%201.5%2012%206.75%202.91%2017.25%202.91Z%20M18.5%2012%2015.25%2017.63%208.75%2017.63%205.5%2012%208.75%206.37%2015.25%206.37Z'/%3E%3C/svg%3E");
  -webkit-mask: var(--hexring) center / contain no-repeat;
          mask: var(--hexring) center / contain no-repeat;
}
.hexmark__wheel {
  display: block; width: 100%; height: 100%;
  background: conic-gradient(from 90deg,
    var(--wing-emd)       0deg  60deg,
    var(--wing-pbn)      60deg 120deg,
    var(--wing-drop)    120deg 180deg,
    var(--wing-links)   180deg 240deg,
    var(--wing-hreflang) 240deg 300deg,
    var(--wing-chain)   300deg 360deg);
  transition: transform 0.5s cubic-bezier(0.58, 0.38, 0.43, 0.61);
}
a.wordmark:hover .hexmark__wheel,
.hub__base:hover .hexmark__wheel { transform: rotate(360deg); }
a.wordmark:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.topbar-spacer { flex: 1; }

.topbar-actions { display: flex; align-items: center; gap: var(--sp-2); }

/* Legacy `.account` link is retired — the topbar no longer shows the display
   name, only a rounded-square avatar that opens the account menu below. */
.account { display: none; }

/* ---- Account menu (avatar → dropdown with theme / settings / logout) ---- */
.acct-menu { position: relative; display: inline-flex; align-items: center; }
/* .iconbtn--avatar strips the icon-button's inner padding + centering rules
   so the avatar fills the whole 34×34 chip. Border, size and hover come from
   the shared .iconbtn styles — pfp lines up pixel-for-pixel with [?] + [bell]. */
.iconbtn.iconbtn--avatar { padding: 0; overflow: hidden; }
.acct-menu.is-open > .iconbtn.iconbtn--avatar {
  border-color: var(--accent); box-shadow: var(--ring);
}
.acct-menu__avatar {
  width: 100%; height: 100%; object-fit: cover; display: block;
  border-radius: inherit; background: var(--surface-2);
}
.acct-menu__avatar--lg {
  width: 40px; height: 40px; border-radius: var(--radius-sm);
  border: 1px solid var(--border);
}

.acct-menu__pop {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 1000;
  min-width: 240px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: 0 14px 34px -14px rgba(0,0,0,.55);
  padding: 6px; display: flex; flex-direction: column; gap: 2px;
}
.acct-menu__pop[hidden] { display: none; }

.acct-menu__who {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3); margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.acct-menu__who-txt { min-width: 0; flex: 1; }
.acct-menu__name { margin: 0; font-weight: 650; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-menu__role { margin: 0; color: var(--text-dim);
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em; }

.acct-menu__item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: .5rem .65rem;
  border: 0; background: transparent; color: var(--text);
  font: inherit; font-size: var(--fs-sm); text-align: left;
  text-decoration: none; cursor: pointer; width: 100%;
  border-radius: var(--radius-sm);
  transition: background .12s, color .12s;
}
.acct-menu__item:hover { background: var(--surface-2); }
.acct-menu__item--danger:hover { color: var(--crit, #e0819e); }
.acct-menu__ic { display: inline-flex; align-items: center;
  justify-content: center; width: 18px; height: 18px;
  color: var(--text-dim); flex: none; }
.acct-menu__item:hover .acct-menu__ic { color: inherit; }
.acct-menu__ic svg { width: 16px; height: 16px; }
.acct-menu__ic--theme { position: relative; }
.acct-menu__ic--theme svg { position: absolute; inset: 0; margin: auto; transition: opacity .12s; }
.acct-menu__row .acct-menu__ic-moon,
.acct-menu__row .acct-menu__ic-sun  { opacity: 0; }
.acct-menu__row:not(:has([aria-checked="true"])) .acct-menu__ic-moon { opacity: 1; }
.acct-menu__row:has([aria-checked="true"]) .acct-menu__ic-sun { opacity: 1; }
.acct-menu__label { flex: 1; }
.acct-menu__form { margin: 0; padding: 0; }

/* Non-clickable row with a switch on the right — used for the theme toggle
   in the account dropdown. Same padding as `.acct-menu__item` so the row
   heights line up cleanly with the other menu items. */
.acct-menu__row { display: flex; align-items: center; gap: var(--sp-3);
  padding: .5rem .65rem; color: var(--text); font-size: var(--fs-sm);
  border-radius: var(--radius-sm); }
.acct-menu__row--switch:hover { background: var(--surface-2); }

/* Compact iOS-style toggle used inside the account menu. Switch state is
   driven by aria-checked so the label + backend stay in sync. */
.theme-switch {
  --tsw-w: 34px; --tsw-h: 20px; --tsw-knob: 14px; --tsw-pad: 3px;
  position: relative; width: var(--tsw-w); height: var(--tsw-h);
  flex: none; padding: 0; border: 1px solid var(--border);
  border-radius: 999px; background: var(--surface-2); cursor: pointer;
  transition: background .18s ease, border-color .18s ease;
}
.theme-switch[aria-checked="true"] {
  background: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 65%, var(--border));
}
.theme-switch:focus-visible { outline: none; box-shadow: var(--ring); }
.theme-switch__knob {
  position: absolute; top: 50%; left: var(--tsw-pad);
  width: var(--tsw-knob); height: var(--tsw-knob);
  border-radius: 50%; background: var(--surface);
  box-shadow: 0 1px 3px rgba(0,0,0,.4);
  transform: translateY(-50%);
  transition: transform .22s cubic-bezier(.34,1.4,.5,1);
}
.theme-switch[aria-checked="true"] .theme-switch__knob {
  transform: translate(calc(var(--tsw-w) - var(--tsw-knob) - var(--tsw-pad) * 2), -50%);
}

/* ---- Icon buttons (bell, theme) --------------------------------------- */
.iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-dim);
  cursor: pointer;
  position: relative;
  transition: color .12s, border-color .12s, background .12s;
}
.iconbtn:hover { color: var(--text); border-color: var(--border-strong); }
.iconbtn svg { width: 18px; height: 18px; }

.bell-count {
  position: absolute;
  top: -6px; right: -6px;
  min-width: 17px; height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--crit);
  color: #fff;
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.bell-count[data-count="0"] { display: none; }

/* ── Per-user notifications: toast stack + bell drop-down ──────────────── */
.notif-stack {
  position: fixed; top: 68px; right: 16px; z-index: 90;
  display: flex; flex-direction: column-reverse; gap: 8px;
  max-width: min(400px, calc(100vw - 32px));
  pointer-events: none;
}
.notif-toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-left-width: 3px;
  border-radius: var(--radius-lg);
  padding: 10px 12px; box-shadow: 0 10px 30px rgba(0,0,0,.18);
  font-size: 0.9rem; line-height: 1.35;
  animation: notif-in .22s ease-out;
}
.notif-toast--info { border-left-color: var(--accent); }
.notif-toast--warn { border-left-color: var(--warn, #d69a3f); }
.notif-toast--crit { border-left-color: var(--crit, #e0819e); }
.notif-toast.is-leaving { animation: notif-out .22s ease-in forwards; }
.notif-toast__subject { flex: 1 1 auto; }
.notif-toast__close {
  flex: 0 0 auto; background: transparent; border: 0; cursor: pointer;
  color: var(--text-dim); font-size: 1.2rem; line-height: 1;
  padding: 0 4px; margin-top: -2px;
}
.notif-toast__close:hover { color: var(--text); }
@keyframes notif-in  { from { opacity: 0; transform: translateX(12px); } to { opacity: 1; transform: none; } }
@keyframes notif-out { from { opacity: 1; transform: none; }             to { opacity: 0; transform: translateX(12px); } }

.notif-pop {
  z-index: 95;
  min-width: 280px; max-width: min(360px, calc(100vw - 24px));
  max-height: min(70vh, 480px); overflow-y: auto;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: 0 14px 40px rgba(0,0,0,.22);
  font-size: 0.9rem;
}
.notif-pop__empty { padding: 16px; text-align: center; color: var(--text-dim); }
.notif-pop__list { list-style: none; margin: 0; padding: 4px 0; }
.notif-pop__item {
  padding: 8px 12px;
  border-left: 3px solid transparent;
}
.notif-pop__item + .notif-pop__item { border-top: 1px solid var(--border); }
.notif-pop__item--info { border-left-color: var(--accent); }
.notif-pop__item--warn { border-left-color: var(--warn, #d69a3f); }
.notif-pop__item--crit { border-left-color: var(--crit, #e0819e); }
.notif-pop__subject { line-height: 1.35; }
.notif-pop__when { margin-top: 2px; font-size: 0.75rem; color: var(--text-dim); }

/* Toggle row (label + switch) used on Персонализация → Уведомления. */
.st-toggle-row {
  display: flex; align-items: center; gap: 16px;
  justify-content: space-between;
}
.st-toggle-title { display: block; font-weight: 500; }
.st-toggle-hint  { display: block; margin-top: 2px;
  font-size: 0.82rem; color: var(--text-dim); max-width: 46ch; }

/* Segmented theme control */
/* Sun/moon light-dark toggle — a mobile-style switch with a sliding knob. */
.theme-toggle {
  --tt-w: 56px; --tt-h: 30px; --tt-knob: 24px; --tt-pad: 3px;
  position: relative; width: var(--tt-w); height: var(--tt-h);
  flex: 0 0 auto; padding: 0; border: 1px solid var(--border);
  border-radius: 999px; background: var(--surface-2); cursor: pointer;
  transition: background .25s ease, border-color .25s ease;
}
.theme-toggle[aria-checked="true"] {
  background: var(--accent-weak); border-color: var(--accent);
}
.theme-toggle__knob {
  position: absolute; top: 50%; left: var(--tt-pad);
  width: var(--tt-knob); height: var(--tt-knob); border-radius: 50%;
  background: var(--surface); color: var(--accent);
  box-shadow: 0 1px 3px rgba(0,0,0,.35);
  display: grid; place-items: center;
  transform: translateY(-50%);
  transition: transform .3s cubic-bezier(.34,1.4,.5,1);
}
.theme-toggle[aria-checked="true"] .theme-toggle__knob {
  transform: translate(calc(var(--tt-w) - var(--tt-knob) - var(--tt-pad) * 2),
                        -50%);
}
.theme-toggle__knob svg {
  grid-area: 1 / 1; width: 15px; height: 15px;
  transition: opacity .2s ease, transform .3s ease;
}
.theme-toggle .ic-sun  { opacity: 1; transform: rotate(0); }
.theme-toggle .ic-moon { opacity: 0; transform: rotate(-90deg); }
.theme-toggle[aria-checked="true"] .ic-sun  { opacity: 0; transform: rotate(90deg); }
.theme-toggle[aria-checked="true"] .ic-moon { opacity: 1; transform: rotate(0); }

.btn {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  height: 34px; padding: 0 var(--sp-4);
  border: 1px solid transparent; border-radius: var(--radius-sm);
  font: inherit; font-size: var(--fs-sm); cursor: pointer;
  transition: background .12s, border-color .12s;
}
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-ghost { background: transparent; color: var(--text-dim);
  border-color: var(--border); }
.btn-ghost:hover { color: var(--text); border-color: var(--border-strong); }

/* ---- Layout ----------------------------------------------------------- */
.page { max-width: 960px; margin: 0 auto; padding: var(--sp-8) var(--sp-6); }

.page-head { margin-bottom: var(--sp-5); }
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 0 0 var(--sp-2);
}
h1.title { font-size: var(--fs-xl); font-weight: 650; margin: 0;
  text-wrap: balance; letter-spacing: -0.01em; }
.lede { color: var(--text-dim); margin: var(--sp-2) 0 0; max-width: 60ch; }

/* ---- Wing grid -------------------------------------------------------- */
.wing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--sp-4);
  margin-top: var(--sp-5);
}

.wing-card {
  position: relative;
  display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: border-color .14s, transform .14s;
}
a.wing-card:hover { transform: translateY(-2px); border-color: var(--wing); }
/* The identity rail — the one place a wing's hue appears. */
.wing-card::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 4px;
  background: var(--wing);
}
.wing-card[data-wing="emd"]   { --wing: var(--wing-emd); }
.wing-card[data-wing="pbn"]   { --wing: var(--wing-pbn); }
.wing-card[data-wing="links"] { --wing: var(--wing-links); }
.wing-card[data-wing="admin"] { --wing: var(--text-dim); }

.wing-name {
  font-family: var(--font-mono);
  font-size: var(--fs-lg);
  font-weight: 650;
  letter-spacing: 0.02em;
  color: var(--wing);
  margin: 0;
}
.wing-desc { color: var(--text-dim); font-size: var(--fs-sm); margin: 0; flex: 1; }
.wing-go {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-faint);
}

.locked { opacity: .55; }
.locked .wing-go { color: var(--text-faint); }

.tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-mono); font-size: var(--fs-xs);
  padding: 2px var(--sp-2); border-radius: 999px;
  background: var(--surface-2); color: var(--text-dim);
  border: 1px solid var(--border); width: max-content;
}

/* ---- Flash ------------------------------------------------------------ */
.flashes { display: flex; flex-direction: column; gap: var(--sp-2);
  margin-bottom: var(--sp-4); }
.flash { padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-sm);
  border: 1px solid; font-size: var(--fs-sm); cursor: pointer;
  transition: opacity .35s ease, transform .35s ease; }
/* Auto-dismiss (base.html) fades the flash out, then removes it from the DOM. */
.flash--out { opacity: 0; transform: translateY(-6px); pointer-events: none; }
/* Overlay mode (base.html global flashes): the flash keeps its old spot at the
   top of the content, but a zero-height anchor + an absolutely-positioned child
   let it float over the page's top padding — so showing/hiding it never reflows
   the content it confirms. */
.flashes-anchor { position: relative; z-index: 1000; }
.flashes--overlay { position: absolute; top: var(--sp-4); left: 0; right: 0;
  max-width: 1120px; margin: 0 auto; padding: 0 var(--sp-6);
  pointer-events: none; }
.flashes--overlay .flash { pointer-events: auto;
  box-shadow: 0 10px 30px -8px rgba(0, 0, 0, .45); }
.flash-error { background: var(--surface); border-color: var(--crit);
  color: var(--crit); }
.flash-success { background: var(--surface); border-color: var(--ok); color: var(--ok); }
.flash-warning { background: var(--surface); border-color: var(--warn); color: var(--warn); }
.flash-info { background: var(--surface); border-color: var(--accent); color: var(--accent); }

/* ---- Version badge (bottom-left) + changelog modal --------------------- */
.verbadge {
  position: fixed; left: var(--sp-4); bottom: var(--sp-4); z-index: 900;
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .25rem .5rem .25rem .6rem; border: 1px solid var(--border);
  border-radius: 999px; background: color-mix(in srgb, var(--surface) 82%, transparent);
  color: var(--text-dim); font-family: var(--font-mono); font-size: var(--fs-xs);
  cursor: pointer; opacity: .72; backdrop-filter: blur(4px);
  transition: opacity .15s, border-color .15s, color .15s;
}
.verbadge:hover { opacity: 1; color: var(--text); border-color: var(--accent); }
.verbadge__ch { text-transform: uppercase; letter-spacing: .05em; font-size: 9px;
  font-weight: 700; padding: .05rem .35rem; border-radius: 999px; }
.verbadge__ch--dev { color: var(--warn, #d69a3f);
  background: color-mix(in srgb, var(--warn, #d69a3f) 22%, transparent); }
.verbadge__ch--retail { color: var(--ok, #5cc98a);
  background: color-mix(in srgb, var(--ok, #5cc98a) 20%, transparent); }

.vermodal { position: fixed; inset: 0; z-index: 1200; display: grid; place-items: center; }
.vermodal[hidden] { display: none; }  /* class display: grid otherwise beats the hidden attr */
.vermodal__backdrop { position: absolute; inset: 0;
  background: color-mix(in srgb, #000 55%, transparent); backdrop-filter: blur(2px); }
.vermodal__panel { position: relative; width: min(560px, calc(100vw - 2 * var(--sp-5)));
  max-height: min(72vh, 660px); display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: 0 22px 64px -14px rgba(0, 0, 0, .6); }
.vermodal__head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-3); padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border); }
.vermodal__eyebrow { margin: 0 0 .12rem; font-size: var(--fs-xs); text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-dim); }
.vermodal__title { margin: 0; font-size: 1.1rem; }
.vermodal__x { border: 0; background: none; color: var(--text-dim); font-size: 1.05rem;
  cursor: pointer; line-height: 1; padding: .2rem .3rem; }
.vermodal__x:hover { color: var(--text); }
.vermodal__body { overflow-y: auto; padding: var(--sp-4) var(--sp-5); }
.vermodal__muted { color: var(--text-dim); font-size: var(--fs-sm); }
.vrel { padding-bottom: var(--sp-4); margin-bottom: var(--sp-4); border-bottom: 1px solid var(--border); }
.vrel:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
.vrel__head { display: flex; align-items: baseline; gap: var(--sp-3); }
.vrel__v { font-family: var(--font-mono); font-weight: 700; color: var(--accent); }
.vrel__date { font-size: var(--fs-xs); color: var(--text-dim); }
.vrel__title { margin: .3rem 0 .5rem; font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.vrel__list { margin: 0; padding-left: var(--sp-5); color: var(--text-dim); font-size: var(--fs-sm); }
.vrel__list li { margin: .32rem 0; line-height: 1.45; }

/* ---- Login ------------------------------------------------------------ */
.login-wrap { min-height: 100vh; display: grid; place-items: center;
  padding: var(--sp-5); }
.login-card {
  width: 100%; max-width: 360px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
  padding: var(--sp-6);
}
.login-mark { font-family: var(--font-mono); font-weight: 700;
  font-size: var(--fs-lg); letter-spacing: 0.14em; text-align: center;
  margin: 0 0 var(--sp-1); }
.login-mark .dot { color: var(--accent); }
.login-sub { text-align: center; color: var(--text-faint);
  font-size: var(--fs-sm); margin: 0 0 var(--sp-5); }
.field { display: flex; flex-direction: column; gap: var(--sp-2);
  margin-bottom: var(--sp-4); }
.field label { font-size: var(--fs-sm); color: var(--text-dim); }
.field input {
  height: 38px; padding: 0 var(--sp-3);
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--fs-sm);
}
.field input:focus-visible { border-color: var(--accent); }
.login-card .btn-primary { width: 100%; justify-content: center; height: 40px; }

/* Standalone kit input — same look as `.field input`, but usable outside a
   `.field` wrapper: the settings profile form, the admin inline account editor,
   and the delegated-admin team form all use `.tm-input`. */
.tm-input {
  height: 38px; padding: 0 var(--sp-3);
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--fs-sm);
}
.tm-input:focus-visible { border-color: var(--accent); outline: none; box-shadow: var(--ring); }
.tm-input::placeholder { color: var(--text-faint); }

/* ═══════════════════════════════════════════════════════════════════════════
 * UNIFIED COMPONENT KIT — one language across the hub and every wing.
 * (documentation/50-design-system.md §7). Tokens only; no literal colour.
 * Mono is reserved for machine data (domains, ids, counts, timestamps); labels,
 * tabs and status read in the UI sans so the console stays calm.
 * ═══════════════════════════════════════════════════════════════════════════ */

.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* ---- Wing context: the topbar badge + the secondary "wing bar" ------------ */
/* A page sets data-wing on <body>; --wing resolves the one hue that names it. */
body[data-wing="emd"]      { --wing: var(--wing-emd); }
body[data-wing="pbn"]      { --wing: var(--wing-pbn); }
body[data-wing="links"]    { --wing: var(--wing-links); }
body[data-wing="hreflang"] { --wing: var(--wing-hreflang); }
body[data-wing="chain"]    { --wing: var(--wing-chain); }
body[data-wing="generate"] { --wing: var(--wing-generate); }
body[data-wing="admin"]    { --wing: var(--wing-admin); }
body:not([data-wing])      { --wing: var(--text-dim); }

.wing-here {
  font-family: var(--font-mono);
  font-size: var(--fs-sm); font-weight: 600; letter-spacing: 0.02em;
  color: var(--wing);
  padding-left: var(--sp-3);
  border-left: 1px solid var(--border);
  margin-left: var(--sp-1);
}

.wingbar {
  display: flex; align-items: stretch; gap: var(--sp-1);
  height: 48px; padding: 0 var(--sp-6);
  background: var(--surface); border-bottom: 1px solid var(--border);
  overflow-x: auto;
}
.wingtab {
  display: inline-flex; align-items: center; padding: 0 var(--sp-4);
  font-size: var(--fs-sm); color: var(--text-dim);
  border-bottom: 2px solid transparent; white-space: nowrap;
  transition: color .12s;
}
.wingtab:hover { color: var(--text); }
.wingtab[aria-current="page"] { color: var(--text); border-bottom-color: var(--wing); }

/* ---- Page frame variants -------------------------------------------------- */
/* .page is the reading width; .page-wide fits dense tables and dashboards. */
.page-wide { max-width: 1120px; margin: 0 auto; padding: var(--sp-8) var(--sp-6); }

/* ---- Toolbar: title row + filters + primary action ------------------------ */
.toolbar { display: flex; align-items: flex-end; gap: var(--sp-4);
  flex-wrap: wrap; margin-bottom: var(--sp-5); }
.toolbar .grow { flex: 1; }
.searchbox {
  display: flex; align-items: center; gap: var(--sp-2);
  height: 34px; padding: 0 var(--sp-3); min-width: 220px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.searchbox input { border: 0; background: transparent; color: var(--text);
  font: inherit; font-size: var(--fs-sm); width: 100%; outline: none; }
.searchbox svg { width: 15px; height: 15px; color: var(--text-faint); flex: none; }

/* ---- Buttons: variants on top of the base .btn ---------------------------- */
.btn svg { width: 15px; height: 15px; }
.btn-danger { background: transparent; color: var(--crit); border-color: var(--border); }
.btn-danger:hover { border-color: var(--crit); }
.btn-sm { height: 28px; padding: 0 var(--sp-3); font-size: var(--fs-xs); }

/* ---- Card / panel --------------------------------------------------------- */
.card { background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow); }
.card-pad { padding: var(--sp-6); }
.seclabel {
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-dim);
  margin: 0 0 var(--sp-4); padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border);
}

/* ---- Data table: quiet header, airy rows, mono only for machine data ------ */
.tablewrap { background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow); }
.tablewrap.scroll { overflow-x: auto; }
table.data { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
table.data thead th {
  position: sticky; top: 0; background: var(--surface);
  text-align: left; font-weight: 500; color: var(--text-faint);
  font-size: var(--fs-xs); letter-spacing: 0.02em;
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border);
}
table.data tbody td { padding: var(--sp-4); border-bottom: 1px solid var(--border);
  color: var(--text); }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: var(--surface-2); }
table.data td.id, table.data td.num { font-family: var(--font-mono);
  font-variant-numeric: tabular-nums; }
table.data td.num, table.data th.num { text-align: right; }
table.data td.dim { color: var(--text-dim); }
.rowactions { display: flex; gap: var(--sp-2); opacity: 0; transition: opacity .1s; }
tr:hover .rowactions, .rowactions:focus-within { opacity: 1; }

/* ---- Status pill: the operator's most-scanned signal ---------------------- */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-xs); padding: 3px var(--sp-3);
  border-radius: 999px; border: 1px solid; letter-spacing: 0.01em;
}
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex: none; }
.pill-ok   { color: var(--ok);   border-color: color-mix(in srgb, var(--ok)   40%, transparent);
  background: color-mix(in srgb, var(--ok)   10%, transparent); }
.pill-run  { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent); }
.pill-warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent);
  background: color-mix(in srgb, var(--warn) 12%, transparent); }
.pill-crit { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 40%, transparent);
  background: color-mix(in srgb, var(--crit) 10%, transparent); }
.pill-idle { color: var(--text-faint); border-color: var(--border); background: var(--surface-2); }
.pill-run::before { animation: pill-pulse 1.4s ease-in-out infinite; }
@keyframes pill-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }

/* ---- Forms: generalized (label · control · hint), sans by default --------- */
.field select, .field textarea {
  padding: 0 var(--sp-3); background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--fs-sm);
}
.field select { height: 38px; }
.field textarea { min-height: 88px; padding: var(--sp-2) var(--sp-3);
  resize: vertical; font-family: var(--font-mono); }
.field select:focus-visible, .field textarea:focus-visible { border-color: var(--accent); }
.field .hint { font-size: var(--fs-xs); color: var(--text-faint); }
/* Machine-data inputs (domains, keys) opt into mono; plain fields stay sans. */
.field-mono input { font-family: var(--font-mono); }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--sp-4); }
@media (max-width: 560px) { .grid2 { grid-template-columns: 1fr; } }

/* ---- Empty state ---------------------------------------------------------- */
.empty { text-align: center; padding: var(--sp-8) var(--sp-5); color: var(--text-dim); }
.empty .mark { font-family: var(--font-mono); font-size: var(--fs-lg);
  color: var(--text-faint); margin-bottom: var(--sp-2); }

/* ---- Launcher tiles: a wing dashboard's entry cards ----------------------- */
.tilegrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--sp-4); margin-top: var(--sp-4); }
.tile { display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-5); background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
  transition: transform .28s cubic-bezier(.34,1.28,.5,1),
              box-shadow .25s ease, border-color .14s ease; }
/* Polaroid pickup: an interactive tile lifts + tilts like a photo taken off a
   stack, with a one-shot "develop" light sweep + a soft wing-hue frame. */
a.tile { position: relative; overflow: hidden; }
a.tile::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 42%,
    rgba(255,255,255,.16) 50%, transparent 58%);
  transform: translateX(-125%); opacity: 0;
}
a.tile:hover {
  transform: translateY(-5px);
  border-color: var(--wing, var(--accent));
  box-shadow: 0 20px 38px -20px rgba(0,0,0,.5),
              0 4px 12px -8px rgba(0,0,0,.35),
              inset 0 0 0 1.5px color-mix(in srgb, var(--wing, var(--accent)) 22%, transparent);
}
a.tile:hover::after { animation: tile-develop .62s ease-out; }
@keyframes tile-develop {
  0%   { transform: translateX(-125%); opacity: 1; }
  100% { transform: translateX(125%);  opacity: 0; }
}
.tile-icon { font-size: 1.5rem; line-height: 1; }
.tile-title { font-size: var(--fs-md); font-weight: 640; margin: 0; color: var(--text); }
.tile-desc { font-size: var(--fs-sm); color: var(--text-dim); margin: 0; flex: 1; }
.tile.is-disabled { opacity: .55; }
/* An empty dashed slot — a visible "coming later" placeholder, no content. */
.tile-placeholder { border: 1.5px dashed var(--border); background: transparent;
  box-shadow: none; min-height: 7rem; }

/* ---- KPI stat tiles: the summary read before the detail -------------------- */
.statgrid { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-5); }
.stat { flex: 1; min-width: 9rem; padding: var(--sp-4);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow); }
.stat .label { font-size: var(--fs-xs); letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-faint); margin: 0 0 var(--sp-1); }
.stat .value { font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--fs-lg); font-weight: 650; color: var(--text); margin: 0; }

/* ---- Inline note (warn) --------------------------------------------------- */
.note-warn { font-size: var(--fs-sm); color: var(--warn);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
  border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-4); }

/* Neutral + success siblings of .note-warn — same box, different accent. */
.note { font-size: var(--fs-sm); color: var(--text-dim);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-4); }
.note-ok { font-size: var(--fs-sm); color: var(--ok);
  background: color-mix(in srgb, var(--ok) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--ok) 30%, transparent);
  border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-4); }

/* ---- Skeleton loaders ----------------------------------------------------
   Universal "content is coming" primitives. Any page that fetches its data
   client-side drops one of these in place of the eventual content and the
   render code either replaces the container's HTML or removes .skel /
   [aria-busy] from it. Rules of thumb:

     * `.skel-line`   — a single bar; sized by width/height where used.
     * `.skel-box`    — a soft block of any dimensions.
     * `.skel-card`   — a card-sized container of a few lines. Compose with
                        `.card .card-pad` to match a real card's chrome.
     * `.skel-row`    — one table-row of shimmer cells (use inside <tbody>).
     * `.skel-shine`  — the shimmer animation. All the above extend it.

   Colour is theme-adaptive via `var(--surface-2)` and `var(--border)`, so
   light/dark palettes both look right.
--------------------------------------------------------------------------- */
@keyframes skelShine {
  0% { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}
@keyframes skelPulse {
  0%, 100% { opacity: .60; }
  50%      { opacity: .90; }
}
.skel-shine {
  background: linear-gradient(90deg,
    var(--surface-2) 0%,
    color-mix(in srgb, var(--surface-2) 55%, var(--border)) 50%,
    var(--surface-2) 100%);
  background-size: 200% 100%;
  animation: skelShine 1.4s ease-in-out infinite,
             skelPulse 1.6s ease-in-out infinite;
  border-radius: var(--radius-sm);
}
.skel-line { display: block; height: 12px; margin: .35rem 0;
  background: linear-gradient(90deg,
    var(--surface-2) 0%,
    color-mix(in srgb, var(--surface-2) 55%, var(--border)) 50%,
    var(--surface-2) 100%);
  background-size: 200% 100%;
  animation: skelShine 1.4s ease-in-out infinite,
             skelPulse 1.6s ease-in-out infinite;
  border-radius: 6px; }
.skel-line--sm   { height: 9px;  width: 55%; }
.skel-line--head { height: 14px; width: 40%; margin-bottom: .6rem; }
.skel-line--wide { width: 90%; }
.skel-box {
  background: linear-gradient(90deg,
    var(--surface-2) 0%,
    color-mix(in srgb, var(--surface-2) 55%, var(--border)) 50%,
    var(--surface-2) 100%);
  background-size: 200% 100%;
  animation: skelShine 1.4s ease-in-out infinite,
             skelPulse 1.6s ease-in-out infinite;
  border-radius: var(--radius-sm); }
.skel-card > .skel-line { max-width: none; }
.skel-grid { display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--sp-3); }
.skel-row td { padding: .5rem .7rem; border-bottom: 1px solid var(--border); }
.skel-row td > span { display: block; height: 10px; border-radius: 5px;
  background: linear-gradient(90deg,
    var(--surface-2) 0%,
    color-mix(in srgb, var(--surface-2) 55%, var(--border)) 50%,
    var(--surface-2) 100%);
  background-size: 200% 100%;
  animation: skelShine 1.4s ease-in-out infinite,
             skelPulse 1.6s ease-in-out infinite; }

/* ---- Multi-zone chip picker ---------------------------------------------
   Used by the section zone assignment (admin_settings.html) AND the
   account-row zone editor (admin_accounts.html) — same visual language
   so multi-select behaves consistently across the admin surfaces. The
   compact variant is for tight table cells; the standard version has a
   little more room to breathe. */
.zone-multi { display: flex; flex-wrap: wrap; gap: .5rem;
  padding: var(--sp-3) 0 var(--sp-2); }
.zone-multi--compact { padding: .15rem 0; gap: .3rem; }
.zone-multi__chip { display: inline-flex; align-items: center; gap: .4rem;
  padding: .32rem .75rem; border: 1px solid var(--border);
  border-radius: 999px; background: var(--surface-2); cursor: pointer;
  font-size: var(--fs-sm); user-select: none;
  transition: border-color .12s, background .12s, color .12s; }
.zone-multi--compact .zone-multi__chip {
  padding: .2rem .55rem; font-size: var(--fs-xs); }
.zone-multi__chip:hover { border-color: color-mix(in srgb, var(--wing) 40%, var(--border)); }
.zone-multi__chip input { margin: 0; accent-color: var(--wing); }
.zone-multi__chip:has(input:checked) {
  border-color: var(--wing);
  background: color-mix(in srgb, var(--wing) 14%, var(--surface));
  color: var(--text); font-weight: 600; }

/* When a container has children that are the real content OR a skeleton,
   toggle `[aria-busy="true"]` while the fetch is in flight — the child skel
   nodes react automatically without JS having to remove classes. */
[aria-busy="true"] .skel-line,
[aria-busy="true"] .skel-box,
[aria-busy="true"] .skel-row td > span { visibility: visible; }

/* Global upgrade: every wing page's ``<div class="table-loading">
   <div class="loader"></div> …</div>`` block used to render as a small
   spinning circle + "Загрузка..." text. Repaint them as a skeleton block
   so loading is visually consistent with the admin surfaces without
   touching every template. The inner spinner/text stays available for
   assistive tech (screen readers still get the loading state). */
.table-loading { position: relative; padding: 0; min-height: 88px;
  border-radius: var(--radius-lg); overflow: hidden;
  background: linear-gradient(90deg,
    var(--surface-2) 0%,
    color-mix(in srgb, var(--surface-2) 55%, var(--border)) 50%,
    var(--surface-2) 100%);
  background-size: 200% 100%;
  animation: skelShine 1.4s ease-in-out infinite,
             skelPulse 1.6s ease-in-out infinite; }
.table-loading > .loader,
.table-loading > span,
.table-loading > .loading-text { position: absolute !important;
  width: 1px; height: 1px; padding: 0; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
