

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Always reserve scrollbar space so content doesn't shift when filtering reduces page height */
html {
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: rgba(139, 148, 158, 0.45) rgba(13, 17, 23, 0.6);
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: rgba(13, 17, 23, 0.6); }
::-webkit-scrollbar-thumb {
  background: rgba(139, 148, 158, 0.45);
  border-radius: 5px;
  border: 2px solid rgba(13, 17, 23, 0.6);
}
::-webkit-scrollbar-thumb:hover { background: rgba(227, 196, 106, 0.7); }
::-webkit-scrollbar-corner { background: rgba(13, 17, 23, 0.6); }
/* …except pages that don't scroll at the page level, where the reserved gutter
   just looks like an empty gap on the right:
   - .main-page    single-screen index
   - .calendar-page calendar (fits the screen)
   - .creeps-page   Neutral Creeps / Neutral Abilities — the page is locked
                    (body:has(.creeps-scroll){overflow:hidden}); the real
                    scrollbar lives INSIDE the .creeps-scroll box, not here. */
html:has(.main-page),
html:has(.calendar-page),
html:has(.creeps-page) { scrollbar-gutter: auto; }

body {
  background: #0a0e13;
  background-image:
    radial-gradient(at 20% 0%, rgba(179, 45, 35, 0.08) 0, transparent 50%),
    radial-gradient(at 80% 100%, rgba(255, 100, 50, 0.05) 0, transparent 50%);
  background-attachment: fixed;
  color: #c9d1d9;
  font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  line-height: 1.55;
  font-size: 15px;
  min-height: 100vh;
}

/* ── Patch pages: Valve-style featured.jpg texture behind a translucent
   content "stage". The faceted texture shows at the page margins; the
   centered content column sits on a dimmed dark panel so text stays readable.
   (First pass — tune the alpha / borders / margin reveal to taste.) ── */
body.patch-page {
  /* Shorthand on purpose: also WIPES the base body rule's radial gradients
     and their background-attachment:fixed (same repaint-on-scroll issue). */
  background: #0e1015;
}
/* Valve's patch-page texture (featured.jpg) as a FIXED UNDERLAY element, not
   `background-attachment: fixed` on body. A fixed-attachment background can't
   be scroll-blitted — with the translucent .cat-panels above it the browser
   repainted the ENTIRE viewport every scroll frame (the "heavy scroll" on the
   huge 7.41 page). A position:fixed pseudo gets its own compositor layer:
   scrolling just re-composites on the GPU, zero repaints. Visual result is
   identical (texture stays pinned while content scrolls over it). */
body.patch-page::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  background: #0e1015 url('https://cdn.steamstatic.com/apps/dota2/images/dota_react/backgrounds/featured.jpg') center top / cover no-repeat;
}
/* Container is transparent; patch-page depth comes from Valve-style section
   bars and per-entity changelog slabs rather than one large outer card. */
body.patch-page .container {
  background: none;
  padding-left: 0;
  padding-right: 0;
  padding-top: 4px;
}
/* Category wrapper = one continuous Valve-style changelog slab below the
   section title. This avoids "gaps" between neighbouring items/entities while
   still letting the orange section bar sit on its own seam. */
body.patch-page .cat-panel {
  background: linear-gradient(90deg, rgba(0, 0, 0, 0.38) 3.07%, rgba(6, 37, 65, 0.35) 88.06%);
  border: 0;
  border-left: 2px solid rgba(255, 255, 255, 0.065);
  border-radius: 0;
  box-shadow: 0 0 50px #000;
  padding: 0 18px 20px;
  margin: 0 0 34px;
}
/* Category header: flat orange-to-transparent strip, like Valve's section
   title layer. No rounded card edge; the strip floats over the page texture. */
body.patch-page h2.section {
  margin: 0 -18px 12px;
  padding: 12px 20px;
  border-radius: 0;
  border-left: 2px solid #ff5005;
  background: linear-gradient(to right, #833312, rgba(131, 51, 18, 0));
  box-shadow: 0 0 30px #000;
  font-family: 'Jersey 25', 'Courier New', monospace;
  font-size: 28px;
  letter-spacing: 1.5px;
}
/* Entity blocks are rows inside the continuous category slab: no separate
   background, only a very faint top seam like Valve's entity rows. */
body.patch-page .entity-block {
  background: none;
  border-left: 0;
  box-shadow: none;
  padding: 0 22px 16px;
  margin: 0;
  border-top: 1px solid;
  border-image: linear-gradient(to right, rgba(238, 250, 255, 0.14), rgba(238, 250, 255, 0.09) 62%, rgba(238, 250, 255, 0.055)) 1;
}
body.patch-page h2.section + .entity-block {
  border-top: 0;
}
/* While filtering, the first SURVIVING block in a section (marked by
   refreshPatchFilterLayout) drops its top hairline too, so no orphan line sits
   between the category header and the first kept entry. */
body.patch-page .entity-block.first-visible {
  border-top: 0;
}
body.patch-page .entity {
  margin: 0 -22px 10px;
  padding: 10px 22px 9px;
  border-radius: 0;
  background: transparent;
  border-top: 0;
  border-image: none;
}
body.patch-page .entity-block > h4.subgroup,
body.patch-page .entity-block > ul.changes,
body.patch-page .entity-block > .ability-block,
body.patch-page .entity-block > .components-box,
body.patch-page .entity-block > .components-change,
body.patch-page .entity-block > .provides-box,
body.patch-page .entity-block > .properties-change {
  margin-left: 0;
  margin-right: 0;
}
/* Align the dynamics-cell row's right edge to the content edge (no overhang),
   so the cells, the change-row %s, and every bordered block (build / cost /
   provides / properties / ability-change) all share one right edge — uniform
   widths, no per-box nudge needed. */
body.patch-page .dyn-row-wrap {
  margin-left: 0;
  margin-right: 0;
}
/* ul.changes carried a 6px right padding to compensate the old cell-row
   overhang. The row now ends at the content edge, so drop it — otherwise the
   change rows and the ability-row boxes (inside ul.changes) sit 6px narrower
   than the components / provides / cost boxes. */
body.patch-page ul.changes {
  padding-right: 0;
}

.container {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 28px 80px;       /* flush top — content starts right under toolbar */
}
.container.calendar-page {
  /* A clear gap below the nav, and a trimmed bottom padding so the calendar +
     infographic still fit the viewport without a page scrollbar. */
  padding-top: 20px;
  padding-bottom: 12px;
}

/* TOP NAV (full-width, sits above container)
   Dark archive strip: keep the header in the same metallic family as the site,
   then add pixel character through seams instead of broad gold glow. */
nav.top-nav {
  background:
    linear-gradient(180deg,
      rgba(255, 244, 209, 0.035) 0%,
      rgba(255, 244, 209, 0.0) 12%),
    linear-gradient(180deg,
      #191b1f 0%,
      #13151a 58%,
      #101216 100%);
  backdrop-filter: blur(8px) saturate(104%);
  -webkit-backdrop-filter: blur(8px) saturate(104%);
  border-bottom: 1px solid rgba(163, 135, 73, 0.16);
  box-shadow:
    inset 0 1px 0 rgba(255, 244, 209, 0.06),
    inset 0 -1px 0 rgba(0, 0, 0, 0.44),
    0 5px 16px rgba(0, 0, 0, 0.24);
  width: 100%;
  /* Pin to the viewport top so the brand + version picker stay visible
     while the page (and sticky table headers below) scroll. Stack-order:
     site nav (z 100) → subnav (z 90) → toolbar (z 80) → table thead. */
  position: sticky;
  top: 0;
  z-index: 100;
  overflow: visible;
}
nav.top-nav::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 1px;
  pointer-events: none;
  background: linear-gradient(90deg,
    rgba(227, 196, 106, 0) 0%,
    rgba(227, 196, 106, 0.08) 20%,
    rgba(227, 196, 106, 0.12) 50%,
    rgba(227, 196, 106, 0.08) 80%,
    rgba(227, 196, 106, 0) 100%);
}
nav.top-nav::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  pointer-events: none;
  background: linear-gradient(90deg,
    rgba(227, 196, 106, 0) 0%,
    rgba(227, 196, 106, 0.10) 12%,
    rgba(227, 196, 106, 0.14) 50%,
    rgba(227, 196, 106, 0.10) 88%,
    rgba(227, 196, 106, 0) 100%);
  opacity: 0.88;
}
/* Table pages (Neutral Creeps / Unit Abilities / Mana Items, all wrap their
   content in .creeps-page) keep the glass LOOK (cool top rim + gradient body)
   but make it fully OPAQUE — same gradient with alpha forced to 1 — so a
   scrolling table never shows through. Unified across all three tables. */
body:has(.creeps-page) nav.top-nav {
  background:
    linear-gradient(180deg,
      rgba(255, 244, 209, 0.035) 0%,
      rgba(255, 244, 209, 0.0) 12%),
    linear-gradient(180deg, #191b1f 0%, #101216 100%);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.nav-inner {
  width: 100%;
  padding: 7px 24px 3px 10px;
  /* Three-column grid: brand left, tabs centred in the middle column, picker
     right. Symmetric 1fr/auto/1fr columns put the tab row at the geometric
     centre of the header WITHOUT overlapping the brand or the version picker
     (which the old `position: absolute` centring did when the picker grew). */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  position: relative;
}
.nav-inner::after {
  content: none;
}
.nav-tabs {
  display: flex;
  gap: 0;
  align-items: flex-end;
}
/* Header brand block (helmet logo + pixel-font title). Replaces the inline
   nav tabs — the full nav list now lives on the main hub page. */
.nav-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  color: #e6edf3;
  padding: 5px 10px 7px 4px;
  margin-bottom: -1px;
  justify-self: start;
  border-radius: 0;
  position: relative;
  transition: opacity 0.15s, background 0.15s, box-shadow 0.15s;
  clip-path: polygon(2px 0, calc(100% - 2px) 0, 100% 2px,
    100% calc(100% - 2px), calc(100% - 2px) 100%, 2px 100%,
    0 calc(100% - 2px), 0 2px);
}
.nav-brand:hover {
  opacity: 1;
  background:
    linear-gradient(180deg,
      rgba(227, 196, 106, 0.035) 0%,
      rgba(227, 196, 106, 0.015) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 244, 209, 0.04),
    inset 0 0 0 1px rgba(163, 135, 73, 0.14);
}
.nav-brand-logo {
  width: 54px;
  height: 54px;
  object-fit: contain;
  flex-shrink: 0;
  filter:
    drop-shadow(0 1px 1px rgba(0, 0, 0, 0.62))
    drop-shadow(0 0 3px rgba(227, 196, 106, 0.05));
}
.nav-brand-text {
  font-family: 'Jersey 10', 'Courier New', monospace;
  font-size: 22px;
  letter-spacing: 1px;
  line-height: 1;
  color: #ccd2d9;
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 0.68);
}
.nav-brand-sikle {
  color: #d6b764;
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 0.72),
    0 0 4px rgba(227, 196, 106, 0.05);
}

/* ---- MAIN PAGE: inventory book ----
   Landing page styled as a game inventory opened in a leather-bound book. An
   ornate parchment panel holds square slots; filled slots are the sections
   (gothic pixel-art icons), the rest are empty inventory cells. Pixel art is
   scaled up with image-rendering:pixelated so it stays crisp.
   Assets: icons/ui/gothic/ (Gothic Pixel UI FREE). */
.main-page {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding-top: 46px;
}
.inv-book {
  position: relative;
  z-index: 2;                          /* above the signature wall (z 1) */
  padding: 26px 38px 36px;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(120, 92, 48, 0.20), rgba(0, 0, 0, 0) 60%),
    linear-gradient(180deg, #3b2e1d 0%, #2a2014 100%);
  border: 3px solid #e3c46a;          /* "sikle" gold */
  border-radius: 8px;
  box-shadow:
    inset 0 0 0 2px #7a5c28,
    inset 0 0 40px rgba(0, 0, 0, 0.45),
    0 12px 44px rgba(0, 0, 0, 0.55);
}
.inv-head { text-align: center; }
.inv-title {
  margin: 0;
  font-family: "Jersey 25", monospace;
  font-size: 30px;
  line-height: 1;
  letter-spacing: 2px;
  color: #e3c46a;                     /* "sikle" gold */
  text-shadow: 2px 2px 0 #1a1206, 0 0 14px rgba(227, 196, 106, 0.25);
}

/* WALL OF SIGNATURES — faint pixel-font usernames scattered around the book,
   like graffiti on a wall. Positioned by scripts.js (no overlap with book /
   nav / each other). Fixed full-viewport layer behind the book. */
.inv-signatures {
  position: fixed;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;          /* layer transparent to clicks… */
  /* The whole wall fades in once scripts.js has positioned every name, so it
     never flashes in "from emptiness" mid-layout. JS flips opacity to 1. */
  opacity: 0;
  transition: opacity 0.5s ease;
}
.inv-sig {
  position: absolute;
  top: 0;
  left: 0;                       /* base origin; JS positions via transform: translate() */
  visibility: hidden;            /* hidden until a beam reveals it */
  opacity: 0;
  white-space: nowrap;
  /* Handjet first: a pixel/dot-matrix font WITH Cyrillic, so Russian member
     names render in-style (Jersey 10 has no Cyrillic → it falls through). */
  font-family: "Handjet", "Jersey 10", monospace;
  font-size: 18px;
  letter-spacing: 0.5px;
  color: rgba(243, 220, 150, 0.72);   /* glowing gold — only seen once revealed */
  pointer-events: auto;
  cursor: default;
  user-select: none;
  transition: opacity 0.7s ease, color 0.15s ease, text-shadow 0.15s ease;
}
.inv-sig.is-lit {                /* revealed by the laser — fades in and stays */
  visibility: visible;
  opacity: 1;
  text-shadow: 0 0 8px rgba(227, 196, 106, 0.45);
}
/* Colour emoji inside a signature (Telegram names sometimes carry a gold star,
   blue check, party popper, etc.) → flatten to the sig's own colour. Classic
   transparent-text + zero-offset text-shadow trick: the glyph silhouette is
   painted in the shadow colour, which we tie to currentColor so VIP/regular/
   lit/hidden variants all inherit the right tone. Wider 0-radius shadows
   sharpen the silhouette without bleeding into surrounding text. */
.inv-emo {
  color: transparent !important;
  text-shadow: 0 0 0 currentColor;
}
/* The .is-lit gold glow above sets a non-zero text-shadow on the whole sig —
   override on the emoji wrapper so its silhouette stays sharp and the glow
   doesn't double up around the emoji shape. */
.inv-sig.is-lit .inv-emo { text-shadow: 0 0 0 currentColor; }
/* Laser the Premium star fires at a name as it lights up (JS positions/animates
   each beam). Thin gold ray with a soft glow, drawn over everything. */
.sig-beam-layer {
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  overflow: hidden;
}
.sig-beam {
  position: absolute;
  height: 2px;
  transform-origin: 0 50%;
  background: linear-gradient(90deg,
    rgba(255, 240, 190, 0) 0%,
    rgba(255, 226, 150, 0.6) 45%,
    rgba(255, 243, 205, 0.8) 100%);
  box-shadow:
    0 0 6px 1px rgba(255, 225, 150, 0.6),
    0 0 13px 2px rgba(227, 196, 106, 0.35);
  will-change: transform, opacity;
}
@media (prefers-reduced-motion: reduce) { .sig-beam-layer { display: none; } }
/* Pixel gold dust a name bursts into when clicked — crisp 1–2px squares (like the
   stars), no blur. JS sets size/position and animates each speck. */
.sig-dust {
  position: absolute;
  width: 2px;
  height: 2px;
  background: #e3c46a;                 /* "sikle" gold */
  image-rendering: pixelated;
  will-change: transform, opacity;
}
/* VIP names — azure instead of gold, with a touch more weight + a stronger glow
   once lit, so they read as "special / legendary" against the gold wall. */
.inv-sig-vip {
  color: rgba(140, 224, 255, 0.85);   /* azure */
  font-weight: 600;
}
.inv-sig-vip.is-lit {
  text-shadow:
    0 0 8px rgba(120, 214, 255, 0.6),
    0 0 16px rgba(90, 190, 255, 0.35);
}
/* Pixel "forge spark" thrown off a VIP name as a beam lights it — same azure as
   the name, crisp little squares like the stars/dust. JS sizes/positions/animates. */
.sig-spark {
  position: absolute;
  width: 2px;
  height: 2px;
  background: #8ce0ff;                  /* azure (matches .inv-sig-vip) */
  box-shadow: 0 0 4px 1px rgba(120, 214, 255, 0.7);
  image-rendering: pixelated;
  will-change: transform, opacity;
}
/* Azure dust when a VIP name is clicked apart (overrides the gold .sig-dust). */
.sig-dust.sig-dust-vip {
  background: #8ce0ff;
  box-shadow: 0 0 3px 1px rgba(120, 214, 255, 0.5);
}
/* Collapsed sign for members without a public @username. */
.inv-sig-hidden {
  font-style: italic;
  letter-spacing: 1px;
  color: rgba(198, 206, 220, 0.6);    /* cool-grey, not gold (shown only once lit) */
}
.inv-sig-hidden.is-lit {
  text-shadow: 0 0 8px rgba(150, 165, 190, 0.4);
}
/* Star sky: a few dim pixel stars behind everything; each twinkles on its own
   slow cadence (JS sets per-star duration/delay). They never overlap names. */
.star-sky {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.star {
  position: absolute;
  background: rgba(232, 224, 200, 0.85);   /* dim warm-white */
  opacity: 0.28;                            /* very faint at rest */
  image-rendering: pixelated;
}
@keyframes starTwinkle {
  0%, 100% { opacity: var(--lo, 0.12); }
  50%      { opacity: var(--hi, 0.42); }
}
@media (prefers-reduced-motion: reduce) { .star { animation: none !important; } }
.star-sky.paused .star { animation-play-state: paused; }
.inv-divider {
  display: block;
  width: 76%;
  height: auto;
  margin: 14px auto 24px;
  image-rendering: pixelated;
  opacity: 0.9;
}
.inv-grid {
  display: grid;
  grid-template-columns: repeat(5, auto);   /* max 5 tiles per row */
  gap: 9px 12px;
  justify-content: center;
}
.inv-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  text-decoration: none;
}
.inv-slot {
  width: 76px;
  height: 76px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-image: url('icons/ui/gothic/slot.png');
  background-size: 100% 100%;
  background-repeat: no-repeat;
  image-rendering: pixelated;
}
.inv-slot.is-empty { background-image: url('icons/ui/gothic/slot_empty.png'); opacity: 0.65; }
.inv-icon {
  width: 50px;
  height: 50px;
  image-rendering: pixelated;
  /* Warm the pack's gold toward the brighter "sikle" gold. */
  filter: brightness(1.12) saturate(1.06);
}
/* Creeps beetle: on hover (the tile lifts/slides out) swap the static PNG for
   an animated GIF so its legs crawl in place. `content: url()` replaces the
   <img>'s rendered image in Chromium/WebKit and reverts to the PNG on mouse-out;
   the GIF restarts each time it's applied. */
.inv-cell-creeps:hover .inv-icon {
  content: url('icons/ui/gothic/icon_creeps.gif');
}
/* Changelogs book: on hover, a page lifts off the right, arcs over the spine and
   settles on the left (replicates icons/ref/book_anim.gif in our gold palette). */
.inv-cell-patch:hover .inv-icon {
  content: url('icons/ui/gothic/icon_patch.gif');
}
/* Dynamics: on hover the bar-chart bars grow/shrink in random order (GIF). */
.inv-cell-dynamics:hover .inv-icon {
  content: url('icons/ui/gothic/icon_dynamics.gif');
}
/* Items: closed chest at rest; on hover a chest-open APNG plays once — a key
   flies in, inserts and turns, the lid pops open (chest squashes wider) and a
   gold light beam + treasure spill out. Driven from scripts.js (JS src-swap
   with a ?v= cache-bust) — NOT CSS content:url — so the APNG replays from the
   first frame on EVERY hover instead of holding its last frame. Reverts to the
   closed PNG on mouse-out. APNG (not GIF) for the translucent beam glow. */
/* Calendar: on hover the date page burns away (gold pixel fire) and a new date
   appears, looping 1→31. Driven from scripts.js (JS src-swap with a cache-bust)
   instead of `content: url()` — the calendar GIF filename predates the others, so
   browsers had a stale copy cached; the cache-bust forces the new burning GIF. */
/* Special star: no spin — on hover it gently PULSES (grows/shrinks) and the
   tile throws off a lot more magic dust. */
.inv-cell-star:hover .inv-icon {
  animation: starPulse 0.85s ease-in-out infinite;
}
@keyframes starPulse {
  0%, 100% { transform: translateY(-3px) scale(1.00); }
  50%      { transform: translateY(-3px) scale(1.17); }
}
/* The "special" tile's slot always emits a soft pixel-gold glow (not tied to
   hover) so it reads as set-apart; it slowly pulses to feel alive. */
.inv-special .inv-slot {
  box-shadow:
    0 0 17px 4px rgba(227, 196, 106, 0.55),
    inset 0 0 11px rgba(227, 196, 106, 0.34);
  animation: starGlow 2.6s ease-in-out infinite;
}
@keyframes starGlow {
  0%, 100% {
    box-shadow:
      0 0 14px 3px rgba(227, 196, 106, 0.47),
      inset 0 0 10px rgba(227, 196, 106, 0.30);
  }
  50% {
    box-shadow:
      0 0 24px 7px rgba(227, 196, 106, 0.72),
      inset 0 0 14px rgba(227, 196, 106, 0.44);
  }
}
.inv-special .inv-slot { position: relative; overflow: visible; }
/* Magic dust: small pale (low-saturation, "magical" not colourful) pixel sparks.
   Two groups so mouse-out fades cleanly:
     .dust-base  — 6 always drift, slow & sparse (independent of hover, so the
                   resting animation never jumps or breaks);
     .dust-burst — 8 extras that ignite on hover. The whole burst LAYER fades its
                   opacity out on mouse-out, so residual particles drift-fade away
                   gradually instead of snapping off. */
