/* =========================================================================
   MagicRPG Guides — design system
   Fonts: Yeseva One (display) + Inter (body/UI), both full Cyrillic sets.
   Loaded via <link> tags in partials/head.ejs, not @import here — @import
   only starts downloading once this stylesheet itself has been fetched and
   parsed, chaining an otherwise-parallel request onto the critical path.
   ========================================================================= */

/* ---------------------------------------------------------------------
   Tokens
   --------------------------------------------------------------------- */
:root {
  /* surfaces */
  --ink-950: #0a0710;
  --ink-900: #120b1e;
  --ink-800: #191228;
  --ink-700: #211934;
  --ink-600: #2a2140;
  --ink-500: #382c54;
  --line: rgba(175, 148, 255, 0.14);
  --line-strong: rgba(175, 148, 255, 0.3);

  /* brand violet */
  --violet-200: #e7ddff;
  --violet-300: #cab3ff;
  --violet-400: #ab8bff;
  --violet-500: #8d5cf5;
  --violet-600: #7440dd;
  --violet-700: #5b2fb3;
  --violet-glow: rgba(141, 92, 245, 0.45);

  /* warm accent (candle / parchment gold) */
  --gold-300: #f2ddab;
  --gold-400: #e2ba6b;
  --gold-500: #c99a49;

  /* text on dark */
  --text-100: #f5f1fc;
  --text-300: #dbd2f0;
  --text-400: #b3a6d4;
  /* #8478a3 measured 4.3:1 against the card gradient background (below the
     4.5:1 AA floor for normal text) — this tone is used for real body copy
     (card descriptions, form hints) not just decorative meta, so it's
     nudged lighter to clear AA against every background it appears on
     while staying visually the same muted secondary tone. */
  --text-500: #8c81a9;
  --text-600: #665a85;

  /* category accents */
  --acc-blood-magic: #e2636b;
  --acc-thaumcraft: #cda05a;
  --acc-witchery: #66b07f;
  --acc-botania: #dd85bb;
  --acc-loli-magically: #ab8bff;
  --acc-loli-dimensions: #5b93d6;
  --acc-loli-combat: #d65a86;

  /* type */
  --font-display: 'Yeseva One', 'PT Serif', Georgia, serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --fs-hero: clamp(2.35rem, 1.9rem + 2.2vw, 4.1rem);
  --fs-h1: clamp(1.9rem, 1.6rem + 1.4vw, 2.75rem);
  --fs-h2: clamp(1.4rem, 1.25rem + 0.7vw, 1.8rem);
  --fs-h3: clamp(1.15rem, 1.08rem + 0.3vw, 1.3rem);
  --fs-h4: 1.02rem;
  --fs-body: 1.0625rem;
  --fs-small: 0.9rem;
  --fs-meta: 0.8rem;

  /* spacing scale (4px base) */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4rem;
  --sp-9: 6rem;

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 8px 24px -8px rgba(6, 3, 14, 0.55);
  --shadow-lg: 0 24px 60px -20px rgba(6, 3, 14, 0.65);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  --dur-fast: 150ms;
  --dur-base: 240ms;
  --dur-slow: 420ms;

  --header-h: 64px;
  --sidebar-w: 300px;
  --content-max: 780px;
}

/* ---------------------------------------------------------------------
   Reset
   --------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html {
  color-scheme: dark;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
body {
  background: var(--ink-950);
  color: var(--text-300);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  overflow-x: hidden;
}
img, picture, svg { max-width: 100%; display: block; }
input, button, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { padding-left: 1.3em; }
table { border-collapse: collapse; width: 100%; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; top: -48px; left: var(--sp-4); z-index: 200;
  background: var(--violet-500); color: #fff; padding: var(--sp-2) var(--sp-4);
  border-radius: var(--radius-sm); transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus { top: var(--sp-4); }

:focus-visible {
  outline: 2px solid var(--violet-400);
  outline-offset: 2px;
  border-radius: 4px;
}

/* subtle magical backdrop: soft violet/gold nebulae, fixed so it never
   competes with reading content while scrolling */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(ellipse 900px 560px at 12% -8%, rgba(141, 92, 245, 0.22), transparent 60%),
    radial-gradient(ellipse 700px 500px at 108% 12%, rgba(226, 186, 107, 0.10), transparent 60%),
    radial-gradient(ellipse 800px 700px at 50% 120%, rgba(116, 64, 221, 0.16), transparent 60%),
    var(--ink-950);
}

/* ---------------------------------------------------------------------
   Headings & prose
   --------------------------------------------------------------------- */
h1, h2, h3 { font-family: var(--font-display); color: var(--text-100); line-height: 1.25; font-weight: 400; }
h4, h5 { font-family: var(--font-body); color: var(--text-100); font-weight: 700; line-height: 1.4; }
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
h4 { font-size: var(--fs-h4); }

