/* ==========================================================================
   Whats-the-meta.com

   Color rule: the page has no brand accent. Every chromatic value on the
   site is a WoW class color, injected inline from app/colors.py, so the
   palette is decided by whatever is actually meta. The chrome around it is
   strictly achromatic warm obsidian. If something isn't a class, it isn't
   colored — dungeons, tiers and UI states are encoded with luminance
   instead, which is the honest encoding for ordinal data anyway.

   Geometry rule: the chamfered "keystone" cut belongs to exactly two
   things — the active key-level bracket and tier badges. Everywhere else
   is square. Spending it more widely turns a signature into a texture.
   ========================================================================== */

:root {
  color-scheme: dark;

  --ink: #0d0c0b;
  --panel: #161513;
  --panel-hi: #1f1d1a;
  --line: #2b2825;
  --line-hi: #3d3833;
  --bone: #ede8df;
  --ash: #8c857a;
  --ash-dim: #625c54;

  /* Semantic only — never decorative. */
  --ok: #7fbf87;
  --bad: #d4635e;

  /* Talent adoption bands. Semantic like the pair above: they encode how much
     of the sample took a talent, not decoration, and they are the one place
     chroma is spent on something that is not a class. */
  --talent-core: #67c07d;
  --talent-some: #4f9ed8;

  --shell: 1080px;
  --chamfer: polygon(0 0, calc(100% - 7px) 0, 100% 7px, 100% 100%, 7px 100%, 0 calc(100% - 7px));

  --display: "Archivo", "Segoe UI", system-ui, sans-serif;
  --body: "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", monospace;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.55;
  background: var(--ink);
  color: var(--bone);
  -webkit-font-smoothing: antialiased;
}

/* Display face is set wide rather than condensed — the expanded cut reads
   as carved/monumental and is the opposite of the condensed default this
   category reaches for. */
h1, h2, h3, .brand, .bracket-tabs a, .tier-badge {
  font-family: var(--display);
  font-variation-settings: "wdth" 112;
  font-weight: 700;
}

h1 {
  margin: 0 0 0.35rem;
  font-size: clamp(1.9rem, 4.2vw, 2.6rem);
  font-weight: 800;
  letter-spacing: -0.018em;
  line-height: 1.08;
}

h2 {
  margin: 0 0 0.9rem;
  font-size: 1.15rem;
  letter-spacing: -0.005em;
}

h3 { margin: 0 0 0.5rem; font-size: 0.95rem; }

a { color: var(--bone); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

a:focus-visible,
button:focus-visible,
input:focus-visible,
.sortable-col:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: 2px;
  border-radius: 1px;
}

code {
  font-family: var(--mono);
  font-size: 0.88em;
  background: var(--panel-hi);
  border: 1px solid var(--line);
  padding: 0.08em 0.35em;
}

/* --------------------------------------------------------------- header */

/* The rule runs full-bleed while its contents align to the same shell as
   <main>, so the wordmark sits on the same left edge as the page heading. */
header {
  background: var(--ink);
  border-bottom: 1px solid var(--line);
}

.header-inner {
  display: flex;
  align-items: center;
  gap: 2rem;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 1rem 1.75rem;
}

header .brand {
  font-size: 1.5rem;
  font-weight: 800;
  font-variation-settings: "wdth" 118;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--bone);
}
header .brand:hover { text-decoration: none; color: var(--ash); }

.nav-game {
  display: flex;
  align-items: baseline;
  gap: 1.25rem;
  padding-top: 0.15rem;
}

.game-title {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ash-dim);
}
a.game-title:hover { color: var(--bone); text-decoration: none; }

.nav-links { display: flex; gap: 1.1rem; align-items: baseline; }

.nav-links a {
  font-size: 0.875rem;
  color: var(--ash);
  transition: color 0.15s ease;
}
.nav-links a:hover { color: var(--bone); text-decoration: none; }

.nav-section {
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ash-dim);
}

/* The class menu. Built on <details> so it opens, closes and takes keyboard
   focus without script; the panel is positioned against .nav-menu, hence the
   relative parent. Square like the rest of the chrome — the chamfer belongs
   to brackets and tier badges only. */
.nav-menu { position: relative; }

.nav-menu > summary {
  font-size: 0.875rem;
  color: var(--ash);
  cursor: pointer;
  list-style: none;
  transition: color 0.15s ease;
}
.nav-menu > summary::-webkit-details-marker { display: none; }
.nav-menu > summary::after {
  content: "";
  display: inline-block;
  margin-left: 0.4rem;
  border: 0.28em solid transparent;
  border-top-color: currentColor;
  transform: translateY(0.14em);
}
.nav-menu > summary:hover,
.nav-menu[open] > summary { color: var(--bone); }
.nav-menu[open] > summary::after { transform: translateY(-0.14em) rotate(180deg); }

.nav-menu-panel {
  position: absolute;
  top: calc(100% + 0.7rem);
  left: -0.9rem;
  /* Above the talent tooltips, which reach 60 and share the root stacking
     context with this. They cannot currently overlap — the header does not
     stick — but chrome losing to page content is not a trade worth leaving
     open. */
  z-index: 80;
  min-width: 15.5rem;
  padding: 0.55rem;
  background: var(--panel);
  border: 1px solid var(--line-hi);
  box-shadow: 0 14px 34px rgb(0 0 0 / 55%);
}

/* Scoped through .nav-links to outrank the `.nav-links a` sizing these
   inherit by sitting inside the nav. */
.nav-links .nav-menu-all {
  display: block;
  padding: 0.3rem 0.5rem 0.55rem;
  margin-bottom: 0.4rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ash-dim);
}
.nav-links .nav-menu-all:hover { color: var(--bone); text-decoration: none; }

.nav-menu-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.05rem 0.5rem; }
/* Two columns suit class names, which are short and of similar length. Dungeon
   names are neither -- "Seat of the Triumvirate" beside another of its length,
   both nowrap, forces the panel wider than the header it hangs from. One
   column lets the panel take its width from the longest single name instead. */
.nav-menu-grid--single { grid-template-columns: 1fr; }

.nav-links .nav-menu-grid a {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.28rem 0.5rem;
  white-space: nowrap;
}
.nav-links .nav-menu-grid a:hover { background: var(--panel-hi); }

/* The one chromatic mark in the chrome, and it earns it the same way every
   other one on the site does: it is a class color, next to that class's
   name. The icon identifies the class faster than the colour does, so it
   takes the slot where one exists and keeps the class colour as its ring;
   the bare swatch stays as the fallback for a class with no icon stored. */
.nav-menu-swatch {
  width: 0.55rem;
  height: 0.55rem;
  flex-shrink: 0;
  background: var(--class-color, #625c54);
}
.nav-menu-icon {
  width: 1.1rem;
  height: 1.1rem;
  flex-shrink: 0;
  border-radius: 3px;
  box-shadow: 0 0 0 1px var(--class-color, #625c54);
}
/* Class icons are square, so they fill the slot as-is; dungeon zone art is
   2:1 and would squash into it. Crop rather than letterbox, and keep the
   square slot rather than widening it for the zone's aspect: that is what
   .dungeon-icon already does everywhere else on the site, and holding the
   slot keeps the dungeon names on the same left edge as the class names. */
.nav-menu-icon--zone { object-fit: cover; }

header .sub { color: var(--ash); font-size: 0.9rem; }

/* Everything pinned to the right of the header: the season selector, and in
   local dev the scan controls. One right-aligned group rather than two
   competing `margin-left: auto` items, which would split the free space
   between them instead of stacking them at the edge. */
.header-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 1.25rem;
}

/* The season selector. A <details> like the class and dungeon menus rather
   than a <select>, for three reasons: the entries are real links, so the
   pre-rendered pages are crawlable and middle-clickable; it needs no script
   to navigate, which a <select> does; and the existing menu script already
   gives it click-outside and Escape dismissal.

   Boxed rather than bare, unlike the nav menus, because it reports state --
   which season you are reading -- rather than offering somewhere to go. */
.season-menu > summary {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  white-space: nowrap;
  padding: 0.42rem 0.7rem;
  border: 1px solid var(--line-hi);
  color: var(--ash);
}
.season-menu[open] > summary { border-color: var(--ash-dim); }

/* Right-aligned against the control, not left: this sits at the header's
   right edge, and a panel opening leftward from there runs off the viewport. */
.season-menu-panel {
  left: auto;
  right: 0;
  min-width: 11rem;
}

.season-option {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.25rem;
  padding: 0.4rem 0.55rem;
  font-size: 0.875rem;
  color: var(--ash);
}
.season-option:hover { background: var(--line); color: var(--bone); text-decoration: none; }
.season-option--active { color: var(--bone); }

.season-option-note {
  font-family: var(--mono);
  font-size: 0.58rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ash-dim);
}