.inv-sparks, .dust-base, .dust-burst {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.inv-sparks { z-index: 1; }
.dust-burst { opacity: 0; transition: opacity 0.55s ease; }
.inv-cell-star:hover .dust-burst { opacity: 1; }
.spark {
  position: absolute;
  width: 2px;
  height: 2px;
  background: #e8e0cc;                 /* pale warm white — soft, not bright/colourful */
  box-shadow: 0 0 3px 1px rgba(232, 224, 200, 0.6);
  opacity: 0;
  animation-name: sparkDrift;
  animation-timing-function: ease-out;
  animation-iteration-count: infinite;
}
@keyframes sparkDrift {
  0%   { opacity: 0; transform: translate(0, 0) scale(1); }
  5%   { opacity: 0; }
  12%  { opacity: 0.9; }
  42%  { opacity: 0; transform: translate(var(--tx), var(--ty)) scale(0.4); }
  100% { opacity: 0; transform: translate(var(--tx), var(--ty)) scale(0.4); }
}
/* Ambient base — always drifting, slow & sparse. */
.dust-base .spark:nth-child(1) { top: 14px; left: 22px; --tx: -10px; --ty: -12px; animation-delay: 0s;   animation-duration: 8.5s; }
.dust-base .spark:nth-child(2) { top: 9px;  left: 48px; --tx: 8px;   --ty: -14px; animation-delay: 1.1s; animation-duration: 9.2s; }
.dust-base .spark:nth-child(3) { top: 30px; left: 60px; --tx: 14px;  --ty: -6px;  animation-delay: 2.2s; animation-duration: 8.0s; }
.dust-base .spark:nth-child(4) { top: 52px; left: 50px; --tx: 9px;   --ty: 11px;  animation-delay: 0.6s; animation-duration: 9.6s; }
.dust-base .spark:nth-child(5) { top: 50px; left: 16px; --tx: -11px; --ty: 9px;   animation-delay: 1.7s; animation-duration: 8.3s; }
.dust-base .spark:nth-child(6) { top: 26px; left: 8px;  --tx: -13px; --ty: -4px;  animation-delay: 2.6s; animation-duration: 9.0s; }
/* Burst extras — paused at rest, run fast on hover. The group opacity fade above
   handles the graceful disappear, so a frozen particle is never seen mid-air. */
.dust-burst .spark { animation-duration: 1.35s; animation-play-state: paused; }
.inv-cell-star:hover .dust-burst .spark { animation-play-state: running; }
.dust-burst .spark:nth-child(1) { top: 38px; left: 6px;  --tx: -15px; --ty: -2px;  animation-delay: 0s;   }
.dust-burst .spark:nth-child(2) { top: 62px; left: 30px; --tx: -4px;  --ty: 15px;  animation-delay: 0.2s; }
.dust-burst .spark:nth-child(3) { top: 58px; left: 58px; --tx: 13px;  --ty: 12px;  animation-delay: 0.4s; }
.dust-burst .spark:nth-child(4) { top: 6px;  left: 32px; --tx: 1px;   --ty: -16px; animation-delay: 0.5s; }
.dust-burst .spark:nth-child(5) { top: 20px; left: 38px; --tx: 4px;   --ty: -13px; animation-delay: 0.7s; }
.dust-burst .spark:nth-child(6) { top: 40px; left: 62px; --tx: 16px;  --ty: 3px;   animation-delay: 0.9s; }
.dust-burst .spark:nth-child(7) { top: 34px; left: 34px; --tx: -7px;  --ty: -9px;  animation-delay: 1.1s; }
.dust-burst .spark:nth-child(8) { top: 46px; left: 26px; --tx: -9px;  --ty: 12px;  animation-delay: 1.3s; }
@media (prefers-reduced-motion: reduce) {
  .inv-special .inv-slot { animation: none; }
  .inv-cell-star:hover .inv-icon { animation: none; }
  .spark { display: none; }
  .inv-cell-terrain:hover .inv-icon { animation: none; }
  .dirt { display: none; }
  /* (Chest APNG swap is JS-driven and already guarded by the same media query
     in scripts.js, so no rule is needed here.) */
}
.inv-cap {
  max-width: 86px;
  font-family: "Jersey 10", monospace;
  font-size: 15px;
  line-height: 1.05;
  letter-spacing: 0.5px;
  text-align: center;
  color: #e3c46a;                     /* "sikle" gold */
}
/* Placeholder slots — filled for the inventory look but not links. */
.inv-ph { cursor: default; }
.inv-ph .inv-icon { filter: brightness(0.95) saturate(0.9); opacity: 0.7; }
.inv-ph .inv-cap { color: #9c8550; }
/* Filled cells are clickable; lift + brighten on hover. */
.inv-filled .inv-slot,
.inv-filled .inv-icon,
.inv-filled .inv-cap {
  transition: transform 0.13s ease, filter 0.13s ease, color 0.13s ease;
}
.inv-filled:hover .inv-slot { transform: translateY(-3px); filter: brightness(1.22); }
.inv-filled:hover .inv-icon { transform: translateY(-3px) scale(1.06); }
.inv-filled:hover .inv-cap { color: #fff5dc; }

/* TERRAIN tile: the earth block levitates + bobs on hover and sheds occasional
   pixel "dirt" from its underside (rare, uneven). */
.inv-cell-terrain .inv-slot { overflow: visible; }
.inv-cell-terrain:hover .inv-icon { animation: terrainBob 2.1s ease-in-out infinite; }
@keyframes terrainBob {
  0%, 100% { transform: translateY(-5px) scale(1.06); }
  50%      { transform: translateY(-9px) scale(1.06); }
}
.terrain-dirt {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}
/* The dirt LAYER bobs in lock-step with the block (same animation) so particles
   shed from the block's CURRENT (floating) underside — not mid-air below it. */
.inv-cell-terrain:hover .terrain-dirt { animation: terrainBob 2.1s ease-in-out infinite; }
.dirt {
  position: absolute;
  width: 2px;                               /* half the old 3x4 footprint */
  height: 2px;
  background: #d8a850;                       /* warm soil — bright enough to read */
  box-shadow: 0 0 0 0.5px rgba(34, 20, 9, 0.9);
  image-rendering: pixelated;
  opacity: 0;
}
/* particles fall while hovering; staggered cycles → sparse but clearly visible.
   They pop in right at the block's bottom edge, then fall + fade. */
.inv-cell-terrain:hover .dirt {
  animation-name: dirtFall;
  animation-timing-function: ease-in;
  animation-iteration-count: infinite;
}
@keyframes dirtFall {
  0%   { transform: translateY(-1px); opacity: 0; }
  14%  { opacity: 1; }
  90%  { opacity: 0.95; }
  100% { transform: translateY(28px); opacity: 0; }   /* falls almost to the tile's bottom edge */
}
/* Five particles (was ten) spread sparsely along the flat tile's underside
   (slot coords ~x 20–56) with long, staggered cycles → the soil sheds
   occasionally and is never clustered. */
.dirt:nth-child(1) { left: 22px; top: 48px; animation-duration: 4.2s; animation-delay: 0s;   }
.dirt:nth-child(2) { left: 31px; top: 52px; animation-duration: 4.8s; animation-delay: 2.3s; }
.dirt:nth-child(3) { left: 39px; top: 47px; animation-duration: 4.5s; animation-delay: 1.2s; }
.dirt:nth-child(4) { left: 47px; top: 53px; animation-duration: 5.1s; animation-delay: 3.4s; }
.dirt:nth-child(5) { left: 54px; top: 49px; animation-duration: 4.6s; animation-delay: 1.9s; }
.inv-filled:focus-visible .inv-slot {
  outline: 2px solid #eccb7e;
  outline-offset: 3px;
}

/* ---- SUPPORT sub-panel ----
   Clicking the Support tile opens this in place of the grid (the book heading +
   divider stay). The grid hides; two tiles the SAME size as the grid tiles
   appear, centred: Telegram (envelope) and Donation (coin jar). A gothic
   left-arrow ornament next to the divider acts as "back". */
/* Grid + panel share one stage. The grid stays in flow (it sets the height) and
   only turns INVISIBLE when Support opens — it keeps its space, so the book
   never resizes and the divider above never shifts. The panel is centred over
   that exact area. */
.inv-stage { position: relative; }
/* A sub-panel (Support / Dynamics) overlays the grid, centred, hidden until its
   tile opens it. Shared base; each shows via its own .<name>-open book class. */
.inv-panel {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
}
.inv-book.support-open .inv-grid,
.inv-book.creeps-open .inv-grid,
.inv-book.heroes-open .inv-grid,
.inv-book.items-open .inv-grid { visibility: hidden; }
.inv-book.support-open .support-panel,
.inv-book.creeps-open .creeps-panel,
.inv-book.heroes-open .heroes-panel,
.inv-book.items-open .items-panel {
  opacity: 1;
  pointer-events: auto;
  animation: supportIn 0.28s ease both;
}
@keyframes supportIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* Same gap as the inventory grid (12px), centred. */
.support-options {
  display: flex;
  justify-content: center;
  gap: 9px 12px;
  flex-wrap: wrap;
}
/* Identical to a grid tile (.inv-cell): same slot/icon/caption sizes + the same
   lift-on-hover. So .support-btn reuses the base .inv-slot / .inv-icon / .inv-cap
   sizing — we only add positioning context for the hover particles. */
.support-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  cursor: pointer;
}
.support-btn .inv-slot { position: relative; overflow: visible; }
.support-btn .inv-cap {
  font-family: 'Jersey 10', monospace;
  font-size: 15px;
  letter-spacing: 0.5px;
  color: #e3c46a;
  transition: color 0.15s ease;
}
.support-btn:hover .inv-slot { transform: translateY(-3px); filter: brightness(1.22); }
.support-btn:hover .inv-icon { transform: translateY(-3px) scale(1.06); }
.support-btn:hover .inv-cap { color: #fff5dc; }
.support-btn:focus-visible { outline: none; }
.support-btn:focus-visible .inv-slot { outline: 2px solid #eccb7e; outline-offset: 3px; }
/* Donation isn't wired up yet — keep it full-colour + alive (the jar animates),
   just non-clickable with a "soon" ribbon. */
.support-soon { cursor: default; }
.support-soon-tag {
  position: absolute;
  right: 2px;
  bottom: 2px;
  padding: 0 5px;
  font-family: 'Jersey 10', monospace;
  font-size: 11px;
  letter-spacing: 0.5px;
  color: #14100a;
  background: #e3c46a;
  border-radius: 3px;
  text-transform: lowercase;
}

/* ---- TELEGRAM envelope: heart-beat pulse + faint hollow hearts that drift out
   and linger, then fade when the cursor leaves (like the star's magic dust). */
.support-telegram:hover .inv-icon { animation: tgBeat 0.85s ease-in-out infinite; }
@keyframes tgBeat {
  0%, 100% { transform: translateY(-3px) scale(1.06); }
  15%      { transform: translateY(-3px) scale(1.2); }
  30%      { transform: translateY(-3px) scale(1.06); }
  45%      { transform: translateY(-3px) scale(1.14); }
  60%      { transform: translateY(-3px) scale(1.06); }
}
.tg-hearts {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  /* The whole layer lingers, then dissolves, after the cursor leaves. The hearts
     keep growing + drifting underneath (their own animation never stops), so on
     mouse-out they float on, swelling and fading, beyond the button edges. */
  transition: opacity 0.9s ease;
}
.support-telegram:hover .tg-hearts { opacity: 1; }
.tg-heart {
  position: absolute;
  width: 12px;            /* thin-walled 9x8 source; starts tiny, swells as it flies */
  height: 11px;
  background: url('icons/ui/gothic/heart_hollow.png') no-repeat center / contain;
  image-rendering: pixelated;
  opacity: 0;
  animation: heartFloat 3.4s ease-out infinite;   /* slow, long swell */
}
@keyframes heartFloat {
  0%   { transform: translate(0, 0) scale(0.28); opacity: 0; }
  20%  { opacity: 0.9; }
  72%  { opacity: 0.7; }
  100% { transform: translate(var(--tx), var(--ty)) scale(1.9); opacity: 0; }
}
/* Emit from around the envelope and travel WELL past the 76px slot edges. */
.tg-heart:nth-child(1) { top: 30px; left: 33px; --tx: -42px; --ty: -46px; animation-duration: 3.2s; animation-delay: 0s;   }
.tg-heart:nth-child(2) { top: 28px; left: 40px; --tx: 40px;  --ty: -50px; animation-duration: 3.8s; animation-delay: 0.4s; }
.tg-heart:nth-child(3) { top: 35px; left: 29px; --tx: -54px; --ty: -16px; animation-duration: 3.5s; animation-delay: 0.9s; }
.tg-heart:nth-child(4) { top: 35px; left: 45px; --tx: 52px;  --ty: -22px; animation-duration: 3.1s; animation-delay: 1.3s; }
.tg-heart:nth-child(5) { top: 32px; left: 37px; --tx: -12px; --ty: -58px; animation-duration: 4.0s; animation-delay: 0.6s; }
.tg-heart:nth-child(6) { top: 36px; left: 39px; --tx: 18px;  --ty: -54px; animation-duration: 3.6s; animation-delay: 1.7s; }
.tg-heart:nth-child(7) { top: 31px; left: 31px; --tx: -34px; --ty: -34px; animation-duration: 4.1s; animation-delay: 1.0s; }
.tg-heart:nth-child(8) { top: 31px; left: 43px; --tx: 34px;  --ty: -34px; animation-duration: 3.4s; animation-delay: 2.1s; }

/* ---- DONATION jar: a coin keeps dropping in on hover (looped). The jar itself
   stays put so the coin lands on the pile consistently. */
.support-donation:hover .inv-icon { transform: none; }   /* jar holds still */
.don-coin {
  position: absolute;
  left: 50%;
  top: 4px;                 /* starts above the open mouth → falls in, lands on pile */
  width: 11px;
  height: 11px;
  margin-left: -5px;
  background: url('icons/ui/gothic/coin.png') no-repeat center / contain;
  image-rendering: pixelated;
  opacity: 0;
  pointer-events: none;
}
.support-donation:hover .don-coin { animation: coinDrop 1.5s cubic-bezier(0.5, 0, 0.85, 1) infinite; }
@keyframes coinDrop {
  0%   { transform: translateY(0);    opacity: 0; }
  12%  { opacity: 1; }
  55%  { transform: translateY(37px); opacity: 1; }   /* lands on the pile */
  63%  { transform: translateY(33px); }                /* tiny bounce */
  70%  { transform: translateY(37px); opacity: 1; }
  88%  { transform: translateY(37px); opacity: 0.9; }
  100% { transform: translateY(37px); opacity: 0; }    /* settles in → loop */
}

/* ---- BACK control: a gothic left-arrow ornament beside the title divider,
   only while the Support panel is open. */
.inv-divider-row {
  position: relative;
  text-align: center;          /* divider centres on its own — unaffected by the
                                  arrow, so the arrow can never shift it */
  margin: 14px 0 24px;
  line-height: 0;
}
.inv-divider-row .inv-divider {
  display: inline-block;
  margin: 0;
  vertical-align: middle;
}
.support-back {
  position: absolute;          /* out of flow → the divider stays put */
  top: 50%;
  right: 89%;                  /* right edge just left of the divider's left end */
  transform: translateY(-50%);
  display: none;
  padding: 5px 6px;
  background: none;
  border: none;
  cursor: pointer;
  line-height: 0;
}
.inv-book.support-open .support-back,
.inv-book.creeps-open .support-back,
.inv-book.heroes-open .support-back,
.inv-book.items-open .support-back { display: inline-flex; }
.support-back-orn {
  height: 20px;
  image-rendering: pixelated;
  opacity: 0.85;
  transition: opacity 0.15s ease, transform 0.15s ease, filter 0.15s ease;
}
.support-back:hover .support-back-orn {
  opacity: 1;
  filter: brightness(1.18);
  transform: translateX(-3px);
}
.support-back:focus-visible { outline: none; }
.support-back:focus-visible .support-back-orn {
  opacity: 1;
  filter: drop-shadow(0 0 2px #e3c46a);
}
@media (prefers-reduced-motion: reduce) {
  .inv-book.support-open .support-panel,
  .inv-book.creeps-open .creeps-panel,
  .inv-book.heroes-open .heroes-panel,
  .inv-book.items-open .items-panel { animation: none; }
  .support-telegram:hover .inv-icon { animation: none; }
  .tg-heart { display: none; }
  .support-donation:hover .don-coin { animation: none; }
}

.nav-tab {
  padding: 7px 14px 10px;
  color: #c9d1d9;
  border-radius: 6px 6px 0 0;
  cursor: pointer;
  text-decoration: none;
  font-weight: 500;
  font-size: 14px;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  font-family: inherit;
  transition: background 0.15s, border-color 0.15s;
  display: inline-flex;
  align-items: center;
}
.nav-tab:hover {
  background: rgba(48, 54, 61, 0.4);
}
.nav-tab.active {
  background: rgba(0, 0, 0, 0.22);
  /* keep weight + transparent border identical to inactive — prevents header jump */
}
.nav-context {
  display: flex;
  align-items: center;
  gap: 10px;                   /* gap between release-info and the version-picker control */
  margin-bottom: 8px;
}
/* Wraps the previous-arrow + version-dropdown + next-arrow into a single
   horizontal control so all three sit on one baseline and the arrows
   are clearly attached to the version button. */
.version-picker {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
/* Nav button: soft rounded-rect "tablet" with a thin SVG chevron drawn
   precisely in the centre. No font glyph (font baselines vary across
   systems) — the chevron is a CSS background-image data URI rendered
   crisp at any size. Direction modifier class swaps the SVG. */
.version-nav-arrow {
  display: inline-block;
  width: 26px;
  height: 38px;
  background: rgba(0, 0, 0, 0.28);
  background-repeat: no-repeat;
  background-position: center;
  background-size: 14px 18px;
  image-rendering: pixelated;
  border-radius: 0;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.5);
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  transition: background 0.15s, filter 0.15s, transform 0.12s;
  clip-path: polygon(2px 0, calc(100% - 2px) 0, 100% 2px,
    100% calc(100% - 2px), calc(100% - 2px) 100%, 2px 100%,
    0 calc(100% - 2px), 0 2px);
}
/* Pixel-art chunky arrows — drawn as a staircase of 2×2 cells (shape-rendering
   keeps the edges crisp at any scale). Yellow accent matches the "sikle" tag
   in the header brand. */
.version-nav-arrow.is-prev {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 18" shape-rendering="crispEdges"><g fill="%23e3c46a"><rect x="10" y="0" width="2" height="2"/><rect x="8" y="2" width="4" height="2"/><rect x="6" y="4" width="6" height="2"/><rect x="4" y="6" width="8" height="2"/><rect x="2" y="8" width="10" height="2"/><rect x="4" y="10" width="8" height="2"/><rect x="6" y="12" width="6" height="2"/><rect x="8" y="14" width="4" height="2"/><rect x="10" y="16" width="2" height="2"/></g></svg>');
}
.version-nav-arrow.is-next {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 18" shape-rendering="crispEdges"><g fill="%23e3c46a"><rect x="2" y="0" width="2" height="2"/><rect x="2" y="2" width="4" height="2"/><rect x="2" y="4" width="6" height="2"/><rect x="2" y="6" width="8" height="2"/><rect x="2" y="8" width="10" height="2"/><rect x="2" y="10" width="8" height="2"/><rect x="2" y="12" width="6" height="2"/><rect x="2" y="14" width="4" height="2"/><rect x="2" y="16" width="2" height="2"/></g></svg>');
}
.version-nav-arrow:hover {
  background-color: rgba(0, 0, 0, 0.45);
  filter: brightness(1.2);
}
/* Tactile nudge: the chevron drifts a hair in the arrow's own direction
   on hover, suggesting where the click will take you. */
.version-nav-arrow.is-prev:hover { background-position: 45% center; }
.version-nav-arrow.is-next:hover { background-position: 55% center; }
.version-nav-arrow.is-disabled {
  opacity: 0.3;
  cursor: default;
  pointer-events: none;
}

/* RELEASE INFO + VERSION — одна высота */
.nav-context .release-info {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: 1px;
  background: rgba(0, 0, 0, 0.28);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.5);
  border: 1px solid transparent;
  padding: 0 12px;
  height: 38px;
  border-radius: 0;
  clip-path: polygon(2px 0, calc(100% - 2px) 0, 100% 2px,
    100% calc(100% - 2px), calc(100% - 2px) 100%, 2px 100%,
    0 calc(100% - 2px), 0 2px);
}
.nav-context .release-date {
  color: #c9d1d9;
  font-family: 'Jersey 25', 'Courier New', monospace;
  font-size: 17px;
  font-weight: 400;
  letter-spacing: 0.3px;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.nav-context .release-version {
  color: #79c0ff;
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.5px;
  line-height: 1.1;
  margin-bottom: 1px;
}
/* Calendar variant — no dropdown; .release-info takes its place on the right.
   Override the fixed 38px height (designed for version+date only) so the
   3rd line (.patch-age) doesn't overflow above and below the box. */
/* Universal rule: every non-patch tab (calendar, creeps, anything added
   later) reserves the same vertical space as the patch-page version
   picker via .nav-context-flat. New tabs only need to use this class —
   no per-tab CSS — so the header height stays identical across pages
   and switching tabs doesn't make the toolbar jump. */
.nav-context-flat {
  /* Reserve picker height on non-patch pages so all tabs sit on one baseline,
     even when the right side has no content. Grid layout handles horizontal
     placement — no `margin-left: auto` needed. */
  justify-self: end;
  min-height: 38px;
}
.nav-context-picker { justify-self: end; }
.nav-context-calendar .release-info {
  align-items: center;
  padding: 6px 18px;
  min-width: 110px;
  height: auto;
}
.nav-context .patch-age {
  color: #a8b3bd;
  font-family: 'Jersey 25', 'Courier New', monospace;
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0.3px;
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}
.nav-context .version {
  color: #c9d1d9;
  font-family: 'Jersey 10', 'Courier New', monospace;
  font-size: 24px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.8px;
  background: rgba(0, 0, 0, 0.28);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.5);
  border: 1px solid transparent;
  padding: 0 14px;
  height: 38px;
  border-radius: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  line-height: 1;
  transition: background 0.15s;
  clip-path: polygon(2px 0, calc(100% - 2px) 0, 100% 2px,
    100% calc(100% - 2px), calc(100% - 2px) 100%, 2px 100%,
    0 calc(100% - 2px), 0 2px);
}
.nav-context .version:hover {
  background: rgba(0, 0, 0, 0.4);
}
/* Non-clickable version display on non-patch pages. Same look as the dropdown
   button on patch pages, but no chevron, no hover, no cursor. */
.nav-context .version.version-static {
  cursor: default;
}
.nav-context .version.version-static:hover {
  background: rgba(0, 0, 0, 0.28);
}
.nav-context .version.version-materials {
  color: #d8e2ec;
  font-size: 21px;
  padding: 0 16px;
}
/* Materials sub-nav context: the nav-inner grid is align-items:center, but
   .nav-context carries margin-bottom:8px to nudge the patch-page picker up
   against the tab row. On Materials (and other flat pages) that margin
   shifts the static label ABOVE the main-nav buttons' baseline. Cancel it
   so the version-static box sits at the same vertical centre as .nav-tab. */
.nav-context.nav-context-materials { margin-bottom: 0; }
.nav-context .version-chev {
  font-size: 14px;
  color: #a8b3bd;
  line-height: 1;
  margin-top: 2px;
  font-weight: 600;
}

/* VERSION DROPDOWN MENU */
.version-dropdown {
  position: relative;
  display: inline-block;
}
.version-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 180px;
  background: #0d1117;
  border: 1px solid #30363d;
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  padding: 6px;
  display: none;
  z-index: 100;
  max-height: 320px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: #30363d transparent;
}
.version-menu.open {
  display: block;
}
.version-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  border-radius: 4px;
  text-decoration: none;
  color: #c9d1d9;
  font-size: 14px;
  transition: background 0.12s ease;
}
.version-item:hover {
  background: #21262d;
}
.version-item.current {
  background: rgba(88, 166, 255, 0.10);
  color: #58a6ff;
  cursor: default;
  pointer-events: none;
}
.version-item .vi-name {
  font-weight: 700;
  letter-spacing: 0.3px;
}
.version-item .vi-date {
  color: #6e7681;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  margin-left: 14px;
}
.version-item.current .vi-date {
  color: rgba(88, 166, 255, 0.6);
}

/* NAV BACK ARROW + BACK-TO-TOP — pixel-art chevrons on an index-style gold /
   leather circle (matches the inventory landing page). Arrows are gold (#e3c46a)
   PNGs at icons/ui/gothic/arrow_left|up.png. Both float in the BOTTOM corners so
   neither overlaps the toolbar tags (the back arrow used to sit at top-left, over
   the tag block). url() is relative to styles.css (repo root) → works on every
   page regardless of depth. */
.nav-back-arrow,
.back-to-top {
  position: fixed;
  bottom: 22px;
  width: 42px;
  height: 42px;
  box-sizing: border-box;
  padding: 0;
  appearance: none;
  -webkit-appearance: none;
  border-radius: 50%;
  border: 2px solid #e3c46a;
  box-shadow:
    inset 0 1px 0 rgba(190, 225, 240, 0.10),
    0 5px 16px rgba(0, 0, 0, 0.55);
  background-repeat: no-repeat;
  background-position: center;
  image-rendering: pixelated;
  display: none;
  cursor: pointer;
  text-decoration: none;
  z-index: 100;
  transition: transform 0.15s ease, filter 0.15s ease, box-shadow 0.15s ease;
}
.nav-back-arrow {
  left: 22px;
  /* Solid pixel triangle (same chunky style as the patch-picker prev arrow). */
  background-image:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 18" shape-rendering="crispEdges"><g fill="%23e3c46a"><rect x="10" y="0" width="2" height="2"/><rect x="8" y="2" width="4" height="2"/><rect x="6" y="4" width="6" height="2"/><rect x="4" y="6" width="8" height="2"/><rect x="2" y="8" width="10" height="2"/><rect x="4" y="10" width="8" height="2"/><rect x="6" y="12" width="6" height="2"/><rect x="8" y="14" width="4" height="2"/><rect x="10" y="16" width="2" height="2"/></g></svg>'),
    linear-gradient(180deg, #3b2e1d 0%, #2a2014 100%);
  background-size: auto 18px, cover;
}
.back-to-top {
  right: 22px;
  /* Solid pixel triangle pointing up. */
  background-image:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 10" shape-rendering="crispEdges"><g fill="%23e3c46a"><rect x="8" y="0" width="2" height="2"/><rect x="6" y="2" width="6" height="2"/><rect x="4" y="4" width="10" height="2"/><rect x="2" y="6" width="14" height="2"/><rect x="0" y="8" width="18" height="2"/></g></svg>'),
    linear-gradient(180deg, #3b2e1d 0%, #2a2014 100%);
  background-size: 18px auto, cover;
}
.nav-back-arrow.visible,
.back-to-top.visible { display: block; }
.nav-back-arrow:hover,
.back-to-top:hover {
  transform: translateY(-3px);
  filter: brightness(1.18);
  box-shadow:
    inset 0 1px 0 rgba(190, 225, 240, 0.14),
    0 8px 20px rgba(0, 0, 0, 0.6);
}

/* CALENDAR PAGE */
.calendar { margin: 4px 0 24px; }
.cal-toggle-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  /* Fixed height (not min-height) so the toolbar occupies the EXACT same
     vertical slot on every page. All child controls are 32px tall + border-
     box (rules below) so the bar measures identically whether a page shows
     a select, toggles, or the price range. */
  height: 32px;
  margin-bottom: 8px;
  padding-left: 0;
  font-size: 13px;
  color: #8b949e;
}
/* One height for every toolbar control so the bar never measures differently
   between Neutral Creeps / Unit Abilities / Mana Items. */
.cal-toggle-bar .cal-mode-select,
.cal-toggle-bar .ua-upgrades-toggle,
.cal-toggle-bar .mr-price-range {
  height: 32px;
  box-sizing: border-box;
}
/* Blurb + toolbar now live INSIDE the .creeps-scroll box (above the table) so
   they scroll away with it, exactly like the Mana Items page — only the site
   nav + sticky table headers stay pinned. They're sticky-left so they don't
   slide off when the table scrolls horizontally, with an opaque background so
   the table never shows through them. */
.creeps-scroll > .inbox-bar {
  position: sticky;
  left: 0;
  z-index: 6;
  background: #0a0e13;
  /* As block children of the scroll box, these are already the box's VISIBLE
     width (percentages/auto resolve against the padding box, not the wide
     table's scroll width), so sticky-left keeps them filling the viewport. */
  box-sizing: border-box;
}
.creeps-scroll > .mr-blurb.inbox-bar {
  margin: 0;
  padding: 22px 28px 0;
}
.creeps-scroll > .cal-toggle-bar.inbox-bar {
  height: auto;
  margin: 0;
  padding: 14px 28px 16px;
}
/* The View group (label + select) is wrapped in its own inline-flex so its
   width is independent of whatever follows in the toolbar. Same width on
   every page → the "View" label sits at the same X regardless of context. */
.cal-toggle-bar .view-group {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.cal-toggle-bar strong {
  color: #c9d1d9;
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
}
.cal-mode-select {
  background: #161b22;
  border: 1px solid #30363d;
  color: #c9d1d9;
  padding: 5px 28px 5px 12px;
  /* Fixed width so the View select on every page is the same size regardless
     of which option label is longest (Expanded vs. Auras vs. Standard). */
  min-width: 130px;
  border-radius: 5px;
  font-family: inherit;
  font-size: 12.5px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='%238b949e' d='M0 0l5 6 5-6z'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
}
.cal-mode-select:hover {
  border-color: #58a6ff;
}
/* "Upgrades" binary switch — iOS-style toggle, defaults ON. When ON, the
   table gains .show-upgrades and each <span class="upg-val"> inside a
   leveled cell renders with a subtle dotted underline so the per-tier
   numbers self-identify without an outline around the cell. */
.ua-upgrades-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  background: #161b22;
  border: 1px solid #30363d;
  border-radius: 5px;
  font-size: 12.5px;
  color: #c9d1d9;
  cursor: pointer;
  user-select: none;
}
.ua-upgrades-toggle:hover { border-color: #58a6ff; }
.ua-upgrades-toggle .ua-switch-input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0;
  height: 0;
}
.ua-upgrades-toggle .ua-switch {
  position: relative;
  display: inline-block;
  width: 30px;
  height: 16px;
  background: #30363d;
  border-radius: 999px;
  transition: background 150ms ease;
  flex-shrink: 0;
}
.ua-upgrades-toggle .ua-switch::after {
  content: '';
  position: absolute;
  top: 2px; left: 2px;
  width: 12px;
  height: 12px;
  background: #c9d1d9;
  border-radius: 50%;
  transition: transform 150ms ease, background 150ms ease;
}
/* ON state = the site-wide gold accent (#e3c46a — the "sikle" wordmark gold, same
   as the active terrain-toolbar buttons). Unifies every toggle's on-colour. */
.ua-upgrades-toggle .ua-switch-input:checked + .ua-switch { background: #e3c46a; }
.ua-upgrades-toggle .ua-switch-input:checked + .ua-switch::after {
  transform: translateX(14px);
  background: #fff;
}
.ua-upgrades-toggle .ua-switch-input:focus-visible + .ua-switch {
  outline: 2px solid #e3c46a;
  outline-offset: 2px;
}
/* Inline link inside an Effect cell — used when an effect mentions another
   ability or unit by name (Hurl Boulder, Rally, Skeleton Warrior, …) and
   should jump to its row. Subtle cyan tone + dotted underline so it reads
   as interactive without screaming. */
.unit-abilities-table .ua-inline-link {
  color: #79d6cf;
  text-decoration: underline dotted rgba(121, 214, 207, 0.55);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  cursor: pointer;
}
.unit-abilities-table .ua-inline-link:hover {
  color: #a6f2ec;
  text-decoration-color: #a6f2ec;
}
/* Marker for leveled cells when Upgrades is ON:
   - faint sky-blue fill (almost translucent) so the cell stands out from the
     row without looking heavy;
   - soft rounded outline (1.5px) inset so the corners pull in slightly from
     the cell edge — reads as a "highlighted" tile rather than a hard frame.
   The native `border-left` from the column divider stays underneath; the
   inset shadow sits on top and visually "owns" the cell. */
.unit-abilities-table.show-upgrades td.leveled {
  background: rgba(120, 200, 235, 0.08);
  position: relative;
}
/* Frame drawn as an inset pseudo-element (3px in from every edge) instead of
   an edge-flush box-shadow, so the row-select gold frame at the cell border
   has clearance and doesn't sit half-hidden behind this blue outline. */
.unit-abilities-table.show-upgrades td.leveled::after {
  content: '';
  position: absolute;
  inset: 3px;
  border: 1.5px solid rgba(120, 200, 235, 0.55);
  border-radius: 6px;
  pointer-events: none;
}

/* YEAR BLOCK (shared between modes, collapsible) */
.cal-year-block {
  margin-bottom: 6px;
  background: #14191f;
  border: 1px solid #21262d;
  border-radius: 6px;
  overflow: hidden;
}
/* While the year dropdown is open, let it escape the block's overflow:hidden
   (which is there to clip the grid to the rounded corners). Without this the
   menu is cut off below the block — most visibly in COMPACT mode, where the
   block is only as tall as its header so the dropdown has nowhere to show. */
.cal-year-block:has(.cal-year-picker.is-open) { overflow: visible; }
.cal-year-label {
  color: #c9d1d9;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.5px;
  cursor: pointer;
  user-select: none;
  padding: 10px 16px;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: background 0.12s;
}
/* The custom .cal-year-picker button carries its own caret now, so the label
   wrapper no longer paints a chevron and isn't itself clickable. */
.cal-year-label { cursor: default; }
/* Current year breaks out of the .container width to give it visual prominence
   over collapsed past years. Negative side margins extend it beyond the 1080px
   cap; inner padding stays so contents don't hit the edge. */
.cal-year-block.is-current {
  margin-left: -140px;
  margin-right: -140px;
  border-color: rgba(170, 220, 240, 0.28);
  box-shadow:
    inset 0 1px 0 rgba(190, 225, 240, 0.10),
    0 4px 18px rgba(0, 0, 0, 0.35);
}
.cal-year-block.is-current .cal-year-label {
  font-size: 20px;
  padding: 11px 20px;
  /* Year picker on the left, Compact toggle on the right (top-right corner of
     the calendar block). */
  justify-content: space-between;
}
.cal-year-block.is-current .cal-mode-full,
.cal-year-block.is-current .cal-mode-compact {
  padding: 0 20px 10px;
}
/* Compact toggle restyled to match the year-picker button (gradient pill,
   cool-blue border) instead of the default dark toolbar pill. It now lives in
   the year block's header row (top-right), not a separate bar above. */
.cal-compact-toggle.ua-upgrades-toggle {
  background:
    linear-gradient(180deg, rgba(38, 46, 56, 0.95) 0%, rgba(26, 34, 42, 0.95) 100%);
  border: 1px solid rgba(170, 220, 240, 0.35);
  border-radius: 6px;
  color: #e6edf3;
  padding: 6px 12px;
  gap: 9px;
  box-shadow:
    inset 0 1px 0 rgba(190, 225, 240, 0.10),
    0 2px 6px rgba(0, 0, 0, 0.35);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.cal-compact-toggle.ua-upgrades-toggle:hover { border-color: rgba(170, 220, 240, 0.6); }
.cal-compact-toggle .ua-upgrades-label {
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.4px;
}
@media (max-width: 1400px) {
  /* Stop the breakout when the viewport can't comfortably host it. */
  .cal-year-block.is-current { margin-left: 0; margin-right: 0; }
}

/* Year selector — a fully custom dropdown (replaces the native <select> so
   the value and every option are perfectly centred and themed; the native
   popup left-aligned its numbers and reserved arrow space). */
.cal-year-picker { position: relative; display: inline-block; }
.cal-year-current {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 90px;             /* symmetric slot keeps the value optically centred */
  background:
    linear-gradient(180deg, rgba(38, 46, 56, 0.95) 0%, rgba(26, 34, 42, 0.95) 100%);
  border: 1px solid rgba(170, 220, 240, 0.35);
  border-radius: 6px;
  color: #e6edf3;
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.5px;
  cursor: pointer;
  padding: 5px 11px;
  box-shadow:
    inset 0 1px 0 rgba(190, 225, 240, 0.10),
    0 2px 6px rgba(0, 0, 0, 0.35);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.cal-year-current-val { font-variant-numeric: tabular-nums; }
.cal-year-caret {
  font-size: 10px;
  color: #a8b3bd;
  line-height: 1;
  transition: transform 0.15s;
}
.cal-year-current:hover { border-color: rgba(170, 220, 240, 0.6); }
.cal-year-picker.is-open .cal-year-current,
.cal-year-current:focus-visible {
  outline: none;
  border-color: rgba(170, 220, 240, 0.75);
  box-shadow:
    inset 0 1px 0 rgba(190, 225, 240, 0.14),
    0 0 0 3px rgba(121, 192, 255, 0.18);
}
.cal-year-picker.is-open .cal-year-caret { transform: rotate(180deg); }
.cal-year-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  margin: 0;
  padding: 5px;
  list-style: none;
  min-width: 100%;
  /* No max-height / overflow → the full year list shows without a scrollbar
     (there are only ~9–15 years). */
  background: #11161d;
  border: 1px solid rgba(170, 220, 240, 0.30);
  border-radius: 8px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55);
}
.cal-year-menu[hidden] { display: none; }
.cal-year-opt {
  text-align: center;
  font-size: 15px;
  font-weight: 600;
  color: #c9d1d9;
  padding: 7px 22px;
  border-radius: 5px;
  cursor: pointer;
  letter-spacing: 0.5px;
  font-variant-numeric: tabular-nums;
  transition: background 0.1s, color 0.1s;
}
.cal-year-opt:hover { background: rgba(121, 192, 255, 0.12); color: #fff; }
.cal-year-opt.is-selected {
  background: rgba(121, 192, 255, 0.18);
  color: #fff;
  box-shadow: inset 0 0 0 1px rgba(121, 192, 255, 0.35);
}
.cal-year-summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px 0;
  margin-top: -8px;
  color: #8b949e;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  border-top: 1px solid #21262d;
  margin-bottom: 8px;
}
.cal-year-summary-key { color: #6e7681; text-transform: uppercase; letter-spacing: 0.4px; font-size: 10.5px; }
.cal-year-summary-val { color: #c9d1d9; font-weight: 600; }
.cal-year-summary-meta { color: #6e7681; font-size: 11px; }
.cal-year-block[data-collapsed="true"] .cal-year-summary { display: none; }
.cal-year-block[data-collapsed="true"] .cal-mode-full,
.cal-year-block[data-collapsed="true"] .cal-mode-compact {
  display: none;
}
.cal-mode-full, .cal-mode-compact {
  padding: 0 16px 14px;
}

/* ---- INFOGRAPHIC: patch-cadence strip under the calendar ----
   A short horizontal band (≈1/3 the calendar's height) of small charts, not a
   full card. Breaks out to the current-year block's width (same -140px). */
.cal-infographic {
  margin: 10px -140px 6px;
  display: flex;
  align-items: stretch;
  padding: 12px 6px;
  background:
    linear-gradient(180deg, rgba(28, 36, 46, 0.5) 0%, rgba(18, 24, 32, 0.5) 100%);
  /* Border + shadow matched to .cal-year-block.is-current. */
  border: 1px solid rgba(170, 220, 240, 0.28);
  border-radius: 6px;
  box-shadow:
    inset 0 1px 0 rgba(190, 225, 240, 0.10),
    0 4px 18px rgba(0, 0, 0, 0.35);
}
@media (max-width: 1400px) {
  .cal-infographic { margin-left: 0; margin-right: 0; }
}
.cal-ig-panel {
  flex: 1 1 0;
  min-width: 0;
  padding: 0 22px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.cal-ig-panel + .cal-ig-panel { border-left: 1px solid #21262d; }
.cal-ig-lead { flex: 0 0 auto; gap: 1px; }
.cal-ig-title { font-size: 15px; font-weight: 700; color: #e6edf3; letter-spacing: 0.3px; }
.cal-ig-sub {
  font-size: 10.5px; color: #6e7681; text-transform: uppercase;
  letter-spacing: 0.5px; margin-bottom: 9px;
}
.cal-ig-statline { display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: #8b949e; }
.cal-ig-statline b {
  color: #79c0ff; font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums;
}
/* Major-patch count in the calendar's blue (matches .cal-*.major cells + chart). */
.cal-ig-statline b.cal-ig-major { color: #79c0ff; }
/* Faint horizontal divider between stat groups. */
.cal-ig-rule {
  height: 1px;
  align-self: stretch;
  margin: 4px 0;
  background: rgba(170, 220, 240, 0.14);
}
.cal-ig-h {
  font-size: 10.5px; font-weight: 600; color: #9fb0c0; text-transform: uppercase;
  letter-spacing: 0.6px; margin-bottom: 8px; text-align: center;
}
/* Sparkline (SVG) — smooth line + gradient area + point dots. */
.cal-ig-spark {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}
.cal-ig-spark-lbl {
  fill: #6e7681;
  font-size: 11px;
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}
.cal-ig-grid { stroke: rgba(170, 220, 240, 0.07); stroke-width: 1; }
.cal-ig-grid-v { stroke: rgba(170, 220, 240, 0.045); }   /* minor vertical lines */
.cal-ig-axis { stroke: rgba(170, 220, 240, 0.18); stroke-width: 1; }
.cal-ig-axis.is-base { stroke: rgba(170, 220, 240, 0.30); }
.cal-ig-ytick {
  fill: #6e7681;
  font-size: 9px;
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}
/* Point dots + hover-revealed value. The wide invisible hit rect makes the
   whole column hoverable so the value is easy to surface. */
.cal-ig-hit { fill: transparent; pointer-events: all; cursor: default; }
.cal-ig-dot { r: 2.1; transition: r 0.12s; }
.cal-ig-pt:hover .cal-ig-dot { r: 4; }
.cal-ig-pt-val {
  fill: #e6edf3;
  font-size: 11px;
  font-weight: 700;
  font-family: inherit;
  font-variant-numeric: tabular-nums;
  opacity: 0;
  transition: opacity 0.12s;
  pointer-events: none;
  paint-order: stroke;
  stroke: #0d1117;
  stroke-width: 3px;
  stroke-linejoin: round;
}
.cal-ig-pt:hover .cal-ig-pt-val { opacity: 1; }
@media (max-width: 760px) {
  .cal-infographic { flex-direction: column; gap: 14px; }
  .cal-ig-panel + .cal-ig-panel { border-left: none; border-top: 1px solid #21262d; padding-top: 12px; }
}

/* MODE 2: COMPACT */
.cal-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 4px;
}
.cal-month {
  display: flex;
  flex-direction: column;
}
.cal-month-name {
  text-align: center;
  font-size: 10.5px;
  color: #6e7681;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  margin-bottom: 6px;
}
.cal-month-cells {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-height: 44px;
  padding: 2px;
  border-left: 1px solid #21262d;
}
.cal-patch {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 4px 2px;
  border-radius: 4px;
  text-decoration: none;
  font-family: inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: filter 0.15s, transform 0.13s ease;
  border: 1px solid transparent;
  position: relative;
}
.cal-patch:hover {
  filter: brightness(1.35);
  transform: scale(1.08);
  z-index: 3;
}
.cal-patch .cal-version {
  font-size: 9.5px;
  font-weight: 500;
  color: #b8b8b8;
  line-height: 1.1;
  margin-bottom: 2px;
  white-space: nowrap;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(255, 255, 255, 0.30);
}
.cal-patch .cal-day {
  font-size: 13px;
  font-weight: 600;
  color: #c9d1d9;
  line-height: 1;
}
.cal-patch.sub {
  background: rgba(110, 118, 129, 0.22);
}
/* Major patches — unified solid blue (compact), matches the cadence chart */
.cal-patch.major {
  background: rgba(88, 166, 255, 0.50);
  border-color: rgba(88, 166, 255, 0.65);
}
.cal-patch.major .cal-day {
  color: #fff;
  font-weight: 700;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}
.cal-patch.major .cal-version {
  color: rgba(255, 255, 255, 0.82);
  font-weight: 500;
  text-decoration-color: rgba(255, 255, 255, 0.40);
}

/* CURRENT patch (compact mode) — emphasised via shadow only, no scale */
.cal-patch.current {
  filter: brightness(1.20);
  z-index: 2;
  box-shadow:
    inset 0 1px 0 rgba(255, 235, 205, 0.35),
    /* pre-divided by the brightness(1.2) filter above so the ring renders
       as the site gold #e3c46a on screen (filter brightens box-shadows too) */
    0 0 0 2px rgba(189, 163, 88, 0.90),
    0 2px 6px rgba(0, 0, 0, 0.4);
}
.cal-patch.current:hover {
  z-index: 4;
  filter: brightness(1.35);
  box-shadow:
    inset 0 1px 0 rgba(255, 235, 205, 0.50),
    /* pre-divided by brightness(1.35) — renders as #e3c46a */
    0 0 0 2.5px rgba(168, 145, 79, 1),
    0 3px 10px rgba(0, 0, 0, 0.5);
}
span.cal-patch {
  cursor: default;
  opacity: 0.55;
}

/* MODE 1: FULL (every day) */
.cal-full-grid {
  display: grid;
  grid-template-columns: 60px repeat(31, 1fr);
  gap: 2px;
  font-variant-numeric: tabular-nums;
}
.cal-full-month-name {
  color: #8b949e;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  font-weight: 500;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 6px;
}
.cal-full-day {
  aspect-ratio: 1;
  min-height: 22px;
  max-height: 33px;          /* cap the square so the full grid fits the viewport */
  border-radius: 3px;
  background: rgba(110, 118, 129, 0.05);
  font-size: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #555c64;
  text-decoration: none;
  position: relative;
  transition: transform 0.13s ease, filter 0.13s, background 0.1s, z-index 0s;
}
.cal-full-day.no-day { background: transparent; }
/* Cross-highlight: when hovering any day cell, JS marks all cells sharing the
   same data-day (column) and data-month (row) so a vertical + horizontal band
   lights up around the cursor. */
.cal-full-grid .cross-row,
.cal-full-grid .cross-col {
  background: rgba(170, 220, 240, 0.07);
}
.cal-full-grid .cross-row.cross-col {
  background: rgba(170, 220, 240, 0.16);
}
.cal-full-month-name.cross-row {
  color: #e6edf3;
}

/* Universal table cross-highlight — applies to every <table> on every page.
   On hover of any cell, the entire row + entire column get an equal DARK
   tint; the pivot cell (intersection) is darker still with a cyan rim.
   Wired up in scripts.js → wireCrossHover(). TH cells are never tagged by
   the JS so header rows stay clean.

   IMPORTANT: the hovered row is BOTH .cross-row AND the :hover target, so a
   table's row-hover background tint would otherwise lighten the horizontal
   band and make it read paler than the vertical column. We zero out the
   hover tint on .cross-row rows (below) so both axes darken equally. */
table tr.cross-row > td,
table td.cross-col {
  background-image: linear-gradient(rgba(0, 0, 0, 0.34),
                                    rgba(0, 0, 0, 0.34));
}
table tr.cross-row > td.cross-col {
  background-image: linear-gradient(rgba(0, 0, 0, 0.50),
                                    rgba(0, 0, 0, 0.50));
  box-shadow: inset 0 0 0 1px rgba(170, 220, 240, 0.55);
}
/* Kill per-table row-hover tints on the cross-row so the horizontal band
   darkens by the same amount as the vertical column. */
.creeps-table tbody tr.cross-row:hover,
.mr-table tbody tr.cross-row:hover {
  background: transparent;
}
.cal-full-day.has-patch {
  font-weight: 700;
  font-size: 10px;
  letter-spacing: -0.2px;
  cursor: pointer;
}
.cal-full-day.has-patch:hover {
  transform: scale(1.6);
  z-index: 5;
  filter: brightness(1.25);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
}
.cal-full-day.has-patch.sub {
  background: rgba(110, 118, 129, 0.4);
  color: #c9d1d9;
}
/* Major patches — unified solid blue (full grid), matches the cadence chart */
.cal-full-day.has-patch.major {
  background: rgba(88, 166, 255, 0.70);
  color: #fff;
  font-weight: 700;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.30);
}

/* CURRENT patch — emphasised without scaling so text never overflows the cell. */
.cal-full-day.has-patch.current {
  z-index: 4;
  filter: brightness(1.20);
  box-shadow:
    /* pre-divided by brightness(1.2) — renders as #e3c46a */
    0 0 0 2px rgba(189, 163, 88, 0.90),
    0 2px 8px rgba(0, 0, 0, 0.55);
}
.cal-full-day.has-patch.current:hover {
  z-index: 6;
  filter: brightness(1.35);
  box-shadow:
    /* pre-divided by brightness(1.35) — renders as #e3c46a */
    0 0 0 2.5px rgba(168, 145, 79, 1),
    0 4px 12px rgba(0, 0, 0, 0.65);
}

span.cal-full-day.has-patch {
  cursor: default;
}

/* Display toggling */
.calendar.mode-compact .cal-mode-full { display: none; }
.calendar.mode-full .cal-mode-compact { display: none; }

/* TOOLBAR — full viewport-width strip below the site header. Holds the tag
   filter row + categories + search box + patch-info block (date + age). */
.toolbar {
  width: 100%;
  margin: 0;
  padding: 0;
  background: #161b22;
  border-top: 1px solid #30363d;
  border-bottom: 1px solid #30363d;
  font-size: 13px;
  color: #8b949e;
  position: sticky;
  top: var(--site-nav-h, 44px);
  z-index: 80;
}
.toolbar-inner {
  display: flex;
  align-items: center;
  gap: 16px;
  row-gap: 8px;
  /* Wrap (instead of overlapping) when zoomed in / narrow: the search box
     shrinks first, then the columns drop to a second line. */
  flex-wrap: wrap;
  padding: 8px 28px;
}
/* Search sits in the MIDDLE column of the toolbar — always expanded (no
   collapse/expand), a fixed-width pill centred between the legend (left)
   and the patch info (right). The two side columns flex:1 so the middle
   stays optically centred. */
.toolbar .search-box {
  /* Centred middle column. Shrinks first when the toolbar runs out of room
     (zoom-in / narrow viewport) so it never overlaps the tag legend. */
  flex: 0 1 320px;
  width: 320px;
  min-width: 0;
}
/* Patch info — three labelled facts on the right of the toolbar. No pill,
   no border: plain inline text with bullet separators between the parts. */
.toolbar-patch-info {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 0;
  white-space: nowrap;
  line-height: 1.2;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  font-size: 12.5px;
  color: #98a2b0;
}
.toolbar-patch-info > span { padding: 0 10px; }
.toolbar-patch-info > span:first-child { padding-left: 0; }
.toolbar-patch-info > span:last-child { padding-right: 0; }
.toolbar-patch-info > span + span {
  border-left: 1px solid rgba(170, 220, 240, 0.18);
}
.toolbar-patch-info b {
  color: #e6edf3;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.toolbar-patch-info .ti-released b {
  font-size: 13px;
  letter-spacing: 0.2px;
}
.patch-age .age-sep {
  margin: 0 8px;
  opacity: 0.45;
  font-weight: 300;
}
/* On narrow viewports the patch meta-info (date / days live) competes with
   the search box. Hide it below 640px — filters and search are the primary
   tools; the date facts are secondary. */
@media (max-width: 640px) {
  .toolbar-patch-info { display: none; }
  .toolbar .search-box { flex: 1 1 auto; width: auto; }
}
.legend-tags {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: nowrap;          /* keep all tag buttons on one line */
  flex-shrink: 0;
  white-space: nowrap;
  /* Hidden until scripts.js computes which tags are actually present in
     this patch and hides the absent filter buttons (e.g. QoL on 7.41).
     Prevents a "flash of all 7 buttons" before the absent ones get
     style.display = 'none' applied. */
  visibility: hidden;
}
.legend-tags strong {
  color: #c9d1d9;
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  margin-right: 4px;
  /* Equal-width label column so the BUFF/NERF/… badges line up vertically
     with the Group category badges in the row below (matched in
     .legend-categories strong). */
  flex: 0 0 auto;
  width: 46px;
}

/* SEARCH BOX */
.search-box {
  position: relative;
  flex: 0 1 280px;
  min-width: 180px;            /* shrinks so categories/tags fit on one line each */
}
.search-box input {
  width: 100%;
  background: #0d1117;
  border: 1px solid #30363d;
  border-radius: 6px;
  padding: 6px 12px;
  color: #c9d1d9;
  font-family: inherit;
  font-size: 13px;
  transition: border-color 0.15s;
}
.search-box input::placeholder { color: #6e7681; }
.search-box input:focus {
  outline: none;
  border-color: #58a6ff;
  box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.2);
}
/* Inside the patch toolbar the search is ALWAYS expanded — a full-width
   pill with the magnifier icon on the left and the placeholder visible. */
.toolbar .search-box input {
  width: 100%;
  padding: 6px 12px 6px 34px;
  cursor: text;
  color: #c9d1d9;
  text-align: center;
  background-image:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="%2398a2b0" stroke-width="1.6"><circle cx="7" cy="7" r="4.5"/><path d="M10.4 10.4 L13.6 13.6" stroke-linecap="round"/></svg>');
  background-repeat: no-repeat;
  background-position: 11px center;
  background-size: 15px 15px;
}
.toolbar .search-box input::placeholder { color: #6e7681; }
.toolbar .search-box input:focus { text-align: left; }
.search-results {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: #161b22;
  border: 1px solid #30363d;
  border-radius: 6px;
  margin-top: 4px;
  max-height: 320px;
  overflow-y: auto;
  z-index: 50;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
}
.search-results.show { display: block; }
.search-results .result-item {
  padding: 6px 12px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: #c9d1d9;
  border-bottom: 1px solid #21262d;
}
.search-results .result-item:last-child { border-bottom: none; }
.search-results .result-item:hover,
.search-results .result-item.active {
  background: rgba(88, 166, 255, 0.1);
  color: #fff;
}
.search-results .result-item img {
  width: 32px;
  height: 18px;
  object-fit: cover;
  border-radius: 2px;
  background: #21262d;
  flex-shrink: 0;
}
.search-results .result-item .kind {
  margin-left: auto;
  font-size: 10.5px;
  color: #6e7681;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.search-results .empty {
  padding: 10px 12px;
  color: #8b949e;
  font-style: italic;
  font-size: 12.5px;
}
.search-results mark {
  background: rgba(240, 198, 116, 0.25);
  color: inherit;
  padding: 0 1px;
  border-radius: 2px;
}

/* MAJOR SECTION */
h2.section {
  background:
    linear-gradient(90deg, rgba(0, 0, 0, 0.45) 0%, transparent 18%, transparent 82%, rgba(0, 0, 0, 0.45) 100%),
    linear-gradient(180deg, #b53528 0%, #4a120c 100%);
  color: #fff;
  font-family: 'Jersey 25', 'Courier New', monospace;
  font-size: 25px;
  font-weight: 400;
  padding: 12px 18px;
  margin: 36px 0 20px;
  border-radius: 4px;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 -1px 0 rgba(0, 0, 0, 0.4),
    0 2px 8px rgba(0, 0, 0, 0.4);
  text-shadow:
    1px 1px 0 rgba(0, 0, 0, 0.55),
    -1px 0 0 rgba(0, 0, 0, 0.35),
    0 -1px 0 rgba(0, 0, 0, 0.3),
    0 0 8px rgba(0, 0, 0, 0.5);
}

/* ENTITY (HERO/ITEM HEADER WITH ICON) */
.entity {
  display: flex;
  align-items: center;
  gap: 14px;
  background: linear-gradient(90deg, #1a1f29 0%, #161b22 100%);
  border-radius: 4px;
  padding: 8px 14px;
  margin: 20px 0 8px;
}
.entity-name {
  color: #f0c674;
  /* Site pixel font (same family as the brand/nav). Jersey renders narrow
     and has a single weight — bigger size + slight tracking compensate. */
  font-family: 'Jersey 25', 'Courier New', monospace;
  font-size: 22px;
  font-weight: 400;
  letter-spacing: 0.5px;
}
.entity-icon img {
  display: block;
  border-radius: 3px;
}
.hero-icon img {
  width: 80px;
  height: 45px;
  object-fit: cover;
}
.item-icon img {
  width: 50px;
  height: 36px;
  object-fit: cover;
}
.ability-icon img {
  width: 45px;
  height: 45px;
  object-fit: cover;
  border-radius: 4px;
}
.plain-entity .entity-name {
  color: #79c0ff;
}
/* "View on map" jump button on the Terrain Changes header → terrain.html with
   this patch preselected. Gold-leather pill matching the site nav-arrow skin. */
.terrain-jump-btn {
  margin-left: auto;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  border-radius: 6px;
  background: linear-gradient(180deg, #3b2e1d, #2a2014);
  border: 1px solid #e3c46a;
  color: #e3c46a;
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  transition: filter 0.15s ease, transform 0.1s ease, box-shadow 0.15s ease;
}
.terrain-jump-btn img {
  display: block;
  image-rendering: pixelated;
}
.terrain-jump-btn:hover {
  filter: brightness(1.18);
  box-shadow: 0 0 10px rgba(227, 196, 106, 0.35);
}
.terrain-jump-btn:active {
  transform: translateY(1px);
}

/* =====================================================================
   PATCH DYNAMICS WIDGET
   Right-aligned row of small diamond pills, one per recent patch. Each
   pill is filled with a vertical linear-gradient whose segments are
   proportional to the tag counts for this entity in that patch. Glassy
   look via translucent borders + faint inner glow; subtle drop-shadow
   gives a hint of color bleed between neighbors. Untouched patches show
   a near-black translucent diamond, are non-clickable, and carry a
   "not touched" tooltip.
   ===================================================================== */
.entity {
  position: relative;
}
.entity-name {
  flex: 1 1 auto;
  min-width: 0;
}
/* Outer wrapper: positions the cell row and provides an anchor for the
   absolutely-placed nav arrows. The ::before pseudo extends the hover zone
   so the cursor can slide from a cell onto an arrow without a gap. */
.dyn-row-wrap {
  position: relative;
  margin-left: auto;
  margin-right: -8px;
}
.dyn-row-wrap::before {
  content: '';
  position: absolute;
  inset: 0;
  left: -18px;
  right: -18px;
}
.patch-dynamics {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 2px 4px;
  position: relative;
}
/* Pixel-art navigation arrows — same visual language as .version-nav-arrow
   (gold staircase SVG, octagonal clip, dark glass background).
   Arrows are flex siblings of .patch-dynamics inside .dyn-row-wrap, so the
   hover zone is continuous: cursor slides from a cell onto an arrow without
   losing the hover state and the arrow stays visible and clickable. */
.dyn-nav-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 24px;
  padding: 0;
  border: none;
  background: rgba(0, 0, 0, 0.28);
  background-repeat: no-repeat;
  background-position: center;
  background-size: 6px 10px;
  image-rendering: pixelated;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.5);
  clip-path: polygon(2px 0, calc(100% - 2px) 0, 100% 2px,
    100% calc(100% - 2px), calc(100% - 2px) 100%, 2px 100%,
    0 calc(100% - 2px), 0 2px);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s, filter 0.15s;
}
.dyn-nav-left {
  right: 100%;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 10" shape-rendering="crispEdges"><g fill="%23e3c46a"><rect x="4" y="0" width="2" height="2"/><rect x="2" y="2" width="4" height="2"/><rect x="0" y="4" width="6" height="2"/><rect x="2" y="6" width="4" height="2"/><rect x="4" y="8" width="2" height="2"/></g></svg>');
}
.dyn-nav-right {
  left: 100%;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 10" shape-rendering="crispEdges"><g fill="%23e3c46a"><rect x="0" y="0" width="2" height="2"/><rect x="0" y="2" width="4" height="2"/><rect x="0" y="4" width="6" height="2"/><rect x="0" y="6" width="4" height="2"/><rect x="0" y="8" width="2" height="2"/></g></svg>');
}
.dyn-row-wrap:hover .dyn-nav-arrow,
.dyn-nav-arrow:hover {
  opacity: 1;
  pointer-events: auto;
}
.dyn-nav-arrow:hover {
  background-color: rgba(0, 0, 0, 0.45);
  filter: brightness(1.2);
}
/* Wrapper holds the rounded-rectangle pill AND the absolutely-positioned
   tooltip. The pill is clipped via overflow:hidden + border-radius so
   pseudo highlights stay inside; the tooltip is a real DOM sibling that
   lives outside the clip. */
.dyn-cell-wrap {
  position: relative;
  display: inline-flex;
  width: 24px;
  height: 24px;
  text-decoration: none;
  cursor: pointer;
}
.dyn-cell-wrap.empty,
.dyn-cell-wrap.current {
  cursor: default;
}
/* Raise the WHOLE wrap of the hovered cell above a just-left neighbour: the
   leaving cell keeps its hover z for 0.14s (delayed z revert) while it
   shrinks, and both cells' z values tie — so without this the leaving cell
   can paint over the newly hovered one mid-descent. The wrap's z (7) beats
   the leaving cell's held z (6); its own cell isn't isolated (wrap z:auto),
   so the comparison happens in the same stacking context. */
.dyn-cell-wrap:not(.empty):hover {
  z-index: 7;
}
.dyn-cell {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 4px;
  /* Background is a multi-layer stack rendered in a SINGLE paint pass —
     no mix-blend-mode pseudos. With ~3000 cells visible on the largest
     patch page, every blend-mode pseudo would force its own compositing
     layer (a known scroll-jank cause). Stack order, top → bottom:
       1. Top-centre specular ellipse (highlight catching the light)
       2. Vertical top sheen + bottom darken (convex shading)
       3. Fluid color gradient driven by JS (--dyn-bg)
     The opaque fallback color at the end ensures rendering even before
     JS runs. */
  /* Glassy pill: thin top light edge + soft diagonal sheen + dark bottom
     edge over the fluid tag gradient — single paint pass, no pseudos
     (~3000 cells on the largest pages; extra layers cause scroll jank). */
  background:
    linear-gradient(160deg,
      rgba(255, 255, 255, 0.16) 0%,
      rgba(255, 255, 255, 0.05) 42%,
      rgba(255, 255, 255, 0.0) 62%,
      rgba(0, 0, 0, 0.10) 100%),
    var(--dyn-bg, rgba(10, 11, 14, 0.55));
  /* z-index reverts 0.14s AFTER un-hover (matches the scale duration) so the
     cell stays on top while it shrinks back — otherwise the right-hand
     neighbour (later in DOM) paints over it mid-descent. */
  transition: transform 0.14s ease, box-shadow 0.14s ease, background-color 0.14s ease, z-index 0s linear 0.14s;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    inset 0 -1px 0 rgba(0, 0, 0, 0.35),
    inset 0 0 0 1px rgba(255, 255, 255, 0.07);
}
/* Misc-only patch — every tag was MISC. The dimmed look is achieved by
   halving the fluid alpha in JS (see scripts.js `miscOnly` branch); we
   deliberately avoid CSS `opacity` here so the hover-time backdrop layer
   can stay fully opaque and block neighbouring cells. */
/* Empty patch — flat dark fill with a barely-there pressed-in inset:
   1px darker line along the top edge (the "lip" of the recess casts a
   shadow into it) + 1px faint highlight along the bottom edge (light
   bouncing off the back of the recess). Together they read as a
   shallow inset without competing with the painted cells. */
.dyn-cell-wrap.empty .dyn-cell {
  background: rgba(5, 6, 9, 0.92);
  box-shadow:
    inset 0  1px 0 rgba(0, 0, 0, 0.45),
    inset 0 -1px 0 rgba(255, 255, 255, 0.05);
  opacity: 0.9;
}
.dyn-cell-wrap:not(.empty):hover .dyn-cell {
  /* Raise immediately on hover (no z-index delay); the delayed revert lives
     on the base .dyn-cell rule for the un-hover descent. */
  transition: transform 0.14s ease, box-shadow 0.14s ease, background-color 0.14s ease, z-index 0s linear 0s;
  transform: scale(2.5);
  /* Opaque backdrop under the translucent fluid: stops neighbouring cells
     from showing through the scaled-up hovered cell. Matches the
     entity-block gradient mid-tone (~#181b25). */
  background-color: rgb(24, 27, 37);
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.6),
    inset 0 0 0 1px rgba(255, 255, 255, 0.12),
    0 0 8px rgba(255, 255, 255, 0.22);
  /* 6 — strictly ABOVE a just-left neighbour, which keeps its hover z (5,
     via the delayed z-index revert) for 0.14s while it shrinks back.
     Without this, the descending cell paints OVER the newly hovered one. */
  z-index: 6;
}
/* Hover on the current patch — keep the same 2× pop + glow but layer the
   blue+black outline back on top so the "you are here" marker stays
   visible. Higher specificity than the generic hover rule above so this
   wins for the current cell. */
.dyn-cell-wrap.current:hover .dyn-cell {
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.6),
    inset 0 0 0 1px rgba(255, 255, 255, 0.12),
    0 0 8px rgba(255, 255, 255, 0.22),
    0 0 0 1.5px rgba(227, 196, 106, 0.95),
    0 0 0 2.5px rgba(0, 0, 0, 0.85);
}
/* Current patch (the page the user is on) — thin faint blue outline in
   the same style as .cal-patch.current in the calendar, just slimmer
   (1px instead of 2px, lower alpha) so it reads as a subtle "you are
   here" marker without competing with the painted color band inside. */
.dyn-cell-wrap.current .dyn-cell {
  /* Gold "you are here" ring: gold on top (1.5px) + a thin black ring
     beneath, giving the marker a crisp dark edge against the page
     background. box-shadow paints in source order top→bottom, so gold
     covers the inner 1.5px of the wider 2.5px black layer and leaves
     1px of black exposed as the outermost ring. */
  box-shadow:
    0 1px 1.5px rgba(0, 0, 0, 0.5),
    inset 0 0 0 1px rgba(255, 255, 255, 0.06),
    0 0 0 1.5px rgba(227, 196, 106, 0.95),
    0 0 0 2.5px rgba(0, 0, 0, 0.85);
}
/* The convex highlight + specular layers used to live on ::before/::after
   with mix-blend-mode: screen. They've been folded directly into
   .dyn-cell's `background` stack above to eliminate ~6000 compositing
   layers on the larger patch pages — fixes scroll jank. */
/* Shared tooltip popup — ONE element on document.body (not inside any
   .dyn-cell-wrap), repositioned on hover. Lives outside .entity-block
   so `contain: paint` from content-visibility:auto doesn't clip it.
   Positioned via JS (position: fixed) — top/left set inline per show. */
.dyn-tip {
  position: fixed;
  left: 0;
  top: 0;
  background: rgba(13, 17, 23, 0.97);
  color: #c9d1d9;
  padding: 8px 10px;
  border: 1px solid rgba(139, 148, 158, 0.35);
  border-radius: 6px;
  pointer-events: none;
  z-index: 1000;
  text-align: left;
  line-height: 1.3;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
  display: none;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  white-space: nowrap;
}
.dyn-tip.is-visible {
  display: flex;
}
.dyn-tip-header {
  font-size: 12px;
  color: #e6edf3;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.4px;
  text-align: center;
  padding-bottom: 5px;
  border-bottom: 1px solid rgba(139, 148, 158, 0.28);
}
.dyn-tip-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 10px;
  row-gap: 5px;
  align-items: center;
}
.dyn-tip-row {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.dyn-tip-row > .badge {
  width: 68px;
  min-width: 68px;
  padding-left: 4px;
  padding-right: 4px;
  flex: 0 0 auto;
  box-sizing: border-box;
}
.dyn-tip-count {
  font-size: 11px;
  color: #c9d1d9;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.dyn-tip-note {
  font-size: 11px;
  color: #8b9099;
  font-style: italic;
}
/* `.is-visible` toggle is handled higher up via display:flex. */

/* NEW-ITEM BLOCK — keeps the standard grid (tag column / text / %).
   Only the FIRST row of the block gets a NEW tag (or RETURNING) in the tag
   column; the original per-row tag is hidden. Subsequent rows show an empty
   placeholder so the text column stays aligned with the rest of the page.
   The tag text comes from data-new-tag on the .entity-block. */
/* Hide the original first-child tag completely (not visibility:hidden) so
   it doesn't occupy cell (1,1) of the grid — otherwise the ::before tag
   gets auto-placed to row 2 and appears below the row content. */
.entity-block.is-new ul.changes li > .badge:first-child,
.entity-block.is-new ul.changes li > .row-tag-empty {
  display: none;
}
/* Per-row NEW tag was removed: the item is now signalled by the type label
   after the name + the components/provides blocks below the header. The
   block-level filter still works because each <li> inside .is-new carries
   data-tag="new" (via t("NEW") on the original li()). */
/* Ability description box — spans one Passive:/Active: starter row plus any
   continuation rows until the next ability starter or end of ul. Post-process
   classifies each li with one of -solo / -start / -cont / -cont-end. */
/* Box hugs the ul width (no overflow). Inside a new-item block the tag column
   is gone, so collapse the grid to [text 1fr][badge auto] and add left padding
   so text sits a few px in from the box border. */
ul.changes li.ability-row-solo,
ul.changes li.ability-row-start,
ul.changes li.ability-row-cont,
ul.changes li.ability-row-end {
  background: rgba(139, 148, 158, 0.04);
  border-left:  1px solid rgba(139, 148, 158, 0.18);
  border-right: 1px solid rgba(139, 148, 158, 0.18);
}
/* Apply Hydras-Breath-style layout (no tag column, text flush-left with 12px
   padding) to ALL ability-row li, not just inside .is-new blocks. Tag column
   collapses; only ability-rows that EXPLICITLY have a non-empty tag will
   show one inline at the front of the row text. */
ul.changes li.ability-row-solo,
ul.changes li.ability-row-start,
ul.changes li.ability-row-cont,
ul.changes li.ability-row-end {
  grid-template-columns: 1fr auto;
  padding-left: 12px;
  padding-right: 12px;
}
ul.changes li.ability-row-solo > .row-tag-empty,
ul.changes li.ability-row-start > .row-tag-empty,
ul.changes li.ability-row-cont > .row-tag-empty,
ul.changes li.ability-row-end > .row-tag-empty {
  display: none;
}
ul.changes li.ability-row-solo > .row-text,
ul.changes li.ability-row-start > .row-text,
ul.changes li.ability-row-cont > .row-text,
ul.changes li.ability-row-end > .row-text {
  grid-column: 1;
  text-align: left;
}
/* Ability-row layout has no left tag — suppress the float-pseudo that
   reserves the tag column. */
ul.changes li.ability-row-solo > .row-text::before,
ul.changes li.ability-row-start > .row-text::before,
ul.changes li.ability-row-cont > .row-text::before,
ul.changes li.ability-row-end > .row-text::before {
  display: none;
}
ul.changes li.ability-row-solo,
ul.changes li.ability-row-start {
  border-top: 1px solid rgba(139, 148, 158, 0.18);
  border-top-left-radius: 6px;
  border-top-right-radius: 6px;
  padding-top: 6px;
  margin-top: 10px;          /* gap between adjacent ability boxes */
}
ul.changes li.ability-row-solo,
ul.changes li.ability-row-end {
  border-bottom: 1px solid rgba(139, 148, 158, 0.18);
  border-bottom-left-radius: 6px;
  border-bottom-right-radius: 6px;
  padding-bottom: 6px;
  margin-bottom: 4px;
}

/* COMPONENTS BOX — visual assembly recipe shown under a new item's header.
   Layout: [icon1 + icon2 + ... + recipe] = TOTAL — total sits IMMEDIATELY
   after the last component (not pushed to the right edge). */
.components-box {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  margin: 6px 0 4px;
  padding: 8px 14px;
  background: rgba(139, 148, 158, 0.04);
  border: 1px solid rgba(139, 148, 158, 0.18);
  border-radius: 6px;
  font-variant-numeric: tabular-nums;
}
.components-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.component {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
}
.component img {
  width: 48px;
  height: 36px;
  border-radius: 3px;
  object-fit: cover;
  box-shadow: 0 0 0 1px rgba(139, 148, 158, 0.25);
}
.component-price {
  font-size: 11px;
  color: #c9d1d9;
  font-weight: 600;
  background: rgba(0, 0, 0, 0.30);
  padding: 1px 6px;
  border-radius: 2px;
  min-width: 32px;
  text-align: center;
}
.components-plus {
  color: #6e7681;
  font-size: 16px;
  font-weight: 700;
  padding: 0 2px;
}
.components-total {
  color: #c9d1d9;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}
.components-total span {
  font-weight: 700;
  color: #f0c674;
}

/* ENCHANT-BY-ATTRIBUTE GRID — 7.41 Tier-X enchantment selection box.
   Layout: rows of [attr icon][attr name] | [enchant chip] [enchant chip]...
   Shares the muted bordered look of components-box / provides-box. */
.enchant-attr-grid {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 8px 0 10px;
  padding: 8px 14px;
  background: rgba(139, 148, 158, 0.04);
  border: 1px solid rgba(139, 148, 158, 0.18);
  border-radius: 6px;
}
.enchant-attr-row {
  display: grid;
  grid-template-columns: 160px 1fr;
  align-items: center;
  gap: 14px;
  padding: 3px 0;
}
.enchant-attr-row + .enchant-attr-row {
  border-top: 1px dashed rgba(139, 148, 158, 0.18);
}
.enchant-attr-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 13.5px;
}
.enchant-attr-ico {
  width: 22px;
  height: 22px;
  object-fit: contain;
  flex-shrink: 0;
}
/* All-Heroes badge: 4 attribute icons packed into a 2×2 mini-grid that
   fills the same 22×22 footprint as a single attribute icon — so the
   "All Heroes" text aligns horizontally with "Strength" / "Agility" /
   etc. on the rows beneath. */
.enchant-attr-icons.all-attrs {
  display: grid;
  grid-template-columns: 10px 10px;
  grid-template-rows: 10px 10px;
  gap: 2px;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
}
.enchant-attr-icons.all-attrs img {
  width: 10px;
  height: 10px;
  object-fit: contain;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.55));
}
.enchant-attr-name { letter-spacing: 0.2px; }
.enchant-attr-name.is-str { color: #e73c06; }
.enchant-attr-name.is-agi { color: #3dcb37; }
.enchant-attr-name.is-int { color: #00d3e7; }
.enchant-attr-name.is-uni { color: #d3e700; }
.enchant-attr-name.is-all { color: #c9d1d9; }
.enchant-attr-list {
  display: flex;
  flex-wrap: wrap;
  gap: 5px 7px;
}
.enchant-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px 3px 4px;
  background: rgba(0, 0, 0, 0.22);
  border: 1px solid rgba(139, 148, 158, 0.22);
  border-radius: 14px;
  font-size: 12.5px;
  color: #c9d1d9;
  line-height: 1.2;
  transition: background 0.15s, border-color 0.15s;
  cursor: help;
}
.enchant-chip:hover {
  background: rgba(0, 0, 0, 0.35);
  border-color: rgba(139, 148, 158, 0.4);
}
.enchant-chip img {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
  flex-shrink: 0;
}
/* Tooltip — appears above the chip on hover. Multi-line via pre-line. */
.enchant-chip[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  white-space: pre-line;
  background: rgba(13, 17, 23, 0.97);
  color: #c9d1d9;
  font-size: 12px;
  font-weight: 400;
  padding: 7px 11px;
  border: 1px solid rgba(139, 148, 158, 0.35);
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  z-index: 100;
  min-width: 180px;
  max-width: 280px;
  text-align: left;
  line-height: 1.45;
  letter-spacing: 0.1px;
  transition: opacity 0.12s 0.05s;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
}
.enchant-chip[data-tooltip]:hover::after {
  opacity: 1;
}
/* Souvenir pills (Ringmaster Dark Carnival pool change). Reuse the
   `.enchant-chip` box style (border + background + tooltip on hover);
   `removed` modifier dims souvenirs no longer in the pool — no strike.
   Dim the icon + label only, NOT the hover tooltip popup — readability
   of the description text must stay full-strength. */
.enchant-chip.souvenir-chip.removed > img,
.enchant-chip.souvenir-chip.removed > span {
  opacity: 0.55;
}
.enchant-chip.souvenir-chip.removed > img {
  filter: grayscale(0.85);
}
.enchant-chip.souvenir-chip.removed::after {
  /* explicit reset — tooltip popup must NOT inherit the dim */
  opacity: 0;
  filter: none;
}
.enchant-chip.souvenir-chip.removed:hover::after {
  opacity: 1;
}
/* Info marker — matches the `.qhint` "?" badge (rounded square), with the
   question mark in our gold. Popup body is the .info-pop child below. */
.info-tip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 13px;
  height: 13px;            /* ~cap height of the row text so it fits the line */
  margin-left: 4px;
  border: 1px solid #6b7682;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.04);
  color: #e3c46a;
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  cursor: help;
  user-select: none;
  position: relative;
  /* Centred on the row text's digit band: vertical-align:middle sits the box
     centre near the x-height; nudge up so it spans cap-to-baseline (no poke
     below the text). */
  vertical-align: middle;
  top: -0.5px;
  transition: background 0.15s, border-color 0.15s;
}
.info-tip:hover,
.info-tip:focus {
  border-color: #e3c46a;
  background: rgba(227, 196, 106, 0.15);
  outline: none;
}
/* HTML popup child — supports rich content (badges, <br>) + a bold header,
   unlike a pure attr() tooltip. Hidden until hover/focus. */
.info-tip .info-pop {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: rgba(13, 17, 23, 0.98);
  color: #c9d1d9;
  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
  font-size: 12px;
  font-style: normal;
  font-weight: 400;
  padding: 8px 12px;
  border: 1px solid rgba(227, 196, 106, 0.35);
  border-radius: 6px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  z-index: 100;
  width: max-content;
  min-width: 150px;
  max-width: 340px;
  text-align: left;
  line-height: 1.55;
  letter-spacing: 0.1px;
  /* Avoid orphan words/numbers on a line of their own — keeps stat ranges
     like "…100–110" intact rather than dropping "110" to the next line. */
  text-wrap: pretty;
  /* No slide — the popup just appears on hover/focus (no transform anim). */
  transition: none;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
}
/* Hover/focus handled by JS body-level popup — suppress CSS-driven version. */
.info-tip:hover .info-pop,
.info-tip:focus .info-pop {
  opacity: 0;
  visibility: hidden;
}
/* Body-level shared popup for .info-tip — positioned by scripts.js so it
   stays inside the viewport even when the badge is near an edge. */
.info-pop-body {
  position: fixed;
  z-index: 1100;
  pointer-events: none;
  display: none;
  background: rgba(13, 17, 23, 0.98);
  color: #c9d1d9;
  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
  font-size: 12px;
  font-weight: 400;
  padding: 8px 12px;
  border: 1px solid rgba(227, 196, 106, 0.35);
  border-radius: 6px;
  width: max-content;
  min-width: 150px;
  max-width: min(340px, calc(100vw - 16px));
  overflow-wrap: break-word;
  text-align: left;
  line-height: 1.55;
  letter-spacing: 0.1px;
  text-wrap: pretty;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
}
.info-pop-body.is-visible { display: block; }
.info-pop-h {
  display: block;
  margin-bottom: 5px;
  font-weight: 700;
  color: #e3c46a;
}
/* Trailing cluster (Aghanim glyph + (i) tip) — kept on one line and bound to
   the preceding word (via a leading nbsp) so it never orphans onto an
   otherwise-empty wrapped line. */
.li-tail {
  white-space: nowrap;
}
/* Group wrapper: label + chips on a single horizontal row, wrapping if
   needed. "In pool:" and "Removed:" are separate groups on their own
   lines (each `.souvenir-group` is a block-level flex row). */
.souvenir-group {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 4px 0;
}
.souvenir-group-label {
  font-weight: 600;
  font-size: 12.5px;
  color: #7c8590;
  margin-right: 2px;
}
@media (max-width: 640px) {
  .enchant-attr-row {
    grid-template-columns: 1fr;
    gap: 4px;
  }
}

/* PROPERTIES BOX — single line of comma-separated attributes shown under the
   components box for new items. Soft outlined to match other blocks. */
.provides-box {
  margin: 4px 0;
  padding: 6px 14px;
  background: rgba(139, 148, 158, 0.04);
  border: 1px solid rgba(139, 148, 158, 0.18);
  border-radius: 6px;
  color: #c9d1d9;
  font-size: 13.5px;
}
/* Flat purchase price for a basic (no-recipe) new item — same bordered box as
   the components() build, with the gold value on the right like a recipe total. */
.item-cost-box {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  margin: 6px 0 4px;
  padding: 8px 14px;
  background: rgba(139, 148, 158, 0.04);
  border: 1px solid rgba(139, 148, 158, 0.18);
  border-radius: 6px;
  font-variant-numeric: tabular-nums;
}
.item-cost-label {
  color: #8b949e;
  font-size: 13px;
  font-weight: 600;
}
.item-cost-val {
  color: #f0c674;
  font-size: 14px;
  font-weight: 700;
}
.provides-box .provides-row {
  padding: 2px 0;
  line-height: 1.5;
}

/* Type label after the item name — small, uppercased, NEW colour family.
   vertical-align: middle so it sits on the item name's optical mid-line
   instead of dropping to the baseline of the larger heading text. */
.entity-name .entity-new-type {
  margin-left: 8px;
  /* The entity name itself is pixel-font (Jersey 25); these add-on labels
     ("NEW ARMAMENT ITEM", "Recipe changed") stay in the regular UI font. */
  font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: #b08c5a;
  opacity: 0.85;
  vertical-align: middle;
}

/* Same position/style as new-type, but neutral colour — used for
   'Recipe changed' on items whose components changed in this patch. */
.entity-name .entity-changed-type {
  margin-left: 8px;
  font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: #c9d1d9;
  opacity: 0.65;
  vertical-align: middle;
}

/* Recipe-changed visual: two SEPARATE component boxes (old and new), joined
   by a bold arrow between them. Uses the same grid 1fr/auto/1fr layout as
   properties-change so the two side-by-side blocks are exactly the same
   width (pane edges line up vertically). */
.components-change {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  column-gap: 12px;
  align-items: stretch;
  margin: 6px 0 4px;
}
.components-change .components-pane {
  margin: 0;
  min-width: 0;
}
.components-change .components-arrow,
.properties-change .properties-arrow {
  align-self: center;
  justify-self: center;
  font-size: 22px;
  font-weight: 700;
  color: #c9d1d9;
  opacity: 0.85;
  padding: 0 2px;
}
.component.component-added {
  border: 1.5px solid rgba(218, 165, 32, 0.85);
  border-radius: 6px;
  padding: 2px;
  box-shadow: 0 0 6px rgba(218, 165, 32, 0.25);
}
.component.component-removed {
  border: 1.5px solid rgba(220, 80, 80, 0.55);
  border-radius: 6px;
  padding: 2px;
  opacity: 0.85;
}

/* ABILITY-CHANGE — two-pane swap visual (old ability removed → new ability
   added). Same 1fr/auto/1fr grid + arrow as components-change, but each
   pane is a card with [icon + name] header on top and a description body
   below (multiple rows + optional scale_pill table). */
.ability-change {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  column-gap: 12px;
  align-items: stretch;
  margin: 6px 0 8px;
}
.ability-change-arrow {
  align-self: center;
  justify-self: center;
  font-size: 22px;
  font-weight: 700;
  color: #c9d1d9;
  opacity: 0.85;
  padding: 0 2px;
}
.ability-change-pane {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 14px;
  background: rgba(139, 148, 158, 0.04);
  border: 1px solid rgba(139, 148, 158, 0.18);
  border-radius: 6px;
  min-width: 0;
}
.ability-change-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 6px;
  border-bottom: 1px dashed rgba(139, 148, 158, 0.22);
}
/* Placeholder head — same layout footprint (~47px) as a real head, but
   visually hidden. Used in is-in-place mode on the new pane so the new
   pane's body top-aligns with the old pane's body (which sits below
   the real head) without needing a per-pane padding-top hack. */
.ability-change-head.is-placeholder { visibility: hidden; }
.ability-change-icon-wrap {
  position: relative;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
}
.ability-change-icon {
  width: 100%;
  height: 100%;
  border-radius: 4px;
  object-fit: cover;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
  display: block;
}
/* Innate marker — matches `.ability-block > .ability-icon-wrap > .innate-marker`
   style (dark circular pad + golden ring), scaled to the 40-px swap-pane icon. */
.ability-change-icon-wrap .innate-marker {
  position: absolute;
  left: 50%;
  bottom: -5px;
  transform: translateX(-50%);
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #0a0e13;
  padding: 1px;
  box-shadow: 0 0 0 1px rgba(220, 175, 95, 0.45);
  pointer-events: none;
  box-sizing: border-box;
}
.ability-change-name {
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: 0.2px;
  color: #c9d1d9;
}
.ability-change-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13.5px;
  line-height: 1.5;
  color: #c9d1d9;
  min-width: 0;
}
.ability-change-row { padding: 4px 0; line-height: 1.45; }
/* Dense stat-list mode — used by per-unit comparison cards (e.g. Brewmaster
   brewlings) where each row is a single short stat line. Tightens vertical
   rhythm so the panes don't tower over the rest of the hero block. */
/* Brewling parent-child connector — dashed line from Primal Split's ability
   icon down to each Brewling block's icon. Same visual idiom as the dashed
   ability_change-connector (e.g. Belligerent → Liquid Courage). The SVG is
   injected by drawBrewlingConnectors() in scripts.js and lives in the
   document body with position:absolute so it can overlay multiple ability
   blocks. */
.brewling-connector {
  position: absolute;
  pointer-events: none;
  z-index: 0;
  overflow: visible;
}
.brewling-connector path {
  fill: none;
  stroke: rgba(139, 148, 158, 0.55);
  stroke-width: 1.5;
  stroke-dasharray: 4 3;
  stroke-linecap: round;
}
/* Unified ability-change layout: renders as a regular ability-block (icon
   + title + tag/summary row) with the swap panes inside the block body.
   Title shows `Old → New` when names differ. The block visually matches
   any other ability section on the page. */
.ability-change-block .ability-title {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  flex-wrap: wrap;
}
.ability-change-block .ability-title-old {
  opacity: 0.55;
}
.ability-change-block .ability-title-arrow {
  font-weight: 700;
  opacity: 0.7;
  padding: 0 1px;
}
.ability-change-block .ability-title-new {
  /* Inherits the normal title color/weight from .ability-title. */
}
.ability-change-summary-ul {
  margin-bottom: 6px;
}
.ability-change-block {
  position: relative;
}
/* Connector curve from the icon's bottom-center down-right toward the
   centre of the old pane — visual cue that the swap panes belong to
   the icon above. Sized to bridge the gap between the icon (col 1) and
   the old pane (column 1 of the unified-panes sub-grid in col 2). */
.ability-change-block > .ability-change-connector {
  position: absolute;
  /* Real position + size set by drawAbilityChangeConnectors() JS at load
     and on resize so the curve tracks live layout. Defaults below cover
     the whole block until JS fires; overflow:visible so any temporary
     pre-JS render isn't clipped. */
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
  overflow: visible;
}
.ability-change-block .ability-change.unified-panes {
  margin-top: 4px;
  align-items: center;
  position: relative;
  z-index: 1;
}
/* When one pane is meaningfully shorter than the other, vertically center
   its content against the taller pane instead of stretching empty space
   below it. `align-items: center` on the grid (above) achieves this by
   default; the shorter pane sizes to its content. */
.ability-change-block .ability-change.unified-panes > .ability-change-pane {
  align-self: center;
  padding: 0 4px;
}
/* When a pane carries an expandable formula table (scale_pill), TOP-anchor
   both panes. Otherwise `align-items: center` re-centers the shorter pane
   every time the <details> table toggles open — visibly shoving the old
   ability block down. Top-anchoring makes the table grow downward only. */
.ability-change-block .ability-change.unified-panes:has(.formula-table-wrap) {
  align-items: start;
}
.ability-change-block .ability-change.unified-panes:has(.formula-table-wrap) > .ability-change-pane {
  align-self: start;
}
/* Arrow also top-anchored when a formula table can expand — otherwise
   `align-self: center` re-aligns to the grid-row centre, which moves
   downward as the table grows, visibly drifting the arrow below the
   old pane's body. */
.ability-change-block .ability-change.unified-panes:has(.formula-table-wrap) > .ability-change-arrow {
  align-self: start;
  padding-top: 6px;
}
/* Single-pane "New ability" card (ability_change old=None): one full-width,
   top-anchored pane — no old side, no arrow. */
.ability-change.unified-panes.is-single-new {
  grid-template-columns: 1fr;
  align-items: start;
}
.ability-change.unified-panes.is-single-new > .ability-change-pane {
  align-self: start;
  justify-self: stretch;
  padding: 0 4px;
}
/* Old-icon underlay: when an ability_change unified block has a different
   icon for old vs new, both icons fully overlap inside the same 48px wrap.
   New is on top by default. Hovering the wrap shuffles them — old comes
   to front, new drops behind — solitaire-style. Innate-marker on each
   side follows its own icon's z-order. */
.ability-icon-wrap.has-old-underlay {
  position: relative;
  cursor: pointer;
  perspective: 600px;
}
.ability-icon-wrap.has-old-underlay > .ability-icon-old-underlay,
.ability-icon-wrap.has-old-underlay > .ability-icon-img {
  position: absolute;
  top: 0;
  left: 0;
  width: 48px;
  height: 48px;
  border-radius: 6px;
  object-fit: cover;
  /* Solid backing so transparent PNGs (e.g. generic innate_icon.png used
     as an underlay when the old ability had no real icon) still render as
     a proper square with border instead of looking see-through. */
  background: #0e1116;
  box-shadow: 0 0 0 1px rgba(210, 168, 255, 0.18), 0 2px 6px rgba(0, 0, 0, 0.4);
  display: block;
  transition: transform 0.32s cubic-bezier(0.42, 0, 0.16, 1.1),
              opacity 0.22s ease,
              z-index 0s linear 0.16s;
  transform-origin: 50% 50%;
  backface-visibility: hidden;
  will-change: transform;
}
.ability-icon-wrap.has-old-underlay > .ability-icon-old-underlay {
  z-index: 1;
}
.ability-icon-wrap.has-old-underlay > .ability-icon-img {
  z-index: 2;
}
/* Solitaire shuffle on hover: front icon glides out to the right and tilts
   slightly while the back icon emerges from the left, tilts the opposite
   way, and lands on top. */
.ability-icon-wrap.has-old-underlay:hover > .ability-icon-img {
  transform: translateX(7px) rotate(6deg);
  z-index: 1;
}
.ability-icon-wrap.has-old-underlay:hover > .ability-icon-old-underlay {
  transform: translateX(-3px) rotate(-3deg);
  z-index: 3;
}
/* Innate-marker swap: new's marker visible by default, hidden on hover;
   old's marker hidden by default, visible on hover. Z-index pinned above
   both icons so the golden marker never hides behind a card. */
.ability-block > .ability-icon-wrap.has-old-underlay > .innate-marker {
  z-index: 5;
  transition: opacity 0.18s ease;
}
.ability-block > .ability-icon-wrap.has-old-underlay > .innate-marker-old {
  opacity: 0;
  pointer-events: none;
}
.ability-block > .ability-icon-wrap.has-old-underlay:hover > .innate-marker-new {
  opacity: 0;
}
.ability-block > .ability-icon-wrap.has-old-underlay:hover > .innate-marker-old {
  opacity: 1;
}
/* In-place rework (same name + icon on both sides): right pane skips the
   header and only shows the description body — so the box should also be
   visually smaller and centered (vertically + horizontally) relative to
   the full-height left card, not stretched to match it. */
.ability-change.is-in-place {
  grid-template-columns: 1fr auto minmax(0, 1fr);
}
.ability-change.is-in-place .ability-change-new {
  align-self: center;
  justify-self: center;
  width: fit-content;
  max-width: 100%;
  justify-content: center;
  padding: 8px 14px;
}
/* In-place rework where NEW outgrew OLD: centering puts new's first row
   at the Y-level of old's HEADER (since new pane has no head). Switch to
   top-anchored, full-width layout for the new pane, with padding-top
   matching the old head's vertical footprint (47px head + ~8px gap) so
   the new body's first row top-aligns with old's body first row. */
.ability-change.is-in-place.is-new-taller .ability-change-new {
  align-self: start;
  justify-self: stretch;
  width: auto;
  max-width: none;
  justify-content: flex-start;
  padding: 8px 14px;
}
/* Asymmetric content: the pane with fewer description rows shrinks and
   centers in its column, instead of stretching to mirror the taller pane
   and leaving visible empty space below its text. */
.ability-change.compact-old .ability-change-old,
.ability-change.compact-new .ability-change-new {
  align-self: center;
  justify-self: center;
  width: fit-content;
  max-width: 100%;
  padding: 8px 14px;
}
/* Aghanim's Scepter upgrade row inside an ability-change pane — same blue
   gradient stripe + scepter glyph as the canonical `ul.changes li.aghanim-
   scepter` rows, but stretched edge-to-edge of the pane (no tag column to
   skip past inside a swap card). */
.ability-change-row.aghanim-scepter,
.ability-change-row.aghanim-shard {
  position: relative;
  /* Stripe is narrower than its `ul.changes li.aghanim-*` cousin (no tag
     column to skip), so the fade starts earlier — solid head, decays by
     30% across, fully transparent before the right edge. */
  background: linear-gradient(90deg,
    rgba(72, 148, 255, 0.26) 0,
    rgba(72, 148, 255, 0.18) 30%,
    rgba(72, 148, 255, 0.06) 70%,
    rgba(72, 148, 255, 0) 100%);
  border-radius: 3px;
  padding: 4px 8px 4px 6px;
}
.ability-change-row.aghanim-scepter .aghanim-marker,
.ability-change-row.aghanim-shard .aghanim-marker {
  margin-left: 0;
  margin-right: 6px;
  vertical-align: -3px;
}
/* Formula table inside a pane: compact + horizontally scrollable when it
   overflows the pane width. */
.ability-change-body .formula-table-wrap {
  margin-top: 6px;
}
/* Formula tables inside ability_change panes show only 7 key levels
   (L1/5/10/15/20/25/30) — columns 3-5, 7-10, 12-15 are the intermediate
   L2-L4, L6-L9, L11-L14 columns (col 1 = label, col 2 = L1, col 6 = L5…). */
.ability-change-body .formula-table {
  font-size: 10.5px;
  margin: 0;
  width: 100%;
  table-layout: fixed;
  font-variant-numeric: tabular-nums;
}
.ability-change-body .formula-table th:is(:nth-child(3),:nth-child(4),:nth-child(5),:nth-child(7),:nth-child(8),:nth-child(9),:nth-child(10),:nth-child(12),:nth-child(13),:nth-child(14),:nth-child(15)),
.ability-change-body .formula-table td:is(:nth-child(3),:nth-child(4),:nth-child(5),:nth-child(7),:nth-child(8),:nth-child(9),:nth-child(10),:nth-child(12),:nth-child(13),:nth-child(14),:nth-child(15)) {
  display: none;
}
.ability-change-body .formula-table th,
.ability-change-body .formula-table td {
  padding: 2px 2px;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Row labels (first column: "old" / "new" / "Δ %" / "value") still need
   room for the text — force them just-wide-enough so the level cells share
   the rest. 44px fits "value" without ellipsis truncation. Centered to
   match the level cells' centered alignment (was right-aligned, which
   looked off-axis next to the centered numbers). */
.ability-change-body .formula-table th:first-child,
.ability-change-body .formula-table td:first-child {
  width: 44px;
  text-align: center;
  overflow: visible;
}
@media (max-width: 720px) {
  .ability-change { grid-template-columns: 1fr; row-gap: 6px; }
  .ability-change-arrow { transform: rotate(90deg); }
}
/* When discrete change rows follow an ability_change card (numerical
   tweaks that complement the prose swap), give the ul.changes the same
   left indent the ability-block's content column would have — otherwise
   its tag column sits flush with the entity-block edge, visually
   detached from the swap card above. Higher specificity than the generic
   `.entity-block > ul.changes { padding-left: 0 }` rule. */
.entity-block > .ability-change + ul.changes {
  padding-left: 60px;
}
/* (is-changed visual blocks inherit the +14px indent from the generic
   .entity-block > * rules above — no special override needed.) */
/* (removed: is-changed ul.changes padding-left: 0 — that made the tag column
   shift 40px left of the default ul padding, misaligning is-changed item tags
   relative to regular items below them. Now is-changed inherits default
   ul.changes padding so tags line up vertically across the whole page.) */

/* Two-pane property diff (old grant set → new grant set).
   ONE grid for both panes so row N on the left lines up with row N on the
   right. Pane "boxes" are drawn by two background divs that span cols 1-3
   and 5-7 respectively, sitting behind content via z-index. */
/* Two equal-width panes joined by a centred bold arrow. Each pane is a
   subgrid for ROWS, so row N on the left vertically aligns with row N on
   the right — no matter which side has taller cells. Each pane has its own
   3-col grid inside (tag | text 1fr | badge auto-pinned right). */
.properties-change {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  column-gap: 12px;
  margin: 6px 0 4px;
}
.properties-change .properties-pane {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: subgrid;
  grid-row: 1 / -1;
  column-gap: 8px;
  row-gap: 4px;
  align-items: center;
  padding: 8px 14px;
  background: rgba(139, 148, 158, 0.04);
  border: 1px solid rgba(139, 148, 158, 0.18);
  border-radius: 6px;
}
.properties-change .pane-old { grid-column: 1; }
.properties-change .pane-new { grid-column: 3; }
.properties-change .properties-arrow {
  grid-column: 2;
  grid-row: 1 / -1;
}
/* Single-pane mode: only one side changed → render just that pane in its
   column position from the 2-pane layout (left = col 1, right = col 3) so
   the visible pane lands in the same horizontal area as it would when both
   panes are present. */
.properties-change.new-only,
.properties-change.old-only {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
}
.properties-change.new-only .pane-new { grid-column: 3; }
.properties-change.old-only .pane-old { grid-column: 1; }
.properties-change .property-text {
  min-width: 0;
  color: #c9d1d9;
  font-size: 13.5px;
  line-height: 1.5;
}
/* ABILITY ORDER FLOW (Spirit Bear 7.40): bare square ability icons in one
   horizontal row, old group → arrow → new group. Hover an icon for its name
   (data-tooltip popup shared with .enchant-chip). Spans the full li width
   below the row text (extras grid-column 1/-1). */
.ability-order-flow {
  /* Mirror .properties-change geometry (1fr | arrow | 1fr, 12px gap) so the
     two icon panes line up flush with the stat-comparison panes above. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  column-gap: 12px;
  align-items: center;
  margin-top: 8px;
  /* full li width — same lane as correction-note/formula-table extras,
     otherwise the row auto-places into the badge column (right edge)
     and shoves the left tag off its row */
  grid-column: 1 / -1;
}
.ability-order-flow .ao-arrow {
  justify-self: center;
}
/* Each order group (old set / new set) sits in its own pane box —
   same skin as .properties-pane. */
.ability-order-flow .ao-group {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 7px;
  padding: 8px 10px;
  background: rgba(139, 148, 158, 0.04);
  border: 1px solid rgba(139, 148, 158, 0.18);
  border-radius: 6px;
}
.ability-order-flow .ao-icon {
  position: relative;
  width: 48px;
  height: 48px;
  flex: 0 0 auto;
  border: 1px solid rgba(139, 148, 158, 0.4);
  border-radius: 4px;
  background: #11161d;
  overflow: visible;          /* tooltip must escape the box */
}
.ability-order-flow .ao-icon img {
  width: 100%;
  height: 100%;
  border-radius: 3px;
  display: block;
  object-fit: cover;
}
/* New-in-this-patch ability — purple NEW ring */
.ability-order-flow .ao-icon.is-new {
  border-color: rgba(184, 127, 255, 0.85);
  box-shadow: 0 0 6px rgba(184, 127, 255, 0.35);
}
/* Hidden/innate slot — dimmed icon, tooltip stays full-strength */
.ability-order-flow .ao-icon.is-dim img {
  opacity: 0.5;
  filter: grayscale(0.5);
}
.ability-order-flow .ao-arrow {
  color: #e3c46a;
  font-size: 18px;
  margin: 0 4px;
  flex: 0 0 auto;
}
/* Tooltip popup — same recipe as .enchant-chip[data-tooltip] */
.ao-icon[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  background: rgba(13, 17, 23, 0.97);
  color: #c9d1d9;
  font-size: 12px;
  font-weight: 400;
  padding: 6px 10px;
  border: 1px solid rgba(139, 148, 158, 0.35);
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  z-index: 100;
  transition: opacity 0.12s 0.05s;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
}
.ao-icon[data-tooltip]:hover::after {
  opacity: 1;
}
.properties-change .property-badge {
  justify-self: end;
  white-space: nowrap;
  /* Pane has padding-right: 14px. Pull the badge cell 12px outside that
     padding so the b() % bands sit only 2px from the pane's right border. */
  margin-right: -12px;
}
.properties-change .property-row-empty {
  min-height: 1.5em;
}
.properties-change .property-extra {
  margin-top: -2px;             /* sit close to the row above; subgrid row-gap
                                   already provides a small natural separation */
}

/* SUBGROUPS — same colour as body text / ability titles, not blue.
   Tormentor / Roshan / Wisdom Shrines / Health Restoration / Lifesteal /
   Miscellaneous / Talents etc. all read as section dividers, not links. */
h4.subgroup {
  color: #c9d1d9;
  font-size: 14px;
  font-weight: 700;
  margin: 16px 0 4px 0;      /* aligned with entity-block left edge */
  text-transform: uppercase;
  letter-spacing: 0.6px;
  border-bottom: 1px solid;
  border-image: linear-gradient(to right, #21262d 0%, #21262d 61%, transparent 71%) 1;
  padding-bottom: 4px;
}
/* ABILITY BLOCK — icon (left, full-height) + title (top-right) + changes (below title) */
.ability-block {
  display: grid;
  grid-template-columns: 48px 1fr;
  column-gap: 12px;
  align-items: start;
  margin: 12px 0 8px 0;
}
.ability-block > .ability-icon-wrap {
  grid-column: 1;
  grid-row: 1 / span 99;
  position: relative;
  width: 48px;
  height: 48px;
  align-self: start;
}
.ability-block > .ability-icon-wrap > .ability-icon-img {
  width: 48px;
  height: 48px;
  border-radius: 6px;
  object-fit: cover;
  box-shadow: 0 0 0 1px rgba(210, 168, 255, 0.18), 0 2px 6px rgba(0, 0, 0, 0.4);
}
/* Facet block — same 48px geometry as ability(), but the icon-wrap renders
   the facet's gradient color + the generic facet icon (extracted from
   pak01_dir.vpk, white silhouette on transparent) overlaid on top.
   Hero h4 column is shared with .ability-title so facet names align with
   ability names. */
.ability-block.facet-block > .facet-icon-wrap {
  width: 48px;
  height: 48px;
  border-radius: 6px;
  background-size: cover;
  background-repeat: no-repeat;
  box-shadow: 0 0 0 1px rgba(210, 168, 255, 0.18), 0 2px 6px rgba(0, 0, 0, 0.4);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.facet-icon-overlay {
  width: 36px;
  height: 36px;
  object-fit: contain;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.55));
  opacity: 0.95;
}
/* Innate-icon overlay: bottom-center of the ability icon */
.ability-block > .ability-icon-wrap > .innate-marker {
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #0a0e13;
  padding: 1px;
  box-shadow: 0 0 0 1px rgba(220, 175, 95, 0.45);
}
.ability-block > .ability-title {
  grid-column: 2;
  margin: 0;
  padding: 0 0 4px 0;       /* no top padding so title-top aligns with icon-top */
  color: #c9d1d9;            /* same as body text — not coloured */
  font-size: 17px;
  font-weight: 600;
  line-height: 1;            /* tight line-height — text top = box top */
  align-self: start;
}
.ability-block > ul.changes {
  grid-column: 2;
  margin-top: 0;
}
.ability-block > ul.subnotes {
  grid-column: 2;
  /* margin-left: 76px (inherited from base ul.subnotes) shifts it under the
     row-text column inside ul.changes — same alignment as subnotes outside
     ability-block. Do NOT override to 0; that puts the ↳ under the tag. */
}
/* Talents-block: same .ability-block layout, but talents.svg gets translucent framing */
.ability-block.talents-block > .ability-icon-wrap > .ability-icon-img {
  background: rgba(121, 192, 255, 0.05);
  padding: 6px;
  box-shadow: 0 0 0 1px rgba(121, 192, 255, 0.18), 0 2px 6px rgba(0, 0, 0, 0.4);
}
/* Other-block: neutral grey framing for the inline-SVG sliders icon */
.ability-block.other-block > .ability-icon-wrap > .ability-icon-img {
  background: rgba(139, 148, 158, 0.05);
  padding: 6px;
  box-shadow: 0 0 0 1px rgba(139, 148, 158, 0.18), 0 2px 6px rgba(0, 0, 0, 0.4);
}
/* Formula tables INSIDE .ability-block — extend back to entity-block left edge,
   not just within content column (else they look right-aligned past the icon). */
.ability-block ul.changes li > .formula-table {
  margin-left: -60px;          /* counter ability-block icon column (48 + 12 gap) */
  width: calc(100% + 60px);
}
/* Raw <li> (no .row-text wrapper) inside ability-block — TWO offsets stack:
   76px to cancel the raw-li padding, plus 60px to clear the ability-block icon. */
.ability-block ul.changes li:not(:has(> .row-text)) > .formula-table,
.ability-block ul.changes li:not(:has(> .row-text)) > .correction-note {
  margin-left: -136px;
  width: calc(100% + 136px);
}

/* CHANGES LIST — grid layout: [tag] [text] [percentages] */
ul.changes {
  list-style: none;
  margin: 3px 0 3px 0;
  padding-left: 0;          /* per-context indent applied via .entity-block,
                               .ability-block etc. (14px to align with icon) */
  padding-right: 6px;       /* right-edge % bands sit 6px in from container
                               border so their right edge lines up with the
                               last cell of the .patch-dynamics row (which
                               itself uses margin-right:-8px against the
                               entity header's 14px padding → also 6px in). */
}
ul.changes li {
  display: grid;
  /* 2-column grid (text | badge). The text-tag is absolutely positioned over
     the top-left of the text area, with row-text using text-indent to push
     the first line past the tag. When the text wraps, subsequent lines flow
     full-width — reusing the empty space below the tag instead of leaving an
     awkward visual gap on the left. */
  grid-template-columns: 1fr auto;
  column-gap: 12px;
  row-gap: 0;
  align-items: baseline;
  padding: 1px 0;
  line-height: 1.5;
  color: #c9d1d9;
  position: relative;
}
/* Hover ruler: dashed line spanning from text toward the % so the eye can
   trace the row. Anchored at the bottom of the FIRST grid row (i.e. just
   under the change-text baseline), not at the bottom of the whole li —
   that way it stays put even when correction-notes/formula-tables span
   full-width below. */
ul.changes li:has(> .badge-group):hover::after {
  content: "";
  position: absolute;
  left: 76px;
  right: 0;
  top: calc(1px + 1.5em);     /* li padding-top + line-height of first row */
  border-bottom: 1px dashed rgba(139, 148, 158, 0.32);
  pointer-events: none;
}
/* Text-tag: absolutely positioned over the top-left of the row, taking it
   out of grid flow so the wrapped text of .row-text can flow under it.
   Fixed 64px width keeps every tag aligned to the same column visually. */
ul.changes li > .badge:first-child,
ul.changes li > .row-tag-empty {
  position: absolute;
  left: 0;
  top: 3px;
  width: 64px;
  min-width: 64px;
}
ul.changes li > .row-tag-empty {
  visibility: hidden;
  display: inline-block;
  padding: 3px 7px;
  font-size: 11px;
  line-height: 1;
  box-sizing: border-box;
  border: 1px solid transparent;
}
/* Description column. A float pseudo reserves the 76px tag area at the start
   of the first line; text wraps AROUND it, with line 2+ flowing under the
   reserved area. Cleaner than text-indent because inline-block triggers like
   .formula-trigger don't get artificially pushed past the indent column. */
ul.changes li > .row-text {
  grid-column: 1;
}
ul.changes li > .row-text::before {
  content: "";
  float: left;
  width: 76px;
  height: 1.4em;
}
/* Right column: numeric percentages — sits in the second grid column. */
ul.changes li > .badge-group {
  grid-column: 2;
  margin-left: 0;
}
/* extras (correction-note, formula-table, NOTE box) span all columns */
/* correction-note + formula-table span the full li width (under the tag
   column too) — for hero-stat rows there's a lot of empty space on the
   left, and stretching the note across uses it instead of leaving an
   awkward gap. The note's own padding-left (12px) keeps the text from
   running flush against the entity-block edge. */
ul.changes li > .correction-note,
ul.changes li > .formula-table {
  grid-column: 1 / -1;
}
/* Raw <li> without .row-text wrapper (rare special-case manual rows) — block layout
   with auto-generated left tag from data-tag attribute via ::before. */
ul.changes li:not(:has(> .row-text)) {
  display: block;
  padding-left: 76px;
  position: relative;
  min-height: 22px;
}
ul.changes li:not(:has(> .row-text))::before {
  /* matches .badge:first-child dimensions exactly (width: 64px) */
  position: absolute;
  left: 0;
  top: 2px;
  display: inline-block;
  padding: 3px 7px;
  border-radius: 2px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  line-height: 1;
  width: 64px;
  text-align: center;
  white-space: nowrap;
  box-sizing: border-box;
  font-variant-numeric: tabular-nums;
  /* default = nerf-text colors (same as .badge.nerf-text) */
  content: "";
  background: rgba(225, 90, 75, 0.10);
  color: #a86158;
  border: 1px solid rgba(225, 90, 75, 0.28);
}
/* Extras inside raw rows (formula-table, correction-note) — extend to left edge */
ul.changes li:not(:has(> .row-text)) > .formula-table,
ul.changes li:not(:has(> .row-text)) > .correction-note {
  margin-left: -76px;
  width: calc(100% + 76px);
}
/* Tag content (most-specific wins by source order; rework beats buff/nerf for hybrid rows) */
ul.changes li:not(:has(> .row-text))[data-tag*="buff"]::before  { content: "BUFF"; background: rgba(85,205,115,0.10); color: #6da375; border-color: rgba(85,205,115,0.28); }
ul.changes li:not(:has(> .row-text))[data-tag*="nerf"]::before  { content: "NERF"; }
ul.changes li:not(:has(> .row-text))[data-tag*="qol"]::before   { content: "QoL"; background: rgba(121,192,255,0.06); color: #87a3bf; border-color: rgba(121,192,255,0.20); }
ul.changes li:not(:has(> .row-text))[data-tag*="misc"]::before  { content: "MISC"; background: rgba(139,148,158,0.06); color: #6e7681; border-color: rgba(139,148,158,0.18); }
ul.changes li:not(:has(> .row-text))[data-tag*="new"]::before   { content: "NEW"; background: rgba(220,175,95,0.06); color: #9a7f4d; border-color: rgba(220,175,95,0.22); }
ul.changes li:not(:has(> .row-text))[data-tag*="del"]::before   { content: "DEL"; background: rgba(180,70,70,0.07); color: #a86060; border-color: rgba(180,70,70,0.25); }
ul.changes li:not(:has(> .row-text))[data-tag*="rework"]::before { content: "REWORK"; background: rgba(180,145,220,0.05); color: #7d6e93; border-color: rgba(180,145,220,0.15); }
/* Hide redundant inline tag spans (since ::before shows them) */
ul.changes li:not(:has(> .row-text)) > .badge.rework,
ul.changes li:not(:has(> .row-text)) > .badge.misc,
ul.changes li:not(:has(> .row-text)) > .badge.qol,
ul.changes li:not(:has(> .row-text)) > .badge.new,
ul.changes li:not(:has(> .row-text)) > .badge.del,
ul.changes li:not(:has(> .row-text)) > .badge.buff-text,
ul.changes li:not(:has(> .row-text)) > .badge.nerf-text {
  display: none;
}

ul.subnotes {
  list-style: none;
  margin: -2px 0 4px 0;        /* flush with parent left edge (was 76px under
                                  text column — but pairs better with the
                                  full-width correction-note that lives just
                                  above it in the same hero row). */
}
ul.subnotes li {
  color: #8b949e;
  font-size: 13.5px;
  padding: 1px 0;
  line-height: 1.4;
}
ul.subnotes li::before { content: "↳ "; color: #6e7681; }
/* When the subnote is collapsible, drop the ↳ arrow — the rotating ▶ chevron
   on <summary> already serves as the indicator, putting both side-by-side
   reads as visual noise. */
ul.subnotes li:has(> .subnote-collapse)::before { content: ""; }

/* Collapsible subnote — long bullet lists hidden behind a "▶ summary (N)"
   click target. Native <details>/<summary> preserves keyboard and SR support. */
.subnote-collapse > summary {
  cursor: pointer;
  list-style: none;
  display: inline;
  user-select: none;
}
.subnote-collapse > summary::-webkit-details-marker { display: none; }
.subnote-collapse > summary::before {
  content: "▶";
  display: inline-block;
  width: 10px;
  margin-right: 4px;
  font-size: 9px;
  color: #6e7681;
  transition: transform 0.15s;
}
.subnote-collapse[open] > summary::before { transform: rotate(90deg); }
.subnote-collapse > summary:hover { color: #c9d1d9; }
.subnote-count {
  color: #6e7681;
  font-weight: 500;
  font-size: 12px;
}
ul.subnote-items {
  margin: 4px 0 4px 18px;
  list-style: none;
}
ul.subnote-items > li {
  padding: 1px 0;
  line-height: 1.45;
  color: #8b949e;
  font-size: 13px;
}
ul.subnote-items > li::before {
  content: "•  ";
  color: #6e7681;
}

/* Block-level 'Show list (N)' info row that drops to a new line below the
   sentence inside an ability box. Matches the visual language of
   subnote-collapse (rotating triangle arrow) but lives inside the box. */
.show-list-inline {
  display: block;
  grid-column: 1 / -1;        /* span full li width inside the row grid */
  margin-top: 0;
}
.show-list-inline > summary {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  list-style: none;
  cursor: pointer;
  color: #8b949e;
  font-size: 12.5px;
  user-select: none;
}
.show-list-inline > summary::-webkit-details-marker { display: none; }
/* ↳ corner arrow (matches the subnote-style indent marker) followed by the
   rotating ▸ chevron. Both rendered via a single ::before so they animate
   together. */
.show-list-inline > summary::before {
  content: '\21B3';           /* ↳ */
  display: inline-block;
  color: #6e7681;
  font-size: 12.5px;
  margin-right: 2px;
}
.show-list-inline > summary > .show-list-chevron {
  display: inline-block;
  color: #6e7681;
  font-size: 11px;
  transition: transform 0.15s ease;
}
.show-list-inline[open] > summary > .show-list-chevron { transform: rotate(90deg); }
.show-list-inline > summary:hover { color: #c9d1d9; }
.show-list-inline[open] > summary { color: #c9d1d9; }
.show-list-inline > .show-list-body {
  display: block;
  margin: 6px 0 2px 6px;
  padding: 0;
}
.show-list-inline > .show-list-body > .show-list-item {
  display: block;
  padding: 1px 0;
  color: #8b949e;
  font-size: 13px;
  line-height: 1.45;
}
.show-list-inline > .show-list-body > .show-list-item::before {
  content: "•  ";
  color: #6e7681;
}

/* One-liner ↳ note hanging off a li (passed via extra= on li()). Drops to a
   new visual line below the row text. Spans the FULL li width (col 1 / -1)
   like correction-note, formula-table, subnote — using the tag column's
   empty space below the tag rather than leaving it blank. The ↳ glyph
   provides the visual indent so the note still reads as a child of the row. */
.inline-note {
  display: block;
  grid-column: 1 / -1;
  margin-top: 4px;
  font-size: 12.5px;
  color: #8b949e;
  line-height: 1.45;
  /* ↳ pseudo is absolute-positioned so the note's body content (including
     wrapping lines and <br>-separated entries) all share the same left
     indent — first line isn't offset by the arrow's inline width. */
  position: relative;
  padding-left: 16px;
}
.inline-note::before {
  content: "↳";
  position: absolute;
  left: 0;
  top: 1px;
  color: #6e7681;
}
/* ---- Section intro (section_intro): descriptive lead-in under a header ----
   Frames a category in prose; deliberately NOT a tagged change row (no badge,
   no bullet). Muted + italic + a thin gold-tinted rail so it reads as context,
   visually distinct from the BUFF/NERF/MISC rows that follow. */
.section-intro {
  margin: 2px 0 14px;
  padding: 8px 14px;
  font-size: 13px;
  font-style: italic;
  line-height: 1.5;
  color: #9aa4b1;
  border-left: 2px solid rgba(227, 196, 106, 0.45);
  background: rgba(227, 196, 106, 0.05);
  border-radius: 0 5px 5px 0;
}
/* ---- Game-formula change (formula_change): old → new, ability_change-style ---- */
.formula-change { margin: 10px 0 8px; }
.formula-change-title {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 9px;
  font-size: 13.5px;
  font-weight: 600;
  color: #cdd6e3;
}
/* Number input — reader plugs in their own value; scripts.js recomputes rows. */
.formula-input-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-bottom: 11px;
  font-size: 12px;
}
.formula-input-label { color: #9aa7b8; }   /* "Set" = normal text */
.formula-input-label .fx-vname {            /* the variable name keeps the var colour */
  font-family: ui-monospace, Menlo, Consolas, monospace;
  color: #9fc1e3;
}
.formula-input {
  width: 150px;
  padding: 4px 9px;
  background: rgba(13, 17, 23, 0.6);
  border: 1px solid rgba(120, 140, 170, 0.30);
  border-radius: 5px;
  color: #e6edf3;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  appearance: textfield;
  -moz-appearance: textfield;    /* no number spinner arrows */
}
.formula-input::-webkit-outer-spin-button,
.formula-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.formula-input::placeholder { color: #5e6b7d; }
.formula-input:focus { outline: none; border-color: rgba(160, 180, 210, 0.6); }
.formula-change-panes {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  column-gap: 12px;
  align-items: stretch;
}
.formula-pane {
  display: flex;
  flex-direction: column;
  align-items: center;           /* formula + table centred in the block */
  gap: 12px;
  min-width: 0;
  padding: 12px 14px;
  background: rgba(139, 148, 158, 0.04);
  border: 1px solid rgba(139, 148, 158, 0.18);
  border-radius: 6px;
}
.formula-expr {
  text-align: center;
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.6;
  color: #8b98a8;               /* parentheses / operators — muted */
  overflow-wrap: anywhere;
}
.formula-expr .f-var { color: #9fc1e3; }              /* variables  */
.formula-expr .f-num { color: #e0b87a; font-weight: 600; }  /* numbers */
/* Worked-example table — faint grid lines, offset below the formula. */
.formula-ex {
  border-collapse: collapse;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
.formula-ex th,
.formula-ex td {
  border: 1px solid rgba(120, 140, 170, 0.14);
  padding: 3px 12px;
}
.formula-ex th {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: #7e8a9b;
  text-align: center;
}
.formula-ex .fx-k    { color: #c2ccd9; text-align: center; }
.formula-ex .fx-gold { color: #e0b87a; font-weight: 600; text-align: right; }
.formula-ex .fx-pct  { text-align: right; }

/* ---- Captains Mode draft board (cm_draft) ----
   The WHOLE draft as a VERTICAL in-game-style pick/ban screen: step numbers run
   down the centre, the acting team's slot on the LEFT (Team 1) or RIGHT (Team 2).
   The Old draft and the New draft sit side by side. No colours — slot SIZE marks
   ban (small) vs pick (large). Compare the two boards directly. */
/* No big outer box — just a margin wrapper. Each Old/New board is its own light
   block instead (more concise). */
.cm-draft { margin: 12px 0 8px; }
.cm-scroll { overflow-x: auto; padding-bottom: 3px; }
/* Old + New boards sit close together with the arrow centred between — like the
   ability_change rework panes. */
.cm-boards {
  display: flex;
  align-items: stretch;            /* Old & New blocks share one (taller) height */
  justify-content: center;
  gap: 12px;
}
/* Each draft (Old / New) in its own block — same look as the ability_change
   rework panes (.ability-change-pane). */
.cm-board {
  padding: 10px 12px;
  border: 1px solid rgba(139, 148, 158, 0.18);
  border-radius: 6px;
  background: rgba(139, 148, 158, 0.04);
}
/* Arrow between the Old and New blocks — same glyph/style as .ability-change-arrow. */
.cm-arrow {
  flex: none;
  align-self: center;
  font-size: 22px;
  font-weight: 700;
  color: #c9d1d9;
  opacity: 0.85;
  padding: 0 2px;
}
.cm-vgrid {
  --cm-lane: 68px;
  --cm-num: 18px;
  --cm-row: 18px;
  display: grid;
  grid-template-columns: var(--cm-lane) var(--cm-num) var(--cm-lane);
  grid-template-rows: auto;        /* header row natural height */
  grid-auto-rows: var(--cm-row);   /* uniform row height → identical ban cells */
  column-gap: 22px;                /* wide gap so the long number→slot dashes fit */
  row-gap: 7px;                    /* more breathing room between slots */
  align-items: center;
}
.cm-vhead {
  text-align: center;
  white-space: nowrap;           /* "Second pick" stays on one line */
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: uppercase;
  color: #aeb8c6;
  padding-bottom: 5px;
  border-bottom: 1px solid rgba(120, 140, 170, 0.22);
}
.cm-vnum {
  position: relative;
  text-align: center;
  font-size: 11px;
  font-weight: 600;
  color: #7e8a9b;
  font-variant-numeric: tabular-nums;
}
/* Thin connector from the number to its slot — starts right at the number (the
   narrow num column keeps it almost touching the digits) and reaches the slot. */
.cm-vnum.cm-to-left::before,
.cm-vnum.cm-to-right::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 20px;
  height: 1px;
  background: rgba(150, 160, 175, 0.4);
}
.cm-vnum.cm-to-left::before  { right: 100%; }
.cm-vnum.cm-to-right::after  { left: 100%; }
/* Neutral slots — dark fill + faint border, no team colour. Bans = narrow boxes
   (1 row) hugging the centre; picks fill the lane and span a 2-row band so the
   left & right pick of a pair sit FACING each other (like the in-game board). */
.cm-token {
  position: relative;
  align-self: stretch;            /* fill the row(s) the slot spans */
  border-radius: 4px;
  border: 1.5px solid rgba(140, 152, 170, 0.42);
  background: rgba(13, 17, 23, 0.85);
}
.cm-token.ban  { width: 42px; }
.cm-token.ban.cm-left  { justify-self: end; }    /* left lane → inner (right) edge by the number */
.cm-token.ban.cm-right { justify-self: start; }  /* right lane → inner (left) edge by the number */
/* Faint bordeaux crosshair in the centre of ban slots (a "banned" mark). Barely
   visible by design. */
.cm-token.ban::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 9px;
  height: 9px;
  transform: translate(-50%, -50%);
  background-image:
    linear-gradient(45deg,  transparent 42%, rgba(150, 46, 58, 0.5) 42%, rgba(150, 46, 58, 0.5) 58%, transparent 58%),
    linear-gradient(-45deg, transparent 42%, rgba(150, 46, 58, 0.5) 42%, rgba(150, 46, 58, 0.5) 58%, transparent 58%);
}
.cm-token.pick {
  border-color: rgba(160, 172, 190, 0.55);   /* picks span 2 rows → naturally taller than bans */
  background: rgba(22, 28, 37, 0.92);
}
.cm-sw.changed-sw {
  width: 12px;
  height: 12px;
  border-color: rgba(227, 196, 106, 0.5);
  box-shadow: 0 0 0 1px rgba(227, 196, 106, 0.16);
}

/* Tarot-card sub-list entries (Oracle Diviner's Deck): small icon + bold card
   name + effect. Each entry is its own inline-flex row so wrapping reads as
   a checklist instead of a wall of text. */
.tarot-card {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.tarot-card-icon {
  width: 22px;
  height: 22px;
  border-radius: 3px;
  vertical-align: middle;
  flex-shrink: 0;
}
/* (ability-row li no longer needs a special override — base rule already
   spans col 1 / -1, which is the full width in both layouts.) */

/* BADGES — flat rectangular tag boxes */
.badge {
  display: inline-block;
  padding: 3px 7px;
  border-radius: 2px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  line-height: 1;
  vertical-align: middle;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  min-width: 56px;
  text-align: center;
  box-sizing: border-box;
}
/* Facet pill — replicates the dota2.com patch-page facet header:
     - Background: 2-stop linear-gradient(to right, light, dark) in the
       facet's themed color (injected inline from FACETS table).
     - Text placement: right-aligned with extra LEFT padding, so the
       facet name lands on the DARK end of the gradient where white text
       reads cleanly even when the light end is bright (Yellow1, etc).
   No overlay needed — Valve's gradient already does the contrast work. */
.badge.facet-badge {
  position: relative;
  display: inline-block;
  padding: 3px 7px 3px 14px;     /* more left pad pushes text toward dark side */
  border-radius: 2px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  line-height: 1;
  vertical-align: middle;
  white-space: nowrap;
  min-width: 0;
  text-align: right;
  color: #ffffff;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.6);
  /* Transparent 1px border keeps total height (19px) matching the
     adjacent tag-badges which carry their own 1px colored border.
     `background-origin: border-box` makes the gradient span the FULL
     box including the border area — without this default would be
     `padding-box`, and the gradient would end 1px short of the right
     edge, exposing a thin strip of the LIGHT (left) end of the gradient
     via the default `background-repeat: repeat` wrap. */
  border: 1px solid rgba(255, 255, 255, 0.08);
  background-origin: border-box;
  background-repeat: no-repeat;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
  margin-right: 4px;
  box-sizing: border-box;
  /* Nudge up to share horizontal line of tag-badges. row-text inline
     baseline vs li-grid centering yields a 2px offset otherwise. */
  top: -2px;
}
/* When inside .badge-group → strip the box, become plain colored text.
   `display: inline` ensures baseline-alignment with surrounding text (was inline-flex
   which sat below the text baseline in raw rows like Bloodstone wrong-line). */
.badge-group {
  display: inline;
  vertical-align: baseline;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 13px;
}
/* Inside a li's right grid cell, the badge-group becomes a grid item (column 2).
   Keep it as an inline-block so its content (start/end pairs, % chips) doesn't
   wrap across multiple lines and pad the row height. */
ul.changes li > .badge-group {
  display: inline-block;
  white-space: nowrap;
}
.badge-group .badge {
  background: none !important;
  border: none !important;
  padding: 0 !important;
  text-shadow: none !important;
  margin: 0;
  min-width: 0;
  text-transform: none;
  letter-spacing: 0;
  font-size: inherit;
  display: inline;            /* override .badge's inline-block — proper baseline align */
  vertical-align: baseline;   /* override .badge's middle */
}
/* Comma separator between multi-level badges */
.badge-group .badge:not(:last-child)::after {
  content: ", ";
  color: #6e7681;
  font-weight: 400;
}
/* Slash separator for paired dimensions (e.g. daytime / nighttime). */
.badge-group.slash-sep .badge:not(:last-child)::after { content: " / "; }
/* Extra leading space before 0% (neutral) — without +/- prefix it visually crowds the comma */
.badge-group .badge.neutral:not(:first-child) {
  margin-left: 0.25em;
}

/* NEUTRAL & TEXT TAGS */
.badge.neutral {
  background: rgba(139, 148, 158, 0.06);
  color: #6e7681;
  border: 1px solid rgba(139, 148, 158, 0.18);
}
.badge.rework {
  background: rgba(180, 145, 220, 0.05);
  color: #7d6e93;
  border: 1px solid rgba(180, 145, 220, 0.15);
}
.badge.misc {
  background: rgba(139, 148, 158, 0.06);
  color: #6e7681;
  border: 1px solid rgba(139, 148, 158, 0.18);
}
.badge.qol {
  background: rgba(121, 192, 255, 0.06);
  color: #87a3bf;
  border: 1px solid rgba(121, 192, 255, 0.20);
}
.badge.new {
  background: rgba(220, 175, 95, 0.06);
  color: #9a7f4d;
  border: 1px solid rgba(220, 175, 95, 0.22);
  font-weight: 700;
  letter-spacing: 0.5px;
}
.badge.del {
  background: rgba(180, 70, 70, 0.07);
  color: #a86060;
  border: 1px solid rgba(180, 70, 70, 0.25);
  font-weight: 700;
  letter-spacing: 0.5px;
}

/* BUFF GRADIENT (10 tiers, soft-saturated greens) */
.badge.buff1  { background: rgba(120, 215, 145, 0.10); color: #b2d8b8; border: 1px solid rgba(120, 215, 145, 0.26); }
.badge.buff2  { background: rgba(115, 215, 140, 0.13); color: #b4dab6; border: 1px solid rgba(115, 215, 140, 0.32); }
.badge.buff3  { background: rgba(110, 215, 135, 0.17); color: #b2dab2; border: 1px solid rgba(110, 215, 135, 0.38); }
.badge.buff4  { background: rgba(105, 215, 130, 0.22); color: #aedaa8; border: 1px solid rgba(105, 215, 130, 0.44); }
.badge.buff5  { background: rgba(95, 210, 125, 0.27);  color: #a8d6a0; border: 1px solid rgba(95, 210, 125, 0.50); }
.badge.buff6  { background: rgba(85, 205, 115, 0.32);  color: #a4d496; border: 1px solid rgba(85, 205, 115, 0.55); }
.badge.buff7  { background: rgba(75, 200, 105, 0.36);  color: #9cd28a; border: 1px solid rgba(75, 200, 105, 0.60); }
.badge.buff8  { background: rgba(65, 195, 95, 0.40);   color: #94d07c; border: 1px solid rgba(65, 195, 95, 0.65); }
.badge.buff9  { background: rgba(55, 190, 85, 0.45);   color: #88cc6e; border: 1px solid rgba(55, 190, 85, 0.70); }
.badge.buff10 { background: rgba(50, 175, 80, 0.34);   color: #b8e2a4; border: 1px solid rgba(50, 175, 80, 0.55); }

/* NERF GRADIENT (10 tiers, soft-saturated reds) */
.badge.nerf1  { background: rgba(230, 130, 120, 0.10); color: #d8b0ac; border: 1px solid rgba(230, 130, 120, 0.24); }
.badge.nerf2  { background: rgba(228, 122, 110, 0.13); color: #dcaca8; border: 1px solid rgba(228, 122, 110, 0.30); }
.badge.nerf3  { background: rgba(225, 115, 100, 0.16); color: #dca8a0; border: 1px solid rgba(225, 115, 100, 0.36); }
.badge.nerf4  { background: rgba(225, 108, 92, 0.20);  color: #dca298; border: 1px solid rgba(225, 108, 92, 0.42); }
.badge.nerf5  { background: rgba(225, 100, 85, 0.25);  color: #de9c8e; border: 1px solid rgba(225, 100, 85, 0.50); }
.badge.nerf6  { background: rgba(225, 90, 75, 0.30);   color: #e09484; border: 1px solid rgba(225, 90, 75, 0.56); }
.badge.nerf7  { background: rgba(225, 80, 65, 0.34);   color: #e08c78; border: 1px solid rgba(225, 80, 65, 0.62); }
.badge.nerf8  { background: rgba(225, 70, 55, 0.38);   color: #e08470; border: 1px solid rgba(225, 70, 55, 0.68); }
.badge.nerf9  { background: rgba(225, 60, 45, 0.42);   color: #e07c66; border: 1px solid rgba(225, 60, 45, 0.74); }
.badge.nerf10 { background: rgba(220, 70, 55, 0.32);   color: #f1bcb0; border: 1px solid rgba(220, 70, 55, 0.55); }

/* Make digits readable on saturated backgrounds */
.badge.buff1, .badge.buff2, .badge.buff3, .badge.buff4, .badge.buff5,
.badge.buff6, .badge.buff7, .badge.buff8, .badge.buff9, .badge.buff10,
.badge.nerf1, .badge.nerf2, .badge.nerf3, .badge.nerf4, .badge.nerf5,
.badge.nerf6, .badge.nerf7, .badge.nerf8, .badge.nerf9, .badge.nerf10 {
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
}

/* Text-tag versions of BUFF/NERF (used by t() for non-numeric changes; pick mid tier) */
.badge.buff-text { background: rgba(85, 205, 115, 0.10); color: #6da375; border: 1px solid rgba(85, 205, 115, 0.28); }
.badge.nerf-text { background: rgba(225, 90, 75, 0.10);  color: #a86158; border: 1px solid rgba(225, 90, 75, 0.28); }

/* TYPO NOTE */
.typo-note {
  color: #f0c674;
  font-size: 11px;
  font-style: italic;
  margin-left: 6px;
}

/* WRONG-CHANGE LINE + CORRECTION NOTE — subtle gray */
.wrong-line {
  text-decoration: line-through;
  text-decoration-color: rgba(201, 209, 217, 0.5);
  text-decoration-thickness: 1px;
  opacity: 0.55;
}
.correction-note {
  display: block;
  margin: 6px 0 4px 0;
  padding: 6px 12px;
  background: rgba(139, 148, 158, 0.05);
  border-left: 2px solid rgba(139, 148, 158, 0.40);
  border-radius: 0 3px 3px 0;
  color: #7c8590;
  font-size: 12.5px;
  font-style: italic;
  line-height: 1.5;
}
.correction-note .badge {
  font-style: normal;
}
/* Bold values inside the note (prev value, prev patch) — same size as surrounding
   text, just bold; drop the inherited italic so they don't visually pop out. */
.correction-note b {
  font-size: inherit;
  font-style: normal;
  font-weight: 700;
}
/* "(N days ago)" annotation — fainter so it reads as a side-note. */
.correction-note .days-ago {
  font-style: normal;
  color: #6e7681;
  font-size: 11.5px;
  font-weight: 500;
}
/* Patch version chip-link inside correction-note ("changed in <ver>"). */
.patch-link {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dotted currentColor;
  transition: color 120ms ease, border-color 120ms ease;
}
/* Patch version reference inside a note_box (i)-popup — plain solid underline,
   not a link (popups aren't clickable). */
.patch-ref {
  text-decoration: underline;
  text-underline-offset: 2px;
}
.patch-link:hover,
.patch-link:focus-visible {
  color: #4894ff;
  border-bottom-color: #4894ff;
  outline: none;
}
/* Badge-group inside correction-note: float to the right edge of the note
   so it visually mirrors the row's main % (which sits in the right grid column). */
.correction-note > .badge-group {
  float: right;
  /* Negative right margin compensates the .correction-note padding-right (12px)
     so the badge sits flush with the right edge — aligns with the row's main %
     above/below the note. */
  margin: 0 -12px 0 12px;
  font-style: normal;
}
.correction-label {
  display: inline-block;
  font-style: normal;
  font-weight: 700;
  font-size: 10.5px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: #8b949e;
  margin-right: 4px;
}

/* IMAGE FALLBACK STYLE */
.entity-icon img[alt]:not([src*="//"]) { background: #21262d; }
img { max-width: 100%; }

/* BACK TO TOP — fixed bottom-right */
/* .back-to-top is styled together with .nav-back-arrow near the top of this file
   (shared pixel-chevron gold/leather buttons). */

/* AGHANIM'S SCEPTER / SHARD ROWS — light blue stripe (begins AT the icon, never
   covers the tag column) + small icon prefix sitting in the gap between tag
   and text columns (absolutely positioned so the row's text stays aligned
   with all other rows in the list). */
ul.changes li.aghanim-scepter,
ul.changes li.aghanim-shard {
  /* Two-layer stripe so it never sits behind the first-line tag yet still
     covers wrapped lines (which flow full-width under the tag column):
       1) FIRST line (~19px band): blue starts at 70px, clearing the tag.
       2) lines 2+ (from 22px down): blue starts at the left edge.
     For single-line rows layer 2 collapses to zero height (nothing below). */
  background:
    linear-gradient(90deg, transparent 0 70px, rgba(72, 148, 255, 0.20) 70px, rgba(72, 148, 255, 0.10) 60%, transparent 100%) no-repeat 0 3px / 100% 22px,
    linear-gradient(90deg, rgba(72, 148, 255, 0.20) 0, rgba(72, 148, 255, 0.10) 60%, transparent 100%) no-repeat 0 25px / 100% calc(100% - 28px);
  border-radius: 3px;
}
/* Aghanim marker — inline icon appended to the change-text inside .row-text,
   so it sits right after the last character of the change description and
   before the % badge column. */
.aghanim-marker {
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-left: 6px;
  vertical-align: -2px;
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
}
.aghanim-marker.scepter {
  background-image: url('icons/stats/aghs_scepter_icon.png');
}
.aghanim-marker.shard {
  background-image: url('icons/stats/aghs_shard_icon.png');
}

/* WRONG-WORD HIGHLIGHT — subtle, neutral marker (no strikethrough) */
.wrong-word {
  background: rgba(139, 148, 158, 0.08);
  color: #8b949e;
  padding: 0 5px;
  border-radius: 3px;
}

/* FORMULA TRIGGER — same neutral, squared-off look as .wrong-word, clickable */
.formula-trigger {
  display: inline-block;
  white-space: nowrap;
  padding: 0 5px;
  border-radius: 3px;
  background: rgba(139, 148, 158, 0.08);
  color: #8b949e;
  border: 1px solid rgba(139, 148, 158, 0.30);
  font-size: inherit;
  cursor: pointer;
  transition: all 0.14s;
  font-variant-numeric: tabular-nums;
  user-select: none;
}
/* Small "start" / "end" labels next to per-level formula badges
   (Δ% at L1 vs Δ% at L30) — visually subtle, sits inline after each
   badge to clarify which hero-level its number represents. */
.formula-endpoint-label {
  display: inline-block;
  font-size: 10px;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: #6e7681;
  margin: 0 4px 0 -2px;
  font-weight: 500;
}
/* Trailing 'end' label sits flush with the right edge of the badge column —
   the inherited 4px right margin would otherwise push the whole start/end
   pair leftward and visually misalign with adjacent single-badge rows. */
.badge-group .formula-endpoint-label:last-child {
  margin-right: 0;
}
.formula-trigger:hover {
  background: rgba(139, 148, 158, 0.16);
  border-color: rgba(139, 148, 158, 0.50);
  color: #c9d1d9;
}
.formula-trigger.active {
  background: rgba(139, 148, 158, 0.22);
  border-color: rgba(139, 148, 158, 0.60);
  color: #c9d1d9;
}
/* OLD FORMULA — just a subtle dotted underline so it stands out as "the old one" */
.formula-old {
  text-decoration: underline dotted rgba(139, 148, 158, 0.45);
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

/* FORMULA COMPARISON TABLE */
.formula-table {
  margin: 8px 0 6px;
  border-collapse: separate;
  border-spacing: 2px;
  font-size: 11px;
  width: 100%;
  /* fixed → every level column is the SAME width (auto sized each cell to its
     own content, making short labels like "L7" narrow). */
  table-layout: fixed;
  font-variant-numeric: tabular-nums;
}
/* Label column (first): fixed width so the level columns share the rest evenly. */
.formula-table th:first-child,
.formula-table td:first-child {
  width: 42px;
}
.formula-table[hidden] { display: none; }
.formula-table thead th {
  background: rgba(48, 54, 61, 0.55);
  color: #79c0ff;
  font-weight: 600;
  font-size: 10.5px;
  padding: 4px 0;
  text-align: center;
  border-radius: 3px;
}
.formula-table tbody th {
  width: 42px;
  text-align: left;
  padding: 4px 8px;
  background: rgba(48, 54, 61, 0.5);
  color: #c9d1d9;
  font-weight: 700;
  font-size: 10.5px;
  border-radius: 3px;
  letter-spacing: 0.3px;
}
.formula-table tbody td {
  padding: 4px 4px;
  text-align: center;
  background: rgba(48, 54, 61, 0.30);
  color: #c9d1d9;
  border-radius: 3px;
  font-size: 11px;
}
.formula-table tbody td .badge {
  margin-left: 0;
  padding: 0;
  font-size: 11px;
  border-radius: 0;
  background: none !important;
  border: none !important;
  text-shadow: none !important;
  display: inline;
  min-width: 0;
  text-transform: none;
  letter-spacing: 0;
}
.formula-table .row-label-old { color: #ff9a8c; }
.formula-table .row-label-new { color: #92c89e; }
/* Visual gap before L20 (after L1-15 sequential block) */
.formula-table .lvl-jump {
  border-left: 6px solid transparent;
  background-clip: padding-box !important;
}

/* Tags row + Categories row stacked vertically inside the toolbar; both rows
   share the same horizontal-button rhythm so labels align under each other. */
.legend-stack {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Symmetric with .toolbar-patch-info (both flex:1 1 0) so the search box in
     the middle stays optically centred. NO min-width:0 → the implicit
     min-width:auto floors the column at its badges' width, so the tag rows can
     never be shrunk/overlapped by the search box when space gets tight. */
  flex: 1 1 0;
}
.legend-categories {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: nowrap;
  white-space: nowrap;
}
.legend-categories strong {
  color: #c9d1d9;
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  margin-right: 4px;
  /* Matches .legend-tags strong so Group badges align with the Tags row. */
  flex: 0 0 auto;
  width: 46px;
}
.cat-filter-btn {
  cursor: pointer;
  user-select: none;
  font-family: inherit;
  font-size: 11px;
  padding: 3px 9px;
  border-radius: 3px;
  background: rgba(121, 192, 255, 0.05);
  color: #87a3bf;
  border: 1px solid rgba(121, 192, 255, 0.22);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  transition: filter 0.12s, background 0.12s, border-color 0.12s;
}
.cat-filter-btn:hover {
  background: rgba(121, 192, 255, 0.10);
  border-color: rgba(121, 192, 255, 0.40);
  color: #c9d1d9;
}
.cat-filter-btn.active {
  background: rgba(121, 192, 255, 0.22);
  border-color: rgba(121, 192, 255, 0.60);
  color: #c9d1d9;
}
body.cat-filter-active .cat-hide { display: none !important; }

/* FILTER BUTTONS in legend — make tags clickable */
.legend-tags .badge.filter-btn {
  cursor: pointer;
  user-select: none;
  font-family: inherit;
  font-size: 11px;
  padding: 2px 9px;
  transition: filter 0.12s, transform 0.12s;
}
.legend-tags .badge.filter-btn:hover {
  filter: brightness(1.18);
  transform: translateY(-1px);
}
.legend-tags .badge.filter-btn.active {
  outline: 2px solid currentColor;
  outline-offset: 2px;
  filter: brightness(1.35);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4) inset;
}

/* ENTITY-BLOCK wrapper (used for filtering hide/show). NOTE: we tried
   `content-visibility: auto` here for scroll-perf, but its implicit
   `contain: paint` clipped the hover-scaled dyn-cells at the block edge
   (last cell on the right got sliced). The lazy-tooltip rebuild in
   scripts.js already removed the bulk of the per-cell DOM weight, so
   leaving CV off is the better trade — visual freedom > the last bit
   of paint-skip. */
.entity-block { margin-bottom: 6px; }
/* All entity-block children flush against the entity-block left edge (margin
   not padding — padding-left controls INTERNAL content inset, which the
   visual boxes need to keep at 14px so their content aligns with the entity
   icon above). */
.entity-block > h4.subgroup,
.entity-block > ul.changes,
.entity-block > .ability-block,
.entity-block > .components-box,
.entity-block > .components-change,
.entity-block > .provides-box,
.entity-block > .properties-change {
  margin-left: 0;
}
/* Tag column inside ul.changes also flush at entity-block left edge — these
   lists have no inner box border to inset from. */
.entity-block > ul.changes,
.entity-block > .ability-block {
  padding-left: 0;
}
/* ul.subnotes is NOT touched here — it keeps its base margin-left: 76px so
   the ↳ arrow lines up under the row text column (after the 64px tag column
   + 12px gap). Resetting it to 0 here would flush subnotes against the
   entity-block left edge, misaligning every "Damage at level 1 ..." note. */
.entity-block.is-new > ul.changes {
  padding-left: 0;
}

/* === FILTER MODE === */
/* Keep the category header (h2.section: General/Item/Neutral Item/Hero Updates)
   VISIBLE while filtering so each surviving group is labelled with its category.
   Empty sections are collapsed wholesale by refreshPatchFilterLayout()
   (section.cat-panel → f-hide), so only non-empty sections keep their header. */
body.filter-active h4.subgroup { display: none; }
body.filter-active ul.subnotes { display: none; }
/* Hide filtered elements via .f-hide class set by JS */
.f-hide { display: none !important; }

/* MOBILE: stack legend label above strip so 10 segments fit */
@media (max-width: 540px) {
  .legend-strip {
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
  }
  .legend-label {
    min-width: 0;
    font-size: 10px;
  }
  .gradient-strip { height: 12px; }
  .gradient-strip .seg { font-size: 8px; }
}


/* ============================================================
   CREEPS TABLE PAGE — verbatim render of the team's neutral-creep
   stats sheet. Single wide table, sticky header, monospace numbers,
   tier-row tinting from the unicode dot indicator in column 1.
   ============================================================ */
.creeps-page {
  max-width: none;
  /* Flush to BOTH page edges — no left/right indent; the .creeps-scroll box
     spans the full width and its vertical scrollbar sits at the window's right
     edge. (top/bottom are overridden to 12px below when a scroll box present.) */
  padding: 16px 0 40px;
}
/* Pages with an inner scroller (Neutral Creeps / Unit Abilities) lock the
   document to the viewport so ONLY the inner .creeps-scroll box scrolls —
   no second, vestigial browser scrollbar. The box's max-height (below) is
   sized to exactly fill the space, so nothing is clipped. Mana Items has no
   .creeps-scroll, so it is untouched and keeps normal page scrolling.
   Body-level tooltips/popups all use position:fixed, so overflow:hidden
   here does not clip them. */
/* CONTAINED SCROLL BOX. The Neutral Creeps / Unit Abilities tables are wide, so
   THIS box is the scroller (capped height + sticky under the nav). The PAGE is
   locked (body:has(.creeps-scroll){overflow:hidden}) so ONLY this box scrolls —
   one vertical scrollbar, plus a horizontal one for the wide table. The <thead>
   and identity columns stay sticky inside the box; the frozen-column divider is
   an overlay (.sticky-frame) drawn in the non-scrolling .creeps-page so Chrome
   keeps repainting it (box-shadow on position:sticky cells drops mid-scroll).
   Mana Items has no .creeps-scroll → it page-scrolls normally. */
body:has(.creeps-scroll) { overflow: hidden; }
body:has(.creeps-scroll) .creeps-page { padding-top: 0; padding-bottom: 12px; }
.creeps-scroll {
  overflow: auto;
  width: 100%;
  position: sticky;
  top: var(--site-nav-h, 96px);
  max-height: calc(100vh - var(--site-nav-h, 96px) - 24px);
  contain: paint;
  will-change: scroll-position;
  /* Always-visible scrollbars across every Materials table (Neutral Creeps,
     Mana Items, Hero Stats, etc). Without this, Hero Stats' Expanded view
     overflows horizontally but the native scrollbar can be hidden on
     overlay-scroll setups (macOS, some Linux/Win configurations) — the
     scroll line is then invisible. Custom thin scrollbar keeps the line
     visible everywhere and matches the site's dark theme. */
  scrollbar-width: thin;
  scrollbar-color: rgba(139, 148, 158, 0.45) rgba(13, 17, 23, 0.6);
}
.creeps-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.creeps-scroll::-webkit-scrollbar-track {
  background: rgba(13, 17, 23, 0.6);
}
.creeps-scroll::-webkit-scrollbar-thumb {
  background: rgba(139, 148, 158, 0.45);
  border-radius: 5px;
  border: 2px solid rgba(13, 17, 23, 0.6);
}
.creeps-scroll::-webkit-scrollbar-thumb:hover {
  background: rgba(227, 196, 106, 0.7);
}
.creeps-scroll::-webkit-scrollbar-corner {
  background: rgba(13, 17, 23, 0.6);
}
.creeps-table {
  /* width:max-content + min-width:100% = stretch evenly to fill the container
     when content is narrow (Standard view — no empty strip on the right), but
     grow to content width and scroll horizontally when wide (Expanded view). */
  width: max-content;
  min-width: 100%;
  /* table-layout: auto lets the browser size each column to the widest
     content (header or cell) — no manual width hints. Every cell is
     centered horizontally, so visual alignment stays clean across the
     auto-sized columns. */
  table-layout: auto;
  /* `collapse` — significantly faster than `separate` on a wide table
     (~1600 cells on Creeps page). The sticky-bleed regression `separate`
     was meant to fix is handled below via a full-area `box-shadow: inset
     0 0 0 9999px var(--sticky-bg)` on sticky cells: the inset draws on
     the cell's paint layer (above the table's collapsed-border layer)
     and fills the whole inner area, masking any seam during scroll. */
  border-collapse: collapse;
  font-size: 11.5px;
  color: #c9d1d9;
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.creeps-table th,
.creeps-table td {
  text-align: center;
  vertical-align: middle;
}
/* Merged Ур. cell — when a level spans multiple rows via rowspan, the
   single cell shows the level number vertically centered against the
   group's full height. */
.creeps-table .lvl-cell {
  font-weight: 600;
  color: #79c0ff;
  background: rgba(170, 220, 240, 0.05);
  border-right: 1px solid rgba(170, 220, 240, 0.18);
}
/* Identity columns (Ур. / icon / Юнит) pinned to the left on horizontal
   scroll. scripts.js sets per-cell `left`; we provide position:sticky +
   an opaque background so scrolled cells pass underneath cleanly. */
.creeps-table .sticky-col {
  position: sticky;
  z-index: 3;
  --sticky-bg: #0e1217;
  background: var(--sticky-bg);
  /* Full-area inset shadow paints over any collapsed-border seam that
     would otherwise bleed through this cell during horizontal scroll. */
  box-shadow: inset 0 0 0 9999px var(--sticky-bg);
}
.creeps-table thead .sticky-col {
  z-index: 51;           /* corner cells: above both sticky axes + hover-zoom */
}
.creeps-table tbody tr:hover .sticky-col {
  --sticky-bg: #161d27;   /* match the row-hover tint, stay opaque */
}
.creeps-table .lvl-cell.sticky-col {
  --sticky-bg: #11161d;   /* keep the level cell's faint blue-tint readable */
}
/* Overlay frame outlining the pinned identity block while scrolled.
   Drawn as a separate absolutely-positioned element in .creeps-page
   (which does NOT horizontally scroll), so it sidesteps the Chrome bug
   where box-shadow/border on position:sticky cells stops repainting
   mid-scroll. scripts.js sets left/top/width/height and toggles
   .visible based on the scroll container's scrollLeft. */
.creeps-page { position: relative; }
/* Frozen-pane dividers (overlays in the non-scrolling .creeps-page so their
   shadows keep repainting — Chrome drops box-shadow on position:sticky cells
   mid-scroll). .sticky-frame = vertical edge at the RIGHT of the pinned
   lvl/unit block (horizontal scroll); .sticky-frame-top = horizontal edge
   under the header (vertical scroll). Neither boxes the pinned cells. */
.sticky-frame, .sticky-frame-top {
  /* Absolute inside the non-scrolling .creeps-page so the divider's box-shadow
     keeps repainting (Chrome drops box-shadow on position:sticky cells
     mid-scroll). scripts.js sets left/top/width/height from the box scroll. */
  position: absolute;
  display: none;
  pointer-events: none;
  z-index: 7;            /* above body + header sticky cells, below the
                            hover-zoomed icon (z-index: 9) */
}
.sticky-frame {
  border-left: 2px solid rgba(120, 200, 235, 0.85);
  box-shadow: 6px 0 12px -2px rgba(0, 0, 0, 0.7);
}
.sticky-frame-top {
  border-top: 2px solid rgba(120, 200, 235, 0.85);
  box-shadow: 0 6px 12px -2px rgba(0, 0, 0, 0.7);
}
.sticky-frame.visible, .sticky-frame-top.visible { display: block; }
/* Dedicated icon column — fixed-size portrait, centered. Vertical-align
   middle on the parent <td> keeps the image baseline-aligned with the
   sibling cells without flexbox (which previously made the wildkin row
   look "split" because flex baselines diverged from table baselines). */
.creeps-table .creep-icon-cell {
  text-align: center;
  padding: 2px 4px;
  vertical-align: middle;
}
/* Raise the icon cell on hover so the zoomed portrait sits above the other
   body cells (the cell is already position:sticky, which establishes a
   stacking context — bump its z-index rather than the inner <img>). Stays
   BELOW the pinned header (z 10/11) so a hovered top row never covers it. */
.creeps-table .creep-icon-cell:hover {
  z-index: 9;
}
/* Attack Range melee/ranged marker — a small frosted-glass rounded block
   holding the icon, to the right of the number. */
/* Attack Range cell: number in a fixed-width box so the icon badge always
   lands at the same x regardless of digit count. */
.creeps-table .col-attack_range,
.mr-table.hs-table .hs-col-range {
  white-space: nowrap;
}
.creeps-table .atk-num,
.mr-table.hs-table .atk-num {
  display: inline-block;
  min-width: 30px;
  text-align: right;
  vertical-align: middle;
}
.creeps-table .atk-badge,
.mr-table.hs-table .atk-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;             /* fixed block — every badge is identical size */
  height: 18px;
  box-sizing: border-box;
  margin-left: 6px;
  border-radius: 5px;
  /* Frosted glass without backdrop-filter (blur is GPU-heavy ×40 cells and
     caused scroll jank) — a soft translucent gradient instead. */
  background: linear-gradient(rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0.04));
  border: 1px solid rgba(255, 255, 255, 0.14);
  vertical-align: middle;
}
.creeps-table .atk-badge img,
.mr-table.hs-table .atk-badge img {
  width: 12px;
  height: 12px;
  object-fit: contain;
  display: block;
  opacity: 0.9;
}
/* Header centre tabs (Main / Patch Reader / Calendar / Materials) — laid out
   in the middle grid column of .nav-inner, so they're always centred between
   brand (left) and the picker / context block (right) without overlapping
   either. Each tab is a 48px-tall pill matching the version picker height,
   so the row sits on one baseline. Font matches the brand block. */
.nav-tabs {
  display: flex;
  gap: 8px;
  align-items: center;
  justify-self: center;
}
.nav-tab {
  font-family: 'Jersey 10', 'Courier New', monospace;
  font-size: 18px;
  letter-spacing: 1px;
  line-height: 1;
  color: #d2d9e0;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
  text-decoration: none;
  height: 38px;                 /* matches the version picker on patch pages */
  padding: 0 14px;
  display: inline-flex;
  align-items: center;
  border-radius: 0;
  background:
    linear-gradient(180deg,
      rgba(255, 244, 209, 0.025) 0%,
      rgba(255, 244, 209, 0.0) 100%),
    rgba(28, 32, 38, 0.84);
  border: 1px solid rgba(122, 131, 144, 0.22);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.035),
    inset 0 -1px 0 rgba(0, 0, 0, 0.38);
  transition: background 0.15s ease, color 0.15s ease,
              border-color 0.15s ease, box-shadow 0.15s ease;
  clip-path: polygon(2px 0, calc(100% - 2px) 0, 100% 2px,
    100% calc(100% - 2px), calc(100% - 2px) 100%, 2px 100%,
    0 calc(100% - 2px), 0 2px);
}
.nav-tab:hover {
  color: #ffffff;
  background:
    linear-gradient(180deg,
      rgba(227, 196, 106, 0.05) 0%,
      rgba(227, 196, 106, 0.015) 100%),
    rgba(32, 36, 43, 0.96);
  border-color: rgba(163, 135, 73, 0.24);
}
/* Active tab — gold "sikle" text colour + a pressed-into-the-header look via
   a darker recessed background and an inset top shadow. */
.nav-tab.active {
  color: #e3c46a;
  background:
    linear-gradient(180deg,
      rgba(227, 196, 106, 0.065) 0%,
      rgba(227, 196, 106, 0.018) 100%),
    rgba(17, 20, 25, 0.98);
  border-color: rgba(227, 196, 106, 0.28);
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 244, 209, 0.05),
    inset 0 -1px 0 rgba(255, 255, 255, 0.03);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.8);
}

/* Materials sub-tabs — flat row of three browser-tab pills, hung BELOW the
   main header on a thin bar (so the header itself stays compact). */
.materials-subnav {
  background: #11161d;
  border-bottom: 1px solid rgba(170, 220, 240, 0.18);
  width: 100%;
}
.materials-subnav-inner {
  padding: 0 10px;        /* flush-left, same left padding as the brand block */
  display: flex;
  justify-content: flex-start;
  align-items: flex-end;
  min-height: 36px;
}
.materials-subnav-links {
  display: flex;
  gap: 4px;
  align-items: flex-end;
  min-width: 0;
}
/* When the sub-tab bar sits INSIDE the table scroll box (Neutral Creeps /
   Neutral Abilities), it scrolls away vertically with the table but stays
   pinned to the left edge during horizontal scroll — same treatment as the
   blurb + toolbar. On Mana Items it stays in the page header and scrolls away
   via the page itself. */
.creeps-scroll > .materials-subnav {
  position: sticky;
  left: 0;
  /* Above sticky headers and hovered dynamics cells. The bar scrolls up out of
     view before the header pins, so the higher z doesn't cause lasting overlap. */
  z-index: 120;
}
.nav-subtab {
  font-size: 13px;
  font-weight: 600;
  color: #9fb0c0;
  text-decoration: none;
  padding: 7px 16px;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: 7px 7px 0 0;   /* rounded top — sits on the subnav's bottom border */
  margin-bottom: -1px;          /* overlap the 1px bottom border */
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
  display: inline-flex;
  align-items: center;
  gap: 0;
}
.nav-subtab:hover { color: #e6edf3; background: rgba(48, 54, 61, 0.45); }
.nav-subtab.active {
  color: #e3c46a;
  background: #0a0e13;          /* matches the page background → tab merges into page */
  border-color: rgba(170, 220, 240, 0.18);
}
.nav-subtab-label { color: inherit; }
/* ---- Grouped sub-nav dropdowns (Creeps ▾ / Items ▾ / Heroes ▾) ---- */
.nav-subgroup { position: relative; display: inline-flex; align-items: flex-end; }
.nav-subtab-group { cursor: pointer; }
.nav-caret { margin-left: 6px; font-size: 9px; opacity: 0.75; }
.nav-submenu {
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 184px;
  background: #11161d;
  border: 1px solid rgba(170, 220, 240, 0.20);
  border-radius: 0 8px 8px 8px;
  padding: 5px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Above the submenu's own stacking context; the bar is lifted so this paints
     over the table, sticky header and dynamics cells. */
  z-index: 125;
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.55);
  opacity: 0;
  visibility: hidden;
  transform: translateY(5px);
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0.14s;
}
/* Child combinator `>` so a nested flyout (a .nav-submenu deeper inside) does
   NOT open just because the outer group is hovered — only its OWN nested
   subgroup hover opens it. `.is-open` is the touch-device tap-toggle class
   that scripts.js adds when the user taps a group trigger (first tap = open,
   second tap = follow link). */
.nav-subgroup:hover > .nav-submenu,
.nav-subgroup:focus-within > .nav-submenu,
.nav-subgroup.is-open > .nav-submenu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.nav-subitem {
  font-size: 12.5px;
  font-weight: 600;
  color: #9fb0c0;
  text-decoration: none;
  padding: 7px 12px;
  border-radius: 5px;
  white-space: nowrap;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  transition: background 0.12s ease, color 0.12s ease;
}
.nav-subitem:hover { color: #e6edf3; background: rgba(48, 54, 61, 0.55); }
.nav-subitem.active {
  color: #e6edf3;
  background: rgba(56, 139, 253, 0.18);
}
.nav-subitem-soon { color: #5b6672; cursor: default; }
.nav-subitem-soon:hover { background: none; }
/* Nested flyout (Neutral Abilities under Neutral Stats): the parent item opens
   a second menu to its RIGHT on hover — a classic cascading menu. */
.nav-subgroup-nested { position: relative; display: block; }
.nav-subitem-parent { cursor: pointer; }
.nav-caret-side { font-size: 9px; opacity: 0.7; margin-left: 8px; }
.nav-submenu-flyout {
  top: -6px;                 /* align the flyout's first row with the parent */
  left: 100%;
  margin-left: 5px;
  border-radius: 8px;
}
/* The nested parent stays visually "open" (highlighted) while its flyout shows. */
.nav-subgroup-nested:hover > .nav-subitem-parent { color: #e6edf3; background: rgba(48, 54, 61, 0.55); }
.nav-soon-tag {
  font-size: 8.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #b59a48;
  border: 1px solid rgba(227, 196, 106, 0.4);
  border-radius: 4px;
  padding: 0 4px;
  line-height: 1.4;
}
/* Ability-icon link on the Creeps Table — no underline, inherits layout. */
.creeps-table .abil-link {
  text-decoration: none;
  display: inline-block;
  line-height: 0;
}
/* Vertical divider after every column (both Tables — Neutral Creeps + Unit
   Abilities). The heavier col-sep dividers (set on category starts) stay
   visible over this lighter base. */
.creeps-table th,
.creeps-table td { border-left: 1px solid rgba(170, 220, 240, 0.12); }
.creeps-table th:first-child,
.creeps-table td:first-child { border-left: none; }
/* Unit Abilities page table — reuses .creeps-table sticky/scroll machinery. */
.unit-abilities-table .ua-ability { text-align: left; }
.unit-abilities-table .ua-ability-inner {
  display: inline-flex;
  align-items: center;
  gap: 13px;            /* room so the hover-zoomed icon doesn't cover the name */
  white-space: nowrap;
}
.unit-abilities-table .ua-ability-name { font-weight: 600; color: #e6edf3; }
/* Neutral Creeps "Unit" name cell matches the Neutral Abilities ability-name:
   left-aligned, semibold, same colour, single line — so both Materials tables
   share one look AND the same row height (no wrap-induced tall creep rows). */
.creeps-table td.col-name {
  text-align: left;
  font-weight: 600;
  color: #e6edf3;
  white-space: nowrap;
}
/* Type — plain coloured fills (no glass effect). */
.unit-abilities-table td.ua-type-active  { background: rgba(80, 155, 230, 0.22); color: #d2e6ff; }
.unit-abilities-table td.ua-type-passive { background: rgba(170, 110, 215, 0.22); color: #e8d3f5; }
.unit-abilities-table td.ua-type-aura    { background: rgba(225, 180, 80, 0.22); color: #f4e3b3; }
/* Damage Type — colour-coded text. */
/* Damage-type colour paints ONLY the numeric runs (.dmg-num) inside the
   Damage cell — words like "в сек." or "+ N% Int" keep the default text
   colour. The standalone Damage Type column was replaced by this scheme. */
.unit-abilities-table td.dt-magical   .dmg-num,
.unit-abilities-table td.dt-magical   .lvl-toggle { color: #5fb1ff; }   /* blue */
.unit-abilities-table td.dt-physical  .dmg-num,
.unit-abilities-table td.dt-physical  .lvl-toggle { color: #e1716a; }   /* bordeaux */
.unit-abilities-table td.dt-hpremoval .dmg-num,
.unit-abilities-table td.dt-hpremoval .lvl-toggle { color: #8aab7a; }   /* dull green */
.unit-abilities-table td.dt-pure      .dmg-num,
.unit-abilities-table td.dt-pure      .lvl-toggle { color: #ead992; }
/* Collapsed upgrade value ("40…26"): a clickable button that keeps the column
   narrow; the full per-tier list opens in a floating popover (scripts.js
   positions it as `fixed`, so the column width never changes). */
.lvl-toggle {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: underline dotted rgba(170, 220, 240, 0.45);
  text-underline-offset: 2px;
}
.lvl-toggle:hover,
.lvl-toggle[aria-expanded="true"] { color: #cde9f5; }
.lvl-popover {
  position: fixed;
  z-index: 80;
  display: none;
  white-space: nowrap;
  background: #0d1117;
  border: 1px solid rgba(170, 220, 240, 0.4);
  border-radius: 6px;
  padding: 5px 9px;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: #e6edf3;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.55);
  pointer-events: none;
}
.lvl-popover.show { display: block; }
/* Vertical category dividers (like Neutral Creeps): a left border on the first
   visible column of each super-category, set by scripts.js markCatEdges() so it
   tracks the column reorder in Auras view. Runs the full column height (header
   col-row + every body cell). */
.unit-abilities-table th.cat-edge,
.unit-abilities-table td.cat-edge {
  border-left: 2px solid rgba(170, 220, 240, 0.30);
}
/* "Only Auras" view filter — hide non-aura rows + irrelevant columns. The
   super-category header STAYS in Auras view (scripts.js reorders columns into
   contiguous category groups + recomputes the cat colspans for what's visible). */
.unit-abilities-table.filter-auras tbody tr:not(.ua-row-aura) { display: none; }
.unit-abilities-table.filter-auras th[data-col="type"],
.unit-abilities-table.filter-auras td[data-col="type"],
.unit-abilities-table.filter-auras th[data-col="dmg_type"],
.unit-abilities-table.filter-auras td[data-col="dmg_type"],
.unit-abilities-table.filter-auras th[data-col="damage"],
.unit-abilities-table.filter-auras td[data-col="damage"],
.unit-abilities-table.filter-auras th[data-col="manacost"],
.unit-abilities-table.filter-auras td[data-col="manacost"],
.unit-abilities-table.filter-auras th[data-col="cooldown"],
.unit-abilities-table.filter-auras td[data-col="cooldown"],
.unit-abilities-table.filter-auras th[data-col="cast_range"],
.unit-abilities-table.filter-auras td[data-col="cast_range"],
.unit-abilities-table.filter-auras th[data-col="dispel"],
.unit-abilities-table.filter-auras td[data-col="dispel"] { display: none; }
/* Auras Only: the empty Effect 1-3 columns take up the slack so the Ability
   column doesn't get over-wide with whitespace. */
.unit-abilities-table.filter-auras th[data-col="effect"],
.unit-abilities-table.filter-auras td[data-col="effect"],
.unit-abilities-table.filter-auras th[data-col="effect2"],
.unit-abilities-table.filter-auras td[data-col="effect2"],
.unit-abilities-table.filter-auras th[data-col="effect3"],
.unit-abilities-table.filter-auras td[data-col="effect3"] { min-width: 180px; }
/* Flag glyphs (Stackable / Dispellable) — colours scoped to .ua-flag so the
   column headers (th.ua-dispel etc.) stay default-coloured. */
.unit-abilities-table .ua-flag { font-size: 15px; font-weight: 700; line-height: 1; display: inline-block; }
.unit-abilities-table .ua-flag.ua-yes  { color: #56d364; }            /* green check */
.unit-abilities-table .ua-flag.ua-no   { color: #f85149; }            /* red x */
.unit-abilities-table .ua-flag.ua-dispel        { color: #e3c14a; text-shadow: 0 0 4px rgba(227,193,74,0.6); }  /* yellow star */
.unit-abilities-table .ua-flag.ua-dispel-strong { color: #f85149; text-shadow: 0 0 4px rgba(248,81,73,0.55); } /* red star */
/* Not-dispellable — hollow (outline) star, same shape/size as the others. */
.unit-abilities-table .ua-flag.ua-dispel-no {
  color: transparent;
  -webkit-text-stroke: 1.1px #7d828b;
  text-stroke: 1.1px #7d828b;
}
/* Aura Stack red-x glyph — small. */
.unit-abilities-table .ua-stack-glyph { font-size: 8px; }
/* Cell dashes — half size. */
.unit-abilities-table .ua-dash,
.creeps-table .ua-dash,
.mr-table .ua-dash { color: #6e7681; font-size: 0.4em; }
/* "Ability" header centred; Type values left-aligned so the words line up. */
.unit-abilities-table th.ua-ability { text-align: center; }
.unit-abilities-table th.ua-type,
.unit-abilities-table td.ua-type { white-space: nowrap; }
/* Coloured yes/no text for Aura, Aura Stack, Dispellable. */
.unit-abilities-table .ua-yn { font-weight: 600; font-size: 11px; }
.unit-abilities-table .ua-yn-yes    { color: #6aa775; }   /* muted green */
.unit-abilities-table .ua-yn-no     { color: #c97f78; }   /* muted red */
.unit-abilities-table .ua-yn-strong { color: #c4ad5e; }   /* muted yellow (strong dispel) */
/* Continuation rows of a multi-ability unit: no top border so the Lvl + Unit
   cells read as one merged cell (icon shown only on the first row). */
.unit-abilities-table td.ua-merge { border-top: none; }
/* Raise the hovered sticky identity cell so its zoomed icon sits above
   neighbouring rows. Stays BELOW the pinned header (z 50/51/52) so a hovered
   row near the top can't paint over the header. */
.creeps-table .creep-icon-cell:hover,
.unit-abilities-table td.ua-ability:hover { z-index: 40; }
.unit-abilities-table td.ua-effect,
.unit-abilities-table td.ua-effect2,
.unit-abilities-table td.ua-effect3,
.unit-abilities-table td.ua-as_effect,
.unit-abilities-table td.ua-ms_effect { white-space: nowrap; color: #9fb0c0; }
/* Damage cell allows wrapping for long compound expressions (e.g. Mana Burn:
   "20/25/30/35 + 200/250/350/400% Int"). */
.unit-abilities-table td.ua-damage { white-space: normal; }
.unit-abilities-table td.ua-damage .dmg-wrap,
.unit-abilities-table .cell-wrap {
  display: inline-flex; align-items: center; flex-wrap: wrap;
  justify-content: center; gap: 2px; line-height: 1.2;
}
/* Multiline damage: stack the two halves vertically (Mana Burn's flat +
   Int-multiplier tail) so the cell stays narrow. */
.unit-abilities-table td.ua-damage .dmg-wrap.dmg-multiline {
  flex-direction: column; gap: 1px;
}
.unit-abilities-table td.ua-damage .dmg-line2 {
  display: inline-flex; align-items: center; gap: 2px;
}
.unit-abilities-table td.ua-damage .stat-ico {
  height: 14px; width: 14px; vertical-align: -2px; margin-left: 2px;
}
/* Question-mark hint badge — appears inside a cell to surface an author note
   without taking over the cell's hover (cell hover is reserved for patchnotes).
   Used in the Damage cell ("от расстояния" for Tornado) and the Ability cell
   ("identical for all frog units" for Riverborn Aura). */
/* Hover affordance for ability icons that carry a body-level tooltip via
   data-tooltip. No visual badge — the icon itself is the trigger. */
.abil-ico-hint {
  display: inline-flex;
  cursor: help;
  outline: none;
}
.abil-ico-hint:focus-visible {
  outline: 2px solid rgba(88, 166, 255, 0.6);
  outline-offset: 2px;
  border-radius: 4px;
}

.qhint {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  margin-left: 6px;
  border: 1px solid #6b7682;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.04);
  color: #e3c46a;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  cursor: help;
  user-select: none;
  position: relative;
  vertical-align: middle;
}
.qhint:hover,
.qhint:focus-visible {
  color: #f4e3b3;
  border-color: #e3c46a;
  background: rgba(227, 196, 106, 0.15);
  outline: none;
}
/* Tooltip body for `.qhint` is rendered at <body> level (fixed-position) by
   scripts.js so it escapes the .creeps-scroll overflow clip — a pure CSS
   `::after` would be hidden behind the sticky header / scroll boundary. */
.qhint-tip {
  position: fixed;
  z-index: 1100;
  pointer-events: none;
  display: none;
  max-width: 320px;
  padding: 5px 9px;
  background: #1b212a;
  color: #e6edf3;
  border: 1px solid #30363d;
  border-radius: 5px;
  font-size: 11px;
  font-weight: 500;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
  white-space: normal;
  text-align: center;
}
.qhint-tip.is-visible { display: block; }
.qhint-tip .qh-line {
  padding: 2px 0;
  /* Wrap long lines within the tooltip's max-width instead of overflowing. */
  white-space: normal;
  text-align: left;
  line-height: 1.35;
}
/* Keep the colored Yes/No glyph + dash on the same line as the start of the
   description — only the trailing words flow onto a new line if needed. */
.qhint-tip .qh-line .ua-yn { white-space: nowrap; }
.qhint-tip .qh-line + .qh-line { border-top: 1px solid rgba(139, 148, 158, 0.18); }
.qhint-tip .ua-yn { font-weight: 600; font-size: 11px; }
.qhint-tip .ua-yn-yes    { color: #6aa775; }
.qhint-tip .ua-yn-no     { color: #c97f78; }
.qhint-tip .ua-yn-strong { color: #c4ad5e; }
/* Valve description variables (%hero_stun_duration% etc.) — values aren't
   resolved here, so just colour the placeholder name to mark it as "variable". */
.qhint-tip .abil-var { color: #79c0ff; }
/* Damage-type colour swatches inside the Damage header tooltip — mirror the
   in-table `td.dt-*` palette so the words match the actual cell numbers. */
.qhint-tip .dt-magical   { color: #5fb1ff; font-weight: 600; }
.qhint-tip .dt-physical  { color: #e1716a; font-weight: 600; }
.qhint-tip .dt-hpremoval { color: #8aab7a; font-weight: 600; }
.qhint-tip .dt-pure      { color: #ead992; font-weight: 600; }
/* "Aura" word inside the Through BKB header tooltip — same warm-yellow tone
   as the Aura cell type tag (.ua-type-aura) so the legend reads at a glance. */
.qhint-tip .hint-aura { color: #f4e3b3; font-weight: 600; }
/* (Legacy `.show-upgrades` rule lived here — replaced by the segmented
    tier control + table[data-tier="all"] selector further below.) */
/* Aura Stack "yes" — green rounded diamond made of stacked tiles. The wrapper
   is NOT rotated and carries a screen-space top-fade mask, so the upper part
   of the icon fades out (weaker colour). ::after = front tile, ::before =
   tile behind/above (the "multiple levels" look). */
.unit-abilities-table .ua-stack-yes {
  position: relative;
  display: inline-block;
  width: 16px;
  height: 18px;
  vertical-align: middle;
  -webkit-mask: linear-gradient(to top, #000 70%, rgba(0, 0, 0, 0.55) 100%);
          mask: linear-gradient(to top, #000 70%, rgba(0, 0, 0, 0.55) 100%);
}
.unit-abilities-table .ua-stack-yes::before,
.unit-abilities-table .ua-stack-yes::after,
.unit-abilities-table .ua-stack-yes > i {
  content: "";
  position: absolute;
  width: 9px;
  height: 9px;
  left: 3px;
  border-radius: 3px;
  transform: rotate(45deg);
  /* dark outer outline separates the overlapping tiles clearly */
  box-shadow: 0 0 0 1px rgba(8, 14, 10, 0.9);
}
/* Painting order: ::before (bottom) → <i> → ::after (top). So ::after is the
   FRONT tile (lowest, brightest), <i> the middle, ::before the back (highest,
   dimmest) — each tile sits behind and a little above the one in front. */
.unit-abilities-table .ua-stack-yes::after {  /* front tile */
  top: 6px;
  background: #5fe06d;
}
.unit-abilities-table .ua-stack-yes > i {     /* middle tile */
  top: 3px;
  background: #45a851;
}
.unit-abilities-table .ua-stack-yes::before { /* back tile (dimmest) */
  top: 0;
  background: #2c6f3a;
}
/* Unit Abilities: duplicate (same-unit continuation) rows hide the repeated
   icon and drop the top border so the unit's rows read as one block. */
.unit-abilities-table tr.ua-dup .ua-unit img { visibility: hidden; }
/* The row jumped to from an ability link is marked via scripts.js with the
   same `.row-marked` class as a manual click (gold frame) — see centerHash().
   The old yellow `:target` flash was replaced so navigation and clicking
   share one consistent selected style. */
/* Camp-type marker (minimap creep-camp glyph: small/mid/big/ancient). */
.creeps-table .camp-ico {
  width: 16px;
  height: 16px;
  object-fit: contain;
  vertical-align: middle;
  max-width: none;
}
/* Keep both glyphs of a dual-camp creep on one line. */
.creeps-table td.col-camp { white-space: nowrap; }
.creeps-table .camp-ico + .camp-ico { margin-left: 2px; }
/* Ability cell icon — changelog style (45px there), smaller here. */
.creeps-table .abil-ico {
  width: 26px;
  height: 26px;
  object-fit: cover;
  border-radius: 4px;
  display: inline-block;
  vertical-align: middle;
  border: 1px solid rgba(170, 220, 240, 0.18);
  transform-origin: center center;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
/* Hover-zoom the ability icon, mirroring the unit-portrait pop-out. Bare
   (non-autocast) icons scale directly; autocast icons scale via their wrapper
   (below) so the snake ring zooms with the icon instead of doubling up. */
.creeps-table .abil-ico:hover {
  transform: scale(1.8);
  position: relative;
  z-index: 30;            /* above the sticky header (z 5/6) and neighbours */
  background: #0e1217;    /* opaque so underlying text doesn't show through */
  box-shadow:
    0 3px 8px rgba(0, 0, 0, 0.6),
    0 0 0 1px rgba(170, 220, 240, 0.4);
}
/* Autocast marker — animated golden ring + pulsing glow around the ability
   icon, mirroring the in-game autocast toggle. The wrapper is needed because
   <img> cannot host ::before / ::after pseudo-elements. */
.creeps-table .abil-ico-wrap {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  line-height: 0;
  transform-origin: center center;
  transition: transform 0.12s ease;
}
/* Autocast icon zoom — scale the whole wrapper (icon + snake) as one, and
   suppress the inner img's own :hover scale so it doesn't compound. */
.creeps-table .abil-ico-wrap:hover {
  transform: scale(1.8);
  z-index: 30;
}
.creeps-table .abil-ico-wrap:hover .abil-ico {
  transform: none;
  z-index: auto;
  box-shadow: none;
}
/* A thin golden "snake" — a short stroke segment that crawls along the icon's
   rounded-rect frame at constant speed, corners included. The SVG <rect> has
   pathLength=100, so the dash (snake) + gap sum to 100 and the dashoffset
   animation loops seamlessly around the perimeter. */
.creeps-table .autocast-snake {
  position: absolute;
  inset: -1px;
  width: calc(100% + 2px);
  height: calc(100% + 2px);
  overflow: visible;
  pointer-events: none;
  z-index: 2;
}
.creeps-table .autocast-snake rect {
  fill: none;
  stroke-linecap: butt;
  stroke-dashoffset: 0;
  /* Every stroke shares one phase-locked lap so the whole comet travels as a
     unit. Painted range [a,b] is expressed as dasharray "0 a (b-a) (100-b)"
     (leading 0-length dash + gap, then the painted dash). Head leads at the
     high end; tail fades toward the low end. */
  animation: autocast-snake 1.73s linear infinite;
}
/* Closed base ring — a continuous, static golden line around the whole frame.
   The animated comet below sweeps around within it, so the snake reads as a
   closed loop with the glow travelling inside itself. */
.creeps-table .autocast-snake .ac-ring {
  stroke: #ffce72;
  stroke-width: 1.5;
  stroke-dasharray: none;
  opacity: 0.34;
  filter: drop-shadow(0 0 1.2px rgba(255, 190, 70, 0.6));
  animation: none;
}
/* Wide, very soft, translucent aura spanning the whole comet — the "fluff". */
.creeps-table .autocast-snake .ac-fluff {
  stroke: #ffc964;
  stroke-width: 9;
  stroke-linecap: round;
  stroke-dasharray: 0 0 100 0;     /* [0, 100] — full ring */
  opacity: 0.10;
}
/* Bright body — the leading, fully-opaque stretch. Head sits at 100, which on
   the closed path coincides with position 0, so the head meets the tail's tip:
   the comet wraps the entire perimeter and touches itself. */
.creeps-table .autocast-snake .ac-body {
  stroke: #fff3c8;
  stroke-width: 1.8;
  stroke-dasharray: 0 60 40 0;     /* [60, 100] */
  filter: drop-shadow(0 0 1.8px rgba(255, 190, 70, 0.95));
}
/* Tail — 4 overlapping steps with a gentle opacity ramp (1 → .78 → .5 → .28
   → .12), tapering width and rising blur toward the tip. The painted union
   covers the whole [0,100] perimeter, so the faint tip (at 0) meets the bright
   head (at 100) for a fully closed loop with no gap. */
.creeps-table .autocast-snake .ac-tail1 {
  stroke: #ffe9ad;
  stroke-width: 1.55;
  stroke-dasharray: 0 46 18 36;    /* [46, 64] */
  opacity: 0.78;
  filter: drop-shadow(0 0 1.5px rgba(255, 190, 70, 0.75));
}
.creeps-table .autocast-snake .ac-tail2 {
  stroke: #ffe09a;
  stroke-width: 1.2;
  stroke-dasharray: 0 30 20 50;    /* [30, 50] */
  opacity: 0.5;
}
.creeps-table .autocast-snake .ac-tail3 {
  stroke: #ffd98e;
  stroke-width: 0.85;
  stroke-dasharray: 0 14 20 66;    /* [14, 34] */
  opacity: 0.24;
}
.creeps-table .autocast-snake .ac-tail4 {
  stroke: #ffd585;
  stroke-width: 0.5;
  stroke-dasharray: 0 0 18 82;     /* [0, 18] */
  opacity: 0.10;
}
/* Pollen — two phase-locked layers of tiny, irregular, heavily-blurred specks
   that ride with the comet. Small widths + strong blur + varied dash sizes
   dissolve the dots into a soft golden dust cloud (scatter) instead of crisp
   beads. Each dasharray sums to 100 for a seamless lap. */
.creeps-table .autocast-snake .ac-pollen {
  stroke: #fff3cf;
  stroke-width: 1;
  stroke-linecap: round;
  stroke-dasharray: 0.5 4 0.4 5 0.6 3.5 0.3 5.5 0.5 4 0.4 4.5 0.5 5 0.3 4 0.6 4.5 0.4 5 0.5 4 0.3 45.7;
  opacity: 0.5;
  filter: drop-shadow(0 0 1.6px rgba(255, 205, 110, 0.85));
}
.creeps-table .autocast-snake .ac-pollen2 {
  stroke: #ffeec2;
  stroke-width: 0.6;
  stroke-linecap: round;
  stroke-dasharray: 0.3 6 0.4 5.5 0.3 6.5 0.4 5 0.3 7 0.4 6 0.3 6.5 0.4 6 0.3 48.4;
  opacity: 0.4;
  filter: drop-shadow(0 0 1.4px rgba(255, 210, 120, 0.8));
}
/* Sparse, bright, sharp twinkles with a strong glow — the "magic" sparkle on
   top of the soft dust haze. */
.creeps-table .autocast-snake .ac-pollen3 {
  stroke: #fffdf2;
  stroke-width: 0.5;
  stroke-linecap: round;
  stroke-dasharray: 0.4 9 0.3 11 0.4 8 0.3 12 0.4 10 0.3 47.9;
  opacity: 0.9;
  filter: drop-shadow(0 0 2px rgba(255, 225, 150, 0.95));
}
@keyframes autocast-snake {
  to { stroke-dashoffset: -100; }
}
@media (prefers-reduced-motion: reduce) {
  .creeps-table .autocast-snake rect { animation: none; }
}
/* Ability name header inside the changelog tooltip (centered). */
.stat-hist-tip .abil-tip-name {
  text-align: center;
  font-weight: 600;
  color: #e6edf3;
  padding-bottom: 3px;
  margin-bottom: 3px;
  border-bottom: 1px solid rgba(139, 148, 158, 0.25);
}
/* Standard view hides Expanded-only columns. Super-category headers span all
   their columns; the colspan shrinks automatically as hidden cells collapse. */
.creeps-table.mode-standard .col-exp { display: none; }
/* BOTH header rows stay pinned: the category row (Basic/Vitality/…) sticks at
   top:0, and the column-name row pins directly beneath it at --cat-row-h
   (set by scripts.js from the category row's rendered height). */
.creeps-table thead tr.cat-row th.cat-head {
  position: sticky;
  top: 0;
  z-index: 52;           /* above the column row so the col-row's gap-fill
                            box-shadow tucks under it (see col-row below) */
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #9fb0c0;
  font-weight: 700;
  /* Same colour as the column-name row below (base thead th = #161b22). A
     single uniform header colour means there's NO colour boundary between the
     two rows, so the zoom-dependent seam/gap is gone at every scale. The
     category labels stay distinct through their uppercase dim styling. */
  background: #161b22;
  padding: 3px 4px;
  /* Opaque lip extending 6px down past the edge: covers any sub-pixel sticky
     positioning gap with the SAME colour, so nothing ever shows through at
     scroll time regardless of zoom level. */
  box-shadow: 0 6px 0 0 #161b22;
}
/* Column-name row — pinned under the category row. The blue scrolled-edge line
   is painted flush on this row's bottom via box-shadow (matches the Mana Items
   table; no separate overlay, so no gap). border-bottom is dropped because
   Chrome drops collapsed borders on sticky cells mid-scroll. */
.creeps-table thead tr.col-row th {
  /* -2px pull-up paired with the JS Math.floor on --cat-row-h: guarantees the
     column row starts ≥2px BEFORE the category row's real bottom, so the two
     pinned rows always overlap and nothing leaks through at scroll time. */
  top: calc(var(--cat-row-h, 0px) - 2px);
  border-bottom: none;
  /* box-shadows, in order:
     1. blue scrolled-edge line, flush on the bottom (like Mana Items);
     2. soft drop shadow onto the body below;
     3. a 14px-tall OPAQUE block extending UPWARD in the header colour — fills
        any sub-pixel/rounding gap between this row and the category row above
        (which sits at z-index 52 and paints over the overlapping part). This
        kills the band where body content showed through, regardless of how
        --cat-row-h rounds. */
  box-shadow:
    inset 0 -2px 0 rgba(120, 200, 235, 0.85),
    0 4px 10px rgba(0, 0, 0, 0.45),
    0 -14px 0 0 #161b22;
}
.creeps-table thead tr.cat-row .cat-head + .cat-head { border-left: 2px solid rgba(170, 220, 240, 0.22); }
/* Freeze the FIRST category cell (Basic = the frozen identity columns) on the
   LEFT too, so during horizontal scroll the other category cells slide UNDER it
   instead of their left borders drifting across the category header over the
   frozen block (the col-row already does this via .sticky-col). Its right edge
   carries the Basic|rest divider permanently so the line stays put when scrolled
   (the sibling's border-left slides away beneath it). */
.creeps-table thead tr.cat-row th.cat-head:first-child {
  left: 0;
  z-index: 53;          /* above sibling cat-heads (52) so they pass beneath it */
  border-right: 2px solid rgba(170, 220, 240, 0.22);
}
/* ===================== Hero Dynamics matrix (heroes_dyn.html) =====================
   Reuses the .creeps-table shell (sticky header + sticky-col + sticky-frame) and
   the global .dyn-cell pill. ROWS = heroes (frozen icon+name column on the left),
   COLUMNS = patches (version; release date on hover), CELLS = the hero's dyn-cell
   that patch. Untouched cells render a static glassy diamond via ::after (keeps the
   ~14k-cell grid's HTML light); touched cells get a JS-built coloured pill.
   Fixed table layout → every patch column is the SAME width so diamonds line up in
   a clean vertical grid. Column widths (--hd-col-w) and the hero column width
   (--hd-hero-w, sized to the longest name + icon + zoom clearance) are set live by
   scripts.js dynLayoutMatrix(), which also keeps the latest patch flush right.
   max-content width (NOT .creeps-table's min-width:100%, which would stretch the
   columns unequally). */
/* AUTO layout (not fixed): a fixed-layout table derives column widths from the
   FIRST row, which here is the colspan'd super-category row — that would ignore
   the per-column widths. Auto layout (like Neutral Creeps) honours the explicit
   width + min/max-width on the leaf columns below AND lets the super-category
   colspans span correctly. width:max-content + min-width:0 stops the inherited
   .creeps-table min-width:100% from stretching the columns. */
.heroes-dyn-table { table-layout: auto; width: max-content; min-width: 0; }
/* Hero identity column. The `.creeps-table` prefix raises specificity ABOVE the
   shared `.creeps-table td:first-child` tier-dots rule (equal-specificity, later
   in the file) which would otherwise force center-align + a symbol font +
   white-space:normal onto this cell — making each row's icon+name land at a
   different x (centred by name length) instead of one clean vertical line. */
.creeps-table.heroes-dyn-table th.hd-hero,
.creeps-table.heroes-dyn-table td.hd-hero {
  width: var(--hd-hero-w, 210px);
  min-width: var(--hd-hero-w, 210px);
  max-width: var(--hd-hero-w, 210px);
  text-align: left;
  white-space: nowrap;
  font-family: inherit;
  letter-spacing: normal;
  /* RULE (all tables): a vertical divider after the frozen identity column(s).
     2px col-sep line at rest; the .sticky-frame overlay continues it during
     horizontal scroll (Chrome drops borders on sticky cells mid-scroll). */
  border-right: 2px solid rgba(170, 220, 240, 0.30);
}
/* The identity-column HEADER ("Hero" / "Item") is centred; the body cells stay
   left-aligned (icon + name). */
.creeps-table.heroes-dyn-table thead th.hd-hero { text-align: center; }
.heroes-dyn-table td.hd-hero { padding: 2px 12px 2px 6px; }
/* Equal patch columns under auto layout: pin width + min + max so every column
   is exactly --hd-col-w (the dyn-cell pill 28px sits centred inside). No
   overflow:hidden on the cell — the 2.5× hover-pop must overflow it (the header
   th below clips its own label separately). */
.heroes-dyn-table th.hd-patch,
.heroes-dyn-table td.hd-cell {
  width: var(--hd-col-w, 40px);
  min-width: var(--hd-col-w, 40px);
  max-width: var(--hd-col-w, 40px);
  box-sizing: border-box;
}
/* Icon + name baseline-aligned in one vertical rhythm across every row: fixed
   icon box + fixed line-height so the text sits centred against the portrait,
   and every name starts at the SAME x (icon width + gap). The 22px gap leaves
   room for the hover-zoom (below) so the popped icon never reaches the name. */
.hd-hero-inner { display: inline-flex; align-items: center; gap: 22px; line-height: 1; }
.hd-hero-inner img {
  width: 40px; height: 23px; object-fit: cover; border-radius: 3px;
  border: 1px solid rgba(170, 220, 240, 0.18); flex: 0 0 auto;
  /* Anchor the zoom at the LEFT edge so it grows rightward only — never past the
     box's left edge (which would clip it behind the frozen-column boundary). */
  transform-origin: left center;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
/* items_dyn: item/enchant icons are 88×64 (aspect 1.375), NOT the hero 16:9 — the
   hero box (40×23) crops them top/bottom. Size the box to the item aspect (40×29)
   so cover fills it without cropping (mirrors mana_items' .mr-ico approach). */
.items-dyn-table .hd-hero-inner img { height: 29px; }
.hd-hero-name { font-weight: 600; color: #e6edf3; }
/* RULE (all tables): hovering a hero/unit portrait pops it out — like Neutral
   Creeps. The 18px gap + name-fitted column width keep the 1.8× zoom from ever
   covering the name. The icon's stacking sits under the sticky header (sticky-col
   z:3 < header), so a top-row zoom slides BEHIND the header (matches creeps). */
.heroes-dyn-table td.hd-hero:hover .hd-hero-inner img {
  /* 1.5× from the left edge → grows 20px right, clearing the 22px gap so it
     stops short of the name; z-index lifts it ABOVE the name text. */
  transform: scale(1.5); position: relative; z-index: 41;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(170, 220, 240, 0.4);
}
/* ============================================================================
   UNIFIED TOOLBAR PANEL — SITE STANDARD (every Materials toolbar).
   All controls live inside ONE bordered surface (.toolbar-panel) instead of a row
   of separate floating pills. The outer .cal-toggle-bar.inbox-bar keeps the
   Materials rhythm (28px side inset + top/bottom gaps); .toolbar-panel is the
   raised surface. RULE: wrap any new page's toolbar controls in
   <div class="toolbar-panel">…</div>.
   ============================================================================ */
.hd-page .cal-toggle-bar.hd-toolbar { flex-wrap: wrap; row-gap: 8px; }
.toolbar-panel {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px 6px; width: 100%; flex: 1 1 100%; box-sizing: border-box;
  background: #11161d; border: 1px solid #30363d; border-radius: 8px;
  padding: 7px 12px;
}
/* Switches + chip/label groups go FLAT (transparent) — the panel border replaces
   their individual pill borders. Inputs/selects (search, price combo, View select)
   keep their own field border. Chips keep their own design. */
.toolbar-panel .ua-upgrades-toggle,
.toolbar-panel .hd-remove-group,
.toolbar-panel .hd-class-group,
.toolbar-panel .hs-attack-filter-group,
.toolbar-panel .hs-attr-filter-group,
.toolbar-panel .hs-level-group,
.toolbar-panel .view-group {
  background: transparent; border-color: transparent;
}
.toolbar-panel .ua-upgrades-toggle:hover { border-color: transparent; }
.toolbar-panel .ua-upgrades-toggle { padding: 4px 4px; gap: 5px; }
/* Thin divider before every control except the first — so the panel reads as one
   surface with sections. EXCEPT between two consecutive switches (they group
   tightly, no divider). */
.toolbar-panel > *:not(:first-child) { position: relative; padding-left: 12px; }
.toolbar-panel > *:not(:first-child)::before {
  content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 1px; height: 18px; background: #30363d;
}
.toolbar-panel > .ua-upgrades-toggle + .ua-upgrades-toggle { padding-left: 4px; }
.toolbar-panel > .ua-upgrades-toggle + .ua-upgrades-toggle::before { content: none; }
.hs-attack-filter-group,
.hs-attr-filter-group,
.hs-level-group {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  box-sizing: border-box;
}
.hs-attack-filter {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 8px 0 6px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.035);
  color: #c9d1d9;
  font-family: inherit;
  font-size: 12px;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease, filter 0.12s ease;
}
.hs-attr-filter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 28px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.035);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, filter 0.12s ease;
}
.hs-attack-filter:hover {
  color: #fff;
  border-color: rgba(227, 196, 106, 0.45);
  filter: brightness(1.12);
}
.hs-attr-filter:hover {
  border-color: rgba(227, 196, 106, 0.45);
  filter: brightness(1.12);
}
.hs-attack-filter.active,
.hs-attack-filter[aria-pressed="true"] {
  border-color: rgba(227, 196, 106, 0.82);
  background: rgba(227, 196, 106, 0.14);
  box-shadow: inset 0 0 0 1px rgba(227, 196, 106, 0.12);
}
.hs-attr-filter.active,
.hs-attr-filter[aria-pressed="true"] {
  border-color: rgba(227, 196, 106, 0.82);
  background: rgba(227, 196, 106, 0.14);
  box-shadow: inset 0 0 0 1px rgba(227, 196, 106, 0.12);
}
.hs-attack-filter .atk-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 18px;
  box-sizing: border-box;
  border-radius: 5px;
  background: linear-gradient(rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0.04));
  border: 1px solid rgba(255, 255, 255, 0.14);
}
.hs-attack-filter .atk-badge img {
  width: 12px;
  height: 12px;
  object-fit: contain;
  display: block;
  opacity: 0.9;
}
.hs-attr-filter img {
  width: 18px;
  height: 18px;
  object-fit: contain;
  display: block;
}

/* ===================== Hero Lab calculator ===================== */
.hero-lab-page {
  max-width: none;
}
.hero-lab {
  display: grid;
  grid-template-columns: minmax(420px, 1fr) minmax(240px, 300px) minmax(420px, 1fr);
  gap: 18px;
  align-items: start;
  margin-top: 12px;
  padding: 0 28px 28px;
}
.hl-panel,
.hl-diff-panel {
  background:
    linear-gradient(180deg, rgba(50, 55, 61, 0.9), rgba(27, 30, 35, 0.96)),
    #181c21;
  border: 1px solid rgba(122, 111, 88, 0.55);
  box-shadow:
    inset 0 1px 0 rgba(255, 242, 200, 0.08),
    inset 0 -1px 0 rgba(0, 0, 0, 0.45),
    0 8px 18px rgba(0, 0, 0, 0.3);
  border-radius: 6px;
  padding: 16px;
}
.hl-hud {
  display: grid;
  /* Wide portrait column matches the hero-art asset's native aspect (≈ 16:9),
     same approach as heroes_stats `.mr-ico` (36×26 = 1.38:1). 124×72 keeps the
     portrait readable without forcing it into a square. */
  grid-template-columns: 124px minmax(0, 1fr);
  gap: 14px;
  align-items: start;
  margin-bottom: 10px;
}
.hl-identity {
  display: block;
  width: 124px;
}
.hl-portrait-wrap {
  position: relative;
  width: 124px;
  height: 72px;
}
.hl-hero-trigger,
.hl-inv-slot,
.hl-shop-tab,
.hl-picker-close,
.hl-picker-clear,
.hl-hero-tile,
.hl-item-tile {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.hl-hero-icon {
  /* Same pattern as heroes_stats `.mr-ico` — wide rect, cover-fit so the hero
     art fills the box without letterboxing. Codex noted: `icons/heroes/*.png`
     is a wide art asset, not square. */
  width: 124px;
  height: 72px;
  object-fit: cover;
  object-position: center;
  border-radius: 4px;
  display: block;
  border: 1px solid rgba(170, 220, 240, 0.18);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.32);
  background: #0e1115;
}
.hl-hero-trigger {
  width: 124px;
  height: 72px;
  display: block;
}
.hl-identity-main {
  display: none;
}
.hl-level-corner {
  position: absolute;
  left: 4px;
  bottom: 4px;
  width: 30px;
  height: 22px;
  z-index: 2;
}
.hl-level-input,
.hl-custom input {
  min-width: 0;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  border: 1px solid rgba(117, 108, 87, 0.72);
  border-radius: 3px;
  background:
    linear-gradient(180deg, rgba(32, 35, 39, 0.95), rgba(10, 12, 15, 0.98)),
    #0c0f12;
  color: #f2efe4;
  font: inherit;
  text-align: center;
  font-variant-numeric: tabular-nums;
  padding: 0 3px;
  -moz-appearance: textfield;
  appearance: textfield;
  box-shadow: inset 0 1px 0 rgba(255, 241, 200, 0.05);
}
.hl-level-input {
  font-size: 12px;
  font-weight: 700;
}
.hl-custom input {
  height: 30px;
}
.hl-level-input::-webkit-outer-spin-button,
.hl-level-input::-webkit-inner-spin-button,
.hl-custom input::-webkit-outer-spin-button,
.hl-custom input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
/* Dota 2 Reborn HUD inventory — visual model from the actual HUD screenshot:
     • the 3×2 main grid is a single piece — slots are very dark almost-black
       wells separated by thin light-gray dividers, achieved with the classic
       "container with a light bg + dark slots + gap" trick (the bg shows
       through the gaps as the dividers — no per-slot borders needed);
     • the round neutral slot sits on its OWN dark backdrop outside the main
       grid, with just a thin dark ring border — NOT the silver `circle_frame`
       texture (that one has a decorative right-side tab and is used elsewhere
       in the HUD, not for inventory slots);
     • backpack/courier slots (the 3 angled ones in Valve's screenshot) are
       NOT modelled — Hero Lab has only 6 + 1 neutral.
   Why I dropped the extracted plate texture (`inventory_bg.png`): on close
   inspection its diagonal lines are random stone scratches, not the slot grid,
   so using it as a background under CSS slots gave a noisy look that didn't
   match the screenshot's clean grid. The HUD's actual visible "slot well"
   look IS the CSS gap/divider trick — Valve's panorama renderer composites a
   subtle slate onto the page behind the slots, then draws the slots as flat
   colour boxes; here we just use the page's own dark colour for the divider
   tone and skip the texture entirely. */
/* Authoritative values from Valve's HUD_Inventory.res + HUD_InventoryStash.res:
   • InventoryButton wide=48 tall=36 → slot is 48×36 px (native).
   • InventoryBackground1 fillcolor "0 0 0"   → outer plate fill = pure black.
   • InventoryBackground2 fillcolor "36 36 36" → inner panel fill = #242424
     (this is what shows through the gaps between slots as the "divider"
     colour — NOT the light gray I had before).
   • Each slot also gets `materials/vgui/hud/item_bg.vmat` as its own image
     (per-slot texture). When the user provides the extracted item_bg PNG it
     will become each slot's background; until then we approximate with a flat
     near-black fill that matches the slot interior in the HUD screenshot. */
.hl-inventory {
  display: grid;
  grid-template-columns: 220px 62px;
  grid-template-rows: 122px;
  gap: 14px;
  justify-content: end;
  align-items: start;
  justify-self: end;
  margin-left: auto;
}
/* Inventory backdrop uses Valve's `inventory_bg_bg_psd.vtex`. `inventory_panel`
   belongs to another HUD surface and is too blue/stone-tiled for item wells. */
.hl-inv-grid {
  grid-column: 1;
  grid-row: 1;
  width: 220px;
  height: 122px;
  display: grid;
  grid-template-columns: repeat(3, 60px);
  grid-template-rows: repeat(2, 52px);
  /* These exact gap+padding values place the CSS slots over the texture's own
     stone-tile cells (figured out by overlaying the panel and walking the seam
     spacing — top 8px, sides 8px, between-rows 8px, between-cols 6px). */
  column-gap: 12px;
  row-gap: 10px;
  padding: 4px 8px;
  background:
    linear-gradient(180deg, rgba(23, 28, 34, 0.96), rgba(14, 18, 22, 0.98)),
    #11161d;
  border: 1px solid rgba(170, 220, 240, 0.14);
  border-radius: 8px;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.04),
    0 8px 18px rgba(0, 0, 0, 0.24);
  box-sizing: border-box;
  align-content: center;
  justify-content: center;
}
/* In-game inventory cells read as recessed rectangular wells: dark inner field,
   heavy black top/left shadow, faint right/bottom lip. */
.hl-inv-slot {
  position: relative;
  width: 60px;
  height: 52px;
  border: 0;
  border-radius: 6px;
  overflow: hidden;
  background:
    linear-gradient(180deg, rgba(50, 55, 61, 0.9), rgba(27, 30, 35, 0.96)),
    #181c21;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.04),
    inset 0 -1px 0 rgba(0,0,0,0.42),
    0 0 0 1px rgba(170, 220, 240, 0.12);
  transition: filter 0.12s ease;
}
/* Bevel overlay (Valve's `.Reborn #inventory #AbilityBevel`) — the thin
   silvery rim at the top of each slot. The texture itself is very low-alpha
   (mostly transparent), so it reads as a subtle highlight, not a hard frame. */
.hl-slot-bevel {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(170, 220, 240, 0.08), transparent 36%);
  z-index: 2;
}
.hl-inv-slot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.hl-inv-slot:hover {
  /* Valve: `#AbilityButton:hover { brightness: 1.6 }`. */
  filter: brightness(1.6);
}
.hl-inv-slot:focus-visible {
  outline: 1px solid rgba(213, 173, 95, 0.85);
  outline-offset: -2px;
}
.hl-inv-slot.is-empty::before { content: none; }
/* Round neutral slot — same matte dark fill as the rectangular slots, with a
   simple 1px dark-gray ring (NOT the silver circle_frame texture, which has a
   decorative tab that doesn't appear on real inventory slots). The slot sits
   in its own grid column outside the main slot block, like the HUD's right
   cluster of currency/neutral/gold discs. */
/* Neutral slot — Valve uses a separate `bg_neutral_slot_png.vtex` disc here.
   It is the full circular slot texture, not just the inner glyph. */
/* Neutral stack lives in column 2, height matches the inv-grid (122px) so the
   neutral slot's top edge HORIZONTALLY ALIGNS with the top edge of the 6-slot
   grid. Internal layout: neutral (52×52) → connector (12px) → enchant (30×30).
   Total = 52+12+30 = 94px, the stack is centered vertically inside 122px so
   both slots read as a paired unit. Nudged 8px further right of the inv-grid
   for breathing room. */
.hl-neutral-stack {
  grid-column: 2;
  grid-row: 1;
  position: relative;
  width: 52px;
  height: 122px;
  display: grid;
  grid-template-rows: 4px 52px 12px 30px 4px;
  /* 4px top spacer matches inv-grid's own 4px top padding, so the neutral
     slot's top edge sits on the same baseline as the item slots inside it. */
  justify-items: center;
  margin-left: 14px;
}
/* Connector line between neutral and enchant — drawn on the gap row of the
   stack grid, so it can never escape the neutral block visually. */
.hl-neutral-stack::after {
  content: "";
  grid-row: 3;
  align-self: stretch;
  justify-self: center;
  width: 2px;
  background: linear-gradient(180deg, rgba(170, 220, 240, 0.28), rgba(72, 88, 102, 0.55));
}
.hl-neutral-slot {
  grid-row: 2;
  width: 52px;
  height: 52px;
  border-radius: 6px;
  background:
    linear-gradient(180deg, rgba(50, 55, 61, 0.9), rgba(27, 30, 35, 0.96)),
    #181c21;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.04),
    0 0 0 1px rgba(170, 220, 240, 0.14),
    0 8px 18px rgba(0, 0, 0, 0.2);
  transition: filter 0.12s ease, box-shadow 0.12s ease;
}
.hl-neutral-slot .hl-slot-bevel { border-radius: 6px; }
.hl-enchant-slot {
  grid-row: 4;
  width: 30px;
  height: 30px;
  border-radius: 6px;
  background:
    linear-gradient(180deg, rgba(50, 55, 61, 0.9), rgba(27, 30, 35, 0.96)),
    #181c21;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.04),
    0 0 0 1px rgba(170, 220, 240, 0.12),
    0 5px 10px rgba(0, 0, 0, 0.18);
}
.hl-enchant-slot .hl-slot-bevel { border-radius: 6px; background: linear-gradient(180deg, rgba(170, 220, 240, 0.08), transparent 36%); }
/* Item icons in neutral + enchant slots: fill the slot completely. Source
   icons (Dota's neutral/enchant PNGs) are rendered as `cover` to remove the
   blank top/bottom strips that appeared when contain was leaving the slot's
   margins exposed; the icons already crop cleanly. */
.hl-enchant-slot img,
.hl-neutral-slot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 6px;
}
.hl-neutral-slot:hover { filter: brightness(1.6); }
.hl-neutral-slot.is-empty::before { content: none; }
.hl-enchant-slot:hover { filter: brightness(1.6); }
.hl-enchant-slot.is-empty::before { content: none; }
.hl-neutral-mark {
  display: none;
}
.hl-enchant-mark {
  display: none;
}
.hl-slot-glow {
  pointer-events: none;
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: transparent;
}
.hl-bars {
  display: grid;
  gap: 8px;
  margin-bottom: 12px;
}
.hl-bar {
  position: relative;
  height: 26px;
  border-radius: 3px;
  overflow: hidden;
  border: 1px solid rgba(0, 0, 0, 0.55);
  background: #040608;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.08),
    inset 0 -1px 0 rgba(0,0,0,0.6);
}
.hl-bar-fill {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 3px;
}
.hl-bar-hp .hl-bar-fill {
  background: linear-gradient(180deg, #425d25 0%, #5ba539 20%, #4d9030 50%, #425d25 100%);
}
.hl-bar-mp .hl-bar-fill {
  background: linear-gradient(180deg, #2b4287 0%, #4165ce 20%, #4a73ea 50%, #2b4287 100%);
}
.hl-bar-fill::before,
.hl-bar-fill::after {
  content: "";
  pointer-events: none;
  position: absolute;
  inset: -45% -22%;
  opacity: 0.38;
  mix-blend-mode: screen;
}
.hl-bar-fill::before {
  background:
    radial-gradient(70px 28px at 18% 52%, rgba(255, 255, 255, 0.32), transparent 68%),
    radial-gradient(92px 30px at 76% 48%, rgba(255, 255, 255, 0.20), transparent 72%),
    linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.14) 42%, transparent 68%);
  transform: translateX(-28%);
  animation: hl-bar-flow 8.5s ease-in-out infinite;
}
.hl-bar-fill::after {
  inset: -28% -10%;
  opacity: 0.24;
  background:
    radial-gradient(34px 70px at 0% 50%, rgba(255, 255, 255, 0.26), transparent 72%),
    radial-gradient(42px 80px at 100% 50%, rgba(255, 255, 255, 0.22), transparent 72%);
  animation: hl-bar-edge-flow 5.8s ease-in-out infinite alternate;
}
.hl-bar-hp .hl-bar-fill::before,
.hl-bar-hp .hl-bar-fill::after {
  filter: sepia(0.25) saturate(1.15) hue-rotate(38deg);
}
.hl-bar-hp .hl-bar-fill::after {
  opacity: 0.30;
}
.hl-bar-mp .hl-bar-fill::before,
.hl-bar-mp .hl-bar-fill::after {
  filter: saturate(1.22) hue-rotate(7deg);
}
@keyframes hl-bar-flow {
  0% { transform: translateX(-34%) scaleX(0.94); opacity: 0.18; }
  42% { transform: translateX(6%) scaleX(1.05); opacity: 0.42; }
  100% { transform: translateX(32%) scaleX(0.98); opacity: 0.20; }
}
@keyframes hl-bar-edge-flow {
  from { transform: translateX(-2%); opacity: 0.16; }
  to { transform: translateX(2%); opacity: 0.30; }
}
@media (prefers-reduced-motion: reduce) {
  .hl-bar-fill::before,
  .hl-bar-fill::after {
    animation: none;
  }
}
.hl-bar-value,
.hl-bar-regen {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.95),
    0 0 3px rgba(0, 0, 0, 0.95);
}
.hl-bar-value {
  left: 50%;
  transform: translate(-50%, -50%);
  color: #f2f7fb;
  padding: 0 8px;
  border-radius: 2px;
  background: linear-gradient(90deg, rgba(0,0,0,0), rgba(0,0,0,0.34), rgba(0,0,0,0));
}
.hl-bar-regen {
  right: 10px;
  color: #9df189;
}
.hl-bar-mp .hl-bar-regen {
  color: #a9d2ff;
}
.hl-custom {
  border-top: 1px solid rgba(190, 177, 134, 0.16);
  border-bottom: 1px solid rgba(190, 177, 134, 0.16);
  padding: 10px 0;
  margin-bottom: 10px;
}
.hl-custom summary {
  cursor: pointer;
  color: #d7c276;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 11px;
}
.hl-custom-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  padding-top: 10px;
}
.hl-custom-grid label {
  display: grid;
  gap: 4px;
  color: #bdc6cf;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.hl-total-list,
.hl-diff-list {
  display: grid;
  gap: 2px;
}
.hl-stat-row,
.hl-diff-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  min-height: 22px;
  padding: 4px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  color: #d6dde4;
}
.hl-stat-row span,
.hl-diff-row span {
  color: #9aa7b2;
  font-size: 12px;
}
.hl-stat-row strong,
.hl-diff-row strong {
  color: #f0f5fa;
  font-variant-numeric: tabular-nums;
}
.hl-cost strong,
.hl-kicker,
.hl-patch {
  color: #d7c276;
}
.hl-diff-head {
  display: grid;
  gap: 4px;
  margin-bottom: 14px;
  text-align: center;
}
.hl-diff-head strong {
  color: #f0f5fa;
  font-size: 22px;
  font-family: "Jersey 25", serif;
  letter-spacing: 0.04em;
}
.hl-diff-row strong.pos { color: #7ee787; }
.hl-diff-row strong.neg { color: #ff7b72; }
.hl-diff-row strong.zero { color: #6e7681; }
.hl-overlay {
  position: fixed;
  inset: 0;
  z-index: 260;
  background: rgba(6, 10, 14, 0.84);
  backdrop-filter: blur(6px);
}
.hl-overlay.is-open {
  display: grid;
  place-items: center;
  padding: 28px;
}
.hl-picker-card {
  width: min(1180px, calc(100vw - 56px));
  max-height: min(820px, calc(100vh - 56px));
  overflow: auto;
  border: 1px solid rgba(114, 103, 75, 0.92);
  border-radius: 4px;
  background:
    linear-gradient(180deg, rgba(28, 30, 33, 0.985), rgba(18, 20, 24, 0.985)),
    #171a1f;
  box-shadow:
    inset 0 1px 0 rgba(255, 244, 205, 0.08),
    0 22px 48px rgba(0, 0, 0, 0.48);
}
.hl-picker-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 14px 16px 10px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.hl-picker-head strong {
  color: #f0f5fa;
  font-size: 18px;
  font-family: "Jersey 25", serif;
  letter-spacing: 0.04em;
}
.hl-picker-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.hl-picker-close,
.hl-picker-clear,
.hl-shop-tab {
  border: 1px solid rgba(121, 109, 81, 0.75);
  background:
    linear-gradient(180deg, rgba(41, 44, 49, 0.96), rgba(15, 18, 22, 0.98)),
    #12161b;
  color: #e6e0d1;
  border-radius: 4px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.05);
}
.hl-picker-close,
.hl-picker-clear {
  height: 28px;
  padding: 0 10px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.hl-picker-close {
  width: 28px;
  padding: 0;
}
.hl-hero-grid-wrap,
.hl-shop-body {
  padding: 14px 16px 16px;
}
.hl-hero-grid-wrap {
  display: grid;
  gap: 16px;
}
.hl-hero-group header,
.hl-item-section header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  color: #d8dee5;
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.11em;
}
.hl-hero-group-icon {
  width: 16px;
  height: 16px;
  display: block;
}
.hl-hero-grid,
.hl-item-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(58px, 58px));
  gap: 6px;
}
.hl-hero-tile,
.hl-item-tile {
  width: 58px;
  height: 43px;
  border: 1px solid rgba(109, 90, 48, 0.68);
  border-radius: 2px;
  overflow: hidden;
  position: relative;
  background: #090c0f;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.05);
}
.hl-hero-tile img,
.hl-item-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.hl-hero-tile.is-selected,
.hl-item-tile.is-selected {
  border-color: #f1d27b;
  box-shadow:
    inset 0 0 0 1px rgba(255, 241, 191, 0.35),
    0 0 0 1px rgba(241, 210, 123, 0.24);
}
.hl-shop-tabs {
  display: flex;
  gap: 4px;
  padding: 0 16px;
}
.hl-shop-tab {
  min-width: 108px;
  height: 36px;
  padding: 0 14px;
  color: #cad3db;
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.hl-shop-tab.is-active {
  color: #fff7df;
  border-color: #d7c276;
  background:
    linear-gradient(180deg, rgba(55, 59, 66, 0.98), rgba(26, 29, 33, 0.98)),
    #1a1d22;
}
.hl-shop-body {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px 28px;
}
.hl-neutrals-layout {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(260px, 0.8fr);
  gap: 28px;
}
.hl-neutrals-tiers {
  display: grid;
  gap: 14px;
}
.hl-neutrals-enchants .hl-item-grid {
  grid-template-columns: repeat(auto-fill, minmax(43px, 43px));
}
@media (max-width: 1040px) {
  .hero-lab {
    grid-template-columns: 1fr;
    padding-left: 18px;
    padding-right: 18px;
  }
  .hl-diff-panel {
    order: 3;
  }
  .hl-neutrals-layout {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 820px) {
  .hl-hud,
  .hl-level-row,
  .hl-shop-body {
    grid-template-columns: 1fr;
  }
  .hl-inventory {
    justify-content: start;
  }
  .hl-picker-card {
    width: min(100vw - 20px, 1180px);
    max-height: calc(100vh - 20px);
  }
}
@media (max-width: 560px) {
  .hero-lab {
    padding-left: 10px;
    padding-right: 10px;
  }
  .hl-identity,
  .hl-custom-grid {
    grid-template-columns: 1fr;
  }
  .hl-name-row {
    flex-wrap: wrap;
  }
  .hl-quick {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .hl-identity {
    display: block;
  }
  .hl-inventory {
    grid-template-columns: 220px 62px;
    grid-template-rows: 122px;
  }
  .hl-inv-grid {
    width: 220px;
    height: 122px;
    grid-template-columns: repeat(3, 60px);
    grid-template-rows: repeat(2, 52px);
    column-gap: 12px;
    row-gap: 10px;
    padding: 4px 8px;
  }
  /* Neutral slot stays circular on mobile. */
  .hl-neutral-slot {
    width: 52px;
    height: 52px;
    aspect-ratio: 1;
    justify-self: center;
  }
  .hl-enchant-slot {
    width: 30px;
    height: 30px;
  }
  .hl-inv-slot {
    width: 60px;
    height: 52px;
    aspect-ratio: auto;
  }
  .hl-hero-grid,
  .hl-item-grid {
    grid-template-columns: repeat(auto-fill, minmax(48px, 48px));
  }
  .hl-hero-tile,
  .hl-item-tile {
    width: 48px;
    height: 36px;
  }
}
.hs-level-group {
  color: #c9d1d9;
  font-size: 12px;
}
.hs-level-group span {
  color: #8b949e;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.hs-level-group input {
  width: 44px;
  height: 28px;
  box-sizing: border-box;
  border: 1px solid #30363d;
  border-radius: 5px;
  background: #0d1117;
  color: #e6edf3;
  font: inherit;
  text-align: center;
  font-variant-numeric: tabular-nums;
  /* Hide native number-spinner controls — easy to misclick when aiming at
     the field itself. Keyboard +/- + scroll-wheel still work. */
  -moz-appearance: textfield;
  appearance: textfield;
}
.hs-level-group input::-webkit-outer-spin-button,
.hs-level-group input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.hs-level-group input:focus {
  outline: none;
  border-color: #e3c46a;
  box-shadow: 0 0 0 1px rgba(227, 196, 106, 0.22);
}
.hs-level-group input:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
/* A toolbar search box always sits on its OWN full-width row at the bottom of the
   panel (flex-basis 100% forces a wrap) — even when there are few other controls.
   No leading divider/indent since it owns its row. */
.toolbar-panel > .hd-search {
  flex: 1 1 100%; min-width: 0; padding-left: 0; margin-top: 2px;
}
.toolbar-panel > .hd-search::before { content: none; }
.toolbar-panel > .hd-search input { width: 100%; box-sizing: border-box; }
/* "Remove" group + search wrapped in the SAME 32px bordered pill-block as the
   switches, so all toolbar controls share one height/baseline. */
.hd-remove-group {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; box-sizing: border-box; padding: 0 10px;
  background: #161b22; border: 1px solid #30363d; border-radius: 5px;
}
.hd-search { flex: 0 1 240px; min-width: 150px; }
.hd-search input { height: 32px; box-sizing: border-box; }
/* "Remove" tag chips: the patch-page badges, clickable. A removed tag sinks in
   (inset shadow + nudge) and greys out — visibly "off". */
.hd-tag {
  cursor: pointer; user-select: none; font-family: inherit; font-size: 11px;
  padding: 2px 9px; transition: filter 0.12s, transform 0.12s, opacity 0.12s, box-shadow 0.12s;
}
.hd-tag:hover { filter: brightness(1.18); transform: translateY(-1px); }
.hd-tag.removed {
  filter: grayscale(1) brightness(0.62); opacity: 0.5; transform: translateY(1px);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.55);
}
/* Multi-select dropdown (items_dyn Type / Category): the button is FLAT inside the
   panel — no own box/border, just text + caret like the switches, so every control
   shares one height and the panel is the only border. The gold badge shows the
   selected count (blank when all are selected = no active filter). Only the popover
   itself is a floating box. */
.hd-dd { position: relative; display: inline-flex; }
.hd-dd-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; box-sizing: border-box; padding: 0 2px;
  background: transparent; border: none; border-radius: 0;
  color: #c9d1d9; font-family: inherit; font-size: 12.5px;
  cursor: pointer; user-select: none; transition: color 0.12s;
}
.hd-dd-btn:hover .hd-dd-label,
.hd-dd-btn[aria-expanded="true"] .hd-dd-label { color: #fff; }
.hd-dd-caret { color: #8b949e; flex: none; transition: transform 0.12s; }
.hd-dd-btn[aria-expanded="true"] .hd-dd-caret { transform: rotate(180deg); color: #c9d1d9; }
.hd-dd-badge { color: #e3c46a; font-weight: 700; font-variant-numeric: tabular-nums; }
.hd-dd-badge:empty { display: none; }
/* Popover is PORTALED to <body> + positioned `fixed` by JS (top/left), so it escapes
   .creeps-scroll's contain:paint clip and never pushes a scrollbar on an empty table. */
.hd-dd-menu {
  position: fixed; z-index: 1000;
  min-width: 150px; white-space: nowrap; max-height: 70vh; overflow: auto;
  background: #0d1117; border: 1px solid #30363d; border-radius: 6px;
  padding: 5px; box-shadow: 0 6px 22px rgba(0, 0, 0, 0.5);
  display: flex; flex-direction: column; gap: 1px;
}
.hd-dd-menu[hidden] { display: none; }
.hd-dd-opt {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px; border-radius: 4px;
  font-size: 12.5px; color: #c9d1d9; cursor: pointer; user-select: none;
}
.hd-dd-opt:hover { background: #161b22; }
.hd-dd-opt input { accent-color: #e3c46a; width: 14px; height: 14px; cursor: pointer; flex: none; }
.hd-dd-all { font-weight: 700; color: #e3c46a; }   /* the "All" master row */
.hd-dd-sep { height: 1px; background: #30363d; margin: 3px 2px; }
/* The dynamics toolbar must out-stack the table's sticky header/corner cells
   (z up to 52, hover-zoom 60) so an open dropdown popover isn't hidden under them.
   Overrides the z-index:6 from `.creeps-scroll > .inbox-bar`. */
.creeps-scroll > .cal-toggle-bar.hd-toolbar { z-index: 62; }
/* Price filter (items_dyn + mana_items, both use .toolbar-panel): FLAT — the range
   has no box, inputs read as underlined fields, label is muted. Same height as the
   rest. The 12px inner left padding keeps the "Price" label off the focus-ring border
   even when the control is the panel's FIRST child (mana_items, where the divider's
   left gap doesn't apply). */
.toolbar-panel .mr-price-range {
  height: 32px; box-sizing: border-box; padding-left: 12px;
  background: transparent; border-color: transparent;
}
.mr-price-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.6px;
  color: #8b949e; padding-right: 4px; user-select: none;
}
.toolbar-panel .mr-price-input {
  width: 44px; padding: 3px 2px;
  border-bottom: 1px solid #30363d; border-radius: 0; transition: border-color 0.12s;
}
.toolbar-panel .mr-price-input:focus { border-bottom-color: #e3c46a; }
.toolbar-panel .mr-price-clear { border-left: none; }
/* Uniform 32px height for the flat switches too, so every panel control lines up. */
.toolbar-panel .ua-upgrades-toggle { height: 32px; }
/* Full-height vertical divider separating super-category (base-version) groups —
   same idea as Neutral Creeps' .col-sep category dividers. On the first column
   of each group (except the first); aligns with the cat-row's group border. */
.heroes-dyn-table th.hd-gsep,
.heroes-dyn-table td.hd-gsep { border-left: 2px solid rgba(170, 220, 240, 0.22); }
/* Patch column header: just the version; release date shows in the hover tip. */
.heroes-dyn-table th.hd-patch {
  white-space: nowrap; padding: 4px 2px; font-weight: 600; font-size: 12px;
  color: #c9d1d9; text-align: center; vertical-align: bottom; cursor: help;
  overflow: hidden;
}
/* Super-category row: base version (e.g. 7.41) spanning its lettered variants
   (7.41/a/b/c). Reuses the shared .cat-head sticky/layout infrastructure;
   overrides the dim-uppercase look for a centred version number. Solo bases
   (single patch, no letters) render an empty cell — no label. JS
   dynRecomputeSupercats() re-spans these to the visible columns after fit. */
.heroes-dyn-table thead tr.cat-row th.hd-supercat {
  font-size: 12px; text-transform: none; letter-spacing: 0.02em;
  color: #aeb9c4; text-align: center;
}
/* The cell over the frozen Hero column continues the hero divider (0.30) up
   through the super-category row. (Beats the shared .cat-head:first-child 0.22
   rule via the extra .heroes-dyn-table class + later source order.) */
.creeps-table.heroes-dyn-table thead tr.cat-row th.hd-hero-cat {
  border-right: 2px solid rgba(170, 220, 240, 0.30);
}
/* Cell holding one dyn-cell pill (or the empty-diamond pseudo). */
.heroes-dyn-table td.hd-cell {
  text-align: center;
  vertical-align: middle;
  padding: 2px;
  line-height: 0;
}
/* Trailing spacer column: fills the reserved right-edge gutter (where the last
   patch column's 2.5× hover-pop overflows) with a clipped slice of an empty
   column, so the grid reaches the box edge instead of ending in blank space.
   Fixed 24px (= HD_RIGHT_GUTTER in scripts.js); the 28px empty diamond is
   left-aligned and clipped by the cell → reads as a column cut off at the edge. */
.creeps-table.heroes-dyn-table th.hd-spacer,
.creeps-table.heroes-dyn-table td.hd-spacer {
  width: 24px; min-width: 24px; max-width: 24px;
  overflow: hidden;
  text-align: left;
}
/* The trailing spacer is pure hover-pop room for the last patch column — render
   NOTHING in it (no empty diamond). A faded square still read as "the grid continues
   past the last patch"; an invisible gutter just lets the grid end cleanly. */
.creeps-table.heroes-dyn-table td.hd-spacer::after { display: none; }
/* Bigger pill in the matrix than on patch pages (28px vs 24px) — more legible
   in a dense grid; scoped so patch pages keep their size. */
.heroes-dyn-table .dyn-cell-wrap { width: 28px; height: 28px; }
/* PERF: empty cells are the bulk of the ~14k-cell grid. A flat fill (NO
   box-shadow, no opacity layer) paints far cheaper than thousands of inset
   shadows — the main scroll-jank source on this page. */
.heroes-dyn-table td.hd-cell.hd-empty::after {
  content: ''; display: inline-block; width: 28px; height: 28px; border-radius: 4px;
  background: rgba(12, 14, 19, 0.72);
}
/* Outside the item's lifespan (items_dyn): the item wasn't in the game in this
   patch — either not added yet, or already removed. NO empty-slot square; just a
   tiny faint dot so the live span reads as a clear band of slots flanked by
   "n/a" gaps. Flat fill (no shadow) keeps the big grid cheap to paint. */
.heroes-dyn-table td.hd-cell.hd-absent::after {
  content: ''; display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  background: rgba(125, 135, 155, 0.16);
}
/* In a marked row the n/a dots STAY visible — they carry meaning (the item
   didn't exist in those patches), unlike the decorative empty diamonds (below)
   which drop out. Bump the alpha a touch so the faint dot reads on the gold band. */
.heroes-dyn-table tbody tr.row-marked td.hd-cell.hd-absent::after { background: rgba(150, 160, 180, 0.42); }
/* Buff/nerf-only: a touched cell with no buff/nerf reads as an EMPTY cell —
   flat fill, no glassy pill, no hover pop. Hover still fires the tooltip. */
.heroes-dyn-table .dyn-cell-wrap.bn-empty .dyn-cell,
.heroes-dyn-table .dyn-cell-wrap.bn-empty:hover .dyn-cell {
  background: rgba(12, 14, 19, 0.72);
  box-shadow: none;
  filter: none;
  transform: none;
}
/* RULE (all tables): the gold row-mark must read as ONE uniform band + frame
   (like Neutral Creeps / Abilities), not per-cell boxes. The decorative empty
   diamonds otherwise sit on the gold tint as separate squares — drop their fill
   inside a marked row so the tint shows through uniformly (coloured pills stay,
   like icons do on the creeps marked row). */
.heroes-dyn-table tbody tr.row-marked td.hd-cell.hd-empty::after { background: transparent; }
/* RULE (all tables): a hovered/zoomed dyn-cell must sit ABOVE the sticky header,
   never behind it. The wrap is position:relative; bumping its z-index over the
   header (z:50) inside the .creeps-scroll stacking context lifts the 2.5× pop and
   its tooltip clear of the pinned header for top rows. */
.heroes-dyn-table .dyn-cell-wrap:hover { z-index: 60; }
/* Reserve room below the grid for the bottom row's 2.5× hover-pop. A scaled
   element's box counts toward the scroll container's scrollable overflow, so
   when the list is filtered short (no scrollbar) the bottom-row pop extends
   past the content and spawns a spurious vertical scrollbar on hover. The pop
   grows into this padding instead of past the scrollable edge. (~21px pop
   overflow for the 28px→70px cell.) */
.creeps-scroll:has(.heroes-dyn-table) { padding-bottom: 24px; }
/* RULE (all tables): use the shared .creeps-scroll shell AS-IS — NO per-page
   left gutter. The subnav (10px), blurb/toolbar (28px) and table (flush) keep
   the SAME left positions as Neutral Creeps / Abilities so every Materials page
   lines up identically. (An earlier .hd-page padding-left shifted this page's
   subnav + table right of the others — removed.) */
.creeps-table .creep-icon-cell img {
  width: 48px;
  height: 27px;
  /* Override the global `img { max-width: 100% }` rule — otherwise the
     auto-sized icon column (its <th> is empty, so it starts ~0px wide)
     caps the image width to the collapsed cell, leaving a 3px-wide
     vertical sliver. max-width: none lets the explicit 48px win and the
     column then grows to fit. */
  max-width: none;
  object-fit: cover;
  border-radius: 4px;
  background: rgba(139, 148, 158, 0.15);
  display: inline-block;
  vertical-align: middle;
  /* Depth treatment — the bare crops read as flat rectangles otherwise.
     A 1px cool rim + soft drop shadow + inner top highlight give the
     portrait a "card" dimensionality matching the dark-glass nav. */
  border: 1px solid rgba(170, 220, 240, 0.18);
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
  /* Scale symmetrically about the exact center so the zoomed portrait
     stays vertically aligned with the unit name beside it. */
  transform-origin: center center;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.creeps-table .creep-icon-cell img:hover {
  transform: scale(1.6);
  position: relative;
  z-index: 30;            /* above sticky header + neighbours */
  background: #0e1217;    /* opaque under the portrait */
  box-shadow:
    0 3px 8px rgba(0, 0, 0, 0.6),
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 0 0 1px rgba(170, 220, 240, 0.4);
}
/* Last row: grow the zoom UPWARD so the enlarged portrait isn't clipped by the
   scroll box / hidden under the horizontal scrollbar at the bottom edge.
   (The .creeps-scroll overflow context can't be widened without breaking the
   sticky header, so we steer the transform instead.) */
.creeps-table tbody tr:last-child .creep-icon-cell img,
.creeps-table tbody tr:last-child .abil-ico,
.creeps-table tbody tr:last-child .abil-ico-wrap {
  transform-origin: center bottom;
}
/* Vertical section separators — a brighter right border grouping the
   table into identity | survivability | offense | economy | utility. */
/* Category boundary — left border on the first column of each super-category
   (always a Standard column, so it shows in both views). */
.creeps-table th.col-sep,
.creeps-table td.col-sep {
  border-left: 2px solid rgba(170, 220, 240, 0.30);
}
/* Sortable headers — click to sort. The ▾ indicator brightens to ▲/▼ on
   the active column. */
.creeps-table th.sortable {
  cursor: pointer;
  user-select: none;
}
.creeps-table th.sortable:hover {
  background: #1d2530;
}
/* Sort indicator — thin line arrows (↕ / ↑ / ↓) instead of heavy filled
   triangles, for a more refined look. Dim until the column is active. */
/* Sort indicator centering — shared by Neutral Creeps, Unit Abilities and
   Mana Items so all three behave identically. The bare ↕/↑/↓ glyphs hang
   low on the baseline; vertical-align: middle + a small upward nudge pulls
   them onto the header text's optical centre. */
/* Label + arrow both vertical-align:middle so they share ONE midline —
   otherwise the uppercase label aligns to its baseline while the arrow
   centres on x-height, dropping it below the letters. */
.creeps-table th.sortable .th-label,
.mr-table thead th.sortable .th-label {
  vertical-align: middle;
}
.creeps-table th.sortable .sort-ind,
.mr-table thead th.sortable .sort-ind {
  margin-left: 4px;
  display: inline-block;
  vertical-align: middle;
  line-height: 1;
  position: relative;
  /* Centered against the cap-height of large table headers. */
  top: -0.04em;
  width: 11px;
  height: 11px;
  /* Coloured via background-color through a PNG mask — neutral default
     greyish, gold when the column is actively sorted (site accent #e3c46a). */
  background-color: rgba(201, 209, 217, 0.55);
  -webkit-mask: url('icons/sort/sort.png') center / contain no-repeat;
  mask: url('icons/sort/sort.png') center / contain no-repeat;
}
.creeps-table th.sortable .sort-ind::after,
.mr-table thead th.sortable .sort-ind::after { content: none; }
.creeps-table th.sort-desc .sort-ind,
.creeps-table th.sort-asc .sort-ind,
.mr-table thead th.sort-desc .sort-ind,
.mr-table thead th.sort-asc .sort-ind {
  background-color: #e3c46a;
}
.creeps-table th.sort-desc .sort-ind,
.mr-table thead th.sort-desc .sort-ind {
  -webkit-mask-image: url('icons/sort/sort-down.png');
  mask-image: url('icons/sort/sort-down.png');
}
.creeps-table th.sort-asc .sort-ind,
.mr-table thead th.sort-asc .sort-ind {
  -webkit-mask-image: url('icons/sort/sort-up.png');
  mask-image: url('icons/sort/sort-up.png');
}
/* Stat cells with a recorded change history get a dotted underline cue +
   help cursor, hinting that hover shows the changelog. */
.creeps-table td.has-history,
.mr-table td.has-history {
  cursor: help;
  text-decoration: underline dotted rgba(170, 220, 240, 0.5);
  text-underline-offset: 3px;
}
/* Per-stat changelog tooltip — body-level (escapes the .creeps-scroll
   overflow clip), positioned by scripts.js above the hovered cell.
   font-size matches the table cells (11.5px). */
.stat-hist-tip {
  position: fixed;
  z-index: 1100;
  pointer-events: none;
  display: none;
  min-width: 180px;
  padding: 7px 11px;
  background: rgba(13, 17, 23, 0.97);
  border: 1px solid rgba(170, 220, 240, 0.35);
  border-radius: 6px;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.5);
  font-size: 11.5px;
  color: #c9d1d9;
  text-align: center;
  /* Tall histories (e.g. Guardian Greaves) would overflow the viewport.
     Cap height and let the tooltip scroll internally; JS clamps the top so
     the box never runs off-screen. pointer-events stays none so the wheel
     scrolls the page, not the tip — fine, the cap keeps it readable. */
  max-height: calc(100vh - 24px);
  overflow-y: auto;
}
.stat-hist-tip.is-visible { display: block; }
.stat-hist-tip .stat-chg { padding: 3px 0; }
.stat-hist-tip .stat-chg + .stat-chg {
  border-top: 1px solid rgba(139, 148, 158, 0.18);
  margin-top: 3px;
}
/* Net first→today summary pinned at the top of the tooltip, divider below. */
.stat-hist-tip .stat-net {
  padding: 1px 0 6px;
  margin-bottom: 5px;
  border-bottom: 1px solid rgba(170, 220, 240, 0.30);
  font-weight: 600;
  white-space: nowrap;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.stat-hist-tip .stat-net-label {
  color: #8b949e;
  font-size: 0.82em;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  margin-right: 5px;
}
/* Entry header: patch number top-left, date top-right (dd.mm.yy). */
.stat-hist-tip .stat-chg-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 14px;
  font-variant-numeric: tabular-nums;
  padding-bottom: 2px;
  margin-bottom: 2px;
  border-bottom: 1px solid rgba(139, 148, 158, 0.14);
}
.stat-hist-tip .chg-patch { font-weight: 600; color: #79c0ff; }
.stat-hist-tip .chg-date { color: #8b949e; font-size: 10px; }
.stat-hist-tip .stat-chg-line {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: left;
  margin-top: 1px;
}
.stat-hist-tip .chg-label { color: #adbac7; }
.stat-hist-tip .chg-cycle { color: #c9d1d9; padding: 0 3px; }
/* Ability tags — same palette as the changelog NEW/DEL badges. */
.stat-hist-tip .chg-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 0 5px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  border: 1px solid;
  border-radius: 4px;
  vertical-align: 1px;
}
.stat-hist-tip .chg-tag.added    { color: #9a7f4d; background: rgba(220,175,95,0.06); border-color: rgba(220,175,95,0.22); }
.stat-hist-tip .chg-tag.removed  { color: #a86060; background: rgba(180,70,70,0.07); border-color: rgba(180,70,70,0.25); }
.stat-hist-tip .chg-tag.replaced { color: #7f9cc4; background: rgba(120,150,200,0.07); border-color: rgba(120,150,200,0.25); }
/* % delta — muted green for an increase (stat up = buff), muted red for
   a decrease. Less saturated than the changelog badges to read softer in
   the tooltip. */
.stat-hist-tip .stat-pct { font-weight: 600; margin-left: 7px; }
.stat-hist-tip .stat-pct.up   { color: #7fb88c; }
.stat-hist-tip .stat-pct.down { color: #cf8b8b; }
.stat-hist-tip .stat-pct.flat { color: #8b949e; }
/* Per-column value tints. */
.creeps-table td.col-hp_regen { color: #9ae66e; }   /* light green (салатовый) */
.creeps-table td.col-mp_regen { color: #6fc3ec; }   /* light blue (голубоватый) */
.mr-table.hs-table td.hs-col-hpr { color: #9ae66e; }
.mr-table.hs-table td.hs-col-mpr { color: #6fc3ec; }
.creeps-table td.col-hp_regen.regen-zero,
.mr-table.hs-table td.hs-col-hpr.regen-zero { color: rgba(154, 230, 110, 0.42); }
.creeps-table td.col-mp_regen.regen-zero,
.mr-table.hs-table td.hs-col-mpr.regen-zero { color: rgba(111, 195, 236, 0.42); }
.creeps-table td.col-gold     { color: #e3b341; }   /* gold (золотой) */
.creeps-table td.col-xp       { color: #8bb8d6; }   /* dusty blue (пыльно-голубой) */
/* Attack-type value colors. */
.creeps-table td.atk-basic    { color: #56d364; }   /* "Обычный" — green */
.creeps-table td.atk-pierce   { color: #b18cd9; }   /* "Проникающий" — muted purple */
/* Tier-break: thicker top border between level groups (Ур. changes). */
.creeps-table tr.tier-break td {
  border-top: 2px solid rgba(170, 220, 240, 0.55);
}
/* "Copied" toast — fixed-positioned near the clicked icon, fades in
   quickly then drifts up and out. JS positions it and toggles
   .is-visible; the transition handles the fade + lift. */
.copy-toast {
  position: fixed;
  transform: translate(-50%, -100%) translateY(4px);
  z-index: 1100;
  pointer-events: none;
  padding: 4px 10px;
  font-size: 11.5px;
  font-weight: 600;
  color: #e6edf3;
  background: rgba(13, 17, 23, 0.92);
  border: 1px solid rgba(170, 220, 240, 0.35);
  border-radius: 5px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.18s ease, transform 0.35s ease;
}
.copy-toast.is-visible {
  opacity: 1;
  transform: translate(-50%, -100%) translateY(-4px);
}
.creeps-table thead th {
  position: sticky;
  top: 0;                /* sticky to the scroll box (which pins under the nav) */
  z-index: 50;           /* above body cells AND the hover-zoomed icon/ability
                            (z 30/40 below), so a hovered row near the top never
                            paints over the pinned header */
  background: #161b22;
  color: #e6edf3;
  font-weight: 600;
  text-align: center;
  padding: 6px 6px;
  border-bottom: 1px solid rgba(139, 148, 158, 0.35);
  white-space: nowrap;
  line-height: 1.2;
}
/* Subscript header sub-label (EHP "phys"/"mag") — small dim text set to
   the bottom-right of the main word, like a formula subscript, so the
   column stays narrow without a full second line. */
.creeps-table thead th .th-sub {
  font-size: 8.5px;
  font-weight: 400;
  color: #8b949e;
  margin-left: 1px;
  /* Nudge down like a subscript via relative offset + zero line-height so the
     sub-label does NOT grow the header's line box. vertical-align:sub would
     enlarge the row only where EHP columns appear (Expanded), making the whole
     header taller than in Standard — the cause of the height "jump" on toggle. */
  display: inline-block;
  position: relative;
  top: 2px;
  line-height: 0;
}
.creeps-table tbody td {
  padding: 5px 8px;
  border-bottom: 1px solid rgba(139, 148, 158, 0.12);
  /* Single-line cells: every value is short (numbers, %, "Ближняя (100)",
     ability dnames like "Envenomed Weapon"). nowrap lets table-layout:
     auto size each column to the widest single-line content, so the
     browser picks ideal widths and nothing wraps awkwardly. */
  white-space: nowrap;
  vertical-align: middle;
  line-height: 1.3;
}
/* Row hover tint on the <tr> (not the <td>) so cell-specific colours — Damage
   Type tints, etc. — paint on top and stay visible; the hover shows through
   transparent cells and underneath the semi-transparent coloured ones. */
.creeps-table tbody tr:hover {
  background: rgba(56, 139, 253, 0.08);
}
/* Click-toggled row mark — matches the Mana Items style: gold inset frame
   around the whole row + soft tint, no fade animation. Single-select
   (clicking another row moves the mark). */
.creeps-table tbody tr.row-marked > td {
  box-shadow:
    inset 0  2px 0 rgba(231, 196, 106, 0.95),
    inset 0 -2px 0 rgba(231, 196, 106, 0.95);
  background-image: linear-gradient(rgba(231, 196, 106, 0.12),
                                    rgba(231, 196, 106, 0.12));
}
.creeps-table tbody tr.row-marked > td:first-child {
  box-shadow:
    inset  2px  0  0 rgba(231, 196, 106, 0.95),
    inset  0    2px 0 rgba(231, 196, 106, 0.95),
    inset  0   -2px 0 rgba(231, 196, 106, 0.95);
}
.creeps-table tbody tr.row-marked > td:last-child {
  box-shadow:
    inset -2px  0  0 rgba(231, 196, 106, 0.95),
    inset  0    2px 0 rgba(231, 196, 106, 0.95),
    inset  0   -2px 0 rgba(231, 196, 106, 0.95);
}
/* Sticky identity cells inside a marked row pick up the same tint without
   the inset frame (the frame would double-paint through the sticky cell). */
.creeps-table tbody tr.row-marked .sticky-col { --sticky-bg: #2b251a; }
.creeps-table tbody tr.row-marked:hover .sticky-col { --sticky-bg: #34291e; }
/* A marked row's sticky identity cell must keep its opaque fill UNDERNEATH the
   gold frame. The generic `.row-marked > td:first-child/:last-child` frame is a
   box-shadow, and box-shadow is a single property — so it REPLACES the sticky
   cell's `inset 0 0 0 9999px var(--sticky-bg)` fill that normally covers the
   collapsed-border seam. Without the fill, the seam bled at the column junction
   (looked like a small gap between Hero and the next column). Re-add the fill as
   the LAST shadow (painted beneath the earlier gold frame lines). */
.creeps-table tbody tr.row-marked > td.sticky-col {
  box-shadow:
    inset  0    2px 0 rgba(231, 196, 106, 0.95),
    inset  0   -2px 0 rgba(231, 196, 106, 0.95),
    inset  0    0   0 9999px var(--sticky-bg);
}
.creeps-table tbody tr.row-marked > td.sticky-col:first-child {
  box-shadow:
    inset  2px  0  0 rgba(231, 196, 106, 0.95),
    inset  0    2px 0 rgba(231, 196, 106, 0.95),
    inset  0   -2px 0 rgba(231, 196, 106, 0.95),
    inset  0    0   0 9999px var(--sticky-bg);
}
.creeps-table tbody tr.row-marked > td.sticky-col:last-child {
  box-shadow:
    inset -2px  0  0 rgba(231, 196, 106, 0.95),
    inset  0    2px 0 rgba(231, 196, 106, 0.95),
    inset  0   -2px 0 rgba(231, 196, 106, 0.95),
    inset  0    0   0 9999px var(--sticky-bg);
}
/* Legacy .row-flash class (one-shot fade) is no longer applied by JS but
   the keyframes still live below — harmless dead CSS, retained so any
   third-party styling doesn't break. */
/* First column (unicode tier dots) — centered glyph, smaller letter-spacing. */
.creeps-table th:first-child,
.creeps-table td:first-child {
  text-align: center;
  font-family: 'Segoe UI Symbol', 'Apple Color Emoji', monospace;
  letter-spacing: 1px;
  white-space: normal;
}
/* Per-tier row tint — lighter for top picks, darker for fallback. */
.creeps-table tr.tier-1 td:first-child { color: #56d364; }   /* best pick — green */
.creeps-table tr.tier-2 td:first-child { color: #d29922; }   /* situational — amber */
.creeps-table tr.tier-3 td:first-child { color: #db6d28; }   /* rare/early — orange */
.creeps-table tr.tier-4 td:first-child { color: #f85149; }   /* worst — red */
/* Tier legend footer rows (no data, just label in column 3). */
.creeps-table tr.creeps-footer td {
  background: rgba(139, 148, 158, 0.06);
  color: #8b949e;
  font-style: italic;
  border-bottom: none;
}
.creeps-table tr.creeps-footer td:nth-child(3) {
  font-style: normal;
  color: #c9d1d9;
  font-weight: 600;
  padding-left: 16px;
}

/* Notes section below the creeps table — long prose cells the spreadsheet
   author parked in random narrow columns get extracted here so they read
   as paragraphs instead of 1700px-tall ribbons. */
.creeps-notes {
  margin-top: 28px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: 16px;
}
.creeps-notes > h2 {
  grid-column: 1 / -1;
  margin: 0 0 4px;
  font-size: 18px;
  font-weight: 600;
  color: #e6edf3;
}
.creeps-note {
  background: rgba(22, 27, 34, 0.6);
  border: 1px solid rgba(139, 148, 158, 0.2);
  border-radius: 6px;
  padding: 12px 14px;
  font-size: 12.5px;
  color: #c9d1d9;
  line-height: 1.45;
}
.creeps-note h3 {
  margin: 0 0 8px;
  font-size: 13.5px;
  color: #e6edf3;
  font-weight: 600;
}
.creeps-note p {
  margin: 0 0 8px;
}
.creeps-note p:last-child { margin-bottom: 0; }
.creeps-note strong {
  color: #79c0ff;
  font-weight: 600;
}

/* ───────────────────────── MANA REGEN TABLE ───────────────────────────── */
.mr-blurb {
  /* Spans the full page width on every table page (Neutral Creeps /
     Neutral Abilities / Mana Items) — no max-width clamp. The toolbar's
     padding-top below handles the gap to the View / Price controls. */
  margin: 4px 0 0;
  font-size: 13px;
  line-height: 1.55;
  color: #8b949e;
}
.mr-blurb strong { color: #c9d1d9; font-weight: 600; }
.mr-blurb code {
  background: rgba(110, 118, 129, 0.12);
  padding: 1px 5px;
  border-radius: 3px;
  font-size: 12px;
  color: #c9d1d9;
}
.mr-table {
  width: 100%;
  border-collapse: collapse;
  /* Match the Neutral Creeps / Unit Abilities tables exactly (11.5px). */
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  /* Fixed layout: the Item column gets a set width, every numeric column then
     splits the remaining page width EQUALLY (no auto-sized narrow/wide mix). */
  table-layout: fixed;
}
/* Item column — wide enough for the icon + longest item name. */
.mr-table .mr-col-name,
.mr-table .mr-name {
  width: 230px;
}
/* The seven numeric columns share the rest equally (table-layout: fixed gives
   every width-less column an equal slice). Keep their text centred + clipped
   cleanly if a header would otherwise overflow its equal slice. */
.mr-table thead th:not(.mr-col-name) {
  white-space: normal;
  overflow-wrap: anywhere;
}
.mr-table thead th,
.mr-table tbody td {
  /* Faint vertical separator on the left edge — matches Neutral Creeps /
     Unit Abilities table convention. Skipped on the first column below. */
  border-left: 1px solid rgba(170, 220, 240, 0.12);
}
.mr-table thead th:first-child,
.mr-table tbody td:first-child {
  border-left: none;
}
.mr-table thead th {
  /* Typography matched 1:1 to .creeps-table thead th — inherits the 11.5px
     table font size (no override), same weight, colour, padding, casing,
     line-height so all three tables share one header design. */
  background: #161b22;
  color: #e6edf3;
  font-weight: 600;
  text-align: center;
  padding: 6px 6px;
  line-height: 1.2;
  /* Cyan bottom rim + drop shadow drawn via box-shadow, NOT border-bottom:
     Chrome drops collapsed borders on sticky cells mid-scroll (the line
     vanished). An inset box-shadow repaints reliably while stuck. */
  box-shadow:
    inset 0 -2px 0 rgba(120, 200, 235, 0.85),
    0 4px 10px rgba(0, 0, 0, 0.45);
  cursor: pointer;
  user-select: none;
  position: sticky;
  /* Mana Items now lives in the same .creeps-scroll box as Neutral Creeps:
     the box is the scroll container, so the header pins at the box's top.
     The subnav, blurb and toolbar scroll away above it (they're in-flow /
     sticky-left only). */
  top: 0;
  z-index: 35;           /* above the hover-zoomed item icon (z 30) so a
                            top-row icon pop-out never paints over the header,
                            but below toolbar/subnav/nav (z 80/90/100) */
  white-space: nowrap;
}
/* Toolbar pins right under the site nav. The painted background extends a
   bit BELOW the buttons (padding-bottom) so that when the headers pin under
   it there's a visible gap — not the buttons sitting flush on the table.
   JS measures the toolbar's full height (incl. this padding) into
   --mr-thead-top, so the headers pin exactly at the painted bottom edge. */
.mr-toolbar {
  /* Was position:sticky — user wants only the site nav and the table category
     headers to stay pinned while scrolling. The Price filter + Hide Active
     toggle now scroll away with the page. The table thead still sticks at
     var(--mr-thead-top) so column names remain visible. */
  position: relative;
  z-index: 5;
  background: #0a0e13;
  box-sizing: content-box;
  /* Unified breathing room: 28px above the toolbar + 14px below, matching
     the Neutral Creeps / Neutral Abilities / Calendar pages. JS reads the
     resulting total height into --mr-thead-top automatically. */
  margin-bottom: 0;
  padding: 28px 0 14px;
}
/* OPAQUE hover background — a translucent one let table rows scrolling
   beneath the sticky header bleed through. This is the #161b22 base blended
   with the old rgba(48,54,61,0.5) tint, fully opaque. */
.mr-table thead th:hover { background: #232930; }
/* Sort indicator — identical glyph + dim/active logic to the creeps-table
   convention: ↕ (neutral, dim), ↓ (desc, active), ↑ (asc, active). */
/* (sort-ind centering is defined once in the shared rule above.) */
/* Denser divider splitting raw stats (Price…Mana regen) from the computed
   columns (Cost per regen onward). */
.mr-table thead th.mr-col-cost_per_regen,
.mr-table tbody td.mr-sep {
  border-left: 2px solid rgba(170, 220, 240, 0.40);
}
/* sort-ind look (mask + colour) is defined once in the shared rule above. */
.mr-table tbody td {
  padding: 6px 10px;
  border-bottom: 1px solid rgba(48, 54, 61, 0.5);
  text-align: center;
  color: #c9d1d9;
}
.mr-table tbody tr:hover { background: rgba(48, 54, 61, 0.3); }
/* Item-name cell: icon stays in a fixed-width left column so every icon
   lines up vertically. Name + dropdown arrow share the right region as
   an inline flex group — the arrow sits flush against the name so it
   reads as "expand THIS item" rather than floating at the cell edge. */
.mr-table .mr-name {
  display: grid;
  grid-template-columns: 40px 1fr;
  align-items: center;
  /* Room so the hover-zoomed icon (.mr-ico scales 1.8) doesn't cover the name
     — same fix as Neutral Abilities' .ua-ability-inner gap. */
  gap: 16px;
  text-align: left;
  min-width: 230px;
}
.mr-name-body {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.mr-name-text {
  text-align: left;
  white-space: nowrap;
}
.mr-name-text { color: #e6edf3; font-weight: 500; }

/* Engine-constant inline chip (+12 max mana, +0.05 mana regen per Int).
   Visually it's just a bold value with the same dotted-underline cue used
   by `.has-history` table cells — hover triggers the stat-hist-tip popup
   driven by data-hist. Per-patch source: Liquipedia's
   /Intelligence/Changelogs (hardcoded engine constants are not in our
   scraped patchnotes_english.txt). */
.mr-const {
  font-weight: 600;
  color: #c9d1d9;
}
.mr-const.has-history {
  cursor: help;
  text-decoration: underline dotted rgba(170, 220, 240, 0.5);
  text-underline-offset: 3px;
}
/* Match the patch-page convention for item icons (rectangular 50×36 cover) */
.mr-ico {
  width: 36px;
  height: 26px;
  object-fit: cover;
  border-radius: 3px;
  flex-shrink: 0;
  box-shadow: 0 0 0 1px rgba(170, 220, 240, 0.18);
  transform-origin: center center;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
/* Hover-zoom the item icon, mirroring the ability-icon pop-out (same scale +
   centre origin as .abil-ico in the other tables) — symmetric expansion so it
   doesn't dump itself onto the item name to the right. */
.mr-ico:hover {
  transform: scale(1.8);
  position: relative;
  z-index: 30;
  box-shadow:
    0 3px 8px rgba(0, 0, 0, 0.6),
    0 0 0 1px rgba(170, 220, 240, 0.4);
}
.mr-ico-blank {
  background: rgba(110, 118, 129, 0.15);
  border: 1px solid rgba(110, 118, 129, 0.3);
  box-shadow: none;
}
.mr-dash { color: #6e7681; }

/* Mana-regen multiplier chip (Kaya = +30%, etc). Sits inline next to the
   flat regen number. */
.mr-mult-chip {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  background: rgba(170, 220, 240, 0.12);
  border: 1px solid rgba(170, 220, 240, 0.32);
  border-radius: 10px;
  font-size: 10.5px;
  color: #79c0ff;
  font-weight: 600;
  vertical-align: middle;
}

/* "Hide Active" toolbar toggle uses the existing ua-upgrades-toggle pill
   styling; rows tagged .mr-active-row collapse via display: none. */
.mr-table tbody tr.mr-active-row.mr-hide-active { display: none; }
.mr-active-row .mr-name-text { color: #a8b3bd; font-style: italic; }

/* Price range combo — one bordered container holds both inputs + the
   clear-X so they read as a single control. Inputs are borderless inside
   (focus ring is drawn on the parent). */
.mr-price-range {
  display: inline-flex;
  align-items: center;
  background: #161b22;
  border: 1px solid #30363d;
  border-radius: 5px;
  transition: border-color 0.12s;
}
.mr-price-range:focus-within { border-color: #58a6ff; }
.mr-price-input {
  width: 56px;
  background: transparent;
  border: none;
  color: #c9d1d9;
  padding: 4px 6px;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  text-align: center;
  outline: none;
}
.mr-price-input::placeholder { color: #6e7681; }
.mr-price-input::-webkit-outer-spin-button,
.mr-price-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.mr-price-input { -moz-appearance: textfield; }
.mr-price-sep { color: #6e7681; font-size: 12px; padding: 0 2px; user-select: none; }
/* X button — flush against the right edge of the combo, no own border.
   `hidden` attr on render hides it until at least one bound is set; JS
   un-hides on input. */
.mr-price-clear {
  border: none;
  background: transparent;
  color: #8b949e;
  cursor: pointer;
  padding: 4px 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.12s;
  border-left: 1px solid #30363d;
}
.mr-price-clear:hover { color: #f85149; }
.mr-price-clear[hidden] { display: none; }
.mr-table tbody tr.mr-filtered-out { display: none; }
.mr-table tbody tr.mr-search-out { display: none; }
.mr-table tbody tr.mr-attack-out,
.creeps-table tbody tr.mr-attack-out {
  display: none;
}

/* Clicked-row highlight — yellow outline around the ENTIRE row, not each
   cell. Achieved with inset box-shadows on the TDs: top / bottom on all
   cells, left edge on the first cell, right edge on the last cell —
   together they form one continuous gold frame around the row. */
.mr-table tbody tr.mr-row-selected > td {
  box-shadow:
    inset 0  2px 0 rgba(231, 196, 106, 0.95),
    inset 0 -2px 0 rgba(231, 196, 106, 0.95);
  background-image: linear-gradient(rgba(231, 196, 106, 0.10),
                                    rgba(231, 196, 106, 0.10));
}
.mr-table tbody tr.mr-row-selected > td:first-child {
  box-shadow:
    inset  2px  0  0 rgba(231, 196, 106, 0.95),
    inset  0    2px 0 rgba(231, 196, 106, 0.95),
    inset  0   -2px 0 rgba(231, 196, 106, 0.95);
}
.mr-table tbody tr.mr-row-selected > td:last-child {
  box-shadow:
    inset -2px  0  0 rgba(231, 196, 106, 0.95),
    inset  0    2px 0 rgba(231, 196, 106, 0.95),
    inset  0   -2px 0 rgba(231, 196, 106, 0.95);
}
.mr-table tbody tr.mr-row-selected .mr-name-text { color: #fff8e0; }

/* ───────────────────────── HERO STATS TABLE (heroes_stats.html) ───────────
   mr-table skin (flat sort / heatmap / search / stat-hist tooltips reused
   via the shared classes+ids) + hero-specific bits: sticky hero column,
   Standard/Advanced column modes, per-attribute level-30 expanders. */
/* Mode visibility. Expanded-only columns hide until the View dropdown adds
   .show-adv to the table. */
/* Extras hidden in Base & Starting; visible only in Expanded. */
.hs-table .hs-extra { display: none; }
.hs-table.hs-mode-expanded .hs-extra { display: table-cell; }
/* Hero Stats uses the same two-row category header pattern as Neutral Creeps,
   but stays on the mr-table sorter/heatmap stack. */
.mr-table.hs-table thead tr.cat-row th.cat-head {
  position: sticky;
  top: 0;
  z-index: 52;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #9fb0c0;
  font-weight: 700;
  background: #161b22;
  padding: 3px 4px;
  box-shadow: 0 6px 0 0 #161b22;
  cursor: default;
  user-select: none;
}
.mr-table.hs-table thead tr.col-row th {
  top: calc(var(--cat-row-h, 0px) - 2px);
  z-index: 50;
  border-bottom: none;
  box-shadow:
    inset 0 -2px 0 rgba(120, 200, 235, 0.85),
    0 4px 10px rgba(0, 0, 0, 0.45),
    0 -14px 0 0 #161b22;
}
.mr-table.hs-table thead tr.cat-row .cat-head + .cat-head {
  border-left: 2px solid rgba(170, 220, 240, 0.22);
}
.mr-table.hs-table th.col-sep,
.mr-table.hs-table td.col-sep {
  border-left: 2px solid rgba(170, 220, 240, 0.30);
}
.mr-table.hs-table thead th .th-sub {
  display: inline-block;
  position: relative;
  top: 2px;
  margin-left: 1px;
  font-size: 8.5px;
  line-height: 0;
  color: #8b949e;
  font-weight: 400;
}
/* Sticky hero column (single frozen column → plain CSS sticky, no JS lefts).
   Solid backgrounds so scrolled columns disappear under it; header corner
   sits above both axes' pinned bars (z 51 > th 50). */
.mr-table.hs-table td.hs-name {
  position: sticky;
  left: 0;
  z-index: 3;
  background: #11161d;
}
/* The shared .mr-name rule sets display:grid on the <td>. A grid (or flex) <td>
   drops out of the table's automatic ROW-HEIGHT equalisation, so the icon-tall
   hero cell rendered a different height than the stat cells and the rows visibly
   mis-aligned (a vertical shift down the Hero column). Keep the hero cell a real
   table-cell and lay the icon + name out inline so it shares the row height with
   every stat cell; vertical-align:middle centres all cells consistently. */
.mr-table.hs-table td.hs-name {
  display: table-cell;
  vertical-align: middle;
  white-space: nowrap;
}
.mr-table.hs-table td.hs-name .hs-ico {
  display: inline-block;
  vertical-align: middle;
  margin-right: 16px;
}
.mr-table.hs-table td.hs-name .mr-name-body { vertical-align: middle; }
.mr-table.hs-table tbody td { vertical-align: middle; }
/* table-layout:fixed derives column widths from the FIRST row (the thead) —
   the body's .mr-name width rule doesn't reach the th, so without this the
   Hero HEADER was narrower than the hero column. */
.mr-table.hs-table th.hs-name { width: 230px; }
.mr-table.hs-table th.hs-col-attr { width: 56px; }
.mr-table.hs-table thead th.hs-name {
  position: sticky;
  left: 0;
  z-index: 51;
}
.mr-table.hs-table thead tr.cat-row th.cat-head:first-child {
  z-index: 53;
}
.mr-table.hs-table tbody tr:hover td.hs-name { background: #1a212b; }
/* Selected-row gold frame on the STICKY Hero cell.
   The generic `.mr-row-selected > td(:first-child)` frame is an inset box-shadow,
   and Chromium renders box-shadow OFFSET on `position:sticky` cells (the same
   quirk the `.sticky-frame` overlay exists for) — so the gold top/bottom lines
   stepped down at the Hero↔Attr seam. Draw the frame as background-gradient
   layers instead (left + top + bottom bars + gold tint over the sticky base);
   backgrounds paint correctly on sticky cells, so the lines align with the
   non-sticky cells' box-shadow frame. The base `td.hs-name` background rule wins
   over `.mr-row-selected > td`'s gradient via the `background` shorthand, so we
   re-assert the whole stack here at higher specificity. */
.mr-table.hs-table tbody tr.mr-row-selected td.hs-name {
  box-shadow: none;
  background:
    linear-gradient(rgba(231, 196, 106, 0.95), rgba(231, 196, 106, 0.95)) left   / 2px 100% no-repeat,
    linear-gradient(rgba(231, 196, 106, 0.95), rgba(231, 196, 106, 0.95)) top    / 100% 2px no-repeat,
    linear-gradient(rgba(231, 196, 106, 0.95), rgba(231, 196, 106, 0.95)) bottom / 100% 2px no-repeat,
    linear-gradient(rgba(231, 196, 106, 0.12), rgba(231, 196, 106, 0.12)),
    #11161d;
  /* MUST come AFTER the `background` shorthand (which resets origin to its
     padding-box default). border-box origin puts the top/bottom bars on the
     cell's full-height edges, matching the box-shadow frame on the other
     columns — padding-box would inset them by the cell's vertical padding and
     draw a SHORTER gold box than the rest of the row. */
  background-origin: border-box;
}
.mr-table.hs-table tbody tr.mr-row-selected:hover td.hs-name {
  background:
    linear-gradient(rgba(231, 196, 106, 0.95), rgba(231, 196, 106, 0.95)) left   / 2px 100% no-repeat,
    linear-gradient(rgba(231, 196, 106, 0.95), rgba(231, 196, 106, 0.95)) top    / 100% 2px no-repeat,
    linear-gradient(rgba(231, 196, 106, 0.95), rgba(231, 196, 106, 0.95)) bottom / 100% 2px no-repeat,
    linear-gradient(rgba(231, 196, 106, 0.16), rgba(231, 196, 106, 0.16)),
    #1a212b;
  background-origin: border-box;
}
/* Hero portrait (16:9) in the name cell — same slot the item icon uses. */
.hs-ico {
  width: 40px;
  height: 23px;
  object-fit: cover;
  border-radius: 3px;
  display: block;
}
.hs-innate-mini {
  width: 14px;
  height: 14px;
  margin-left: 6px;
  vertical-align: -2px;
  opacity: 0.9;
  filter: drop-shadow(0 0 2px rgba(10, 12, 16, 0.6));
}
.hs-innate-mini.is-hidden {
  display: none;
}
/* Primary-attribute icon cell */
.hs-attr-cell { padding: 4px 6px; }
.hs-attr-ico {
  width: 20px;
  height: 20px;
  display: block;
  margin: 0 auto;
}
/* Expanded layout grows the table HORIZONTALLY (Neutral Creeps convention) —
   auto layout + max-content width so extra columns add width and the
   .creeps-scroll box scrolls sideways, instead of fixed-layout squeezing
   every column narrower until headers clip. */
.mr-table.hs-table {
  table-layout: auto;
  width: max-content;
  min-width: 100%;
}
/* Even grid: every numeric column shares one width floor; headers never
   wrap or clip — a longer header (Attack Range) widens its own column. */
.mr-table.hs-table thead th {
  white-space: nowrap;
}
.mr-table.hs-table thead th:not(.hs-name):not(.hs-col-attr) {
  min-width: 68px;
}
.mr-table.hs-table tbody td {
  white-space: nowrap;
}

/* ============================================================================
   TERRAIN COMPARE  (terrain.html — Materials » Terrain)
   A before/after swipe slider over two pixel-aligned map renders + the patch's
   Terrain Changes list. The NEW map overlays the OLD one and is revealed
   left-to-right via clip-path up to --pos (set on .tc-stage by scripts.js
   terrainCompareInit). Gold/leather handle matches the index + nav buttons.
   ========================================================================= */
.terrain-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  align-items: flex-start;
  padding: 16px 28px 30px;
}
.terrain-compare-col {
  flex: 1 1 540px;
  max-width: 720px;
  margin: 0 auto;
}
.terrain-compare { width: 100%; }
.tc-stage {
  --pos: 50%;
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  border: 2px solid #e3c46a;
  border-radius: 8px;
  overflow: hidden;
  background: #11140f;
  box-shadow:
    inset 0 1px 0 rgba(190, 225, 240, 0.08),
    0 6px 22px rgba(0, 0, 0, 0.5);
  cursor: grab;
  touch-action: none;          /* JS owns the pointer drag — no scroll hijack */
  user-select: none;
  -webkit-user-select: none;
}
.tc-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* fill (not cover) so the map stretches to the stage box exactly like the
     SVG overlay (preserveAspectRatio="none") — keeps tree/camp markers aligned
     even if the stage isn't a perfect square. */
  object-fit: fill;
  display: block;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}
/* NEW map + its change markers revealed from the left edge up to the handle.
   clip-path is compositor-friendly so the wipe stays smooth while dragging.
   Clipping the whole layer (not just the img) means the markers reveal together
   with the new terrain. */
.tc-new-layer {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc(100% - var(--pos)) 0 0);
  will-change: clip-path;
}
.tc-tag {
  position: absolute;
  top: 10px;
  z-index: 3;
  padding: 1px 9px;
  font-family: 'Jersey 10', monospace;
  font-size: 16px;
  letter-spacing: 0.5px;
  color: #f4e6bf;
  background: rgba(20, 16, 9, 0.78);
  border: 1px solid rgba(227, 196, 106, 0.6);
  border-radius: 4px;
  pointer-events: none;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
}
.tc-tag-old { left: 10px; }
.tc-tag-new { right: 10px; }
.tc-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--pos);
  width: 44px;
  transform: translateX(-50%);
  will-change: transform;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 4;
  cursor: grab;
  touch-action: none;
}
.tc-line {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  background: #e3c46a;
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.65);
}
.tc-grip {
  position: relative;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 2px solid #e3c46a;
  background: linear-gradient(180deg, #3b2e1d 0%, #2a2014 100%);
  box-shadow:
    inset 0 1px 0 rgba(190, 225, 240, 0.12),
    0 4px 12px rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  transition: filter 0.15s ease, box-shadow 0.15s ease;
}
/* Slider arrows use the shared pixel-triangle nav-arrow design (same chunky
   gold triangle as .back-to-top / .version-nav-arrow). See the "Navigation
   arrows" rule in AGENTS.md. */
.tc-chev {
  width: 10px;
  height: 14px;
  background-repeat: no-repeat;
  background-position: center;
  background-size: auto 13px;
  image-rendering: pixelated;
}
.tc-chev-l {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 18" shape-rendering="crispEdges"><g fill="%23e3c46a"><rect x="10" y="0" width="2" height="2"/><rect x="8" y="2" width="4" height="2"/><rect x="6" y="4" width="6" height="2"/><rect x="4" y="6" width="8" height="2"/><rect x="2" y="8" width="10" height="2"/><rect x="4" y="10" width="8" height="2"/><rect x="6" y="12" width="6" height="2"/><rect x="8" y="14" width="4" height="2"/><rect x="10" y="16" width="2" height="2"/></g></svg>');
}
.tc-chev-r {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 18" shape-rendering="crispEdges"><g fill="%23e3c46a"><rect x="2" y="0" width="2" height="2"/><rect x="2" y="2" width="4" height="2"/><rect x="2" y="4" width="6" height="2"/><rect x="2" y="6" width="8" height="2"/><rect x="2" y="8" width="10" height="2"/><rect x="2" y="10" width="8" height="2"/><rect x="2" y="12" width="6" height="2"/><rect x="2" y="14" width="4" height="2"/><rect x="2" y="16" width="2" height="2"/></g></svg>');
}
.tc-handle:hover .tc-grip { filter: brightness(1.16); }
.tc-handle:focus-visible { outline: none; }
.tc-handle:focus-visible .tc-grip {
  box-shadow:
    0 0 0 3px rgba(227, 196, 106, 0.45),
    inset 0 1px 0 rgba(190, 225, 240, 0.12);
}
/* While actively dragging, hide the I-beam everywhere + brighten the grip. */
.tc-stage.is-dragging { cursor: grabbing; }
.tc-stage.is-dragging .tc-grip { filter: brightness(1.22); }

/* ---- change list beside / below the slider ---- */
.terrain-list-box {
  flex: 1 1 360px;
  min-width: 290px;
}
/* Heading row = the patch picker (as the version) + "Terrain Changes" label.
   Sized to the map toolbar's footprint so the heading sits on the toolbar's
   horizontal line and the first change row lines up with the map's top edge:
   28px control height (matches .tc-controls-bar's 28px buttons) + 10px bottom
   gap (matches the toolbar's 10px bottom padding). */
.terrain-list-head {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 28px;            /* HARD-capped to the toolbar's control height — the
                             Jersey label's tall line-box must not grow the row
                             (it just centers/overflows), so the first change row
                             stays at 28px + 10px = the map's top edge. */
  margin: 0 0 10px;
  line-height: 1;
}
.terrain-list-head-label {
  font-family: 'Jersey 25', 'Jersey 10', sans-serif;
  font-weight: 400;
  font-size: 24px;
  letter-spacing: 0.5px;
  color: #e3c46a;
}
.terrain-list { padding-left: 4px; }
.terrain-list li { padding: 3px 0; }
/* First change row meets the map's TOP edge: drop the list's 3px top margin
   (from ul.changes) and the first row's 3px top padding — together they pushed
   the first tag ~6px below the map's top border. Specificity beats ul.changes. */
.terrain-list.changes { margin-top: 0; }
.terrain-list > li:first-child { padding-top: 0; }
/* Nudge the first change row up so it sits flush with the map's top edge. */
.terrain-list-pane { margin-top: -2px; }

/* ---- tree + camp overlays (SVG), toggled by the top-bar buttons ---- */
/* Each SVG fills the stage; viewBox 0..1280 == map native px. pointer-events
   off so hover/click reach the stage. Hidden until .show-trees / .show-camps
   are set on .terrain-compare. */
.tc-markers {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.tm-layer { opacity: 0; transition: opacity 0.15s ease; }
.terrain-compare.show-trees .tm-layer-trees,
.terrain-compare.show-camps .tm-layer-camps,
.terrain-compare.show-towers .tm-layer-towers,
.terrain-compare.show-lotus .tm-layer-lotus,
.terrain-compare.show-twinGates .tm-layer-twinGates,
.terrain-compare.show-tormentors .tm-layer-tormentors,
.terrain-compare.show-bounty .tm-layer-bounty,
.terrain-compare.show-power .tm-layer-power,
.terrain-compare.show-wisdom .tm-layer-wisdom,
.terrain-compare.show-outposts .tm-layer-outposts,
.terrain-compare.show-watchers .tm-layer-watchers,
.terrain-compare.show-roshan .tm-layer-roshan { opacity: 1; }
/* trees — ONE colour everywhere. The 7.40 layout (old side) and 7.41 layout
   (new side) are split by the slider, so sweeping reveals how the forest moved. */
/* match the spectral/leamare tree style (styleDefinitions tree.alive) */
.tm-trees-g rect {
  fill: rgba(0, 255, 0, 0.3);
  stroke: rgba(0, 255, 0, 0.85);
  stroke-width: 0.8;
}
/* point-entity markers — recoloured GAME icons (32–40px), shown small + smooth
   so they read like the real minimap icons (not hard-pixelated). */
.tm-ent-g image { image-rendering: auto; }
/* every old/new layer splits at the slider (old = right of handle, new = left) */
.tc-trees-old,
.tc-camps-old,
.tm-old { clip-path: inset(0 0 0 var(--pos)); }
.tc-trees-new,
.tc-camps-new,
.tm-new { clip-path: inset(0 calc(100% - var(--pos)) 0 0); }

/* ---- control bar ABOVE the map (no longer overlaid — the map is now
   edge-to-edge, so the toggles live in their own gold bar that wraps). ---- */
.tc-controls-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 0 10px;
}
.tc-sep {
  width: 1px;
  align-self: stretch;
  min-height: 22px;
  margin: 0 3px;
  background: rgba(227, 196, 106, 0.35);
}
.tc-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  box-sizing: border-box;
  padding: 0 10px;
  border-radius: 5px;
  border: 1px solid rgba(227, 196, 106, 0.55);
  background: rgba(20, 16, 9, 0.72);
  color: #e6edf3;
  font-size: 13px;
  cursor: pointer;
  user-select: none;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
  transition: filter 0.12s ease, background 0.12s ease;
}
/* icon-only square buttons (the layer toggles) */
.tc-btn-icon {
  position: relative;
  width: 30px;
  padding: 0;
  justify-content: center;
}
/* Bigger icons INSIDE the toolbar buttons (button size itself unchanged). */
.tc-btn-icon img { width: 22px; height: 22px; }
.tc-btn-zoom img { width: 18px; height: 18px; }
.tc-btn img { image-rendering: pixelated; }
/* layer-toggle icons are the recoloured game icons → smooth, not pixelated */
.tc-layer-btn img { image-rendering: auto; }
.tc-btn:hover { filter: brightness(1.14); }
/* ON = lit gold chip; OFF (default) = greyed + pressed-in */
.tc-btn[aria-pressed="true"] {
  background: rgba(227, 196, 106, 0.22);
  border-color: #e3c46a;
  color: #fff8e0;
}
.tc-btn[aria-pressed="false"] {
  background: rgba(28, 28, 28, 0.66);
  border-color: rgba(120, 120, 120, 0.5);
  color: #9aa0a6;
  box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.55);
}
.tc-btn[aria-pressed="false"] img { filter: grayscale(0.85) brightness(0.85); }
.tc-inline-loupe { image-rendering: pixelated; vertical-align: -3px; margin: 0 3px 0 1px; }

/* version chips → map corners (OLD bottom-left, NEW top-right) */
.tc-ver {
  position: absolute;
  z-index: 7;
  padding: 1px 9px;
  border-radius: 4px;
  border: 1px solid rgba(227, 196, 106, 0.6);
  background: rgba(20, 16, 9, 0.78);
  color: #f4e6bf;
  font-family: 'Jersey 10', monospace;
  font-size: 17px;
  letter-spacing: 0.5px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
  pointer-events: none;
}
.tc-ver-new { right: 8px; top: 8px; }
.tc-ver-old { left: 8px; top: 8px; }

/* ---- tree tally + camp note under the change list ---- */
.terrain-counts { margin: 14px 4px 2px; font-size: 13px; color: #8b949e; line-height: 1.55; }
/* Trees + Neutral-camps lines are one logical block — keep them tight (the
   default 14px top would read as an empty line between them). */
.terrain-counts + .terrain-counts { margin-top: 1px; }
.terrain-counts b { color: #e6edf3; }
.tm-add-text { color: #6fe089; font-weight: 600; }
.tm-rem-text { color: #ff7a7a; font-weight: 600; }

/* ---- data-source credit under the slider (centred on the map) ---- */
.tc-source {
  margin: 12px 2px 0;
  font-size: 12px;
  line-height: 1.5;
  color: #6e7681;
  text-align: center;
}
.tc-source a {
  color: #e3c46a;
  text-decoration: none;
  border-bottom: 1px dotted rgba(227, 196, 106, 0.45);
}
.tc-source a:hover { border-bottom-color: #e3c46a; }

/* ---- patch picker (lives in the change-list heading) + per-patch panes ---- */
/* The picker reuses the calendar year-picker structure but in the terrain
   page's GOLD theme (the calendar's is blue). It sits inline in the heading as
   the version, so the heading reads "[7.41 ▾] Terrain Changes". */
/* Nudge the patch picker down 1px (the "Terrain Changes" label stays put). */
.tc-picker { transform: translateY(1px); position: relative; }
/* `transform` above turns .tc-picker into a stacking context that TRAPS the
   menu's z-index:60 inside it — and since the change-list pane comes LATER in
   the DOM than this heading, those rows painted OVER the (trapped) menu, making
   the open dropdown look transparent. Lift the whole picker above the rows
   while it's open so its menu paints on top. */
.tc-picker.is-open { z-index: 80; }
.tc-picker .cal-year-current {
  background: linear-gradient(180deg, #3b2e1d 0%, #2a2014 100%);
  border: 1px solid #e3c46a;
  color: #f4e6bf;
  /* Match the map toolbar's 28px control height so the heading row lines up
     with the toolbar and the first change row meets the map's top edge. */
  height: 28px;
  padding-top: 0;
  padding-bottom: 0;
}
.tc-picker .cal-year-caret { color: #e3c46a; }
.tc-picker .cal-year-current:hover { border-color: #f0d896; }
.tc-picker.is-open .cal-year-current,
.tc-picker .cal-year-current:focus-visible {
  border-color: #f0d896;
  box-shadow: inset 0 1px 0 rgba(190, 225, 240, 0.12),
              0 0 0 3px rgba(227, 196, 106, 0.28);
}
.tc-picker .cal-year-menu {
  background: #15110a;
  border: 1px solid rgba(227, 196, 106, 0.4);
}
.tc-picker .cal-year-opt { color: #d8c79a; }
.tc-picker .cal-year-opt:hover { background: rgba(227, 196, 106, 0.14); color: #fff5dc; }
.tc-picker .cal-year-opt.is-selected {
  background: rgba(227, 196, 106, 0.2);
  box-shadow: inset 0 0 0 1px rgba(227, 196, 106, 0.45);
}
.terrain-map-pane[hidden],
.terrain-list-pane[hidden] { display: none; }

/* ---- "comparison not available yet" fallback (patch with changes but no map
   pair): the latest map, blurred + dimmed, with a centred overlay. ---- */
.terrain-fallback {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  border: 2px solid #e3c46a;
  border-radius: 8px;
  overflow: hidden;
  background: #11140f;
  box-shadow: inset 0 1px 0 rgba(190, 225, 240, 0.08),
              0 6px 22px rgba(0, 0, 0, 0.5);
}
.tc-fallback-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  filter: blur(5px) grayscale(0.4) brightness(0.5);
  transform: scale(1.06);     /* hide the blur's soft edges */
}
.tc-fallback-veil {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 45%,
              rgba(17, 20, 15, 0.35), rgba(8, 8, 8, 0.78));
}
.tc-fallback-msg {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-align: center;
  padding: 16px;
}
.tc-fallback-title {
  font-family: 'Jersey 25', 'Jersey 10', sans-serif;
  font-size: 26px;
  letter-spacing: 0.5px;
  color: #f4e6bf;
  text-shadow: 0 2px 5px rgba(0, 0, 0, 0.8);
}
.tc-fallback-sub {
  font-family: 'Jersey 25', 'Jersey 10', sans-serif;
  font-size: 20px;
  color: #e3c46a;
  text-shadow: 0 2px 5px rgba(0, 0, 0, 0.8);
}
.tc-fallback-note {
  margin-top: 8px;
  font-size: 12px;
  color: #b9c0c8;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
}

/* ---- magnifier lens (Loupe MODE) ---- */
/* Loupe is a mode toggled by the top-bar button (adds .loupe-on). Only then
   does hovering show the pixel-loupe cursor + a gold magnifier circle that
   follows the pointer over the MAP (not the handle / top-bar). Click pins it
   (cursor back to normal); sweeping the handle then compares that spot old↔new
   inside the circle, which also carries the toggled tree/camp markers (cloned
   by scripts.js). */
.tc-stage { cursor: default; }
.terrain-compare.loupe-on .tc-stage {
  cursor: url('icons/ui/gothic/icon_loupe.png') 11 11, zoom-in;
}
.terrain-compare.loupe-on .tc-stage.lens-pinned { cursor: default; }
.tc-lens {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--lens, 184px);
  height: var(--lens, 184px);
  border-radius: 50%;
  overflow: hidden;
  border: 3px solid #e3c46a;
  box-shadow:
    0 0 0 2px rgba(8, 8, 8, 0.7),
    0 6px 18px rgba(0, 0, 0, 0.6);
  pointer-events: none;        /* clicks pass through to the stage */
  display: none;
  will-change: transform;
  z-index: 8;
}
.tc-lens.visible { display: block; }
.tc-stage.lens-pinned .tc-lens {
  box-shadow:
    0 0 0 2px rgba(8, 8, 8, 0.7),
    0 8px 24px rgba(0, 0, 0, 0.72);
}
.tc-lens-img,
.tc-lens-markers {
  position: absolute;
  top: 0;
  left: 0;
  max-width: none;             /* override the global img max-width */
  user-select: none;
  -webkit-user-drag: none;
  will-change: transform;
}
.tc-lens-new { clip-path: inset(0 calc(100% - var(--pos)) 0 0); }
.tc-lens-markers { pointer-events: none; overflow: visible; }
.tc-lens-rim {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  box-shadow: inset 0 0 16px rgba(0, 0, 0, 0.55);
}

/* ---- What's New badge (index.html, anchored to BETA label) ---- */
.version-beta-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
/* Seen state = BETA stays at its default colour, no animation */

/* Unseen state = BETA pulses between its base colour and the sikle gold */
.version-beta-wrap:not(.wn-seen) .version-beta-text {
  display: inline-block;
  animation: whatsnew-breathe 2s ease-in-out infinite;
}
.version-beta-wrap { cursor: pointer; }
.version-beta-wrap .version.version-beta { cursor: pointer; }
@keyframes whatsnew-breathe {
  0%   { color: #c9d1d9; transform: scale(1);    }
  50%  { color: #d6b764; transform: scale(1.12); }
  100% { color: #c9d1d9; transform: scale(1);    }
}

.whatsnew-popup {
  position: fixed;
  z-index: 200;
  background: #0d1117;
  border: 1px solid rgba(170, 220, 240, 0.35);
  border-radius: 7px;
  min-width: 240px;
  max-width: 310px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.65);
  display: none;
  overflow: hidden;
}
.whatsnew-popup.wn-open { display: block; }
.whatsnew-popup-head {
  padding: 8px 12px;
  border-bottom: 1px solid rgba(139, 148, 158, 0.18);
  font-family: 'Jersey 10', monospace;
  font-size: 15px;
  color: #e3c46a;
  letter-spacing: 0.4px;
}
.whatsnew-list { padding: 5px 0; }
.whatsnew-row {
  display: grid;
  grid-template-columns: 44px 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  font-size: 12px;
  color: #c9d1d9;
  text-decoration: none;
  transition: background 0.12s;
}
a.whatsnew-row:hover {
  background: rgba(139, 148, 158, 0.1);
  color: #e6edf3;
}
.whatsnew-tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  padding: 2px 5px;
  border-radius: 2px;
  text-align: center;
  box-sizing: border-box;
}
.whatsnew-tag-page  { background: rgba(88, 166, 255, 0.10); color: #79afd1; border: 1px solid rgba(88, 166, 255, 0.26); }
.whatsnew-tag-patch { background: rgba(227, 196, 106, 0.10); color: #b89d5a; border: 1px solid rgba(227, 196, 106, 0.26); }
.whatsnew-tag-hero  { background: rgba(120, 215, 145, 0.10); color: #7aaa84; border: 1px solid rgba(120, 215, 145, 0.24); }
.whatsnew-date {
  font-size: 10px;
  color: #6b7280;
  white-space: nowrap;
  justify-self: end;
}