.prose { max-width: var(--content-max); }
.prose > * + * { margin-top: var(--sp-4); }
.prose h2 {
  margin-top: var(--sp-8);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--line);
  position: relative;
  scroll-margin-top: calc(var(--header-h) + var(--sp-4));
}
.prose > h2:first-child,
.prose > h2:first-of-type:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.prose h2::before {
  content: "";
  position: absolute;
  top: -1px; left: 0;
  width: 64px; height: 1px;
  background: linear-gradient(90deg, var(--violet-400), transparent);
}
.prose h3 {
  margin-top: var(--sp-7);
  color: var(--violet-200);
  scroll-margin-top: calc(var(--header-h) + var(--sp-4));
}
.prose h4 {
  margin-top: var(--sp-6);
  color: var(--gold-300);
  font-size: 1rem;
  letter-spacing: 0.01em;
}
.prose p { color: var(--text-300); }
.prose strong { color: var(--text-100); font-weight: 650; }
.prose em { color: var(--text-100); }
.prose a:not(.btn) {
  color: var(--violet-300);
  text-underline-offset: 3px;
  text-decoration: underline;
  text-decoration-color: rgba(171, 139, 255, 0.4);
  transition: color var(--dur-fast) var(--ease), text-decoration-color var(--dur-fast) var(--ease);
}
.prose a:not(.btn):hover { color: var(--gold-300); text-decoration-color: currentColor; }

.prose ul, .prose ol { color: var(--text-300); padding-left: 1.1em; }
.prose li { padding-left: 0.3em; margin-top: 0.4em; }
.prose li::marker { color: var(--violet-400); }
.prose li p { margin: 0; }
.prose ul ul, .prose ol ol, .prose ul ol, .prose ol ul { margin-top: 0.4em; }

.prose blockquote {
  border-left: 3px solid var(--gold-500);
  padding: var(--sp-1) var(--sp-5);
  color: var(--text-400);
  font-style: italic;
}

/* in-game chat / dialogue callouts (from the docx's highlighted quotes) */
q.game-line {
  display: inline-block;
  quotes: none;
  font-family: var(--font-body);
  font-style: italic;
  color: var(--gold-300);
  background: linear-gradient(180deg, rgba(226, 186, 107, 0.12), rgba(226, 186, 107, 0.05));
  border: 1px solid rgba(226, 186, 107, 0.28);
  border-radius: var(--radius-sm);
  padding: 0.15em 0.6em;
  line-height: 1.6;
}
q.game-line::before { content: "\201C"; color: var(--gold-500); margin-right: 0.15em; }
q.game-line::after { content: "\201D"; color: var(--gold-500); margin-left: 0.15em; }

.update-banner {
  position: fixed; top: var(--sp-4); left: 50%; transform: translate(-50%, -20px);
  z-index: 300; display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-md);
  background: linear-gradient(160deg, var(--ink-700), var(--ink-800));
  border: 1px solid var(--line-strong); box-shadow: var(--shadow-md);
  font-size: var(--fs-small); color: var(--text-200, var(--text-300));
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-in), transform var(--dur-base) var(--ease-in);
}
.update-banner.is-open { opacity: 1; pointer-events: auto; transform: translate(-50%, 0); transition-timing-function: var(--ease); }
.update-banner button {
  font: inherit; font-weight: 600; cursor: pointer;
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-sm);
  border: 1px solid var(--violet-400); background: rgba(141,92,245,0.14); color: var(--violet-300);
}
.update-banner button:hover { background: rgba(141,92,245,0.24); }

.video-embed {
  position: relative; aspect-ratio: 16 / 9; max-width: 100%;
  margin: var(--sp-5) 0; border-radius: var(--radius-md); overflow: hidden;
  background: var(--ink-900); border: 1px solid var(--line);
}
.video-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.section-credit {
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: 1px dashed var(--line-strong);
  font-size: var(--fs-small);
  color: var(--text-500);
  font-style: italic;
}

/* tables */
.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--ink-800);
  -webkit-overflow-scrolling: touch;
}
.prose table { font-size: 0.94rem; min-width: 100%; table-layout: auto; }
.prose th, .prose td {
  text-align: left;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line);
  vertical-align: top;
  overflow-wrap: break-word;
}
.prose thead th {
  font-family: var(--font-body);
  font-weight: 700;
  color: var(--gold-300);
  background: var(--ink-700);
  box-shadow: 0 1px 0 var(--line-strong);
}
.prose tbody tr:last-child td { border-bottom: none; }
.prose tbody tr:hover { background: rgba(141, 92, 245, 0.06); }
.prose td:first-child, .prose th:first-child { color: var(--text-100); font-weight: 600; }