main {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 2.25rem 1.75rem 1rem;
}

/* Intro/description text under a heading. */
.meta { color: var(--ash); }
h1 + .meta, .run-header + .meta { max-width: 62ch; margin-top: 0; }

.empty { color: var(--ash-dim); }

/* ------------------------------------------- signature: bracket selector */

/* Key level is the axis the whole site turns on, so it gets the page's
   loudest typography rather than the usual row of filter chips. Content
   here must stay text-only: base.html's bracket-memory script matches on
   the anchor's textContent, so decoration is pseudo-elements only. */
.bracket-tabs {
  display: flex;
  gap: 0;
  align-items: stretch;
  margin: 0 0 1.75rem;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--line);
}

.bracket-tabs a {
  position: relative;
  padding: 0.5rem 1.05rem 0.65rem;
  font-size: 1.12rem;
  font-weight: 700;
  font-variation-settings: "wdth" 118;
  letter-spacing: -0.01em;
  color: var(--ash-dim);
  transition: color 0.15s ease;
}
.bracket-tabs a:hover { color: var(--bone); text-decoration: none; }

.bracket-tabs a.active {
  color: var(--ink);
  background: var(--bone);
  clip-path: var(--chamfer);
}

/* Secondary bracket rows (PvP region, PvP mode) carry words rather than
   key levels, so they step down to a quieter size. */
.bracket-tabs + .bracket-tabs {
  margin-top: -1.25rem;
  border-bottom: 0;
}
.bracket-tabs + .bracket-tabs a {
  font-family: var(--mono);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.4rem 0.8rem 0.45rem;
}

/* ------------------------------------------------------------- landing */

/* The one page above the game sections. It carries the site's whole claim,
   so the question gets set at display scale and everything else recedes. */
.landing-title {
  margin-top: 3rem;
  font-size: clamp(2.4rem, 7vw, 4.5rem);
  font-variation-settings: "wdth" 118;
}
.landing-lede { max-width: 46ch; margin-bottom: 3rem; font-size: 1.05rem; }

.game-list {
  list-style: none;
  margin: 0 0 4rem;
  padding: 0;
  border-top: 1px solid var(--line);
}
.game-list li { border-bottom: 1px solid var(--line); }

/* A row, not a tile: with one game a grid of cards would read as a mostly
   empty shelf. Rows extend downward without looking unfinished. */
.game-card {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 15rem) 1fr;
  align-items: baseline;
  gap: 1.25rem;
  padding: 1.5rem 0.6rem;
  color: var(--bone);
  transition: background-color 0.15s ease, padding-left 0.15s ease;
}
.game-card:hover {
  background: var(--panel);
  padding-left: 1.1rem;
  text-decoration: none;
}

.game-card-short {
  font-family: var(--mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ash-dim);
}
.game-card-name {
  font-family: var(--display);
  font-variation-settings: "wdth" 112;
  font-weight: 700;
  font-size: 1.3rem;
  letter-spacing: -0.01em;
}
.game-card-blurb { color: var(--ash); font-size: 0.88rem; }

@media (max-width: 760px) {
  .landing-title { margin-top: 1.5rem; }
  .game-card {
    grid-template-columns: 1fr;
    gap: 0.35rem;
    padding: 1.15rem 0.6rem;
  }
  .game-card:hover { padding-left: 0.6rem; }
}

/* ---------------------------------------------------------- home: cards */

.snapshot-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1px;
  margin: 2rem 0 2.75rem;
  background: var(--line);
  border: 1px solid var(--line);
}

/* The compact member of the family: a label and a figure on one line, for
   strips that carry more cells than the card layout has room for. It still
   has to paint its own background -- the grid above is a hairline pattern,
   drawing its gutters by letting --line show through the 1px gaps between
   children, so a child without a background is not an unstyled cell but a
   hole in the panel. */
.snapshot-cell {
  background: var(--ink);
  padding: 0.7rem 0.95rem;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
.snapshot-cell strong { font-size: 0.95rem; }

/* Six cells rather than the card grid's four, so they are allowed to be
   narrower: at the shared 230px floor a six-cell strip wraps to a second row
   and leaves the empty tracks showing as gaps in the panel. */
.snapshot-grid.is-compact {
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

.snapshot-label {
  font-family: var(--mono);
  color: var(--ash-dim);
  font-size: 0.66rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
}

/* Hairline grid: cards share 1px gutters rather than each carrying its own
   border and radius, which keeps the home page reading as one instrument
   panel instead of four floating tiles. */
.snapshot-card {
  display: block;
  background: var(--ink);
  padding: 1.15rem 1.25rem 1.3rem;
  color: var(--bone);
  transition: background-color 0.15s ease;
}
.snapshot-card:hover { background: var(--panel); text-decoration: none; }

.snapshot-card h3 {
  margin: 0 0 0.75rem;
  font-family: var(--mono);
  color: var(--ash-dim);
  font-size: 0.66rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
}

.snapshot-highlight {
  display: flex;
  align-items: center;
  font-family: var(--display);
  font-variation-settings: "wdth" 112;
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.snapshot-sub { margin: 0.4rem 0 0; color: var(--ash); font-size: 0.85rem; }
.snapshot-highlight + .snapshot-sub { font-family: var(--mono); font-size: 0.8rem; }

.snapshot-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.45rem; }
.snapshot-list li { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; font-size: 0.85rem; }
.snapshot-role {
  font-family: var(--mono);
  color: var(--ash-dim);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  flex-shrink: 0;
}
.snapshot-spec { display: flex; align-items: center; text-align: right; }

.dungeon-list {
  list-style: none;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.dungeon-list li {
  background: var(--ink);
  padding: 0.85rem 1rem;
  transition: background-color 0.15s ease;
}
.dungeon-list li:has(a:hover) { background: var(--panel); }
.dungeon-list a { display: flex; align-items: center; }
.dungeon-list a:hover { text-decoration: none; }

/* ------------------------------------------------------ tier rows / bars */

.tier-role, .stats-dungeon { margin-bottom: 2.75rem; }

.tier-role h2,
.stats-dungeon h2 {
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--line);
}

.tier-rows { display: flex; flex-direction: column; }

.tier-row {
  display: grid;
  grid-template-columns: 2.6rem minmax(180px, 300px) 1fr auto;
  align-items: center;
  gap: 0.85rem;
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--line);
  transition: background-color 0.15s ease;
}
.tier-row:hover { background: var(--panel); }

/* Deliberately not a flex container: these hold an icon followed by
   "Class – Spec" as loose text nodes, and flex strips the whitespace around
   them, jamming the en dash against the class name. */
.spec-name {
  font-size: 0.9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.spec-counts {
  font-family: var(--mono);
  color: var(--ash);
  white-space: nowrap;
  font-size: 0.76rem;
  font-variant-numeric: tabular-nums;
}

/* Square-ended rather than rounded pills — a rounded cap misreports where
   a value actually ends. Fill color is set inline per class; the neutral
   fallback here is what dungeons and other non-class rows get. */
.spec-bar, .pvp-bar {
  height: 4px;
  background: var(--line-hi);
  overflow: hidden;
}
.spec-bar-fill, .pvp-bar-fill {
  display: block;
  height: 100%;
  background: var(--bone);
  transform-origin: left center;
  animation: bar-extend 0.55s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

@keyframes bar-extend {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

.tier-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  font-size: 0.9rem;
  font-weight: 800;
  font-variation-settings: "wdth" 118;
  clip-path: var(--chamfer);
}

/* Tier is a rank, so it's encoded as a luminance ramp rather than the
   usual gold/green/blue/orange rainbow — S is brightest, D recedes. This
   also leaves class color as the only chroma in the row. */
.tier-S { background: var(--bone); color: var(--ink); }
.tier-A { background: #b9b1a4; color: var(--ink); }
.tier-B { background: #857d71; color: var(--ink); }
.tier-C { background: #4f4941; color: var(--bone); }
.tier-D { background: #302b26; color: var(--ash); }

/* ------------------------------------------------------------ pvp table */

.pvp-rows { display: flex; flex-direction: column; }

.pvp-row {
  display: grid;
  grid-template-columns: 2.1rem minmax(200px, 250px) 1fr 3.6rem auto;
  align-items: center;
  gap: 0.85rem;
  padding: 0.45rem 0.6rem;
  border-bottom: 1px solid var(--line);
  transition: background-color 0.15s ease;
}
.pvp-row:hover { background: var(--panel); }
.pvp-row .tier-badge { width: 1.75rem; height: 1.75rem; font-size: 0.78rem; }

.pvp-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.9rem;
}
.pvp-name a { color: var(--bone); }

.class-chip {
  display: inline-block;
  width: 3px;
  height: 14px;
  vertical-align: -2px;
  margin-right: 0.4rem;
}

.pvp-avg {
  font-family: var(--mono);
  text-align: right;
  font-weight: 600;
  font-size: 0.88rem;
  font-variant-numeric: tabular-nums;
}
.pvp-cutoff {
  font-family: var(--mono);
  color: var(--ash-dim);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ----------------------------------------------------- per-class panels */

.role-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1px;
  margin-bottom: 2rem;
  background: var(--line);
  border: 1px solid var(--line);
}
.role-stats-col { background: var(--ink); padding: 1rem 1.15rem; }
.role-stats-col h3 { display: flex; align-items: center; margin: 0 0 0.6rem; font-size: 0.95rem; }
.role-stats-col ul { list-style: none; margin: 0; padding: 0; }
.role-stats-col li {
  display: grid;
  grid-template-columns: 1fr 80px 3rem;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.82rem;
  padding: 0.2rem 0;
}
.stats-dungeon .role-stats-col li,
.role-stats-col li:has(.tier-badge) { grid-template-columns: 1fr auto; }
.role-stats-col .spec-counts { display: inline-flex; align-items: center; gap: 0.5rem; }

/* ---------------------------------------------------------- chart wraps */

/* Single column, not a multi-column grid: the templates set each wrapper's
   height inline as (spec count x 28px), which only produces sane bar
   proportions at full content width. A side-by-side grid squeezes the same
   height into a third of the width and the bars balloon. */
.role-charts {
  display: flex;
  flex-direction: column;
  gap: 1.75rem;
  margin-bottom: 1.75rem;
}
.role-chart-block h3 {
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ash-dim);
}
.role-chart-wrap { position: relative; }

/* --------------------------------------------------------------- tables */

table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }

th {
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--ash-dim);
  text-align: left;
  padding: 0.55rem 0.65rem;
  border-bottom: 1px solid var(--line-hi);
}

td {
  text-align: left;
  padding: 0.5rem 0.65rem;
  border-bottom: 1px solid var(--line);
}

tbody tr { transition: background-color 0.12s ease; }
tbody tr:hover td { background: var(--panel); }

/* Figures get the mono face with tabular figures so key levels and
   durations stay in vertical register when the table is sorted. */
td[data-label="Key Level"],
td[data-label="Duration"],
td[data-label="#"] {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

.region { font-family: var(--mono); font-size: 0.78rem; color: var(--ash); letter-spacing: 0.05em; }
.comp span { margin-right: 0.25rem; }

.sortable-col { cursor: pointer; user-select: none; }
.sortable-col:hover { color: var(--bone); }
.sortable-col.sort-asc::after { content: " \2191"; }
.sortable-col.sort-desc::after { content: " \2193"; }

/* Faction is one of the few non-class identities that carries a real
   in-game color, so it keeps a tint — but a muted one, so it never
   competes with class color in the same row. */
.alliance { color: #7ea3d4; }
.horde { color: #c4706b; }

/* ---------------------------------------------------------- breadcrumb */

.breadcrumb {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 1.25rem;
}
.breadcrumb a { color: var(--ash-dim); }
.breadcrumb a:hover { color: var(--bone); }
.breadcrumb-sep { margin: 0 0.5rem; color: var(--line-hi); }
.breadcrumb-current { color: var(--ash); }

/* -------------------------------------------------------- run detail */

.run-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.75rem;
}
.run-header h1 { display: flex; align-items: center; flex-wrap: wrap; gap: 0.15rem 0.85rem; }
.run-header h1 .meta {
  font-family: var(--mono);
  font-size: 0.85rem;
  font-weight: 400;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.team-member { border-top: 1px solid var(--line); padding-top: 1.5rem; margin-bottom: 1.75rem; }
.team-member h2 { margin-bottom: 0.15rem; }
.team-member > .meta { font-family: var(--mono); font-size: 0.8rem; margin-top: 0; }
.realm { color: var(--ash-dim); font-size: 0.85rem; font-weight: 400; }

.talent-tier { margin-bottom: 1.25rem; }
.talent-tier h3 {
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ash-dim);
  margin-bottom: 0.4rem;
}
.talent-tier ul { margin: 0; padding-left: 1.1rem; font-size: 0.88rem; }
.talent-tier li { margin-bottom: 0.15rem; }

.build-count { font-family: var(--mono); font-size: 0.8rem; color: var(--ash); }

/* -------------------------------------------------------- class detail */

.class-detail { margin-bottom: 3rem; }
.class-detail h1 { display: flex; align-items: center; gap: 0.7rem; }

.class-spec-tabs {
  display: flex;
  gap: 0;
  margin: 1.75rem 0 1.4rem;
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
}
.class-spec-tabs button {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  padding: 0.7rem 1.1rem;
  border: 0;
  border-bottom: 3px solid transparent;
  background: transparent;
  color: var(--ash);
  font-family: var(--display);
  font-size: 0.95rem;
  font-weight: 700;
}
.class-spec-tabs button:hover { color: var(--bone); background: var(--panel); }
.class-spec-tabs button.active {
  color: var(--bone);
  border-bottom-color: var(--class-color);
  background: color-mix(in srgb, var(--class-color) 8%, transparent);
}

.class-spec-panel { margin-bottom: 3rem; }

/* Talent sample source (M+ / Solo Shuffle). Deliberately lighter than
   .class-spec-tabs above it: these sit inside a spec panel and switching them
   changes which sample is drawn, not which spec you are reading. */
.talent-source-tabs {
  display: flex;
  gap: 0.4rem;
  margin: 2rem 0 0;
  overflow-x: auto;
}
.talent-source-tabs button {
  flex: 0 0 auto;
  padding: 0.45rem 0.95rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--ash);
  font-family: var(--display);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.talent-source-tabs button:hover { color: var(--bone); background: var(--panel); }
.talent-source-tabs button.active {
  color: var(--bone);
  border-color: var(--class-color);
  background: color-mix(in srgb, var(--class-color) 14%, transparent);
}
.talent-source-caveat {
  max-width: 62ch;
  margin-top: 0.5rem;
  font-size: 0.84rem;
  font-style: italic;
  opacity: 0.85;
}

.spec-standing-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  margin-bottom: 2.25rem;
  padding: 1px;
  background: var(--line);
}
.spec-standing {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.3rem 0.75rem;
  min-height: 106px;
  padding: 1rem 1.1rem;
  background: var(--panel);
}
.spec-standing-label {
  grid-column: 1 / -1;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ash-dim);
}
.spec-standing strong { font-size: 1.25rem; font-variant-numeric: tabular-nums; }
.spec-standing-number { grid-column: 1 / -1; }
.spec-standing-detail {
  grid-column: 1 / -1;
  font-size: 0.75rem;
  color: var(--ash);
}
/* Sits directly under the standings grid, which owns the gap below the pair. */
.spec-standing-note {
  margin: -1.5rem 0 2.25rem;
  max-width: 78ch;
  font-size: 0.75rem;
  line-height: 1.55;
  color: var(--ash-dim);
}

.talent-section-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 1.25rem;
  margin-bottom: 1rem;
}
.talent-section-header h2 { margin-bottom: 0.25rem; }
.talent-section-header .meta { max-width: 68ch; margin: 0; }
.talent-export { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.talent-export-status {
  min-width: 4.5rem;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--ok);
}
/* The one control that produces something the game will accept, so it leads
   the row. The note beside it is not fine print: everything else on this page
   is an aggregate, and this single button is not. */
.btn-import-string {
  border-color: var(--line-hi);
  background: var(--panel-hi);
  color: var(--bone);
}
.btn-import-string:hover { border-color: var(--bone); }
.talent-import-note {
  flex-basis: 100%;
  max-width: 52ch;
  margin: 0.15rem 0 0;
  font-size: 0.72rem;
  line-height: 1.4;
  color: var(--ash-dim);
}

.talent-tree {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  align-items: start;
}
.talent-branch {
  min-width: 0;
  padding: 1rem;
  border: 1px solid var(--line);
  border-top: 3px solid var(--class-color);
  background: var(--panel);
}
.talent-branch h3 {
  margin-bottom: 0.9rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash);
}
.talent-node-list { position: relative; display: grid; gap: 0.65rem; padding-left: 0.9rem; }
.talent-node-list::before {
  content: "";
  position: absolute;
  top: 1rem;
  bottom: 1rem;
  left: 0.2rem;
  width: 1px;
  background: var(--line-hi);
}
.talent-node {
  position: relative;
  min-width: 0;
  padding: 0.65rem 0.7rem 0.55rem;
  border: 1px solid var(--line-hi);
  background: var(--panel-hi);
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--class-color) 13%, var(--panel-hi)) var(--talent-rate),
    var(--panel-hi) var(--talent-rate)
  );
}
.talent-node::before {
  content: "";
  position: absolute;
  top: 50%;
  left: -0.75rem;
  width: 0.7rem;
  height: 1px;
  background: var(--line-hi);
}
.talent-node::after {
  content: "";
  position: absolute;
  top: calc(50% - 3px);
  left: -0.9rem;
  width: 7px;
  height: 7px;
  border: 1px solid var(--class-color);
  border-radius: 50%;
  background: var(--panel);
}
.talent-node-main { display: flex; justify-content: space-between; gap: 0.75rem; }
.talent-node-name { min-width: 0; font-size: 0.84rem; font-weight: 600; line-height: 1.3; }
.talent-node-count {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--ash);
  font-variant-numeric: tabular-nums;
}
.talent-node-count strong { color: var(--bone); font-size: 0.84rem; }
.talent-node-meter { height: 2px; margin: 0.55rem 0 0.35rem; background: var(--line-hi); }
.talent-node-meter span { display: block; width: var(--talent-rate); height: 100%; background: var(--class-color); }
.talent-node-meta { font-family: var(--mono); font-size: 0.64rem; color: var(--ash-dim); }
.talent-method-note { max-width: 78ch; margin-top: 1rem; font-size: 0.78rem; }