/* images */
.prose p > span.inline-shot,
.prose p > span.inline-shot img {
  display: block;
}
.shot--icon, .shot--small { border-radius: var(--radius-sm); }
img[class^="shot--"] {
  /* Screenshots carry real width/height attributes when uploaded through the
     editor (see assets/admin.js), so the browser can derive the correct
     aspect ratio and reserve space before the file downloads — height:auto
     here is what lets that scale proportionally under each size class's
     max-width instead of stretching. */
  height: auto;
  border: 1px solid var(--line);
  background: var(--ink-800);
  border-radius: var(--radius-md);
  cursor: zoom-in;
  transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
img[class^="shot--"]:hover { border-color: var(--line-strong); }
img[class^="shot--"]:not(.shot--icon):active { transform: scale(0.98); transition-duration: var(--dur-fast); }
.shot--icon { max-width: 56px; display: inline-block; vertical-align: middle; border-radius: var(--radius-sm); background: none; border: none; cursor: default; }
.shot--small { max-width: 220px; }
.shot--medium { max-width: 420px; }
.shot--large { max-width: 100%; }

figure { margin: 0; }
figure figcaption { display: none; }

.gallery {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin: var(--sp-4) 0;
}
.gallery figure { flex: 1 1 260px; max-width: 100%; }
.gallery img { width: 100%; height: auto; max-width: 100%; }
.gallery:has(> figure:only-child) { max-width: 460px; }

/* ---------------------------------------------------------------------
   Header / top nav
   --------------------------------------------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  height: var(--header-h);
  display: flex; align-items: center;
  background: rgba(10, 7, 16, 0.78);
  backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--line);
}
.site-header__inner {
  width: 100%; max-width: 1280px; margin: 0 auto;
  padding: 0 var(--sp-5);
  display: flex; align-items: center; gap: var(--sp-5);
}
.brand {
  display: flex; align-items: center; gap: var(--sp-2);
  font-family: var(--font-display); font-size: 1.15rem; color: var(--text-100);
  white-space: nowrap;
}
.brand__mark {
  width: 28px; height: 28px; flex: none;
  filter: drop-shadow(0 0 6px var(--violet-glow));
}
.brand small { display: block; font-family: var(--font-body); font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-500); font-weight: 600; }

.top-nav { display: flex; align-items: center; gap: var(--sp-1); margin-left: auto; }
.top-nav a {
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  font-size: var(--fs-small);
  color: var(--text-400);
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.top-nav a:hover, .top-nav a.is-active { color: var(--text-100); background: rgba(141, 92, 245, 0.12); }
.top-nav a:active { transform: scale(0.96); }
.nav-logout-form { display: contents; }
.nav-logout-btn {
  font: inherit;
  cursor: pointer;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  font-size: var(--fs-small);
  color: var(--text-400);
  background: transparent;
  border: none;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.nav-logout-btn:hover { color: var(--text-100); background: rgba(141, 92, 245, 0.12); }
.nav-logout-btn:active { transform: scale(0.96); }
@media (max-width: 860px) {
  .nav-logout-btn { width: 100%; text-align: left; padding: var(--sp-4); border-radius: 0; border-bottom: 1px solid var(--line); font-size: 1rem; }
}

.menu-toggle {
  display: none;
  width: 40px; height: 40px; border-radius: var(--radius-sm);
  background: transparent; border: 1px solid var(--line);
  align-items: center; justify-content: center;
  color: var(--text-100);
}

/* ---------------------------------------------------------------------
   Buttons
   --------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-5);
  border-radius: var(--radius-sm);
  font-weight: 600; font-size: var(--fs-small);
  border: 1px solid transparent;
  transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.btn:active { transform: translateY(1px) scale(0.98); }
.btn--primary {
  background: linear-gradient(180deg, var(--violet-500), var(--violet-600));
  color: #fff;
  box-shadow: 0 6px 20px -6px var(--violet-glow);
}
.btn--primary:hover { box-shadow: 0 10px 28px -6px var(--violet-glow); transform: translateY(-1px); }
.btn--ghost {
  background: rgba(255,255,255,0.02);
  border-color: var(--line-strong);
  color: var(--text-200, var(--text-300));
}
.btn--ghost:hover { border-color: var(--violet-400); color: var(--text-100); }

/* ---------------------------------------------------------------------
   Homepage: hero
   --------------------------------------------------------------------- */
.hero {
  position: relative;
  padding: clamp(3.5rem, 6vw, 6.5rem) var(--sp-5) clamp(2.5rem, 5vw, 4rem);
  overflow: hidden;
  isolation: isolate;
}
.hero__inner { max-width: 880px; margin: 0 auto; text-align: center; }
.hero__eyebrow {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-meta); letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--gold-300); font-weight: 600;
  padding: var(--sp-1) var(--sp-4); border: 1px solid rgba(226,186,107,0.3);
  border-radius: 999px; background: rgba(226,186,107,0.06);
  margin-bottom: var(--sp-5);
}
.hero h1 {
  font-size: var(--fs-hero);
  color: var(--text-100);
  text-shadow: 0 0 44px rgba(141, 92, 245, 0.35);
}
.hero h1 em {
  font-style: normal;
  background: linear-gradient(120deg, var(--violet-300), var(--gold-300));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero p.lede {
  margin: var(--sp-5) auto 0; max-width: 640px;
  font-size: 1.1rem; color: var(--text-400);
}
.hero__cta { margin-top: var(--sp-7); display: flex; gap: var(--sp-3); justify-content: center; flex-wrap: wrap; }
.hero__meta { margin-top: var(--sp-8); display: flex; gap: var(--sp-7); justify-content: center; flex-wrap: wrap; }
.hero__meta div { text-align: center; }
.hero__meta strong { display: block; font-family: var(--font-display); font-size: 1.6rem; color: var(--text-100); }
.hero__meta span { font-size: var(--fs-meta); color: var(--text-500); text-transform: uppercase; letter-spacing: 0.08em; }

/* floating ember particles behind the hero */
.embers { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.embers span {
  position: absolute; bottom: -10%;
  width: var(--size, 5px); height: var(--size, 5px);
  border-radius: 50%;
  background: var(--c, var(--violet-400));
  box-shadow: 0 0 8px 1px var(--c, var(--violet-400));
  opacity: 0;
  animation: ember-rise var(--dur, 9s) var(--delay, 0s) infinite ease-in;
}
@keyframes ember-rise {
  0% { transform: translateY(0) translateX(0); opacity: 0; }
  8% { opacity: 0.75; }
  85% { opacity: 0.35; }
  100% { transform: translateY(-620px) translateX(var(--drift, 30px)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .embers { display: none; } }

/* ---------------------------------------------------------------------
   Homepage: category cards
   --------------------------------------------------------------------- */
.section-heading {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4);
  margin: 0 auto var(--sp-6); max-width: 1180px; padding: 0 var(--sp-5);
}
.section-heading__text { display: flex; align-items: baseline; gap: var(--sp-4); flex-wrap: wrap; }
.section-heading h2 { font-size: var(--fs-h1); }
.section-heading p { color: var(--text-500); font-size: var(--fs-small); }

.cards {
  max-width: 1180px; margin: 0 auto; padding: 0 var(--sp-5) var(--sp-9);
  display: grid; gap: var(--sp-5);
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  /* Without this, grid's default stretch forces every card in a row to
     match the tallest one — so one oversized card (see .card's own
     max-height/scroll above) was still dragging its row-mates up to its
     height instead of letting them keep their own natural size. */
  align-items: start;
}
.card {
  --accent: var(--violet-400);
  position: relative;
  background: linear-gradient(160deg, var(--ink-700), var(--ink-800));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-3);
  /* Fixed (not max-) height: every card is the same size regardless of
     description length or tag count, matching a regular tile grid instead
     of stretching row-mates or growing unpredictably. */
  height: 300px;
  transition: transform var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), box-shadow var(--dur-base) var(--ease);
}
.card__body {
  flex: 1;
  /* Overrides the implicit min-height:auto flex items get by default,
     which otherwise refuses to shrink below content size and defeats
     overflow/scrolling. Deliberately on this inner wrapper rather than on
     .card itself: .card is simultaneously a grid item (of .cards) and a
     flex container, and Chromium does not reliably clip overflow on an
     element playing both roles at once — verified via devtools (declared
     max-height/overflow computed correctly, but content still painted past
     the box). A plain flex-child-in-flex-container wrapper doesn't hit
     that bug. */
  min-height: 0;
  display: flex; flex-direction: column; gap: var(--sp-3);
  overflow-y: auto; overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-500) transparent;
  /* .card__link below covers the whole card (position:absolute; inset:0)
     as a click target and, being positioned, paints above ordinary static
     content by default — which would also sit "on top" for hit-testing and
     silently swallow wheel-scroll input meant for this region. Raising
     .card__body onto its own stacking level puts it back above the overlay
     so scrolling and the tag-chip links inside it work; the header/footer
     rows outside .card__body remain part of the whole-card click target;
     assets/js/main.js forwards clicks on this element's own blank space to
     .card__link so the whole card still opens the guide on click. */
  position: relative;
  z-index: 1;
  cursor: pointer;
}
.card::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(360px 160px at 20% -10%, color-mix(in srgb, var(--accent) 22%, transparent), transparent);
  opacity: 0; transition: opacity var(--dur-base) var(--ease);
}
.card:hover, .card:focus-within { transform: translateY(-4px); border-color: color-mix(in srgb, var(--accent) 55%, var(--line-strong)); box-shadow: var(--shadow-md); }
.card:hover::before, .card:focus-within::before { opacity: 1; }
.card:active { transform: translateY(-4px) scale(0.98); transition-duration: var(--dur-fast); }
.card__top { display: flex; align-items: center; justify-content: space-between; }
.card__icon {
  width: 44px; height: 44px; border-radius: var(--radius-md);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  background: color-mix(in srgb, var(--accent) 16%, var(--ink-800));
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}
.card__icon svg { width: 24px; height: 24px; }
.card__top-right { display: flex; align-items: center; gap: var(--sp-2); position: relative; }
.card__kind {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.1em; font-weight: 700;
  color: var(--accent);
}