/* Exact Blizzard tree geometry with top-100 selection heat overlaid. */
.talent-patch-badge {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.08rem 0.38rem;
  border: 1px solid var(--line-hi);
  font-family: var(--mono);
  font-size: 0.62rem;
  color: var(--bone);
  vertical-align: 0.08rem;
}

.talent-heatmap-legend {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0 0 0.8rem;
  font-family: var(--mono);
  font-size: 0.64rem;
  color: var(--ash-dim);
}
.talent-heatmap-legend i {
  display: inline-block;
  width: 18px;
  height: 10px;
  margin-left: 0.35rem;
  border: 1px solid var(--line-hi);
  background: var(--panel-hi);
}
/* Node shape is load-bearing now — same meaning as in the game — so it needs
   a key rather than only the choice-node callout the old flat grid needed. */
.talent-legend-shapes {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-left: auto;
  color: var(--ash);
}
.legend-shape {
  width: 11px;
  height: 11px;
  margin-left: 0.5rem;
  background: #6a6255;
}
.legend-shape-passive { border-radius: 50%; }
.legend-shape-active { border-radius: 2px; }
.legend-shape-choice {
  clip-path: polygon(30% 0, 70% 0, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0 70%, 0 30%);
}
.talent-heatmap-legend .heat-some {
  border-color: var(--talent-some);
  background: color-mix(in srgb, var(--talent-some) 60%, var(--panel-hi));
}
.talent-heatmap-legend .heat-core {
  border-color: var(--talent-core);
  background: color-mix(in srgb, var(--talent-core) 70%, var(--panel-hi));
}

/* The three trees read top to bottom in the order a character fills them —
   class, then spec, then the one hero path they pick — rather than side by
   side. Each gets the full shell width, which is what stops Blizzard's wider
   layouts from being ellipsised into unreadability. */
.talent-tree-sections {
  display: flex;
  flex-direction: column;
  gap: 1.9rem;
}
/* Class and spec trees are the same dimensions and get read against each
   other, so they share a row: 2 x 522px plus the gap sits inside the 1080px
   shell, and pairing them removes ~750px of scrolling from every spec. Below
   the shell width they stack, because two boards squeezed narrower than their
   own width would clip rather than shrink. */
.talent-tree-pair {
  display: grid;
  /* min() rather than a bare 500px: below a 500px shell the bare form keeps
     a track wider than the screen and scrolls the whole page sideways, which
     is a worse failure than the narrow board it was protecting. */
  grid-template-columns: repeat(auto-fit, minmax(min(500px, 100%), 1fr));
  gap: 1rem;
  align-items: start;
}
.talent-tree-section-title { margin: 0 0 0.15rem; font-size: 1.02rem; }
.talent-tree-section-note { margin: 0 0 0.7rem; max-width: 68ch; }
/* Track floor is a hero board's own width, not a smaller guess: a class with
   four hero paths (Druid) must wrap to a second row rather than squeeze four
   columns narrower than the board and clip each one. */
/* At most two hero boards now reach the page (a spec can only play two), so
   this is a two-up grid that collapses to one column when there is no room,
   rather than an auto-fit that used to spread three or four across. */
.talent-layout-heroes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
  gap: 1rem;
  align-items: start;
}
/* These boards used to break out of the 1080px shell the rest of the site
   reads at, because at full scale every one of them overflowed it. At
   two-thirds scale the median board is 636px and 36 of 40 specs fit the shell
   outright, so the breakout is gone: it would add dead space around those 36
   to spare four. The four that still overflow — Evoker and Priest, whose class
   trees run 22 columns — scroll inside the viewport, which is built to scroll
   and labelled for it. Two hero paths side by side fit at any width. */

.talent-layout-card {
  min-width: 0;
  border: 1px solid var(--line);
  border-top: 3px solid var(--class-color);
  background: var(--panel);
}
.talent-layout-card-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.6rem 0.85rem;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(90deg, color-mix(in srgb, var(--class-color) 9%, var(--panel)), var(--panel));
}
.talent-layout-card-header h4 {
  margin: 0;
  font-family: var(--display);
  font-variation-settings: "wdth" 112;
  font-weight: 700;
  font-size: 0.92rem;
}
.talent-layout-coverage {
  font-family: var(--mono);
  font-size: 0.59rem;
  line-height: 1.25;
  color: var(--ash-dim);
  white-space: nowrap;
}

/* No height cap: a stacked section has room for the whole tree, and the game
   shows one too. Only Blizzard's widest spec layouts still scroll, and only
   sideways. */