/* admin-only indicators — reused on the homepage card and the guide page
   itself; gold matches the existing admin-role badge color (see admin.css
   .badge--admin) so "gold = admin-only" reads consistently across the site. */
.visibility-flag {
  display: inline-flex; align-items: center;
  color: var(--gold-400);
  opacity: 0.85;
}
.visibility-flag svg { width: 14px; height: 14px; }
.admin-only-banner {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-bottom: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm);
  border: 1px solid rgba(226,186,107,0.3);
  background: rgba(226,186,107,0.1);
  color: var(--gold-400);
  font-size: var(--fs-small); font-weight: 600;
}
.card h3 { font-size: 1.3rem; color: var(--text-100); position: relative; }
.card p { color: var(--text-500); font-size: var(--fs-small); flex: 1; position: relative; }
.card__footer {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: var(--sp-3); margin-top: auto;
  border-top: 1px solid var(--line);
  font-size: var(--fs-meta); color: var(--text-500);
  position: relative;
}
.card__go {
  display: inline-flex; align-items: center; gap: 0.35em;
  color: var(--accent); font-weight: 600;
  transition: gap var(--dur-fast) var(--ease);
}
.card:hover .card__go { gap: 0.55em; }
.card__link { position: absolute; inset: 0; border-radius: inherit; }

/* admin-only: homepage drag-and-drop reorder + right-click quick actions */
.card--reorderable { cursor: grab; }
.card--reorderable.is-dragging { opacity: 0.4; }

/* failed admin quick-action (publish/visibility toggle, reorder) — no
   toast component, the card itself briefly outlines red instead.
   Deliberately longer than this codebase's usual ≤300ms direct-feedback
   budget: this is an alert cue ("that action failed"), not press/release
   micro-feedback — cutting it to 300ms would make it too brief to actually
   register as an error before it's gone. */
@keyframes card-flash-error {
  0%, 100% { box-shadow: none; border-color: var(--line); }
  30% { box-shadow: 0 0 0 2px rgba(255, 90, 90, 0.45); border-color: rgba(255, 90, 90, 0.6); }
}
.card.flash-error { animation: card-flash-error 1.1s ease; }
.card__drag-handle {
  position: absolute; top: var(--sp-3); right: var(--sp-3); z-index: 2;
  color: var(--text-500); font-size: 1.1rem; line-height: 1;
  opacity: 0; transition: opacity var(--dur-fast) var(--ease);
}
.card--reorderable:hover .card__drag-handle, .card--reorderable:focus-within .card__drag-handle { opacity: 1; }

.card-context-menu {
  position: absolute; z-index: 500;
  display: flex; flex-direction: column; min-width: 200px;
  background: var(--ink-800); border: 1px solid var(--line-strong);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  padding: var(--sp-2); gap: 2px;
}
.card-context-menu button {
  text-align: left; padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-sm);
  color: var(--text-200, var(--text-300)); font-size: var(--fs-small);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.card-context-menu button:hover, .card-context-menu button:focus-visible {
  background: rgba(141,92,245,0.12); color: var(--text-100);
}
.card-context-menu button:active { transform: scale(0.98); }

/* ---------------------------------------------------------------------
   Guide page layout
   --------------------------------------------------------------------- */
.guide-layout {
  max-width: 1280px; margin: 0 auto;
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  gap: var(--sp-7);
  align-items: start;
  padding: var(--sp-6) var(--sp-5) var(--sp-9);
}

.sidebar {
  position: sticky; top: calc(var(--header-h) + var(--sp-5));
  max-height: calc(100vh - var(--header-h) - var(--sp-6));
  overflow-y: auto;
  padding-right: var(--sp-3);
  scrollbar-width: thin;
  scrollbar-color: var(--ink-500) transparent;
}
.sidebar__title {
  display: flex; align-items: center; gap: var(--sp-3);
  margin-bottom: var(--sp-5);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--line);
}
.sidebar__title .card__icon { width: 38px; height: 38px; }
.sidebar__title h2 { font-size: 1.05rem; font-family: var(--font-body); font-weight: 700; color: var(--text-100); }
.sidebar__title span:not(.card__icon) { display: block; font-size: var(--fs-meta); color: var(--text-500); }