.talent-layout-viewport {
  position: relative;
  /* Boards are scaled to fit by fitStage(), so there is nothing left to
     scroll and the bar was only ever a place for the wheel to get caught:
     Chrome redirects a vertical wheel into a container that scrolls only
     sideways, which is what made scrolling past a tree feel wrong. Kept as
     `auto` rather than `hidden` so the board is still reachable by keyboard
     and by drag if a layout ever exceeds the fit, with the bar itself
     hidden. */
  overflow: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
  background-color: #10100f;
  background-image:
    radial-gradient(ellipse 70% 60% at 50% 32%, color-mix(in srgb, var(--class-color) 10%, transparent), transparent 70%),
    radial-gradient(ellipse 90% 80% at 50% 50%, transparent 45%, rgba(0,0,0,0.55)),
    linear-gradient(rgba(255,255,255,0.016) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.016) 1px, transparent 1px);
  background-size: auto, auto, 22px 22px, 22px 22px;
}
.talent-layout-viewport::-webkit-scrollbar { width: 0; height: 0; }
.talent-layout-viewport:focus-visible { outline: 2px solid var(--class-color); outline-offset: -2px; }
/* The stage keeps its own width so a tree narrower than the card centres in
   it rather than hugging the left edge; the board background lives on the
   viewport, so the card still fills. */
/* Scaled from the top-left by fitStage(); the viewport's own height is set to
   match so the card does not keep reserving the unscaled height. */
.talent-layout-stage { position: relative; margin: 0 auto; transform-origin: 0 0; }
.talent-layout-edges { position: absolute; inset: 0; overflow: visible; pointer-events: none; }
.talent-layout-edges path {
  fill: none;
  stroke: #45423c;
  stroke-width: 2.5;
  stroke-linecap: butt;
  vector-effect: non-scaling-stroke;
}
/* Neutral rather than class-coloured: the nodes now carry hue as the adoption
   signal, and a third colour running between them would read as a fourth
   band. Luminance alone separates a taken path from an untaken one. */
.talent-layout-edges path.is-active {
  stroke: #8d857a;
  stroke-width: 3;
}
.talent-layout-gate {
  position: absolute;
  left: 20px;
  right: 20px;
  z-index: 1;
  height: 1px;
  border-top: 1px dashed color-mix(in srgb, var(--class-color) 36%, var(--line-hi));
  pointer-events: none;
}
/* Anchored left rather than centred: the middle of a tree is where the nodes
   are, so a centred label lands on a talent caption more often than not. */
.talent-layout-gate span {
  position: absolute;
  top: -9px;
  left: 0;
  padding: 0 0.4rem 0 0;
  background: #10100f;
  font-family: var(--mono);
  font-size: 0.56rem;
  color: var(--ash-dim);
  white-space: nowrap;
}

/* Node shape carries the same meaning it does in the game: a circle is a
   passive, a square grants an ability, an octagon is a choice between two.
   That mapping is free information for anyone who has seen the real tree, so
   it is worth more than a uniform grid of rounded squares.

   The frame is warm metal rather than the game's gold, because the site's
   colour rule reserves chroma for class colours (see the file header). The
   class colour is spent on the thing that is actually ours: adoption. */
/* Adoption is encoded by hue as well as luminance: green at 75%+ of the
   sample, blue below that, bare metal at zero. This is a deliberate exception
   to the file-header colour rule — the count badge still carries the exact
   number, so the hue is a second read of the same fact, not the only one. */
.talent-heat-node {
  --shape: 7px;
  --metal: #6a6255;
  --metal-lo: #2f2c27;
  --tint: var(--metal);
  --tint-strength: 0%;
  position: absolute;
  z-index: 2;
  /* Two-thirds of the original 60px, in step with COL_STEP/ROW_STEP/
     NODE_RADIUS in app/queries.py -- the arrow trim is computed from that
     radius, so changing one alone leaves connectors short of the rims. */
  width: 40px;
  height: 40px;
  padding: 0;
  transform: translate(-50%, -50%);
  border: 0;
  background: none;
  color: var(--bone);
  filter: drop-shadow(0 3px 7px rgba(0,0,0,0.6));
  transition: transform 0.14s ease, filter 0.14s ease;
}
.talent-heat-node-passive { --shape: 50%; }
.talent-heat-node:hover,
.talent-heat-node:focus-visible {
  z-index: 20;
  transform: translate(-50%, -50%) scale(1.1);
  outline: none;
}

/* Two nested shapes make the bevelled rim: the frame paints the metal, the
   icon sits 3px inside it. A border cannot do this because the octagon needs
   clip-path, which borders do not follow. */
.talent-heat-frame {
  display: block;
  width: 100%;
  height: 100%;
  padding: 2px;
  border-radius: var(--shape);
  background:
    linear-gradient(165deg,
      color-mix(in srgb, var(--tint) var(--tint-strength), var(--metal)),
      var(--metal-lo) 52%,
      color-mix(in srgb, var(--tint) calc(var(--tint-strength) * 0.6), #575046));
}
.talent-heat-node.is-picked { --tint: var(--talent-some); --tint-strength: 72%; }
.talent-heat-node.is-core { --tint: var(--talent-core); --tint-strength: 82%; }
.talent-heat-node-choice .talent-heat-frame,
.talent-heat-node-choice .talent-heat-icon {
  border-radius: 0;
  clip-path: polygon(30% 0, 70% 0, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0 70%, 0 30%);
}
.is-picked .talent-heat-frame {
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--tint) 55%, transparent),
              0 0 10px color-mix(in srgb, var(--tint) calc(var(--heat) * 0.3), transparent);
}
.talent-heat-node:hover .talent-heat-frame,
.talent-heat-node:focus-visible .talent-heat-frame {
  background: linear-gradient(165deg, var(--bone), #8a8276 55%, var(--bone));
}
/* Granted-by-default nodes are outside the player's control, so they get the
   doubled rim the game uses for automatic grants rather than reading as an
   unusually popular choice. */
.talent-heat-node.is-granted .talent-heat-frame { padding: 3px; }

.talent-heat-icon {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: calc(var(--shape) - 1px);
  background: #171613;
  font-family: var(--display);
  font-weight: 800;
  font-size: 0.7rem;
  color: var(--ash);
}
.talent-heat-node-passive .talent-heat-icon { border-radius: 50%; }
/* Unpicked reads as the game's greyed-out talent and picked as a lit one, so
   the heatmap and the in-game idiom are the same gesture rather than two. */
.talent-heat-icon img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter:
    grayscale(calc(100% - var(--heat)))
    brightness(calc(0.34 + 0.0066 * var(--heat-n)))
    contrast(1.04);
}

/* The game's rank pip: bottom-right, dark, tight. Here it counts sampled
   characters rather than points spent. */
.talent-heat-count {
  position: absolute;
  right: -3px;
  bottom: -4px;
  display: grid;
  place-items: center;
  min-width: 15px;
  height: 13px;
  padding: 0 2px;
  border: 1px solid #0a0908;
  border-radius: 3px;
  background: #100f0d;
  color: var(--ash-dim);
  font-family: var(--mono);
  font-size: 0.5rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.is-picked .talent-heat-count {
  border-color: color-mix(in srgb, var(--tint) 45%, #0a0908);
  color: color-mix(in srgb, var(--tint) 72%, var(--bone));
}
.talent-choice-mark {
  position: absolute;
  left: -3px;
  top: -3px;
  display: grid;
  place-items: center;
  width: 12px;
  height: 12px;
  border: 1px solid color-mix(in srgb, var(--class-color) 50%, var(--line-hi));
  border-radius: 50%;
  background: #100f0d;
  font-size: 0.6rem;
  color: var(--ash);
}
/* Three lines fits all but a handful of Blizzard's longest names. Anything
   longer fades out instead of being chopped mid-word — the tooltip has the
   full name, so a hard ellipsis would only add noise. */
.talent-heat-caption {
  position: absolute;
  top: calc(100% + 4px);
  left: 50%;
  width: 84px;
  max-height: 3.45em;
  overflow: hidden;
  transform: translateX(-50%);
  color: var(--ash-dim);
  font-size: 0.6rem;
  font-weight: 600;
  line-height: 1.15;
  text-align: center;
  text-wrap: balance;
  text-shadow: 0 1px 3px #000, 0 0 6px #000;
  pointer-events: none;
  mask-image: linear-gradient(#000 2.3em, transparent 3.4em);
}
.is-picked .talent-heat-caption { color: var(--ash); }
.talent-heat-tooltip {
  display: none;
  position: absolute;
  z-index: 60;
  top: calc(100% + 18px);
  left: 50%;
  width: 270px;
  max-width: 72vw;
  padding: 0.75rem;
  transform: translateX(-50%);
  border: 1px solid var(--class-color);
  background: #171714;
  box-shadow: 0 12px 28px rgba(0,0,0,0.75);
  color: var(--ash);
  font-family: var(--body);
  font-size: 0.73rem;
  font-weight: 400;
  line-height: 1.4;
  text-align: left;
  pointer-events: none;
}
.talent-heat-node:hover .talent-heat-tooltip,
.talent-heat-node:focus-visible .talent-heat-tooltip { display: block; }
.talent-tooltip-entry { display: block; }
.talent-tooltip-entry + .talent-tooltip-entry { margin-top: 0.7rem; padding-top: 0.7rem; border-top: 1px solid var(--line); }
.talent-tooltip-entry strong { display: block; color: var(--bone); font-family: var(--display); font-size: 0.84rem; }
.talent-tooltip-entry em { display: block; color: var(--class-color); font-family: var(--mono); font-size: 0.64rem; font-style: normal; }
.talent-tooltip-entry small { display: block; color: var(--ash-dim); }
.talent-tooltip-entry > span { display: block; margin-top: 0.35rem; }
.talent-granted-label { display: block; margin-top: 0.55rem; color: var(--ok); font-family: var(--mono); }

.talent-unmapped { margin-top: 1rem; padding: 0.7rem 0.85rem; border: 1px dashed var(--line-hi); font-size: 0.78rem; }
.talent-unmapped summary { cursor: pointer; color: var(--ash); }
.talent-unmapped p { margin: 0.5rem 0; }
.talent-unmapped ul { margin: 0; padding-left: 1.1rem; color: var(--ash-dim); }

/* ------------------------------------------------------------ controls */

.spec-filter { margin-bottom: 1.75rem; border: 1px solid var(--line); padding: 0.9rem 1.1rem; }
.spec-filter legend {
  font-family: var(--mono);
  color: var(--ash-dim);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  padding: 0 0.45rem;
}
.spec-check {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0.2rem 0.85rem 0.2rem 0;
  font-size: 0.84rem;
  color: var(--ash);
  cursor: pointer;
  transition: color 0.15s ease;
}
.spec-check:hover { color: var(--bone); }
.spec-check input[type="checkbox"] { accent-color: var(--bone); }

button {
  font-family: var(--body);
  font-size: 0.82rem;
  cursor: pointer;
}

.spec-filter button, .btn-copy-link, .nav-toggle {
  padding: 0.4rem 0.9rem;
  background: transparent;
  border: 1px solid var(--line-hi);
  color: var(--bone);
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.spec-filter button:hover, .btn-copy-link:hover, .nav-toggle:hover {
  background: var(--panel-hi);
  border-color: var(--bone);
}
.spec-filter button { margin-top: 0.7rem; }
.btn-copy-link { flex-shrink: 0; }
.clear-filter { margin-left: 0.85rem; font-size: 0.85rem; }

.scan-controls { margin-left: auto; display: flex; align-items: center; gap: 0.85rem; }

.btn-scan {
  padding: 0.4rem 0.95rem;
  background: var(--bone);
  color: var(--ink);
  border: 1px solid var(--bone);
  font-weight: 600;
  transition: opacity 0.15s ease;
}
.btn-scan:hover:not(:disabled) { opacity: 0.82; }
.btn-scan:disabled { opacity: 0.4; cursor: not-allowed; }

.scan-status {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--ash);
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.scan-status--running { color: var(--bone); }
.scan-status--done { color: var(--ok); }
.scan-status--error { color: var(--bad); }

.nav-toggle { display: none; margin-left: auto; font-size: 1.1rem; line-height: 1; padding: 0.4rem 0.65rem; }

@media (max-width: 760px) {
  .spec-standing-grid, .talent-tree { grid-template-columns: 1fr; }
  .talent-layout-heroes { grid-template-columns: 1fr; }
  .talent-tree-sections { gap: 1.4rem; }
  .talent-layout-card-header { align-items: start; }
  .talent-legend-shapes { width: 100%; margin-left: 0; }
  .talent-section-header { align-items: flex-start; flex-direction: column; }
  .talent-export-status { min-width: 0; }
}

/* ----------------------------------------------------------- icons */

.spec-icon {
  width: 17px;
  height: 17px;
  vertical-align: -3px;
  margin-right: 0.4rem;
  flex-shrink: 0;
}

.dungeon-icon {
  width: 22px;
  height: 22px;
  object-fit: cover;
  vertical-align: -5px;
  margin-right: 0.5rem;
  flex-shrink: 0;
}

.dungeon-icon-lg {
  width: 44px;
  height: 44px;
  object-fit: cover;
  vertical-align: middle;
  margin-right: 0.75rem;
  flex-shrink: 0;
  clip-path: var(--chamfer);
}

/* ---------------------------------------------------------- footer */

.site-footer {
  max-width: var(--shell);
  margin: 3rem auto 0;
  padding: 1.25rem 1.75rem 2.5rem;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  color: var(--ash-dim);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* ------------------------------------------------------- responsive */

@media (max-width: 760px) {
  main { padding: 1.5rem 1.15rem 1rem; }
  .header-inner { flex-wrap: wrap; align-items: center; gap: 1rem; padding: 0.9rem 1.15rem; }
  .nav-toggle { display: inline-flex; }
  .nav-game { display: none; width: 100%; padding-top: 0.5rem; flex-direction: column; align-items: flex-start; gap: 0.5rem; }
  .nav-game.open { display: flex; }
  .nav-links { flex-direction: column; align-items: flex-start; gap: 0.7rem; }
  .scan-controls { width: 100%; margin-left: 0; }

  /* Reordered so the header reads brand — season — burger, with the nav
     stack wrapping below. Source order puts the burger first (it lives in
     the nav block), which would otherwise sit between the brand and the
     season control. */
  .header-right { order: 0; flex: 0 0 auto; }
  .nav-toggle { order: 1; margin-left: 0.75rem; }
  .nav-game { order: 2; }

  /* The burger nav is already a vertical stack, so the class menu drops into
     the flow rather than floating over a page the reader can't see anyway. */
  .nav-menu { width: 100%; }
  .nav-menu-panel {
    position: static;
    width: 100%;
    min-width: 0;
    margin-top: 0.5rem;
    box-shadow: none;
  }

  /* The season control is the exception: it sits in the header itself, not
     inside the collapsed nav, so it keeps floating over the page rather than
     pushing the header open to its full height. */
  .season-menu { width: auto; }
  .season-menu .season-menu-panel {
    position: absolute;
    right: 0;
    left: auto;
    width: auto;
    min-width: 11rem;
    box-shadow: 0 14px 34px rgb(0 0 0 / 55%);
  }

  .pvp-row { grid-template-columns: 1.75rem 1fr 3.6rem; }
  .pvp-bar, .pvp-cutoff { display: none; }

  .tier-row { grid-template-columns: 2.6rem 1fr auto; }
  .tier-row .spec-bar { display: none; }

  .bracket-tabs a { font-size: 1rem; padding: 0.45rem 0.85rem 0.6rem; }
  .spec-check { font-size: 0.9rem; padding: 0.35rem 0; }
  .spec-check input[type="checkbox"] { width: 1.15rem; height: 1.15rem; }

  table.responsive-table thead { display: none; }
  table.responsive-table,
  table.responsive-table tbody,
  table.responsive-table tr,
  table.responsive-table td { display: block; width: 100%; }
  table.responsive-table tr {
    border: 1px solid var(--line);
    padding: 0.7rem 0.85rem;
    margin-bottom: 0.6rem;
    background: var(--panel);
  }
  table.responsive-table td { border-bottom: none; padding: 0.25rem 0; }
  table.responsive-table td[data-label]:not([data-label=""]) {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 0.85rem;
  }
  table.responsive-table td[data-label]:not([data-label=""])::before {
    content: attr(data-label);
    font-family: var(--mono);
    color: var(--ash-dim);
    font-size: 0.64rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    flex-shrink: 0;
    padding-top: 0.2rem;
  }
  table.responsive-table td.comp { flex-direction: column; align-items: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* --- Patch notes ---------------------------------------------------------
   Change bullets carry a semantic left edge (buff/nerf/neutral) rather than
   coloured text, so a long list stays readable and the --ok/--bad tokens
   keep meaning something. */

.patch-list { list-style: none; margin: 1.5rem 0 0; padding: 0; border-top: 1px solid var(--line); }
.patch-list-item { border-bottom: 1px solid var(--line); }

.patch-list-link {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  padding: 0.9rem 0.75rem;
  color: var(--bone);
  transition: background-color 0.15s ease;
}
.patch-list-link:hover { background: var(--panel); text-decoration: none; }

.patch-list-title { font-family: var(--display); font-weight: 600; font-size: 1.02rem; }

.patch-list-meta {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--ash-dim);
  white-space: nowrap;
}

.patch-summary { max-width: 62ch; color: var(--ash); }

.patch-source {
  font-size: 0.8rem;
  color: var(--ash-dim);
  border-left: 2px solid var(--line-hi);
  padding-left: 0.75rem;
  margin: 1.25rem 0 2rem;
}

.patch-class { margin: 2.5rem 0; }
.patch-class h2 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding-left: 0.7rem;
  margin-bottom: 1rem;
}

.patch-spec { margin: 1.5rem 0 1.5rem 0.7rem; }
.patch-spec h3 {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.5rem;
  font-size: 0.95rem;
}

/* The site's own data, so it gets the visual weight -- the quoted bullets
   below it are supporting evidence, not the point of the page. */
.pickrate-delta {
  margin: 0 0 0.7rem;
  padding: 0.5rem 0.7rem;
  background: var(--panel);
  clip-path: var(--chamfer);
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--ash);
}
.pickrate-delta strong { color: var(--bone); font-weight: 600; }
.pickrate-delta.up .pickrate-change { color: var(--ok); }
.pickrate-delta.down .pickrate-change { color: var(--bad); }
.pickrate-delta.empty { color: var(--ash-dim); background: transparent; padding-left: 0; }
.pickrate-dates { display: block; margin-top: 0.2rem; font-size: 0.68rem; color: var(--ash-dim); }

.patch-changes { list-style: none; margin: 0; padding: 0; }
.patch-changes .change {
  padding: 0.4rem 0 0.4rem 0.8rem;
  border-left: 2px solid var(--line-hi);
  margin-bottom: 0.3rem;
  font-size: 0.88rem;
  line-height: 1.5;
}
.patch-changes .change-buff { border-left-color: var(--ok); }
.patch-changes .change-nerf { border-left-color: var(--bad); }

.hero-talent {
  display: inline-block;
  margin-right: 0.4rem;
  font-family: var(--mono);
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ash-dim);
}

@media (max-width: 600px) {
  .patch-list-meta { margin-left: 0; white-space: normal; }
}

/* PTR pages describe intent, not fact. The warning is the one piece of
   chrome allowed to be loud, because a reader who mistakes these for shipped
   patch notes has been actively misled. */
.ptr-warning {
  margin: 1.25rem 0;
  padding: 0.75rem 0.9rem;
  background: var(--panel);
  border-left: 3px solid var(--bad);
  clip-path: var(--chamfer);
  font-size: 0.86rem;
  color: var(--ash);
}
.ptr-warning strong { color: var(--bone); }

.pickrate-delta.standing .tier-badge {
  display: inline-block;
  padding: 0 0.35rem;
  vertical-align: baseline;
}

/* --------------------------------------------------------------- tft */

/* Ungraded: seen on too few boards to tier. Deliberately outside the S–D
   luminance ramp above — an outline rather than a fill, so it reads as
   "no grade" instead of as a sixth, worse-than-D tier. */
.tier-none {
  background: transparent;
  border: 1px dashed var(--line-hi);
  color: var(--ash-dim);
  font-weight: 500;
}

/* Every figure in a TFT row is a number meant to be compared down the
   column, so the whole table gets tabular figures rather than tagging
   individual columns by data-label the way the run tables do. */
.tft-table td { font-variant-numeric: tabular-nums; }
.tft-table td:nth-child(2) { font-variant-numeric: normal; }
.tft-table .spec-icon { margin-right: 0.4rem; vertical-align: middle; }

/* The build listing's second column is a row of unit icons with no names, so
   here the icon is the label rather than decoration next to one -- hence the
   larger size than the inline .spec-icon, which only ever has to be picked
   out beside the word it belongs to. Flex with a gap rather than the shared
   per-icon margin: no trailing space after the last icon, and a long core
   wraps as a block instead of dragging the column wider. */
.tft-build-core {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem;
}

.tft-build-core .spec-icon {
  width: 24px;
  height: 24px;
  margin-right: 0;
  border: 1px solid var(--line);
  border-radius: 3px;
}

/* The carries are what the build is grouped and named by, and they lead the
   row. Once the names are gone that position is not visible on its own, so
   they are marked rather than left to be inferred. */
.tft-build-core .spec-icon.is-carry { border-color: var(--ash); }

/* Stands in for a single unit with no icon inside an otherwise iconed row.
   Sized to the icons it sits between so the row keeps one baseline. */
.tft-build-chip {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 0.34rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  font-size: 0.7rem;
  color: var(--ash);
  white-space: nowrap;
}
.tft-build-chip.is-carry { border-color: var(--ash); color: var(--bone); }

/* The names variant, used until the unit catalog carries icons. Tighter gap
   than the icon row because the separators already do the spacing work. */
.tft-build-core.is-named { gap: 0.3rem 0.24rem; }

/* Carries lead the row in both variants, so they get the same emphasis here
   that the border gives an icon: the supporting core is what steps back. */
.tft-build-core.is-named .tft-build-unit { color: var(--ash); }
.tft-build-core.is-named .tft-build-unit.is-carry { color: inherit; }

.tft-build-sep {
  color: var(--ash-dim);
  font-size: 0.82rem;
}

/* The build detail page reuses .snapshot-grid and .role-stats wholesale, so
   what is left here is only the few pieces those patterns have no slot for. */
.build-title { display: flex; align-items: center; gap: 0.6rem; }

/* Costs and shares are secondary to the name they sit beside: same tabular
   figures as the tables so they line up down a column, one step quieter. */
.build-cost {
  margin-left: 0.45rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--ash-dim);
  font-variant-numeric: tabular-nums;
}
.build-share {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--ash);
  font-variant-numeric: tabular-nums;
}

/* Only rendered before any items have been fetched, in place of the item
   panels -- a plain list of the carries so the page still says what the
   build is. */
.build-carries {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.4rem;
}
.build-carries li { display: flex; align-items: center; }

/* ------------------------------------------------- tft comp builder */

/* The builder is the one TFT page that recalculates in the browser, so its
   layout has to hold still while numbers change underneath it: fixed panel
   columns and a stat strip that keeps its slots whether or not a selection
   matches anything. */
.builder { margin-top: 1.2rem; }

.builder-picked {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  min-height: 42px;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
}

.builder-hint { margin: 0; color: var(--ash); font-size: 0.84rem; }

.builder-picked-unit,
.builder-pick {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  padding: 0.24rem 0.42rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel-hi);
  color: inherit;
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}