.toc { list-style: none; padding: 0; font-size: 0.9rem; }
.toc > li { margin-bottom: var(--sp-1); }
.toc-section > button {
  width: 100%;
  display: flex; align-items: center; gap: var(--sp-2);
  background: none; border: none; text-align: left;
  padding: var(--sp-2) var(--sp-2);
  border-radius: var(--radius-sm);
  color: var(--text-400);
  font-weight: 600;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.toc-section > button:hover { color: var(--text-100); background: rgba(141,92,245,0.08); }
.toc-section > button:active { transform: scale(0.98); }
.toc-section > button .chev {
  width: 14px; height: 14px; flex: none; color: var(--text-600);
  transition: transform var(--dur-fast) var(--ease);
}
.toc-section.is-open > button .chev { transform: rotate(90deg); }
.toc-section > button .dot {
  width: 5px; height: 5px; border-radius: 50%; background: var(--text-600); flex: none;
}
.toc a.is-active .dot, .toc-section > button.is-active .dot { background: var(--violet-400); box-shadow: 0 0 6px var(--violet-glow); }
.toc a.is-active, .toc-section > button.is-active { color: var(--text-100); }

.toc-children {
  list-style: none;
  padding-left: var(--sp-5);
  overflow: hidden;
  max-height: 0;
  transition: max-height var(--dur-base) var(--ease-in);
}
.toc-section.is-open > .toc-children { max-height: 800px; transition-timing-function: var(--ease); }
.toc-children a {
  display: block; padding: var(--sp-1) var(--sp-2);
  border-radius: var(--radius-sm);
  color: var(--text-500);
  font-size: 0.86rem;
  border-left: 1px solid var(--line);
  margin-left: 2px;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.toc-children a:hover { color: var(--text-100); }
.toc-children a:active { transform: scale(0.98); }
.toc-children a.is-active { color: var(--violet-300); border-left-color: var(--violet-400); background: rgba(141,92,245,0.06); }
.toc-flat a {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2); border-radius: var(--radius-sm); color: var(--text-400); font-weight: 600;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.toc-flat a:hover { color: var(--text-100); background: rgba(141,92,245,0.08); }
.toc-flat a:active { transform: scale(0.98); }

.guide-main { min-width: 0; }
.guide-header { margin-bottom: var(--sp-7); }
.guide-header__crumb {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-meta); color: var(--text-500); margin-bottom: var(--sp-4);
  text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600;
}
.guide-header__crumb a { color: var(--text-500); transition: color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease); }
.guide-header__crumb a:hover { color: var(--violet-300); }
.guide-header__crumb a:active { transform: scale(0.96); }
.guide-header__crumb-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-4);
}
.guide-header__crumb-row .guide-header__crumb { margin-bottom: 0; }
.guide-header__edit { flex-shrink: 0; padding: var(--sp-2) var(--sp-4); font-size: var(--fs-meta); }
.guide-header__top { display: flex; align-items: center; gap: var(--sp-4); margin-bottom: var(--sp-4); }
.guide-header .card__icon { width: 52px; height: 52px; }
.guide-header h1 { font-size: var(--fs-h1); }
.guide-header .lede { font-size: 1.08rem; color: var(--text-400); max-width: var(--content-max); }

.mobile-toc-toggle { display: none; }

/* prev / next footer nav */
.pagenav {
  max-width: var(--content-max);
  margin-top: var(--sp-9);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--line);
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4);
}
.pagenav a {
  display: flex; flex-direction: column; gap: var(--sp-1);
  padding: var(--sp-4); border: 1px solid var(--line); border-radius: var(--radius-md);
  transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.pagenav a:hover { border-color: var(--violet-400); transform: translateY(-2px); }
.pagenav a:active { transform: translateY(-2px) scale(0.98); transition-duration: var(--dur-fast); }
.pagenav a.next { text-align: right; align-items: flex-end; grid-column: 2; }
.pagenav small { color: var(--text-500); font-size: var(--fs-meta); text-transform: uppercase; letter-spacing: 0.08em; }
.pagenav strong { font-family: var(--font-display); font-weight: 400; color: var(--text-100); font-size: 1.15rem; }

/* subtle prev/next arrows, floating in the margins beside the content —
   deliberately low-opacity so they don't compete with the bottom .pagenav
   cards, which stay the primary way to move between guides. */
.guide-nav-arrow {
  position: fixed; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--ink-800);
  color: var(--text-400); opacity: 0.35; z-index: 40;
  transition: opacity var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.guide-nav-arrow:hover, .guide-nav-arrow:focus-visible { opacity: 1; color: var(--text-100); border-color: var(--violet-400); }
.guide-nav-arrow:active { transform: translateY(-50%) scale(0.95); }
.guide-nav-arrow--prev { left: var(--sp-5); }
.guide-nav-arrow--next { right: var(--sp-5); }
/* Below this width there isn't enough margin outside the (max-width: 1280px)
   guide-layout for the arrows to float without crowding its own edge padding. */
@media (max-width: 1440px) {
  .guide-nav-arrow { display: none; }
}

/* ---------------------------------------------------------------------
   Footer
   --------------------------------------------------------------------- */
.site-footer {
  border-top: 1px solid var(--line);
  padding: var(--sp-7) var(--sp-5);
  margin-top: var(--sp-8);
}
.site-footer__inner {
  max-width: 1280px; margin: 0 auto;
  display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: var(--sp-4);
  color: var(--text-500); font-size: var(--fs-small);
}
.site-footer strong { color: var(--text-300); }

/* ---------------------------------------------------------------------
   Back to top + lightbox
   --------------------------------------------------------------------- */
.to-top {
  position: fixed; right: var(--sp-5); bottom: var(--sp-5); z-index: 90;
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--ink-700); border: 1px solid var(--line-strong);
  color: var(--text-300);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity var(--dur-base) var(--ease-in), transform var(--dur-base) var(--ease-in), visibility var(--dur-base);
  box-shadow: var(--shadow-md);
}
.to-top.is-visible { opacity: 1; visibility: visible; transform: translateY(0); transition-timing-function: var(--ease); }
.to-top:hover { border-color: var(--violet-400); color: var(--text-100); }
.to-top:active { transform: scale(0.95); }

.lightbox {
  /* Above .update-banner (z-index: 300 above) — the lightbox is a true
     modal (freezes background scroll) and both can plausibly be open at
     once on the guide page (an SSE update banner appearing while a
     screenshot is open), so it must win the stacking order. */
  position: fixed; inset: 0; z-index: 310;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-7);
  background: rgba(8, 5, 14, 0.88);
  backdrop-filter: blur(6px);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-base) var(--ease-in), visibility var(--dur-base);
}
.lightbox.is-open { opacity: 1; visibility: visible; transition-timing-function: var(--ease); }
.lightbox img {
  max-width: min(1100px, 92vw); max-height: 86vh; width: auto;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--line-strong);
  transform: scale(0.96);
  transition: transform var(--dur-base) var(--ease-in);
  cursor: zoom-out;
}
.lightbox.is-open img { transform: scale(1); transition-timing-function: var(--ease); }
.lightbox__close {
  position: absolute; top: var(--sp-5); right: var(--sp-5);
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--ink-700); border: 1px solid var(--line-strong); color: var(--text-100);
  display: flex; align-items: center; justify-content: center;
}

/* scroll-reveal (progressive enhancement only): content is visible by
   default so it never depends on JS to be seen. Only once main.js
   confirms it's running (html.js-anim, set synchronously in <head>)
   do .reveal elements start hidden and fade in via IntersectionObserver. */
html.js-anim .reveal { opacity: 0; transform: translateY(14px); transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease); }
html.js-anim .reveal.is-in { opacity: 1; transform: translateY(0); }

/* ---------------------------------------------------------------------
   Responsive
   --------------------------------------------------------------------- */
@media (max-width: 1024px) {
  .guide-layout { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed; inset: var(--header-h) 0 0 auto; width: min(320px, 86vw);
    background: var(--ink-900); border-left: 1px solid var(--line);
    padding: var(--sp-5); max-height: none; height: calc(100vh - var(--header-h));
    transform: translateX(100%);
    transition: transform var(--dur-base) var(--ease-in);
    z-index: 95;
    box-shadow: var(--shadow-lg);
  }
  .sidebar.is-open { transform: translateX(0); transition-timing-function: var(--ease); }
  .mobile-toc-toggle {
    display: flex; align-items: center; gap: var(--sp-2);
    margin-bottom: var(--sp-6);
    background: var(--ink-700); border: 1px solid var(--line-strong);
    padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-md);
    color: var(--text-300); font-size: var(--fs-small); font-weight: 600;
  }
  .sidebar-scrim {
    display: none; position: fixed; inset: 0; background: rgba(6,3,10,0.6); z-index: 94;
  }
  .sidebar-scrim.is-open { display: block; }
}
@media (max-width: 860px) {
  .top-nav { position: fixed; inset: var(--header-h) 0 0 0; height: 0; overflow: hidden;
    flex-direction: column; align-items: stretch; background: var(--ink-900); gap: 0;
    transition: height var(--dur-base) var(--ease-in); border-bottom: 1px solid var(--line); }
  .top-nav.is-open { height: calc(100vh - var(--header-h)); overflow-y: auto; padding: var(--sp-4); transition-timing-function: var(--ease); }
  .top-nav a { padding: var(--sp-4); border-radius: 0; border-bottom: 1px solid var(--line); font-size: 1rem; }
  .menu-toggle { display: flex; margin-left: auto; }
  .pagenav { grid-template-columns: 1fr; }
  .pagenav a.next { grid-column: 1; text-align: left; align-items: flex-start; }
}
/* ---------------------------------------------------------------------
   Forms: auth pages + admin panel share this vocabulary
   --------------------------------------------------------------------- */