.builder-pick:hover { border-color: var(--line-hi); }
.builder-pick.is-picked { border-color: var(--ash); background: var(--line); }

.builder-remove { color: var(--ash); font-size: 0.9rem; line-height: 1; }

.builder-clear {
  margin-left: auto;
  padding: 0.24rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: none;
  color: var(--ash);
  font: inherit;
  font-size: 0.76rem;
  cursor: pointer;
}
.builder-clear:hover { color: var(--bone); border-color: var(--line-hi); }

.builder-unit { display: inline-flex; align-items: center; gap: 0.3rem; }
.builder-unit .spec-icon {
  width: 22px;
  height: 22px;
  margin-right: 0;
  border: 1px solid var(--line);
  border-radius: 3px;
}
.builder-unit-name { white-space: nowrap; }

/* The stat strip. Tabular figures so a number changing between clicks does
   not shift the ones beside it. */
.builder-verdict {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.8rem;
  align-items: baseline;
  margin: 0.9rem 0 0.2rem;
  font-variant-numeric: tabular-nums;
}
.builder-stat { display: flex; flex-direction: column; gap: 0.1rem; }
.builder-stat-label {
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ash-dim);
}
.builder-stat strong { font-size: 1.15rem; }
.builder-verdict-note {
  flex-basis: 100%;
  margin: 0;
  color: var(--ash);
  font-size: 0.82rem;
}

.builder-panels {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1.1rem;
  margin-top: 1.1rem;
}

.builder-panel {
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
  padding: 0.75rem 0.85rem;
  min-width: 0;
}
.builder-items-panel { margin-top: 1.1rem; }

.builder-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-bottom: 0.6rem;
}
.builder-panel-head h2 { margin: 0; font-size: 0.95rem; }

.builder-search {
  flex: 0 1 190px;
  padding: 0.28rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--ink);
  color: var(--bone);
  font: inherit;
  font-size: 0.78rem;
}

/* Capped and scrolled rather than left to run: the pool is every unit in the
   set, and a panel that grows to 60 rows pushes the results it exists to
   feed off the screen. */
.builder-pool,
.builder-pairs { max-height: 460px; overflow-y: auto; }

.builder-cost-group { margin-bottom: 0.7rem; }
.builder-cost-label {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ash-dim);
}
.builder-cost-units { display: flex; flex-wrap: wrap; gap: 0.3rem; }

.builder-table { width: 100%; }
.builder-table td:first-child { font-variant-numeric: normal; }
.builder-delta.is-better { color: var(--ok); }
.builder-delta.is-worse { color: var(--bad); }

.builder-item-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.9rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--line);
}
.builder-item-row:last-child { border-bottom: 0; }
.builder-item-list { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.builder-item {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  font-size: 0.76rem;
  color: var(--ash);
}
.builder-item .spec-icon { width: 18px; height: 18px; margin-right: 0; }

@media (max-width: 760px) {
  .builder-panels { grid-template-columns: minmax(0, 1fr); }
  .builder-pool, .builder-pairs { max-height: 340px; }
}

/* ------------------------------------------- tft builder: the board */

.builder-stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(210px, 280px);
  gap: 1.2rem;
  align-items: start;
  margin-top: 1.1rem;
}

.builder-board-wrap { min-width: 0; }

/* Pointy-top hexes: rows interlock by overlapping a quarter of a hex and
   offsetting every other row by half its width, which is the only stagger
   that tiles without gaps. The board is deliberately not a grid of squares
   with rounded corners -- the shape is the thing being recognised. */
.builder-board {
  --hex-w: 58px;
  --hex-h: 67px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 0.6rem 0.4rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
  overflow-x: auto;
}

.hex-row { display: flex; gap: 4px; }
.hex-row + .hex-row { margin-top: calc(var(--hex-h) * -0.25 + 3px); }
.hex-row.is-offset { margin-left: calc(var(--hex-w) / 2 + 2px); }

.hex {
  width: var(--hex-w);
  height: var(--hex-h);
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
  background: var(--panel-hi);
  cursor: pointer;
  transition: background 90ms linear;
}

.hex.is-empty:hover { background: var(--line); }
.hex.is-target { background: var(--ash-dim); }

.hex img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hex-name {
  padding: 0 2px;
  font-size: 0.6rem;
  text-align: center;
  color: var(--ash);
}

/* Cost is the one thing a portrait alone does not say, and it is what a
   player scans a board for. Carried as a rim rather than a badge so it does
   not cover the art. */
.hex.is-filled { background: var(--metal-lo, var(--line)); }
.hex.cost-1 { box-shadow: inset 0 0 0 2px #6f6f78; }
.hex.cost-2 { box-shadow: inset 0 0 0 2px #4f9e6a; }
.hex.cost-3 { box-shadow: inset 0 0 0 2px #4f86c6; }
.hex.cost-4 { box-shadow: inset 0 0 0 2px #a45fc0; }
.hex.cost-5 { box-shadow: inset 0 0 0 2px #d1a24a; }

.builder-board-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-top: 0.5rem;
}

.builder-count {
  font-size: 0.78rem;
  color: var(--ash);
  font-variant-numeric: tabular-nums;
}
.builder-count.is-full { color: var(--bone); }

.builder-note {
  margin: 0.5rem 0 0;
  font-size: 0.78rem;
  color: var(--ash-dim);
  max-width: 62ch;
}

.builder-pool-hint { margin: 0 0 0.5rem; }

/* ------------------------------------------ tft builder: the traits */

.builder-traits-panel {
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
  padding: 0.7rem 0.8rem;
}
.builder-traits-panel h2 { margin: 0 0 0.55rem; font-size: 0.95rem; }

.builder-traits { display: flex; flex-direction: column; gap: 0.24rem; }

.builder-trait {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.26rem 0.4rem;
  border-radius: 3px;
  font-size: 0.8rem;
  color: var(--ash-dim);
}

/* An inactive trait is listed but visibly not counting for anything yet --
   the panel's job is to show what is on and what is close, and washing the
   two together would make neither readable at a glance. */
.builder-trait.is-active { color: var(--bone); background: var(--panel-hi); }

.builder-trait-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  opacity: 0.45;
}
.builder-trait.is-active .builder-trait-icon { opacity: 1; }

.builder-trait-name { flex: 1 1 auto; }
.builder-trait-count {
  font-family: var(--mono);
  font-size: 0.74rem;
  font-variant-numeric: tabular-nums;
}

.builder-trait.style-bronze { box-shadow: inset 2px 0 0 #a97142; }
.builder-trait.style-silver { box-shadow: inset 2px 0 0 #b9c0c7; }
.builder-trait.style-gold { box-shadow: inset 2px 0 0 #e0b64a; }
.builder-trait.style-prismatic { box-shadow: inset 2px 0 0 #c8a2e8; }
.builder-trait.style-unique { box-shadow: inset 2px 0 0 var(--ok); }

.builder-add {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.builder-add:hover .builder-unit-name { color: var(--bone); }

@media (max-width: 860px) {
  .builder-stage { grid-template-columns: minmax(0, 1fr); }
  .builder-board { --hex-w: 48px; --hex-h: 55px; }
}

/* The share a champion's boards-with-items gave this item. Quieter than the
   name and monospaced so the column of them lines up down the panel. */
.builder-item-share {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--ash-dim);
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------ tft champion page */

.champion-portrait {
  width: 46px;
  height: 46px;
  border: 1px solid var(--line);
  border-radius: 4px;
}

.champion-traits {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.7rem;
  margin: 0.4rem 0 0.9rem;
}
.champion-trait {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  font-size: 0.82rem;
  color: var(--ash);
}
.champion-trait .builder-trait-icon { opacity: 1; }

.champion-items {
  list-style: none;
  margin: 0.5rem 0 1.2rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.2rem;
}
.champion-items li { display: flex; align-items: center; gap: 0.35rem; }
.champion-item-name { font-size: 0.9rem; }