.auth-shell {
  display: flex; align-items: center; justify-content: center;
  min-height: calc(100vh - var(--header-h) - 220px);
  padding: var(--sp-7) var(--sp-5);
}
.form-card {
  width: 100%; max-width: 400px;
  background: linear-gradient(160deg, var(--ink-700), var(--ink-800));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--sp-6);
  box-shadow: var(--shadow-md);
}
.form-card h1 { font-size: var(--fs-h2); margin-bottom: var(--sp-2); }
.form-card__hint { color: var(--text-400); font-size: var(--fs-small); margin-bottom: var(--sp-5); }
.form-field { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.form-label { font-size: var(--fs-small); font-weight: 600; color: var(--text-300); }
.form-input, .form-select, .form-textarea {
  font: inherit;
  background: var(--ink-900);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--sp-3);
  color: var(--text-100);
  transition: border-color var(--dur-fast) var(--ease);
}
.form-input:focus, .form-select:focus, .form-textarea:focus { border-color: var(--violet-400); }
.form-textarea { resize: vertical; min-height: 120px; font-family: var(--font-body); }
.form-hint { font-size: var(--fs-meta); color: var(--text-500); }
.form-error {
  background: color-mix(in srgb, var(--acc-blood-magic) 14%, var(--ink-800));
  border: 1px solid color-mix(in srgb, var(--acc-blood-magic) 40%, transparent);
  color: var(--text-100);
  border-radius: var(--radius-sm);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-small);
  margin-bottom: var(--sp-4);
}
.form-actions { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); margin-top: var(--sp-5); }
.form-actions .btn { width: 100%; }
.form-footnote { margin-top: var(--sp-5); text-align: center; font-size: var(--fs-small); color: var(--text-400); }
.form-footnote a { color: var(--violet-300); }
.form-footnote a:hover { color: var(--violet-200); }

/* ---------------------------------------------------------------------
   tags, sorting, voting, "О сервере"
   --------------------------------------------------------------------- */
.guides-controls {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp-4); max-width: 1180px; margin: 0 auto var(--sp-6); padding: 0 var(--sp-5);
}
.sort-control { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.sort-control__btn {
  font-size: var(--fs-meta); font-weight: 600; color: var(--text-400);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.sort-control__btn:hover { color: var(--text-100); }
.sort-control__btn:active { transform: scale(0.96); }
.sort-control__btn.is-active { color: var(--text-100); background: rgba(141, 92, 245, 0.14); border-color: var(--line-strong); }

.tag-cloud { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.tag-chip {
  display: inline-flex; align-items: center; gap: 0.35em;
  font-size: var(--fs-meta); color: var(--text-400);
  padding: 0.3em 0.75em; border-radius: 999px;
  border: 1px solid var(--line); background: var(--ink-900);
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.tag-chip:hover { color: var(--text-100); border-color: var(--line-strong); }
.tag-chip:active { transform: scale(0.96); }
.tag-chip.is-active { color: var(--text-100); background: rgba(141, 92, 245, 0.16); border-color: var(--violet-400); }
.tag-chip__count { color: var(--text-500); }
.tag-chip--clear { color: var(--text-500); }
.tag-chip--sm { font-size: 0.72rem; padding: 0.2em 0.6em; }
.tag-chip.is-disabled { color: var(--text-500); opacity: 0.45; cursor: default; }

.card__tags { position: relative; z-index: 1; display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
.card__stats { color: var(--text-500); font-size: var(--fs-meta); }

.guide-tags { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-4); }

.vote-widget { display: flex; gap: var(--sp-3); margin-top: var(--sp-6); }
.vote-form { display: contents; }
.vote-btn {
  font: inherit; font-weight: 600; cursor: pointer;
  display: inline-flex; align-items: center; gap: 0.4em;
  padding: var(--sp-2) var(--sp-4); border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--ink-900); color: var(--text-300);
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.vote-btn:hover { border-color: var(--line-strong); color: var(--text-100); }
.vote-btn:active { transform: scale(0.95); }
.vote-btn.is-active { border-color: var(--violet-400); background: rgba(141, 92, 245, 0.16); color: var(--text-100); }
.vote-btn:disabled { cursor: default; opacity: 0.7; }
/* Momentary confirmation pop after a successful vote (assets/js/main.js
   toggles this class on and removes it once the animation ends) — the same
   "acknowledge the action, then get out of the way" role .card.flash-error
   plays for failures, just for the success path instead. Kept under 300ms
   (direct click feedback, unlike an alert cue) and within a subtle
   0.95–1.05 deformation range. */
@keyframes vote-pop { 0% { transform: scale(1); } 35% { transform: scale(1.05); } 100% { transform: scale(1); } }
.vote-btn.just-voted { animation: vote-pop var(--dur-base) var(--ease); }

.page-shell { max-width: 900px; margin: 0 auto; padding: var(--sp-6) var(--sp-5) var(--sp-9); }
.page-shell > h1 { font-size: var(--fs-h1); margin-bottom: var(--sp-5); }
.authors-section { margin-top: var(--sp-8); padding-top: var(--sp-6); border-top: 1px solid var(--line); max-width: var(--content-max); }
.authors-section h2 { font-size: var(--fs-h3); margin-bottom: var(--sp-4); }
.authors-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.authors-list__item { display: flex; align-items: baseline; gap: var(--sp-3); }
.authors-list__role { color: var(--text-500); font-size: var(--fs-small); }

@media (max-width: 640px) {
  .hero__meta { gap: var(--sp-5); }
  .guide-header__top { flex-direction: column; align-items: flex-start; }
  .guides-controls { flex-direction: column; align-items: flex-start; }
}
