/* WoW Forever Guides brand v2.1: flat addon frames (Details!, ElvUI) on the game's dark
   stone, the class color as the page accent, Classic red buttons for the one primary action.
   Source of truth: docs/brand/ (tokens.json, components.css, README.md).
   Tokens first, then base type, then components in page order.
   Accent model: every component reads --accent (and --accent-text). The document default is
   Classic yellow; .class-<slug> swaps in the class color. */

@font-face {
  font-family: "Accidental Presidency";
  src: url("/fonts/accidental-presidency.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Expressway";
  src: url("/fonts/expressway.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* Inter, the body face: the official v4.1 release (SIL OFL, /fonts/inter-LICENSE.txt), Latin
   subset built by scripts/fonts/subset-fonts.py to match what Google Fonts served before. The
   roman file is variable from 400 to 600 (700 resolves to 600, as it did); italic is 400 only. */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-4.1-latin.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-4.1-latin-italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
/* Content headings: official Titillium Web 1.002 SemiBold, SIL OFL 1.1. Latin subset and
   actual font-derived fallback metrics: ops/design/titillium-heading-font.json. */
@font-face {
  font-family: "Titillium Web";
  src: url("/fonts/titillium-web-1.002-latin-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Titillium Web Fallback";
  src: local("Arial Bold"), local("Arial-BoldMT");
  font-weight: 600;
  font-style: normal;
  size-adjust: 96.42185%;
  ascent-override: 117.50449%;
  descent-override: 40.23984%;
  line-gap-override: 0%;
}

:root {
  color-scheme: dark;

  /* Grounds: the game's dark warm stone */
  --bg: #141210;
  --bg-band: #181613;
  --bg-2: #1c1a17;
  --bg-3: #24211d;

  /* Frames: 1px black edge outside, warm stone line inside */
  --edge: #000000;
  --edge-inner: #3d3933;
  --line: #3d3933;
  --line-strong: #75706a;
  --hairline: #26231f;
  /* The stone line inside every frame, a shade lighter along the top so a panel reads as
     raised a step off the ground (tldr-accent-2026-09-30). */
  --edge-lit: #4c473f;
  --panel-frame: inset 0 1px 0 var(--edge-lit), inset 0 0 0 1px var(--edge-inner);
  /* Title bars: --bg-3 with a faint lift at the top edge. */
  --bar-fill: linear-gradient(180deg, #2a2621, var(--bg-3));

  /* Text */
  --ink: #f4f1ea;
  --ink-2: #d8d2c6;
  --ink-3: #a39c90;

  /* Accent and the game's own colors */
  --classic-yellow: #ffd100;
  --gold: var(--classic-yellow);
  --gold-deep: #3a3000;
  --ember: var(--classic-yellow);
  --ember-deep: var(--gold-deep);
  --arcane: var(--classic-yellow);
  --arcane-deep: var(--gold-deep);
  --frost: #5aa9e6;
  --frost-deep: #1d3a52;
  --blood: #e0525c;
  --blood-deep: #5a1a1f;
  --horde: #e0525c;
  --horde-text: #f0747c;
  --alliance: #5aa9e6;
  --enter-red: #9a2418;
  --enter-red-2: #6e160e;
  --enter-edge: #6b645a;
  /* The affiliate (loot) button: WoW's legendary orange, so the one buy action on a page
     stands apart from the red navigation buttons. Dark ink on it: 7.9:1 at the top of the
     gradient, 5.1:1 at the bottom. */
  --loot-orange: #ff8a1f;
  --loot-orange-2: #d96300;
  --loot-edge: #7a3a00;
  --loot-ink: #1c0f00;

  /* Talent states: the game's green, orange, stone gray */
  --new: #1eff00;
  /* Reworked is lemon yellow (TJ, 2026-10-11): paler and greener than --classic-yellow so the two
     stay apart, and 15.9:1 on --bg and 13.6:1 on --bg-3. It was orange until 2026-10-03 (moved off
     orange so the legendary-orange RestedXP button stays the only orange on a page), then epic
     purple, then teal, then lime from 2026-10-08. Epic item quality keeps its own purple in --q4. */
  --reworked: #fff05a;
  /* Item quality (rarity) colors, the game's standard values: Poor #9d9d9d, Common #ffffff,
     Uncommon #1eff00, Rare #0070dd, Epic #a335ee, Legendary #ff8000. Rare and Epic are
     lightened for text the way --reworked is, because the game values sit near 3.3:1 on
     --bg-3; every token here clears 4.5:1 on --bg, --bg-2, --bg-3 and the tooltip ground
     (lowest: Rare, 4.8:1 on --bg-3). Used by item links and their tooltips
     (scripts/lib/core/entity-links.mjs). */
  --q0: #9d9d9d;
  --q1: #ffffff;
  --q2: #1eff00;
  --q3: #3d8fea;
  --q4: #b779ff;
  --q5: #ff8000;
  --q6: #e6cc80;
  --q7: #00ccff;
  /* "Crafting Reagent" and similar lines in an item tooltip: the game's light blue. */
  --tip-reagent: #68ccef;
  --unchanged: #8c877e;
  --unchanged-text: #b8b2a7;
  --fel: var(--new);
  --fel-deep: #0c3a00;

  /* Evidence tiers: one icon per tier, told apart by shape and by these colors. Each clears
     3:1 on every ground, --bg-3 and the tooltip's included (lowest: not-announced, 4.0:1 on
     --bg-3). The green is softer than --new so a Confirmed icon never reads as "New". */
  --evidence-confirmed: #4fc26b;
  --evidence-planned: #6aa6f8;
  --evidence-reported: #e3a743;
  --evidence-unverified: var(--ink-3);
  --evidence-not-announced: #857f75;
  /* Blizzard's own words: the blue-post blue players know from the forums and blue trackers,
     on quoted Blizzard text only (BlueQuote, markdown.mjs). 7.6:1 on --bg, 6.5:1 on --bg-3. */
  --blue-post: #00aeff;
  /* Blizzard's dated corrections: the white pip on a corrected talent, the tooltip's
     correction line and the calculator's diff rule. */
  --correction: var(--ink);

  /* Blizzard class colors */
  --class-warrior: #c69b6d;
  --class-paladin: #f48cba;
  --class-hunter: #aad372;
  --class-rogue: #fff468;
  --class-priest: #ffffff;
  --class-shaman: #0070dd;
  --class-shaman-text: #3a9bff;
  --class-mage: #3fc7eb;
  --class-warlock: #8788ee;
  --class-druid: #ff7c0a;
  --class-death-knight-text: #f16b7b;
  --class-demon-hunter-text: #c77ce5;
  --class-monk: #00ff98;
  --class-evoker: #67bda5;

  /* Local accent defaults */
  --accent: var(--classic-yellow);
  --accent-deep: var(--gold-deep);
  --accent-text: var(--accent);
  --focus: #ffffff;
  /* How much of the page accent a surface borrows (ops/design/tldr-accent-2026-09-30.md).
     Components mix at the point of use, color-mix(in srgb, var(--accent) var(--mix-wash),
     transparent), so a .class-<slug> accent reaches them: faint for table stripes, wash for
     a hovered row or the TL;DR title bar, line for a rule drawn in the accent. */
  --mix-faint: 4%;
  --mix-wash: 12%;
  --mix-line: 55%;

  --font-display: "Accidental Presidency", "Arial Narrow", "Roboto Condensed", sans-serif;
  --font-heading: "Titillium Web", "Titillium Web Fallback", "Segoe UI", system-ui, Arial, sans-serif;
  --heading-weight: 600;
  --font-ui: "Expressway", "Segoe UI", system-ui, Arial, sans-serif;
  --font-body: "Inter", system-ui, "Segoe UI", Arial, sans-serif;

  /* Type scale: the eight sizes the stylesheets already use most. New CSS takes a step;
     `npm run lint:css` lists sizes off the scale. Display headings stay fluid clamp(). */
  --fs-2xs: .72rem;
  --fs-xs: .8rem;
  --fs-sm: .9rem;
  --fs-md: .95rem;
  --fs-base: 1rem;
  --fs-lg: 1.15rem;
  --fs-xl: 1.35rem;
  --fs-2xl: 1.6rem;

  /* Breakpoints. Custom properties cannot drive @media, so media queries write these values
     directly, as min-width: N or max-width: N - .01rem: 40rem (phone to small tablet),
     48rem, 60rem, 64rem (desktop), 72rem, 80rem. `npm run lint:css` flags any other width. */

  --s-1: .25rem;
  --s-2: .5rem;
  --s-3: .75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;
  --s-8: 4rem;
  --s-9: 6rem;
  --s-section: clamp(2rem, 4vw, 3.5rem);

  --page: 76rem;
  /* The sticky site header (T1). Anchors land at scroll-padding-top (--header-h plus --s-5,
     80px), below it. */
  --header-h: 3.5rem;
  --sticky-top: calc(var(--header-h) + var(--s-4));
  --gutter: clamp(1rem, 3.5vw, 2.5rem);
  --measure: 66ch;

  /* Square, like an addon skin: controls keep 2px so they read as pressable */
  --r-md: 0;
  --r-lg: 0;
  --r-pill: 2px;
  --r-ctl: 2px;
  --shadow: none;
  --t: 150ms ease;

  /* Tables (card #1015): one set of tokens for every table. --tbl-head-ink and --tbl-row-hover are
     not declared here on purpose: they default to the page accent where the table sits, so a
     .class-<slug> page gets its class color. A page or skin may set either. */
  --tbl-font: var(--font-ui);
  --tbl-fs: var(--fs-sm);
  --tbl-fs-phone: .86rem;
  --tbl-fs-sub: var(--fs-xs);
  --tbl-fs-head: var(--fs-2xs);
  --tbl-lh: 1.3;
  --tbl-pad-y: 6px;
  --tbl-pad-x: 8px;
  --tbl-head-pad-y: 5px;
  --tbl-head-rule: var(--line);
  --tbl-row-rule: var(--hairline);
  --tbl-group-rule: var(--line-strong);
  --tbl-ink: var(--ink-2);
  --tbl-ink-strong: var(--ink);
  --tbl-ink-sub: var(--ink-3);
  --tbl-key-ink: color-mix(in srgb, var(--new) 30%, var(--ink));
  --tbl-icon: 1.25em;
  --tbl-badge-pad: 1px 7px;
  --tbl-badge-radius: var(--r-pill);
  --tbl-stack-pad-x: 0px;

  /* Tooltips: the game's near-black stone ground. The "New in Forever" line is the game's
     own green (12.7:1 on the tooltip ground); descriptions are Classic yellow. */
  --tip-ground: #0f0e0c;
  --tip-muted: #bdb6aa;
  --spell-green: #1eff00;

  /* Brand token names (docs/brand/tokens.json) for the shared components in
     public/components/. Each one aliases a site token above, so every color still has one
     value. */
  --panel: var(--bg-2);
  --panel-2: var(--bg-3);
  --line-control: var(--line-strong);
  --on-accent: var(--bg);
  --classic-green: var(--new);
  --shadow-inner: var(--panel-frame);
  --shadow-ring: 0 0 0 1px var(--edge);
  --shadow-enter: inset 0 1px 0 rgba(255, 210, 150, .3), inset 0 -2px 0 rgba(0, 0, 0, .35), 0 0 0 1px var(--edge);
  --shadow-loot: inset 0 1px 0 rgba(255, 236, 205, .55), inset 0 -2px 0 rgba(0, 0, 0, .28), 0 0 0 1px var(--edge);
}

/* The Analytics settings panel: a small nonmodal toast docked at the bottom, opened only from
   the footer's Analytics settings button. Need and Pass get equal weight. It hides at once on
   a choice and never animates; enlarged text scrolls inside it. */
.analytics-consent[hidden], .analytics-settings[hidden] { display: none; }
.analytics-consent {
  position: fixed;
  z-index: 1100;
  left: 1rem;
  bottom: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .375rem .75rem;
  width: max-content;
  max-width: min(46rem, calc(100vw - 2rem));
  max-height: 50vh;
  overflow: auto;
  padding: .375rem .375rem .375rem .75rem;
  background: var(--bg-2);
  color: var(--ink-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: 0 .25rem 1rem #0008;
  font: 400 .8125rem/1.35 var(--font-body);
}
/* At enlarged text the line no longer fits beside the buttons, which then wrap below it. */
.analytics-consent-text { flex: 1 1 14rem; min-width: 0; margin: 0; }
.analytics-consent-text b { font-weight: 600; color: var(--ink); }
.analytics-consent-text a { color: var(--ink); }
.analytics-consent-actions { display: flex; flex: none; gap: .375rem; }
.analytics-consent-actions button {
  width: 3.5rem;
  min-height: 2.25rem;
  padding: .25rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  color: var(--ink);
  background: var(--bg-3);
  font: 400 .875rem/1 var(--font-ui);
  cursor: pointer;
}
.analytics-consent-actions button:hover { border-color: var(--ink); }
.analytics-consent-actions button:disabled { cursor: not-allowed; color: var(--ink-3); border-color: var(--line); }
.analytics-settings { padding: .25rem 0; border: 0; background: transparent; color: var(--ink); font: inherit; text-decoration: underline; text-underline-offset: .2em; cursor: pointer; min-height: 2.75rem; }
@media (max-width: 47.99rem) {
  .analytics-consent { left: .5rem; right: .5rem; bottom: .5rem; width: auto; max-width: none; gap: .375rem .5rem; padding: .3125rem .3125rem .3125rem .625rem; font-size: .75rem; line-height: 1.25; }
  .analytics-consent-actions button { width: 2.875rem; min-height: 2.5rem; }
}

/* ---------- Accent variants ---------- */

.class-warrior { --accent: var(--class-warrior); --accent-text: var(--class-warrior); }
.class-paladin { --accent: var(--class-paladin); --accent-text: var(--class-paladin); }
.class-hunter  { --accent: var(--class-hunter);  --accent-text: var(--class-hunter); }
.class-rogue   { --accent: var(--class-rogue);   --accent-text: var(--class-rogue); }
.class-mage    { --accent: var(--class-mage);    --accent-text: var(--class-mage); }
.class-warlock { --accent: var(--class-warlock); --accent-text: var(--class-warlock); }
.class-druid   { --accent: var(--class-druid);   --accent-text: var(--class-druid); }
/* Shaman blue is too dark for text on the stone grounds; its text accent is a lighter blue.
   Priest white drives bars and rings; its text stays --ink. */
.class-shaman  { --accent: var(--class-shaman);  --accent-text: var(--class-shaman-text); }
.class-priest  { --accent: var(--class-priest);  --accent-text: var(--ink); }

/* ---------- Reset and base ---------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + var(--s-5));
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.6;
  overflow-x: hidden;
  font-synthesis: none;
}

/* Reflow (WCAG 1.4.4, 1.4.10): <main> is the page's size container. Viewport media queries
   measure rem at the browser's default size, so 200% text (or a larger root font from a user
   style sheet) never collapses a layout they set. A container query measures rem at the
   root's real size: under 200% text 1440 has 44.5rem of page and 768 has 24rem. The body's
   layouts pair each width query with a `page` query 1rem under it, so a classic 15px
   scrollbar keeps every window width's default layout (the header does the same,
   .site-header). Under 20rem of page (390 at 200% text or zoom, 768 at 400% zoom) the
   label columns stack; no default width reaches that, since 320 is 20rem. */
main { container: page / inline-size; }
/* Under 20rem of page a word longer than its line (a URL, an email, a file path,
   "Warrior/Mage/Warlock") breaks instead of running past the screen, where the body's
   overflow-x: hidden would cut it off. Wider pages keep normal wrapping: a column head
   that overhangs its cell by a few pixels is not split mid-word, and Chrome counts the
   breaks in a table cell's width, so tables keep their columns. The home page sets
   overflow-wrap: anywhere on its <main> (.hp) and keeps it. */
@container page (width < 20rem) { main:not(.hp) > * { overflow-wrap: break-word; } }

img { max-width: 100%; height: auto; display: block; }

a { color: var(--accent-text); text-decoration-thickness: .08em; text-underline-offset: .18em; }
a:hover { color: var(--ink); }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

h1 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: .01em;
  margin: 0;
  color: var(--ink);
  text-wrap: balance;
}
h1 { font-size: clamp(2.2rem, 5vw, 3.25rem); line-height: .95; }
h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: var(--heading-weight);
  line-height: 1.2;
  letter-spacing: 0;
  margin: 0;
  color: var(--ink);
  text-wrap: balance;
}
h2 { font-size: clamp(1.4rem, 2.6vw, 1.75rem); }
h3 { font-size: 1.2rem; line-height: 1.25; }
h4 { font-size: 1.05rem; line-height: 1.3; }
h5, h6 { font-size: 1rem; line-height: 1.3; }

/* Section heads: a short rule in the page accent under each body h2, so a long page reads in
   chapters (ops/design/tldr-accent-2026-09-30.md). Headings with a class of their own (panel
   title bars, cards) and the home page keep theirs. */
main:not(.hp) h2:not([class], .wfg-panel *)::after {
  content: "";
  display: block;
  width: 2rem;
  height: 2px;
  margin-top: .4rem;
  border-radius: 1px;
  background: var(--accent);
}

p { margin: 0; }
p + p { margin-top: 1em; }

.wrap {
  width: min(100% - 2 * var(--gutter), var(--page));
  margin-inline: auto;
}

/* Grid children must be allowed to shrink so wide tables never widen the page. */
.article-column, .prose, .story, .breadcrumb, .breadcrumb li { min-width: 0; }

.eyebrow, .chip, .btn, .contents, .crumb, .breadcrumb, .status {
  font-family: var(--font-ui);
}

.eyebrow {
  font-size: .82rem;
  font-weight: 400;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin: 0 0 var(--s-2);
}

.chip {
  display: inline-block;
  font-size: .78rem;
  font-weight: 400;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent-text);
  background: var(--bg-3);
  border: 1px solid var(--edge);
  border-radius: var(--r-ctl);
  padding: .2em .6em;
  line-height: 1.3;
}

.btn svg { width: 1em; height: 1em; transition: transform var(--t); }
@media (hover: hover) and (pointer: fine) {
  .btn:hover svg { transform: translateX(3px); }
}

/* The primary button is Classic's red character-select button. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5em;
  min-height: 2.75rem;
  padding: .55em 1.15em;
  font: 400 1.25rem/1 var(--font-display);
  letter-spacing: .03em;
  color: var(--classic-yellow);
  text-shadow: 0 1px 0 #000;
  background: linear-gradient(180deg, var(--enter-red), var(--enter-red-2));
  border: 1px solid var(--enter-edge);
  border-radius: var(--r-ctl);
  box-shadow: inset 0 1px 0 rgba(255, 210, 150, .3), inset 0 -2px 0 rgba(0, 0, 0, .35), 0 0 0 1px #000;
  text-decoration: none;
  transition: filter var(--t);
}
.btn:hover { color: var(--classic-yellow); filter: brightness(1.15); }

/* Shared primitives for scripts/lib/components/ (docs/brand/components-code.md): the two
   buttons, the monochrome evidence tag, the talent state tag, and the visually hidden radio
   behind every picker. They are small and used across templates, so they live here rather
   than in a per-component file. */
.wfg-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  min-height: 2.75rem;
  padding: .6rem 1.1rem;
  border: 0;
  border-radius: var(--r-ctl);
  font: 400 1rem/1.2 var(--font-ui);
  text-decoration: none;
  cursor: pointer;
  transition: filter var(--t), color var(--t), box-shadow var(--t);
}
.wfg-btn svg { width: 1em; height: 1em; transition: transform var(--t); }
.wfg-btn--enter {
  border: 1px solid var(--enter-edge);
  background: linear-gradient(180deg, var(--enter-red), var(--enter-red-2));
  box-shadow: var(--shadow-enter);
  font: 400 1.3rem/1 var(--font-display);
  letter-spacing: .03em;
  text-shadow: 0 1px 0 var(--edge);
}
.wfg-btn--enter, .wfg-btn--enter:hover { color: var(--classic-yellow); }
/* The affiliate button (partner CTA, the RestedXP and Zygor page buttons, the exit offer). */
.wfg-btn--loot {
  border: 1px solid var(--loot-edge);
  background: linear-gradient(180deg, var(--loot-orange), var(--loot-orange-2));
  box-shadow: var(--shadow-loot);
  font: 400 1.3rem/1 var(--font-display);
  letter-spacing: .03em;
}
.wfg-btn--loot, .wfg-btn--loot:hover { color: var(--loot-ink); }
.wfg-btn--ghost { background: transparent; box-shadow: inset 0 0 0 1px var(--line-control); }
.wfg-btn--ghost, .wfg-btn--ghost:hover { color: var(--ink); }
@media (hover: hover) and (pointer: fine) {
  .wfg-btn--enter:hover { filter: brightness(1.15); }
  .wfg-btn--loot:hover { filter: brightness(1.08) saturate(1.05); }
  .wfg-btn--ghost:hover { box-shadow: inset 0 0 0 1px var(--accent); }
  .wfg-btn:hover svg { transform: translateX(3px); }
}

/* EvidenceTag (evidence.mjs): one icon per tier, no visible word. Shape and color both
   name the tier, so it never rests on color alone: a filled disc with a check for Confirmed
   (green), a clock for Planned (blue), a speech bubble for Reported (amber), a question mark
   for Unverified (gray), an empty dashed ring for Not announced (dim gray). The word is
   visually hidden text; a title or the EvidenceMark tooltip shows it on hover. Legends print
   it (.wfg-evidence-word).
   The zero-width ::before gives the tag a text baseline at its vertical center, so the icon
   sits on the middle of the line in running text, in baseline-aligned flex rows and in the
   fact grid alike, without a fix per context. */
.wfg-evidence {
  --ev: var(--evidence-unverified);
  display: inline-flex;
  align-items: center;
  line-height: 1;
  color: var(--ev);
  white-space: nowrap;
}
.wfg-evidence::before { content: "\200b"; }
.wfg-ev-icon { flex: none; display: block; width: .95rem; height: .95rem; }
.wfg-evidence-word { align-self: baseline; margin-left: 6px; font: 400 .8rem/1.3 var(--font-ui); letter-spacing: .03em; color: var(--ink); }
.wfg-evidence[data-tier="confirmed"], .tip-ev[data-tier="confirmed"] { --ev: var(--evidence-confirmed); }
.wfg-evidence[data-tier="planned"], .tip-ev[data-tier="planned"] { --ev: var(--evidence-planned); }
.wfg-evidence[data-tier="reported"], .tip-ev[data-tier="reported"] { --ev: var(--evidence-reported); }
.wfg-evidence[data-tier="not-announced"], .tip-ev[data-tier="not-announced"] { --ev: var(--evidence-not-announced); }
@media (forced-colors: active) {
  .wfg-evidence { color: CanvasText; }
}

/* EvidenceMark (evidence.mjs): the icon as a button whose tooltip leads with the tier word,
   then the source. The hit area reaches 27px square without making the row taller; hover
   brightens and slightly magnifies the icon, focus gets the site's ring, and a pinned
   tooltip rings the button. */
.wfg-ev {
  position: relative;
  display: inline-flex;
  align-items: center;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  line-height: 1;
  color: inherit;
  cursor: help;
  vertical-align: baseline;
}
.wfg-ev::after { content: ""; position: absolute; inset: -6px; }
.wfg-ev .wfg-evidence-word { text-decoration: underline dotted 1px; text-decoration-color: var(--ink-3); text-underline-offset: 3px; }
.wfg-ev .wfg-evidence { position: relative; transition: filter var(--t); }
.wfg-ev .wfg-ev-icon { transition: transform var(--t); }
.wfg-ev.is-pinned { background: var(--panel-2); box-shadow: 0 0 0 3px var(--panel-2), 0 0 0 4px var(--line-strong); }
.wfg-ev.is-pinned .wfg-evidence, .wfg-ev:focus-visible .wfg-evidence { filter: brightness(1.25); }
.wfg-ev.is-pinned .wfg-ev-icon, .wfg-ev:focus-visible .wfg-ev-icon { transform: scale(1.15); }
@media (hover: hover) and (pointer: fine) {
  .wfg-ev:hover .wfg-evidence { filter: brightness(1.25); }
  .wfg-ev:hover .wfg-ev-icon { transform: scale(1.15); }
}
/* Desktop tooltip discovery: two rings on entry, one on a quiet 55s reminder (at most two
   reminders per page). Only transform and opacity change, inside the existing hit area.
   tooltip.js stops hints on use, offscreen or hidden tabs. Not announced stays still. */
.wfg-ev .wfg-evidence::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: .95rem;
  height: .95rem;
  margin-top: -.475rem;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .wfg-ev.is-hint .wfg-evidence:not([data-tier="not-announced"])::after {
    animation: wfg-ev-hint 1.2s cubic-bezier(.2, .6, .35, 1) var(--hint-delay, 0ms) 2;
  }
  .wfg-ev.is-hint.is-hint-reminder .wfg-evidence::after { animation-iteration-count: 1; }
}
@keyframes wfg-ev-hint {
  0% { opacity: .7; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.75); }
}
@media (prefers-reduced-motion: reduce) {
  .wfg-ev .wfg-evidence, .wfg-ev .wfg-ev-icon { transition: none; }
  .wfg-ev .wfg-ev-icon { transform: none !important; }
  .wfg-ev .wfg-evidence::after { animation: none !important; }
}
.wfg-ev-tips { display: none; }
/* InlineCite (cite.mjs): a markdown ^[...] citation as one EvidenceMark-style icon after the
   claim, the tier's icon when the writer labeled one and a gray page glyph otherwise. The
   citation, its link and checked date are in the tooltip; the <small popover> that holds them
   stays hidden until the button opens it (tooltip.css). The claim's last word and the icon
   share a no-wrap span (.wfg-cite-join), so the icon never starts a line of its own. */
.wfg-cite-join { white-space: nowrap; }
.wfg-cite-join .wfg-cite-tip { white-space: normal; }
.wfg-cite { margin-left: .2em; }
.wfg-cite-icon { --ev: var(--ink-3); }
.wfg-cite-blue .wfg-cite-icon { --ev: var(--blue-post); }

.wfg-state {
  --st: var(--unchanged);
  --st-text: var(--unchanged-text);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 7px;
  border-radius: var(--r-ctl);
  background: var(--panel-2);
  font: 400 .74rem/1.35 var(--font-ui);
  letter-spacing: .03em;
  color: var(--st-text);
  white-space: nowrap;
}
.wfg-state::before { content: ""; width: 8px; height: 8px; background: var(--st); }
.wfg-state[data-state="new"] { --st: var(--new); --st-text: var(--new); }
.wfg-state[data-state="reworked"] { --st: var(--reworked); --st-text: var(--reworked); }
/* Under 20rem of page (390 at 200% text or zoom) a long last word
   ("25/29/32/36/39/43/46/50%.") would push the pair past the screen, and a printed tier or
   state ("Confirmed by RestedXP", "Same as Classic") past a 195px one: they may wrap. */
@container page (width < 20rem) { .wfg-cite-join, .wfg-evidence, .wfg-state { white-space: normal; } }

.wfg-radio { position: absolute; width: 1px; height: 1px; margin: 0; padding: 0; border: 0; opacity: 0; pointer-events: none; }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.skip-link {
  position: absolute;
  left: var(--s-4);
  top: -100%;
  z-index: 100;
  padding: .6em 1em;
  background: var(--classic-yellow);
  color: var(--bg);
  font-family: var(--font-ui);
  font-weight: 400;
  border-radius: var(--r-ctl);
  text-decoration: none;
}
.skip-link:focus { top: .5rem; }

/* ---------- Shell: header, menus, search (T1, ops/design/templates/shell/contract.md) ---------- */

/* One 56px bar that stays at the top of the screen (--header-h). Anchors land 80px down
   (html scroll-padding-top), so a heading never sits under it; sticky rails inside pages
   start at --sticky-top. Menus are native <details>: they open without JavaScript, and
   site-nav.js adds Escape, outside click and one-open-at-a-time. From 64rem the menu sits in
   the bar and each panel drops as a full-width band under it; below 64rem the Menu button
   opens one drawer (search first, then the menus as accordions). Without JavaScript that
   button is a link to the footer's site directory.

   Reflow (WCAG 1.4.4 and 1.4.10): the header's breakpoints are container queries on the
   header itself, in rem, so they follow the reader's text size as well as the window. A
   media query's rem is the browser's default size and ignores a larger root text size (text
   zoom, iOS text size, a user style sheet), which pushed the desktop menu off the side at
   200% text; a container query's rem is the root's actual size. The thresholds sit 1rem below
   the old viewport ones where a classic scrollbar (about 15px) would otherwise change the
   layout at the same window width. Under 20rem the strip takes its own row under the logo and
   Menu. site-nav.js lets a header taller than a sixth of the window scroll away
   (data-unstuck), so large text or 400% zoom never leaves it covering the page. */

.site-header {
  container: site-header / inline-size;
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--bg-2);
  box-shadow: inset 0 -1px 0 var(--edge), inset 0 -2px 0 var(--edge-inner);
  color: var(--ink);
  font-family: var(--font-ui);
}
.site-header[data-unstuck] { position: relative; }
/* The bar may run 4.5rem past the page on each side (85rem, not 76rem): seven menu items, search,
   the strip, the wordmark and the visitor count need about 1,360px (menu audit, 2026-10-08). */
.site-header-bar {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  width: min(100% - 2 * var(--gutter), 85rem);
  height: var(--header-h);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  flex: none;
  min-height: 2.75rem;
  color: var(--ink);
  text-decoration: none;
}
.brand img { width: 2.25rem; height: 2.25rem; flex: none; }
.brand-name { font: 400 1.55rem/1 var(--font-display); letter-spacing: .01em; white-space: nowrap; }
.brand:hover { color: var(--ink); }
.brand:hover .brand-name { color: var(--accent-text); }

/* Realm status strip: the home page's realm panel folded into two rows, where the road
   stands and what comes next, each a title, a status tag and a note ("to Oct 21", the
   launch countdown). The wrapper is the bar's flex item; with scripting on, site-status.js
   turns the link into the button of the release dates panel below and draws a chevron in
   the right padding reserved for it, so hydration does not move the strip. A state tag is word, dot and fill (never the color alone); an upcoming
   tag is a date, so it drops the status dot and keeps the yellow fill. No pulse: that stays
   the realm panel's. Notes are quiet except the live countdown. The note column keeps a
   minimum width, and with scripting on the build's "3 p.m. PST" (data-hold) stays hidden
   until the countdown replaces it, so hydration neither moves nor swaps visible text. */
.site-status-wrap { position: relative; display: flex; flex: 0 1 auto; min-width: 0; }
.site-status {
  display: grid;
  grid-template-columns: auto auto minmax(4rem, auto);
  align-content: center;
  gap: 3px .5rem;
  flex: 0 1 auto;
  min-width: 0;
  min-height: 2.75rem;
  padding: 0 .35rem;
  color: var(--ink);
  font: 400 .8rem/1.2 var(--font-ui);
  letter-spacing: .03em;
  text-decoration: none;
  white-space: nowrap;
}
.site-status[hidden] { display: none; }
.site-status-row { display: contents; }
.site-status-row[hidden] { display: none; }
.site-status b { overflow: hidden; text-overflow: ellipsis; font-weight: 400; color: var(--ink-2); }
.site-status-tag {
  display: inline-flex;
  align-items: center;
  justify-self: start;
  gap: .3rem;
  padding: 0 .4rem;
  border-radius: var(--r-ctl);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  color: var(--ink-2);
  font-size: .76rem;
  line-height: 1.45;
}
.site-status-tag::before { content: ""; width: .4rem; height: .4rem; flex: none; border-radius: 50%; background: currentColor; }
.site-status-tag[data-state="open"] { color: var(--new); background: color-mix(in srgb, var(--new) 12%, transparent); }
.site-status-tag[data-state="soon"] { color: var(--accent-text); background: color-mix(in srgb, var(--accent) 13%, transparent); }
.site-status-tag[data-state="unclear"] { box-shadow: inset 0 0 0 1px var(--line-control); }
.site-status-tag[data-state="done"] { color: var(--ink-3); }
.site-status-tag[data-state="soon"]::before { content: none; }
.site-status-note { color: var(--ink-2); font-size: .76rem; line-height: 1.45; font-variant-numeric: tabular-nums; }
.site-status-note[data-live] { color: var(--ink); }
@media (scripting: enabled) {
  .site-status-note[data-hold] { visibility: hidden; }
}
a.site-status:hover b, .site-status[aria-current="page"] b { color: var(--ink); text-decoration: underline; text-underline-offset: .2em; }
button.site-status { margin: 0; border: 0; border-radius: var(--r-ctl); background: transparent; text-align: left; cursor: pointer; }
@media (scripting: enabled) {
  .site-status-wrap > .site-status { position: relative; padding-right: 1rem; }
}
/* The same room once the script has run, for browsers without the scripting media feature. */
.site-status-wrap.is-enhanced > .site-status { position: relative; padding-right: 1rem; }
.site-status-chevron { position: absolute; top: 50%; right: .2rem; width: .6rem; height: .6rem; margin-top: -.3rem; color: var(--ink-3); transition: transform var(--t); }
.site-status[aria-expanded="true"] { background: var(--panel-2); }
.site-status[aria-expanded="true"] b { color: var(--ink); }
.site-status[aria-expanded="true"] .site-status-chevron { color: var(--ink); transform: rotate(180deg); }
@media (hover: hover) and (pointer: fine) {
  button.site-status:hover { background: var(--panel-2); }
  button.site-status:hover b, button.site-status:hover .site-status-chevron { color: var(--ink); }
}

/* Release dates: the panel the strip opens (site-status.js draws it from the strip's own
   data). Every stop of the home realm panel: its name as a link, the dates Blizzard states
   (the launch in the reader's time first), the evidence tier as an icon, the realm panel's
   status word, and quiet detail lines (the level cap under the beta). A key under the rows
   names the tier icons. From 64rem it hangs under the strip at 24rem; from 40rem it centers
   under it; on phones it spans the header like the drawer. */
.site-status-panel {
  position: absolute;
  z-index: 45;
  top: calc(100% + (var(--header-h) - 2.75rem) / 2);
  left: 0;
  width: 24rem;
  max-width: calc(100vw - 2 * var(--gutter));
  max-height: calc(100dvh - var(--header-h) - var(--header-extra, 0px));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 10px 14px 12px;
  background: var(--panel);
  border: 1px solid var(--edge);
  box-shadow: var(--panel-frame);
}
.site-status-panel[hidden] { display: none; }
/* The open panel sits above the analytics prompt (z-index 1100) the reader did not open. */
.site-header.is-status-open { z-index: 1101; }
.site-status-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin: 0; padding-bottom: 6px; border-bottom: 1px solid var(--hairline); font: 400 .75rem/1.3 var(--font-ui); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }
.site-status-head b { font-weight: 400; color: var(--ink-2); }
.site-status-head small { font-size: inherit; }
.site-status-list { margin: 0; padding: 0; list-style: none; }
.site-status-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: .75rem; padding: 8px 0; border-top: 1px solid var(--hairline); }
.site-status-item:first-child { border-top: 0; }
.site-status-name { grid-column: 1; justify-self: start; display: inline-flex; align-items: center; min-height: 1.5rem; color: var(--ink); font-size: 1rem; line-height: 1.3; text-decoration: none; }
a.site-status-name:hover { color: var(--accent-text); text-decoration: underline; text-underline-offset: .2em; }
.site-status-item > .site-status-tag { grid-column: 2; grid-row: 1 / span 2; align-self: center; justify-self: end; }
/* In the panel "In 25 days" is a status, as on the home realm panel, so it keeps the dot. */
.site-status-item > .site-status-tag[data-state="soon"]::before { content: ""; }
.site-status-when { grid-column: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 0 .35rem; color: var(--ink-2); font-size: .82rem; line-height: 1.4; font-variant-numeric: tabular-nums; }
.site-status-detail { grid-column: 1 / -1; margin: 3px 0 0; color: var(--ink-2); font: 400 .82rem/1.4 var(--font-body); }
.site-status-detail.is-quiet { color: var(--ink-3); font-size: .78rem; }
.site-status-detail .wfg-evidence { vertical-align: -.15em; }
.site-status-key { display: flex; flex-wrap: wrap; gap: 2px 16px; margin: 0; padding-top: 8px; border-top: 1px solid var(--hairline); }
.site-status-key .wfg-evidence-word { color: var(--ink-3); font-size: .76rem; }
.site-status-panel .site-status-foot { margin-top: 2px; padding-top: 0; border-top: 0; }
.site-status-tag[data-state="undated"] { color: var(--ink-3); background: transparent; box-shadow: inset 0 0 0 1px var(--edge-inner); }

/* The Menu control: a link to the footer directory until site-nav.js swaps in the button. */
.site-menu-link,
.site-menu-toggle {
  display: none;
  align-items: center;
  gap: .45rem;
  min-height: 2.75rem;
  margin-left: auto;
  padding: 0 .8rem;
  border: 1px solid var(--line-control);
  border-radius: var(--r-ctl);
  background: transparent;
  color: var(--ink);
  font: 400 .95rem/1 var(--font-ui);
  letter-spacing: .06em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
}
.site-menu-icon { width: 1.1rem; height: 1.1rem; flex: none; }
.site-menu-icon-close { display: none; }
.site-menu-toggle[aria-expanded="true"] { background: var(--panel-2); border-color: var(--ink-3); }
.site-menu-toggle[aria-expanded="true"] .site-menu-icon-open { display: none; }
.site-menu-toggle[aria-expanded="true"] .site-menu-icon-close { display: inline; }

.site-menu { display: flex; align-items: center; gap: var(--s-4); flex: 1 1 auto; min-width: 0; height: 100%; }

/* Search: a flat box on the ground with a control border, the results in a panel under it. */
/* The box may shrink to 8rem so the bar never runs past the viewport at 64 to 70rem, or at
   80rem where the wordmark returns; under 11.5rem the "/" hint gives the query its room. */
.site-search { position: relative; flex: 0 1 19rem; min-width: 8rem; margin: 0; container-type: inline-size; }
@container (max-width: 11.5rem) { .site-search-box kbd { display: none; } }
.site-search-box {
  display: flex;
  align-items: center;
  gap: .5rem;
  height: 2.25rem;
  padding: 0 .45rem 0 .6rem;
  background: var(--bg);
  border: 1px solid var(--line-control);
  border-radius: var(--r-ctl);
}
.site-search-box:focus-within { border-color: var(--focus); outline: 2px solid var(--focus); outline-offset: 2px; }
.site-search-box svg { width: 1rem; height: 1rem; flex: none; color: var(--ink-3); }
.site-search-box input {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: 400 .95rem/1.2 var(--font-body);
}
.site-search-box input::placeholder { color: var(--ink-3); opacity: 1; }
.site-search-box input:focus { outline: none; }
.site-search-box input::-webkit-search-cancel-button { filter: invert(.7); }
.site-search-box kbd { padding: 0 .35rem; border: 1px solid var(--line); border-radius: var(--r-ctl); font: 400 .72rem/1.5 var(--font-ui); color: var(--ink-3); }
.site-search-box:focus-within kbd { display: none; }
.site-search-results {
  position: absolute;
  z-index: 45;
  top: calc(100% + .35rem);
  left: 0;
  width: max(100%, 24rem);
  max-width: calc(100vw - 2 * var(--gutter));
  max-height: min(60vh, 26rem);
  overflow-y: auto;
  margin: 0;
  padding: 4px 0;
  list-style: none;
  background: var(--panel);
  border: 1px solid var(--edge);
  box-shadow: var(--panel-frame);
}
.site-search-results[hidden] { display: none; }
.site-search-results a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 7px 12px;
  color: var(--ink);
  text-decoration: none;
  font: 400 .95rem/1.3 var(--font-body);
}
.site-search-results a mark { background: none; color: var(--accent-text); }
.site-search-results a .site-search-kind { flex: none; font: 400 .7rem/1.3 var(--font-ui); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); }
.site-search-results [aria-selected="true"] a { background: var(--panel-2); box-shadow: inset 2px 0 var(--accent); }
.site-search-empty { position: absolute; z-index: 45; top: calc(100% + .35rem); left: 0; width: max(100%, 24rem); max-width: calc(100vw - 2 * var(--gutter)); margin: 0; padding: 8px 12px; background: var(--panel); border: 1px solid var(--edge); box-shadow: var(--panel-frame); font: 400 .9rem/1.4 var(--font-body); color: var(--ink-3); }
.site-search-empty[hidden] { display: none; }
.site-search-empty a { color: var(--accent-text); }

/* Primary menu */
.site-nav { margin-left: auto; height: 100%; }
.site-nav-list { display: flex; align-items: stretch; height: 100%; margin: 0; padding: 0; list-style: none; }
.site-nav-list > li { display: flex; }
.site-nav-menu { display: flex; }
.site-nav-top {
  display: flex;
  align-items: center;
  gap: .3rem;
  padding: 2px .4rem 0;
  border-bottom: 2px solid transparent;
  color: var(--ink-2);
  font: 400 .95rem/1 var(--font-ui);
  letter-spacing: .03em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
}
.site-nav-top::-webkit-details-marker { display: none; }
.site-nav-top:focus-visible { outline-offset: -2px; }
.site-nav-chevron { width: .6rem; height: .6rem; color: var(--ink-3); transition: transform var(--t); }
.site-nav-menu[open] > .site-nav-top { background: var(--panel-2); color: var(--ink); }
.site-nav-menu[open] .site-nav-chevron { transform: rotate(180deg); }
.site-nav-top[aria-current="page"],
.site-nav-top[data-current="true"],
.site-nav-menu[data-current="true"] > .site-nav-top { color: var(--accent-text); border-bottom-color: var(--accent); }
@media (hover: hover) and (pointer: fine) {
  .site-nav-top:hover { background: var(--panel-2); color: var(--ink); }
}

/* A menu's panel: a full-width band under the bar, in the page's width. */
.site-nav-panel {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  max-height: calc(100dvh - var(--header-h));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 14px max(var(--gutter), calc((100% - var(--page)) / 2)) 12px;
  background: var(--panel);
  border-top: 1px solid var(--edge);
  border-bottom: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-inner), inset 0 -1px 0 var(--edge-inner);
  font-family: var(--font-ui);
}
.site-nav-panel ul { margin: 0; padding: 0; list-style: none; }
.site-nav-panel a { color: var(--ink); text-decoration: none; }
.site-nav-panel a:hover { color: var(--accent-text); text-decoration: underline; text-underline-offset: .2em; }
.site-nav-panel a[aria-current="page"] { color: var(--accent-text); }

.site-nav-columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 12px 24px; }
.site-nav-columns--races { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.site-nav-group-title {
  margin: 0 0 4px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--hairline);
  font: 400 .75rem/1.3 var(--font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.site-nav-group li > a { display: inline-flex; align-items: center; min-height: 1.75rem; font-size: 1rem; }
.site-nav-kids { display: block; margin: -2px 0 3px; font-size: .84rem; line-height: 1.75rem; }
.site-nav-kids a { display: inline-block; min-height: 1.75rem; color: var(--ink-2); }
.site-nav-sep { color: var(--ink-3); }
/* Leveling by class: each class name in its class color, as in the Classes menu. */
.site-nav-group li[class^="class-"] > a { color: var(--accent-text); }

.site-nav-classes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px 24px; align-items: start; }
.site-nav-class-link { display: flex; align-items: center; gap: .6rem; min-height: 2.5rem; }
.site-nav-class-link img { width: 2.25rem; height: 2.25rem; flex: none; border: 1px solid var(--edge); box-shadow: 0 0 0 1px var(--edge-inner); }
.site-nav-class-link span { color: var(--accent-text); font-size: 1.05rem; line-height: 1.2; }
.site-nav-class-link:hover span { text-decoration: underline; text-underline-offset: .2em; }
.site-nav-class-link[aria-current="page"] span { color: var(--accent-text); text-decoration: underline; }
.site-nav-class .site-nav-kids { margin: 0; padding-left: calc(2.25rem + .6rem); }

.site-nav-race { display: inline-flex; align-items: center; gap: .6rem; min-height: 2.5rem; font-size: 1rem; }
.site-nav-race img { width: 2rem; height: 2.25rem; flex: none; object-fit: cover; border: 1px solid var(--edge); background: var(--bg); }
.site-nav-race small { display: block; margin-top: 1px; font: 400 .78rem/1.3 var(--font-ui); color: var(--ink-3); }

.site-nav-foot { display: flex; flex-wrap: wrap; gap: 4px 24px; margin: 12px 0 0; padding-top: 8px; border-top: 1px solid var(--hairline); }
.site-nav-foot a { display: inline-flex; align-items: center; gap: .3rem; min-height: 1.75rem; color: var(--accent-text); font-size: .95rem; }

/* Tablets and phones, or large text: the Menu button and one drawer. */
@container site-header (width < 75rem) {
  .site-menu-link { display: inline-flex; }
  .site-menu-toggle { display: inline-flex; }
  .site-menu-link[hidden], .site-menu-toggle[hidden] { display: none; }
  .site-status-wrap { margin-inline: auto; }
  .site-status-panel { left: 50%; transform: translateX(-50%); }
  .site-menu-link, .site-menu-toggle { margin-left: 0; }
  .site-menu {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    height: auto;
    max-height: calc(100dvh - var(--header-h) - var(--header-extra, 0px));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 12px var(--gutter) 16px;
    background: var(--panel);
    border-top: 1px solid var(--edge);
    border-bottom: 1px solid var(--edge);
    box-shadow: inset 0 1px 0 var(--edge-inner);
  }
  .site-header[data-menu-open="true"] .site-menu { display: block; }
  .site-search { width: 100%; margin-bottom: 8px; }
  .site-search-box { height: 2.75rem; }
  .site-search-box input { font-size: 1rem; }
  .site-search-results { position: static; width: 100%; max-width: none; margin-top: 4px; }
  .site-nav { margin: 0; height: auto; }
  .site-nav-list { display: block; height: auto; }
  .site-nav-list > li { display: block; border-bottom: 1px solid var(--hairline); }
  .site-nav-menu { display: block; }
  .site-nav-top { justify-content: space-between; min-height: 3rem; padding: 0 4px; border-bottom: 0; font-size: 1.05rem; }
  .site-nav-top[aria-current="page"],
  .site-nav-top[data-current="true"],
  .site-nav-menu[data-current="true"] > .site-nav-top { border-bottom: 0; box-shadow: inset 3px 0 var(--accent); padding-left: 12px; }
  .site-nav-menu[open] > .site-nav-top { background: transparent; }
  .site-nav-panel { position: static; max-height: none; overflow: visible; padding: 2px 4px 14px; background: transparent; border: 0; box-shadow: none; }
  .site-nav-classes { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 16px; }
  .site-nav-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 16px; }
  .site-nav-columns--races { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .site-nav-columns--races > [data-faction="both"] { grid-column: 1 / -1; }
  .site-nav-group li > a, .site-nav-race { min-height: 2.75rem; }
  .site-nav-kids { line-height: 2.75rem; font-size: .92rem; }
  .site-nav-kids a { min-height: 2.75rem; }
  .site-search-box kbd { display: none; }
  .site-search-empty { position: static; width: 100%; max-width: none; margin-top: 4px; }
  .site-header[data-menu-open="true"] .site-menu { height: calc(100dvh - var(--header-h) - var(--header-extra, 0px)); }
  .site-nav-foot a { min-height: 2.75rem; }
  /* Long menus (Leveling, Guides): site-nav.js turns each group title into a 44px toggle row
     and closes its list, so the open menu is a short stack of rows instead of four screens. */
  .site-nav-columns--guides, .site-nav-columns--leveling { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .site-nav-group[data-collapsible] > .site-nav-group-title:has(.site-nav-group-toggle:not([hidden])) { margin: 0; padding: 0; }
  .site-nav .site-nav-group-toggle { width: calc(100% + 8px); margin-inline: -4px; color: var(--ink-2); font: 400 .8rem/1.3 var(--font-ui); letter-spacing: .1em; text-transform: uppercase; }
  .site-nav-group[data-collapsible] > ul { padding-bottom: 6px; }
}
/* The wordmark shows where the bar has room for it beside the strip: 39 to 75rem of header
   (the Menu button's widths) and from 81rem. Below 25.6rem (393px phones and narrower) the
   Menu control keeps only its icon, so the strip's three columns fit; below 22rem the strip
   tightens; below 20rem (200% text on a phone, 400% zoom) it takes its own row. */
@container site-header (width < 39rem) or ((width >= 75rem) and (width < 81rem)) {
  .brand-name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
@container site-header (width < 25.6rem) {
  .site-menu-link span, .site-menu-toggle span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .site-menu-link, .site-menu-toggle { padding: 0; width: 2.75rem; justify-content: center; }
}
@container site-header (width < 24rem) {
  .site-header-bar { gap: var(--s-3); }
}
@container site-header (width < 75rem) {
  /* A 44px target, the drawer's: the name's hit area reaches into the padding above it and
     under the dates line, which lets taps through. Layout keeps the text's own height, so a
     name that wraps at large text never runs into its dates. */
  .site-status-name { position: relative; }
  .site-status-name::before { content: ""; position: absolute; inset: calc((1.3em - 2.75rem) / 2) 0; }
  .site-status-when { position: relative; pointer-events: none; }
}
@container site-header (width < 39rem) {
  .site-status-wrap { position: static; }
  .site-status-panel { top: 100%; left: 0; right: 0; width: auto; max-width: none; padding: 8px var(--gutter) 12px; border-width: 1px 0; transform: none; }
}
@container site-header (width < 22rem) {
  .site-header-bar { gap: var(--s-2); }
  .site-status { grid-template-columns: auto auto minmax(3.5rem, auto); padding-inline: 0; column-gap: .25rem; font-size: .76rem; }
  @media (scripting: enabled) { .site-status-wrap > .site-status { padding-right: .8rem; } }
  .site-status-wrap.is-enhanced > .site-status { padding-right: .8rem; }
  .site-status-chevron { right: .05rem; }
  .site-status-tag, .site-status-note { font-size: .72rem; }
  .site-status > .site-status-row .site-status-tag { padding-inline: .3rem; }
}
/* Logo and Menu on the first row, the strip centered on a second that may use the gutters
   (the Tab order stays logo, strip, Menu). --header-extra tells the drawer and the strip's
   panel the bar is taller. Under 12rem (400% zoom on a tablet) the gutters shrink and each
   strip row wraps on its own instead of holding three columns. */
@container site-header (width < 20rem) {
  .site-header-bar { flex-wrap: wrap; height: auto; row-gap: 0; padding-block: calc((var(--header-h) - 2.75rem) / 2); --header-extra: 2.75rem; }
  .site-status-wrap { order: 1; flex: 1 0 calc(100% + 2 * var(--gutter)); justify-content: center; margin-inline: calc(-1 * var(--gutter)); }
  .site-status { grid-template-columns: auto auto minmax(3rem, auto); }
  .site-menu-link, .site-menu-toggle { margin-left: auto; }
}
@container site-header (width < 12rem) {
  .site-header-bar { --gutter: .25rem; gap: .25rem; }
  .site-status { display: block; padding-block: .25rem; }
  .site-status-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0 .3rem; }
}
@container site-header (width <= 22rem) {
  .site-nav-classes, .site-nav-columns { grid-template-columns: minmax(0, 1fr); }
}
@container site-header (width >= 75rem) {
  .site-status-wrap { flex: none; }
  .site-status { flex: none; padding-inline: 0; }
  .site-nav-columns--guides { grid-template-columns: repeat(6, minmax(0, 1fr)); column-gap: 16px; }
  .site-nav-columns--leveling { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 16px; }
  /* Talents: a short list hangs under its own item instead of spanning the page. */
  .site-nav-menu--compact { position: relative; }
  .site-nav-menu--compact > .site-nav-panel { right: auto; width: max-content; min-width: 15rem; padding: 12px 16px; }
}
@container site-header (75rem <= width <= 80rem) {
  .site-header-bar { gap: .5rem; }
  .site-status { grid-template-columns: auto auto minmax(3.5rem, auto); column-gap: .35rem; }
  .site-search { flex-basis: 13rem; }
  .site-nav-top { padding-inline: .3rem; }
  /* Eight items (Broadcasts addition, 2026-10-10): the chevrons go at these widths so search keeps
     "Search guides" in full; the items are still disclosure buttons. */
  .site-nav-chevron { display: none; }
}
@container site-header (width >= 81rem) {
  .site-header-bar { gap: .75rem; }
}

/* ---------- Bands and section headings ---------- */

/* ---------- Partner creatives in the page flow ---------- */

.partner-creative-float { float: right; width: min(100%, 320px); margin: 0 0 var(--s-6) var(--s-6); }
.partner-creative-float .affiliate-creative-link { width: 100%; }

.affiliate-creatives {
  padding-block: var(--s-7);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line-strong);
  background: var(--bg-2);
}
.affiliate-creatives-heading { max-width: 62ch; margin-bottom: var(--s-5); }
.affiliate-creatives-heading h2 { margin-top: var(--s-2); }
.affiliate-creatives-heading p:not(.eyebrow) { margin-top: var(--s-3); color: var(--ink-2); }
.affiliate-creatives-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-5);
  align-items: start;
}
.affiliate-creative-item { min-width: 0; display: flex; justify-content: center; }
.affiliate-creative-link {
  display: block;
  max-inline-size: 100%;
  border-radius: var(--r-lg);
}
.affiliate-creative-link img {
  display: block;
  width: 100%;
  max-inline-size: 100%;
  height: auto;
  border-radius: inherit;
}
.affiliate-creatives--restedxp .affiliate-creative-item[data-creative="14"] .affiliate-creative-link { width: min(100%, 728px); }
.affiliate-creatives--restedxp .affiliate-creative-item[data-creative="13"] .affiliate-creative-link { width: min(100%, 320px); }
.affiliate-creatives--restedxp .affiliate-creative-item[data-creative="22"] .affiliate-creative-link { width: min(100%, 300px); }
.affiliate-creatives--restedxp .affiliate-creatives-grid:has([data-creative="14"]) { grid-template-columns: 1fr; }
.affiliate-creatives--compact .affiliate-creatives-grid { grid-template-columns: 1fr; }
.affiliate-creatives--compact .affiliate-creative-item[data-creative="22"] { justify-content: center; }
.affiliate-creatives--compact .affiliate-creative-item[data-creative="22"] .affiliate-creative-link { width: min(100%, 340px); }
.affiliate-creatives--inline { padding-block: var(--s-5); border: 0; background: transparent; }
.affiliate-creatives--inline .wrap { width: 100%; }
.affiliate-creatives--inline .affiliate-creative-item[data-creative="14"] { justify-content: center; }
.affiliate-break {
  width: min(100% - (2 * var(--gutter)), var(--page));
  margin: var(--s-7) auto;
  display: grid;
  justify-items: center;
  gap: var(--s-3);
}
/* A script can set [hidden] on a slot (the /guides filter with no match); display: grid would win. */
.affiliate-break[hidden] { display: none; }
.affiliate-break--leaderboard .affiliate-creative-link { width: min(100%, 728px); }
.affiliate-break--square .affiliate-creative-link { width: min(100%, 300px); }
.affiliate-break--vertical .affiliate-creative-link { width: min(100%, 320px); }
.affiliate-creatives--zygorguides .affiliate-creative-link { width: min(100%, 32rem); }

/* Partner creatives sit inline in the page flow; the footer carries the affiliate disclosure. */
.affiliate-break--inline { gap: var(--s-2); }
/* A registry slot (scripts/lib/components/partner-slot.mjs) with a phone creative shows the
   300 by 250 square under 40rem and the 728 by 90 leaderboard above it. */
.wfg-partner-wide, .wfg-partner-narrow { display: grid; justify-items: center; width: 100%; }
.wfg-partner-narrow { display: none; }
.wfg-partner-narrow .affiliate-creative-link { width: min(100%, 300px); }
@media (max-width: 40rem) {
  .wfg-partner-wide { display: none; }
  .wfg-partner-narrow { display: grid; }
}

.sitemap-link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 2.75rem;
  padding: .7rem .8rem;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-2);
  color: var(--ink);
  text-decoration: none;
  transition: border-color var(--t), background var(--t), color var(--t);
}
.sitemap-link:hover { border-color: var(--gold); background: var(--bg-3); color: var(--ink); }

/* ---------- Story cards ---------- */

.story {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-height: 100%;
  overflow: hidden;
  background: var(--bg-2);
  border: 1px solid var(--edge);
  border-radius: 0;
  box-shadow: var(--panel-frame);
  transition: border-color var(--t);
}
.story:hover { border-color: var(--accent); }
.story h2, .story h3 { font-size: 1.22rem; line-height: 1.25; }
.story h2 a, .story h3 a { color: var(--ink); text-decoration: none; }
.story h2 a:hover, .story h3 a:hover { color: var(--gold); text-decoration: underline; }
.story .excerpt { color: var(--ink-2); font-size: .98rem; flex: 1; }

/* ---------- Article and guide headers ---------- */

.crumb { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; font-size: .9rem; }
.crumb a { color: var(--ink-2); text-decoration: none; }
.crumb a:hover { color: var(--accent-text); text-decoration: underline; }

/* Visible breadcrumb trail. One small line above the title, wrapping rather than
   scrolling on narrow viewports; the separator is decorative and never announced. */
.breadcrumb ol {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  /* The 28px link targets already space wrapped rows. */
  gap: 0 var(--s-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: .9rem;
  min-width: 0;
}
.breadcrumb li { display: flex; align-items: center; gap: var(--s-2); min-width: 0; }
.breadcrumb li + li::before { content: "/"; color: var(--ink-3); }
.breadcrumb a { display: inline-flex; align-items: center; min-height: 1.75rem; color: var(--ink-2); text-decoration: none; }
.breadcrumb a:hover { color: var(--accent-text); text-decoration: underline; }
.breadcrumb [aria-current="page"] {
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: min(100%, 28ch);
}

.talent-calculator-embed {
  width: calc(100% - 2 * var(--gutter));
  max-width: var(--page);
  margin-inline: auto;
  padding-block: var(--s-6);
}
.talent-calculator-loading {
  margin: 0;
  padding: var(--s-5) var(--s-6);
  border: 1px solid var(--edge);
  box-shadow: var(--panel-frame);
  background: var(--bg-2);
  color: var(--ink-2);
  font-family: var(--font-ui);
}
.talent-calculator-attribution {
  margin: calc(var(--s-3) * -1) 0 0;
  color: var(--ink-3);
  font-family: var(--font-ui);
  font-size: .78rem;
  text-align: right;
}
.talent-calculator-attribution a {
  color: var(--ink-3);
  text-decoration-color: var(--line-strong);
}
.talent-calculator-attribution a:hover,
.talent-calculator-attribution a:focus-visible {
  color: var(--ink-2);
}

.article-column {
  width: min(100% - 2 * var(--gutter), 46rem);
  margin-inline: auto;
  padding-block: var(--s-7);
}

.article-figure { margin: 0 0 var(--s-6); }
.article-figure img { border-radius: var(--r-lg); width: auto; max-width: 100%; height: auto; border: 1px solid var(--line-strong); }
/* Responsive figures (scripts/lib/core/responsive-image.mjs): the <picture> wrapper takes no
   box, and a figure with a width-descriptor srcset (sizes) fills its column, as a source at
   least 76rem wide already did at width: auto. */
.article-figure picture { display: contents; }
.article-figure img[sizes] { width: 100%; }
.article-figure figcaption {
  font-family: var(--font-ui);
  font-size: .8rem;
  color: var(--ink-3);
  margin-top: var(--s-3);
}
.article-figure figcaption a, .article-figure figcaption :is(span, button).spell-link { color: inherit; text-decoration-color: var(--line-strong); }
.article-figure figcaption a:hover, .article-figure figcaption a:focus-visible { color: var(--ink-2); }

/* Two figures side by side; stacks on narrow screens. */
.figure-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-4);
  margin-bottom: var(--s-6);
}
.figure-row .article-figure { margin-bottom: 0; }

@media (max-width: 640px) {
  .figure-row { grid-template-columns: minmax(0, 1fr); }
}

/* Responsive YouTube embed. */
.video-embed { margin: 0 0 var(--s-6); }
.video-frame {
  width: 100%;
  max-width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-lg);
  border: 1px solid var(--line-strong);
  overflow: hidden;
  background: var(--bg-2);
}
.video-frame iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}
.video-embed figcaption {
  font-family: var(--font-ui);
  font-size: .8rem;
  color: var(--ink-3);
  margin-top: var(--s-3);
}
.video-embed figcaption a, .video-embed figcaption :is(span, button).spell-link { color: inherit; text-decoration-color: var(--line-strong); }
.video-embed figcaption a:hover, .video-embed figcaption a:focus-visible { color: var(--ink-2); }

/* Status pills */
.status {
  display: inline-block;
  font-size: .7rem;
  font-weight: 400;
  letter-spacing: .05em;
  text-transform: uppercase;
  border-radius: var(--r-ctl);
  padding: .15em .55em;
  line-height: 1.3;
  white-space: nowrap;
}
.status-confirmed  { color: var(--ink);   background: rgba(255, 255, 255, .08); border: 1px solid var(--ink); }
.status-planned    { color: var(--ink-2); background: transparent;              border: 1px solid var(--ink-2); }
.status-reported   { color: var(--ink-3); background: transparent;              border: 1px dashed var(--ink-3); }
.status-unverified { color: var(--ink-3); background: transparent;              border: 1px dotted var(--ink-3); }
.status-not-announced { color: var(--ink-3); background: transparent; border: 1px solid transparent; font-style: italic; }

/* ---------- On this page ---------- */

/* Two renderings of one list: a sticky sidebar at desktop widths and a
   collapsed disclosure inside the article column below them. Only one is
   displayed at a time, so focus order never sees a duplicate. */
.guide-grid {
  width: min(100% - 2 * var(--gutter), var(--page));
  margin-inline: auto;
}
.guide-grid > .article-column {
  width: 100%;
  max-width: 46rem;
  margin-inline: auto;
}
.guide-aside { display: none; }
/* A wide guide grid (guideFrame in scripts/lib/components/toc.mjs): the column takes the full
   width beside the rail, for lower pages that run their own two-column sections (spec,
   reference, comparison). */
.guide-grid--wide > .article-column { max-width: none; }
/* A guide grid under a page's top band (the answer panels, the class sheet, the hub picker,
   the race panel) starts one section gap below it: the grid's own margin, with no padding
   above the column or the table of contents, so the body and "On this page" start on one
   line. Each template used to add its own padding on top of the gap (24 to 104px). */
:is(.tg-lower, .class-lower, .hub-lower, .race-guide, .spec-guide) { margin-top: var(--s-section); }
:is(.tg-lower, .class-lower, .hub-lower, .race-guide, .spec-guide) > :is(.article-column, .guide-aside) { padding-top: 0; }

.contents-collapsible {
  margin-bottom: var(--s-6);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg-2);
  font-size: .9rem;
}
.contents-collapsible summary {
  cursor: pointer;
  list-style: none;
  font-family: var(--font-ui);
  font-size: .8rem;
  font-weight: 400;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--accent-text);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  min-height: 2.25rem;
}
.contents-collapsible summary::-webkit-details-marker { display: none; }
.contents-collapsible summary::after { content: "▾"; color: var(--ink-3); }
.contents-collapsible[open] summary::after { content: "▴"; }
.contents-collapsible ol {
  list-style: none;
  margin: var(--s-3) 0 0;
  padding: 0;
  display: grid;
  gap: var(--s-1);
  counter-reset: toc;
}
.contents-collapsible li { counter-increment: toc; display: flex; gap: .45em; min-width: 0; }
.contents-collapsible li::before { content: counter(toc) "."; color: var(--ink-3); flex: none; }
.contents-collapsible a {
  color: var(--ink-2);
  text-decoration: none;
  display: block;
  padding: .3em 0;
  min-width: 0;
}
.contents-collapsible a:hover { color: var(--accent-text); text-decoration: underline; }
.contents-collapsible a[aria-current="true"] { color: var(--accent-text); font-weight: 600; }

.guide-toc { font-family: var(--font-ui); font-size: .86rem; line-height: 1.3; }
.guide-toc ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .1rem;
  border-left: 1px solid var(--line);
}
.guide-toc a {
  display: block;
  padding: .3em var(--s-3);
  margin-left: -1px;
  border-left: 2px solid transparent;
  color: var(--ink-2);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.guide-toc a:hover { color: var(--accent-text); }
.guide-toc a[aria-current="true"] {
  color: var(--accent-text);
  font-weight: 400;
  border-left-color: var(--accent);
}
.guide-toc-top {
  display: inline-block;
  margin-top: var(--s-4);
  padding: .4em 0;
  font-size: .8rem;
  color: var(--ink-3);
  text-decoration: none;
}
.guide-toc-top:hover { color: var(--accent-text); text-decoration: underline; }

/* The rail also needs 63rem of page (main), so under 200% text 1440 keeps the article at
   full width with the contents as a disclosure, as 1024 does. */
@media (min-width: 64rem) { @container page (width >= 63rem) {
  .guide-grid:not(.guide-grid--no-toc) {
    display: grid;
    grid-template-columns: 14rem minmax(0, 1fr);
    gap: var(--s-8);
    align-items: start;
  }
  .guide-grid:not(.guide-grid--no-toc) > .article-column { margin-inline: 0; }
  /* A long table of contents scrolls inside the sticky rail without a scrollbar: the wheel
     and keyboard focus still move it, guide-toc.js keeps the current section in view, and a
     fade at the cut edge (data-toc-overflow) says there is more. */
  .guide-aside {
    display: block;
    position: sticky;
    top: var(--sticky-top);
    max-height: calc(100vh - var(--sticky-top) - var(--s-3));
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: none;
    padding-block: var(--s-2) var(--s-5);
  }
  .guide-aside::-webkit-scrollbar { display: none; }
  .guide-aside[data-toc-overflow="bottom"] { mask-image: linear-gradient(to bottom, #000 calc(100% - 3rem), transparent); }
  .guide-aside[data-toc-overflow="top"] { mask-image: linear-gradient(to top, #000 calc(100% - 3rem), transparent); }
  .guide-aside[data-toc-overflow="both"] { mask-image: linear-gradient(to bottom, transparent, #000 3rem, #000 calc(100% - 3rem), transparent); }
  .contents-collapsible { display: none; }
} }

/* ---------- Prose ---------- */

.prose { max-width: var(--measure); }
.prose > * + * { margin-top: 1.1em; }
.prose h2 {
  margin-top: 1.8em;
  position: relative;
}
.prose h3 { margin-top: 1.8em; }
.prose h4 { margin-top: 1.5em; }
/* A heading that opens a prose block takes its gap from the block's container, not twice. */
.prose > :first-child { margin-top: 0; }
.prose ul, .prose ol { padding-left: 1.4em; }
.prose li + li { margin-top: .4em; }
.prose li::marker { color: var(--ink-3); }
.prose strong { font-weight: 600; color: var(--ink); }
.prose code {
  font-family: ui-monospace, Consolas, monospace;
  font-size: .9em;
  background: var(--bg-3);
  border: 1px solid var(--line);
  padding: .1em .35em;
  border-radius: 4px;
  overflow-wrap: anywhere;
}
.prose pre {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-4);
  overflow-x: auto;
}
.prose pre code { background: none; border: 0; padding: 0; overflow-wrap: normal; }
.prose blockquote {
  margin: 0;
  padding: var(--s-2) var(--s-5);
  border-left: 2px solid var(--line-strong);
  color: var(--ink-2);
}
/* BlueQuote (markdown.mjs): Blizzard's own words in the blue-post color. A `> [!blizzard]`
   quote takes a blue rule and a "Blizzard" or "Blue post" label in real text, so the color is
   never the only cue; quoted words before a `^[Blizzard: ...]` citation are a <q> that keeps
   the writer's own quotation marks, and its citation icon turns blue. The citation's tooltip
   keeps the tooltip's own colors. */
.wfg-blue, .prose blockquote.wfg-blue { color: var(--blue-post); }
q.wfg-blue { quotes: none; }
q.wfg-blue .wfg-cite-tip { color: var(--ink); }
.prose blockquote.wfg-blue { border-left-color: var(--blue-post); }
.prose blockquote.wfg-blue > p + p { margin-top: .5em; }
.wfg-blue-by {
  font: 400 .78rem/1.3 var(--font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
}
@media (forced-colors: active) {
  .wfg-blue, .prose blockquote.wfg-blue { color: CanvasText; }
}
/* PlayerQuote (player-quotes.mjs): a streamer's words, set as a plain pull quote. It is never
   blue (blue is Blizzard's own words): the rule is the neutral gray the base blockquote uses,
   and the "Reported" mark in the caption says it is a player's view. Inter body for the quote,
   the UI face small for the caption. The caption wraps on its separators at 390px and a long
   channel name breaks instead of overflowing. */
.wfg-player-quote { margin: 0; max-width: var(--measure); }
.wfg-player-quote blockquote {
  margin: 0;
  padding: var(--s-2) var(--s-5);
  border-left: 2px solid var(--line-strong);
  font-family: var(--font-body);
  color: var(--ink-2);
}
.wfg-player-quote blockquote p { margin: 0; }
.wfg-player-quote figcaption {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-1) var(--s-3);
  padding: var(--s-1) var(--s-5) 0;
  font: 400 .8rem/1.4 var(--font-ui);
  color: var(--ink-3);
  overflow-wrap: anywhere;
}
.wfg-pq-part { min-width: 0; }
@media (forced-colors: active) {
  .wfg-player-quote blockquote { border-left-color: CanvasText; color: CanvasText; }
  .wfg-player-quote figcaption { color: CanvasText; }
}
.prose hr { border: 0; border-top: 1px solid var(--line); margin-block: var(--s-6); }

/* Caveat box: a pixel frame with a "Heads up" label, so the warning never rests on a stripe. */
.callout {
  border: 1px solid var(--edge);
  box-shadow: var(--panel-frame);
  background: var(--bg-3);
  border-radius: 0;
  padding: var(--s-3) var(--s-4) var(--s-4);
  color: var(--ink);
}
.callout::before {
  content: "Heads up";
  display: block;
  margin-bottom: var(--s-1);
  font: 400 .78rem/1.3 var(--font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--classic-yellow);
}

/* A floated partner creative must never squeeze a table: tables start below it. */
.table-scroll { clear: both; overflow-x: auto; max-width: 100%; }
.prose > table { clear: both; }
.prose table { width: 100%; }
.edition-comparison td { vertical-align: top; }
.edition-comparison td:first-child { min-width: 10.5rem; }
.edition-comparison td:nth-child(2) { color: var(--gold); font-size: 1rem; white-space: nowrap; }
.edition-comparison td:nth-child(3) { min-width: 13rem; }
.edition-comparison td:nth-child(3) strong:first-child { color: var(--ink-3); font-weight: 400; font-size: .76rem; letter-spacing: .06em; text-transform: uppercase; }
.table-list { display: grid; gap: .34rem; margin: 0; padding-left: 1.05rem; }
.edition-comparison .table-list { font-size: .86rem; line-height: 1.42; }
.edition-comparison .table-list li::marker { color: var(--gold); }
/* ---------- Tables, site-wide (card #1015) ----------
   One look for every table: shape from forever.burley.gg, color and type from our tokens.
   The header is a small uppercase label in the page accent (class color on class pages) with
   no fill; one hairline per row, no stripes; 6px/8px cells, top aligned; a muted sub-line under a
   value (small or .sub); the row lifts to a faint accent tint under a mouse pointer. Every selector
   sits in :where(), so a tool's own rule (layout, widths, sticky headers, selection states) wins
   without a specificity fight. Appearance only: no display, table-layout, widths, white-space,
   overflow or sticky here. Opt in with .wfg-table; .wfg-table--sticky floats the header.
   Tokens: --tbl-* in :root. A page or skin overrides --tbl-head-ink or --tbl-row-hover. */
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) {
  border-collapse: collapse;
  border: 0;
  border-radius: 0;
  font: 400 var(--tbl-fs) / var(--tbl-lh) var(--tbl-font);
  color: var(--tbl-ink);
  font-variant-numeric: tabular-nums;
}
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) :is(th, td) {
  padding: var(--tbl-pad-y) var(--tbl-pad-x);
  text-align: left;
  vertical-align: top;
  border: 0;
  border-bottom: 1px solid var(--tbl-row-rule);
  background: none;
  box-shadow: none;
  overflow-wrap: break-word;
}
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) > thead th {
  padding-block: var(--tbl-head-pad-y);
  font-size: var(--tbl-fs-head);
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--tbl-head-ink, var(--accent-text));
  border-bottom-color: var(--tbl-head-rule);
}
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) > tbody th,
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) .tbl-name {
  font-weight: 400;
  color: var(--tbl-ink-strong);
}
/* Names in the first column read in the strong ink; the underline is a hint and the accent shows on hover. */
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) > tbody th a:not(.wh-link),
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) .tbl-name a:not(.wh-link) {
  color: var(--tbl-ink-strong);
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 22%, transparent);
  text-underline-offset: .18em;
}
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) > tbody th a:not(.wh-link):is(:hover, :focus-visible),
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) .tbl-name a:not(.wh-link):is(:hover, :focus-visible) {
  color: var(--accent-text);
  text-decoration-color: currentColor;
}
/* Item and game-tooltip links keep their rarity color; the dotted underline drops to a hint (solid on hover, as everywhere). */
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) :is(td, th) > .wfg-fact-change { margin-left: 0; }
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) :is(td, th) > .wfg-fact-change::before { content: none; }
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) a.wh-link { text-decoration-color: color-mix(in srgb, currentColor 22%, transparent); }
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) .wfg-fact-change[data-change="new"] { color: color-mix(in srgb, var(--new) 55%, var(--ink-3)); }
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) .wfg-fact-change[data-change="changed"] { color: color-mix(in srgb, var(--reworked) 55%, var(--ink-3)); }
@media (hover: hover) and (pointer: fine) {
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) > tbody > tr:not(.tbl-group) { transition: background-color var(--t); }
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) > tbody > tr:not(.tbl-group):hover { background: var(--tbl-row-hover, color-mix(in srgb, var(--accent) 7%, transparent)); }
}
/* A muted second line under a value: Req 22, 56% (Classic), a zone. */
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) :is(td, th) > :is(small, .sub) {
  display: block;
  margin-top: 0;
  font-size: var(--tbl-fs-sub);
  line-height: 1.3;
  color: var(--tbl-ink-sub);
}
/* A sub-line under an item name clears the item's icon; icons sit a step larger in a table. */
@media not (forced-colors: active) {
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) :is(td, th):has(> .wh-item[style]) > :is(small, .sub) { margin-left: calc(var(--tbl-icon) + .35em); }
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) .wh-item[style] {
    padding-left: calc(var(--tbl-icon) + .35em);
    background-size: var(--tbl-icon) var(--tbl-icon);
  }
}
:where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) > caption {
  caption-side: bottom;
  padding: var(--tbl-pad-y) var(--tbl-pad-x) 0;
  text-align: left;
  font: 400 var(--tbl-fs-sub) / 1.45 var(--tbl-font);
  color: var(--tbl-ink-sub);
}
@media (max-width: 40rem) {
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) { font-size: var(--tbl-fs-phone); }
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) :is(th, td) { padding: 5px 6px; }
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) > thead th { letter-spacing: .05em; }
}
@container page (width < 39rem) {
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) { font-size: var(--tbl-fs-phone); }
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) :is(th, td) { padding: 5px 6px; }
  :where(.prose table, .wfg-answer-table table, table.ref-table, .versus-article table, .wfg-realm, .pet-table, .lc-zones, .rpp-table, .tpr-table, .qrp-guide-lower table, .wt-tool table, .st-table, .ab-table, .cp-ledger, .tr-paths, .wfg-table, .wfg-table table) > thead th { letter-spacing: .05em; }
}

/* Opt-in helpers: .wfg-table on a table (or its wrapper). */
.wfg-table .sub-warn { color: var(--evidence-reported); }
.wfg-table .tbl-num, .wfg-table [data-align="right"] { font-variant-numeric: tabular-nums; }
.wfg-table [data-align="right"] { text-align: right; }
.wfg-table .tbl-na { font-style: italic; color: var(--tbl-ink-sub); }
.wfg-table .tbl-dim > :is(td, th) { opacity: .7; }
/* A badge or icon column hugs its content so the text columns get the width. */
.wfg-table .tbl-tag { width: 1%; white-space: nowrap; }
.wfg-table :is(td, th):has(> .wfg-ev:only-child) { width: 1%; padding-left: 0; }
.wfg-table .wfg-fact-change { display: block; margin-top: 0; font-size: var(--tbl-fs-sub); line-height: 1.3; letter-spacing: 0; }
.wfg-table .wfg-fact-change::before { content: none; }
.wfg-table :is(small, .sub) > .wfg-fact-change { display: inline; margin: 0 0 0 .5em; }
/* Group rows (loot by boss): one line in the strong ink over a firmer rule, more air above than below. */
.wfg-table > tbody > tr.tbl-group > th {
  padding-top: calc(var(--tbl-pad-y) + 8px);
  padding-bottom: 3px;
  border-top: 1px solid var(--tbl-group-rule);
  color: var(--tbl-ink-strong);
}
.wfg-table > tbody > tr.tbl-group:first-child > th { border-top: 0; padding-top: var(--tbl-pad-y); }
.wfg-table > tbody > tr.tbl-group > th > a { color: var(--accent-text); }
.wfg-table > tbody > tr.tbl-group > th > :is(small, .sub) { display: inline; margin-left: .5em; }
/* A long reference table can float its header: its own ground and a rule while it floats. */
.wfg-table--sticky > thead th { position: sticky; top: var(--sticky-top); z-index: 1; background: var(--tbl-sticky-bg, var(--bg-2)); box-shadow: 0 1px 0 var(--tbl-head-rule); }

/* Phones: .wfg-table--stack with data-label on its cells. A row is a block: the name, the verdict
   badge at the top right with the tier icon beside it, then the other cells run inline as
   LABEL value, so a four-column row costs two or three lines. Empty cells vanish. A table
   without data-label stays in .table-scroll and scrolls sideways. */
@media (max-width: 40rem) {
  .wfg-table--stack > thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .wfg-table--stack, .wfg-table--stack > tbody, .wfg-table--stack > tbody > tr, .wfg-table--stack > tbody > tr > :is(th, td), .wfg-table--stack > caption { display: block; }
  .wfg-table--stack { display: flex; flex-direction: column; }
  .wfg-table--stack > caption { order: 2; padding-inline: var(--tbl-stack-pad-x); }
  .wfg-table--stack > tbody > tr { position: relative; padding: 7px var(--tbl-stack-pad-x); border-bottom: 1px solid var(--tbl-row-rule); }
  .wfg-table--stack > tbody > tr > :is(th, td) { padding: 1px 0; border: 0; }
  .wfg-table--stack > tbody > tr > td[data-label] { display: inline; margin-right: .85em; font-size: var(--tbl-fs-phone); line-height: 1.5; }
  .wfg-table--stack > tbody > tr > td[data-label]::before { content: attr(data-label); display: inline; margin-right: .3em; font-size: var(--tbl-fs-head); letter-spacing: .05em; text-transform: uppercase; color: var(--tbl-ink-sub); }
  .wfg-table--stack > tbody > tr > td[data-label]:empty { display: none; }
  .wfg-table--stack > tbody > tr > td[data-label] > :is(small, .sub) { display: inline; margin: 0 0 0 .4em; }
  .wfg-table--stack > tbody > tr > td[data-label] > :is(small, .sub)::before { content: "\00b7\00a0"; }
  /* Verdict badge top right, tier icon beside it. */
  .wfg-table--stack > tbody > tr > :is(th, td):has(> .tbl-badge) { position: absolute; top: 7px; right: var(--tbl-stack-pad-x); padding: 0; }
  .wfg-table--stack > tbody > tr > :is(th, td):has(> .tbl-badge)::before { content: none; }
  .wfg-table--stack > tbody > tr > :is(th, td):has(> .wfg-ev:only-child) { position: absolute; top: 7px; right: var(--tbl-stack-pad-x); padding: 0; }
  .wfg-table--stack > tbody > tr:has(.tbl-badge) > :is(th, td):has(> .wfg-ev:only-child) { right: 7.25rem; }
  .wfg-table--stack > tbody > tr:not(.tbl-group) > :is(th, td):first-child:not(.tbl-tag) { padding-right: 2rem; }
  .wfg-table--stack > tbody > tr:not(.tbl-group):has(.tbl-badge) > :is(th, td):first-child:not(.tbl-tag) { padding-right: 8.5rem; }
  /* Group rows become sub-headings in the flow. */
  .wfg-table--stack > tbody > tr.tbl-group { padding: 10px var(--tbl-stack-pad-x) 4px; border-bottom: 0; border-top: 1px solid var(--tbl-group-rule); }
  .wfg-table--stack > tbody > tr.tbl-group:first-child { border-top: 0; padding-top: 2px; }
  .wfg-table--stack > tbody > tr.tbl-group > th { padding: 0; border: 0; }
}

/* Data badge: a small outlined, tinted label for a column that needs a word. Square-cornered like
   every control here (--r-pill is 2px). data-tier takes the evidence colors; data-fit is a verdict;
   data-origin="derived" draws a dashed outline for client or datamined data, or a number we
   computed, as opposed to something seen in play. A badge with a tooltip is a button.tbl-badge. */
.tbl-badge {
  --badge: var(--ink-3);
  display: inline-block;
  padding: var(--tbl-badge-pad);
  border: 1px solid color-mix(in srgb, var(--badge) 40%, transparent);
  border-radius: var(--tbl-badge-radius);
  font: 400 var(--tbl-fs-head) / 1.5 var(--tbl-font);
  letter-spacing: .03em;
  color: var(--badge);
  background: color-mix(in srgb, var(--badge) 8%, transparent);
  white-space: nowrap;
  vertical-align: top;
}
.tbl-badge[data-tier="confirmed"] { --badge: var(--evidence-confirmed); }
.tbl-badge[data-tier="planned"] { --badge: var(--evidence-planned); }
.tbl-badge[data-tier="reported"] { --badge: var(--evidence-reported); }
.tbl-badge[data-tier="unverified"] { --badge: var(--evidence-unverified); }
.tbl-badge[data-tier="not-announced"] { --badge: var(--evidence-not-announced); }
.tbl-badge[data-fit] { background: color-mix(in srgb, var(--badge) 12%, transparent); }
.tbl-badge[data-fit="best"] { --badge: var(--evidence-confirmed); }
.tbl-badge[data-fit="good"] { --badge: var(--evidence-confirmed); background: transparent; }
.tbl-badge[data-fit="ok"] { --badge: var(--frost); }
.tbl-badge[data-fit="stretch"] { --badge: var(--blood); }
.tbl-badge[data-fit="low"] { --badge: var(--unchanged-text); background: transparent; }
.tbl-badge[data-origin="derived"] { border-style: dashed; }
button.tbl-badge { cursor: help; font-family: var(--tbl-font); }
button.tbl-badge:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
@media (forced-colors: active) {
  .tbl-badge { color: CanvasText; border-color: CanvasText; background: none; }
}
/* The one column a reader scans, in any table: a pale game green mixed from --new. */
.tbl-key { color: var(--tbl-key-ink); }

/* ---------- Sources and related ---------- */

.source-block {
  margin-top: var(--s-8);
  background: var(--bg-2);
  border: 1px solid var(--edge);
  border-radius: 0;
  padding: var(--s-5) var(--s-6);
  box-shadow: var(--panel-frame);
}
.source-block h2 { font: 400 1rem/1.3 var(--font-ui); color: var(--accent-text); margin-bottom: var(--s-4); text-transform: uppercase; letter-spacing: .08em; }
.source-list {
  margin: 0;
  padding-left: 1.4em;
  font-family: var(--font-ui);
  font-size: .9rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-3);
}
.source-list li::marker { color: var(--accent); }
.source-publisher { color: var(--ink-3); }
.source-publisher::before { content: " · "; }
/* The cited address beside a citation whose affiliate link opens another page (tagVendorLinks). */
.source-locator { font: .85em/1.4 ui-monospace, Consolas, monospace; color: var(--ink-3); overflow-wrap: anywhere; }
.source-list time { color: var(--ink-3); }
.source-list time::before { content: " · "; }

.related-block { margin-top: var(--s-8); }
.related-block h2 { font: 400 1rem/1.3 var(--font-ui); margin-bottom: var(--s-4); text-transform: uppercase; letter-spacing: .08em; color: var(--ink-2); }
.related-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
  gap: var(--s-4);
}
.related-list a {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  height: 100%;
  min-width: 0;
  padding: var(--s-4);
  background: var(--bg-2);
  border: 1px solid var(--edge);
  box-shadow: var(--panel-frame);
  border-radius: 0;
  text-decoration: none;
  color: var(--ink);
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.3;
  transition: border-color var(--t);
}
.related-list a:hover { border-color: var(--accent); color: var(--ink); }
.related-list .chip { align-self: flex-start; }

/* ---------- Class races and racials ---------- */

.class-racials { margin-block: var(--s-6) var(--s-8); }
.racial-filter { display: inline-flex; align-items: center; gap: var(--s-2); font-family: var(--font-ui); font-size: .9rem; color: var(--ink-2); cursor: pointer; min-height: 2.75rem; }
.racial-filter input { width: 1.1rem; height: 1.1rem; margin: 0; accent-color: var(--accent); }

.racial-pills { list-style: none; margin: 0 0 var(--s-6); padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); gap: var(--s-2); }
.racial-pill { position: relative; display: grid; grid-template-columns: 2.25rem minmax(0, 1fr); align-items: center; gap: var(--s-3); padding: var(--s-2) var(--s-3) var(--s-2) var(--s-2); background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--r-md); text-decoration: none; color: var(--ink); transition: border-color var(--t); min-height: 3.25rem; }
.racial-pill::after { content: ""; position: absolute; right: .7rem; top: 50%; width: .45rem; height: .45rem; border-right: 2px solid var(--ink-3); border-bottom: 2px solid var(--ink-3); transform: translateY(-70%) rotate(45deg); transition: border-color var(--t), transform var(--t); }
.racial-pill:hover, .racial-pill:focus-visible { border-color: var(--accent); }
.racial-pill:hover::after { border-color: var(--accent); transform: translateY(-50%) rotate(45deg); }
.racial-pill:has(.racial-new)::after { display: none; }
.racial-pill img { width: 2.25rem; height: 2.25rem; border-radius: 50%; object-fit: cover; object-position: top; border: 2px solid var(--line-strong); }
.racial-pill.faction-alliance img { border-color: var(--alliance); }
.racial-pill.faction-horde img { border-color: var(--horde); }
.racial-pill span { display: grid; }
.racial-pill small { font-family: var(--font-ui); font-size: .65rem; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); }
.racial-pill b { font-family: var(--font-ui); font-weight: 400; font-size: .95rem; }
.racial-new { font-family: var(--font-ui); font-size: .62rem; font-weight: 400; letter-spacing: .08em; text-transform: uppercase; color: var(--new); background: transparent; border: 1px solid var(--new); border-radius: var(--r-pill); padding: .1em .5em; font-style: normal; }
.racial-new { position: absolute; top: .4rem; right: .4rem; }

.racial-columns { display: grid; gap: var(--s-5); }
.racial-faction h3 { font-family: var(--font-ui); font-size: .8rem; font-weight: 400; letter-spacing: .1em; text-transform: uppercase; margin: 0 0 var(--s-3); }
.faction-horde > h3 { color: var(--horde); }
.faction-alliance > h3 { color: var(--alliance); }
.faction-neutral > h3 { color: var(--gold); }
.racial-cards { display: grid; gap: var(--s-3); }

/* The faction color rides on the portrait ring only; the card is a plain pixel frame. */
.racial-card { background: var(--bg-2); border: 1px solid var(--edge); box-shadow: var(--panel-frame); border-radius: 0; padding: var(--s-3) var(--s-4) var(--s-4); }
.racial-card[data-race-eligible="0"] { opacity: .72; }
.racial-card { scroll-margin-top: var(--s-5); transition: box-shadow var(--t); }
.racial-card:target { box-shadow: var(--panel-frame), 0 0 0 1px var(--accent); }
.racial-card[hidden] { display: none; }
.class-racials:has(.racial-filter input:checked) .racial-card[hidden] { display: block; }

.racial-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.racial-row { display: grid; grid-template-columns: 2rem minmax(0, 1fr); gap: var(--s-3); align-items: start; }
.racial-row img { width: 2rem; height: 2rem; border-radius: var(--r-md); border: 1px solid var(--line-strong); }
.racial-row b { display: block; font-family: var(--font-ui); font-weight: 400; font-size: .9rem; color: var(--gold); line-height: 1.25; }
.racial-row p { margin: .15rem 0 0; font-size: .875rem; line-height: 1.45; color: var(--ink-2); }
.racial-type { margin-left: .3rem; font-size: .68rem; font-weight: 400; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }
.racial-row .racial-change { margin-top: .3rem; padding-left: var(--s-2); border-left: 2px solid var(--line-strong); font-size: .8rem; }
.racial-change span { font-family: var(--font-ui); font-weight: 600; color: var(--ink); }

@media (min-width: 60rem) {
  .racial-columns { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
  .faction-neutral { grid-column: 1 / -1; }
  .faction-neutral .racial-cards { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* ---------- Empty and 404 ---------- */

.empty-state {
  text-align: center;
  padding-block: var(--s-9);
}
.empty-state h1 { font-size: clamp(1.8rem, 4vw, 2.6rem); margin-bottom: var(--s-4); }
.empty-state p { color: var(--ink-2); }
.empty-state .eyebrow { color: var(--ember); }

/* ---------- Footer: site directory and legal band ---------- */

/* The affiliate disclosure first, then the site directory (eight topic groups, then a bar
   with the site's own pages and the Follow row; scripts/lib/templates/shell-nav.mjs), then the
   legal band: the Blizzard line and the analytics notice. Every list is open in the HTML. On
   phones public/site-nav.js turns each group heading into a toggle button and closes its list
   (hidden="until-found"), so the directory is a stack of 44px rows; from 48rem the groups are
   columns and there is nothing to toggle. */
.site-footer {
  margin-top: var(--s-7);
  background: var(--bg-2);
  border-top: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-inner);
  color: var(--ink-2);
  font-family: var(--font-ui);
}
.site-owner { border-bottom: 1px solid var(--hairline); }
.site-owner-inner { padding-block: 10px; font: 400 .84rem/1.5 var(--font-body); color: var(--ink-3); }
.site-owner p { margin: 0; }
.site-owner b { color: var(--ink-2); font-weight: 500; }
.site-dir { display: grid; grid-template-columns: minmax(0, 1fr); padding-block: 2px 14px; }
.site-dir:focus { outline: none; }
.site-dir-title {
  margin: 0;
  font: 400 .78rem/1.3 var(--font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--accent-text);
}
.site-dir-label[hidden], .site-dir-toggle[hidden], .site-nav-group-toggle[hidden] { display: none; }
.site-dir-toggle, .site-nav-group-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  width: 100%;
  min-height: 2.75rem;
  margin: 0;
  padding: 0 4px;
  background: none;
  border: 0;
  border-radius: 0;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
  cursor: pointer;
}
.site-dir-toggle:focus-visible, .site-nav-group-toggle:focus-visible { outline-offset: -2px; }
/* The header menus' chevron, drawn once here rather than as an SVG in every page. */
.site-dir-toggle::after, .site-nav-group-toggle::after {
  content: "";
  flex: none;
  width: .6rem;
  height: .6rem;
  background: var(--ink-3);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M1 3h8L5 8z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M1 3h8L5 8z'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: transform var(--t);
}
.site-dir-toggle[aria-expanded="true"]::after, .site-nav-group-toggle[aria-expanded="true"]::after { transform: rotate(180deg); }
@media (forced-colors: active) { .site-dir-toggle::after, .site-nav-group-toggle::after { background: ButtonText; } }
.site-dir ul { margin: 0; padding: 0; list-style: none; }
.site-dir li { display: flex; align-items: center; gap: .35rem; break-inside: avoid; }
.site-dir a { display: inline-flex; align-items: center; min-height: 1.75rem; padding-block: 3px; color: var(--ink-2); font-size: .9rem; line-height: 1.25; text-decoration: none; text-wrap: balance; }
.site-dir a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: .2em; }
.site-dir a[aria-current="page"] { color: var(--accent-text); }
.site-dir-tag { padding: 0 .25rem; border: 1px solid var(--line); border-radius: var(--r-ctl); font: 400 .66rem/1.45 var(--font-ui); color: var(--ink-3); }
/* The bar under the groups: the site's own pages, then the Follow row (the site's own
   profiles as 36px glyph targets; the names are the visually hidden link text). */
.site-dir-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 24px; padding-top: 10px; }
.site-dir-site { display: flex; flex-wrap: wrap; gap: 0 18px; }
.site-social { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; }
.site-social-label { margin: 0; font: 400 .78rem/1.3 var(--font-ui); letter-spacing: .1em; text-transform: uppercase; color: var(--accent-text); }
.site-social ul { display: flex; flex-wrap: wrap; gap: 2px; margin: 0; padding: 0; list-style: none; }
.site-social a { display: inline-flex; align-items: center; justify-content: center; width: 2.25rem; height: 2.25rem; border-radius: var(--r-ctl); color: var(--ink-2); transition: color var(--t), background-color var(--t); }
.site-social a:hover { color: var(--ink); background: var(--bg-3); }
/* Single-color brand glyphs as masks over currentColor, drawn on a 20px grid. */
.site-social-icon { width: 16px; height: 16px; flex: none; background: currentColor; -webkit-mask: var(--icon) center / contain no-repeat; mask: var(--icon) center / contain no-repeat; }
.site-social-icon--youtube { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill-rule='evenodd' d='M18.6 5.8a2.3 2.3 0 0 0-1.6-1.6C15.6 3.8 10 3.8 10 3.8s-5.6 0-7 .4a2.3 2.3 0 0 0-1.6 1.6C1 7.2 1 10 1 10s0 2.8.4 4.2a2.3 2.3 0 0 0 1.6 1.6c1.4.4 7 .4 7 .4s5.6 0 7-.4a2.3 2.3 0 0 0 1.6-1.6c.4-1.4.4-4.2.4-4.2s0-2.8-.4-4.2ZM8.2 12.7V7.3l4.7 2.7-4.7 2.7Z'/%3E%3C/svg%3E"); }
.site-social-icon--bluesky { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M4.3 2.9C6.4 4.5 8.7 7.7 10 9.6c1.3-1.9 3.6-5.1 5.7-6.7 1.5-1.1 4-2 4 .8 0 .6-.3 4.7-.5 5.4-.7 2.4-3.1 3-5.3 2.6 3.8.6 4.8 2.8 2.7 5-4 4.1-5.7-1-6.2-2.3l-.4-1-.4 1c-.5 1.3-2.2 6.4-6.2 2.3-2.1-2.2-1.1-4.4 2.7-5-2.2.4-4.6-.2-5.3-2.6C.6 8.4.3 4.3.3 3.7c0-2.8 2.5-1.9 4-.8Z'/%3E%3C/svg%3E"); }
.site-social-icon--pinterest { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cmask id='k'%3E%3Crect width='20' height='20' fill='%23fff'/%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' d='m8.4 16.6 1.9-8M8 11.6c.5.4 1.1.6 1.8.6 2 0 3.4-1.6 3.4-3.6S11.7 5.2 9.6 5.2c-2.4 0-3.9 1.7-3.9 3.6 0 .7.2 1.3.6 1.8'/%3E%3C/mask%3E%3Ccircle cx='10' cy='10' r='9' mask='url(%23k)'/%3E%3C/svg%3E"); }
.site-social-icon--tumblr { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M11.6 18.2c-2.4 0-4.2-1.2-4.2-4.2V9.2H5.2V6.5c2.4-.6 3.4-2.7 3.5-4.5h2.5v4h2.9v3.2h-2.9v4.1c0 1.2.6 1.6 1.6 1.6h1.4v3.3h-2.6Z'/%3E%3C/svg%3E"); }
.site-social-icon--medium { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='5.8' cy='10' r='5'/%3E%3Cellipse cx='14' cy='10' rx='2.6' ry='4.8'/%3E%3Cellipse cx='18.3' cy='10' rx='1.1' ry='4.3'/%3E%3C/svg%3E"); }
.site-social-icon--substack { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M3 2h14v2H3V2Zm0 3.5h14v2H3v-2ZM3 9h14v9l-7-3.9L3 18V9Z'/%3E%3C/svg%3E"); }
.site-social-icon--mastodon { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill-rule='evenodd' d='M10 1.5c-3 0-6.3.4-7.3 2.3C2.2 4.8 2.2 6.6 2.2 8.6c0 2.6.1 5 1.5 6.6 1.4 1.6 4 2.3 6.3 2.3 1 0 2-.1 2.9-.4v-1.6s-1.3.4-2.8.4c-1.5-.1-3-.2-3.3-1.8a3.8 3.8 0 0 1 0-.6c1.3.3 2.6.4 3.9.4 1.6 0 3.2-.1 4.6-.5 1.9-.6 2.6-2 2.8-3.4.2-1 .2-2.4.2-3.8 0-1.5-.2-2.9-.8-3.6C16.3 1.9 13 1.5 10 1.5ZM5.9 11.6V6.9c0-1.2.9-1.9 2-1.9s1.9.7 1.9 1.9v2.6h.4V6.9c0-1.2.8-1.9 1.9-1.9s2 .7 2 1.9v4.7h-1.7V7.2c0-.6-.3-.9-.8-.9s-.9.4-.9 1v2.5h-1.7V7.3c0-.6-.3-1-.9-1s-.8.3-.8.9v4.4H5.9Z'/%3E%3C/svg%3E"); }
.site-social-icon--x { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18.9 1.15h3.68l-8.04 9.19L24 22.85h-7.4l-5.8-7.59-6.64 7.59H.47l8.6-9.83L0 1.15h7.6l5.24 6.93 6.06-6.93Zm-1.29 19.5h2.04L6.49 3.24H4.3l13.31 17.41Z'/%3E%3C/svg%3E"); }
.site-social-icon--facebook { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cmask id='k'%3E%3Crect width='20' height='20' fill='%23fff'/%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M11 18V8.6c0-1.6.7-2.4 2.4-2.4h.9M8.4 10.6h5'/%3E%3C/mask%3E%3Ccircle cx='10' cy='10' r='9' mask='url(%23k)'/%3E%3C/svg%3E"); }
@media (forced-colors: active) { .site-social-icon { background: LinkText; } }
.site-legal { background: var(--bg); border-top: 1px solid var(--edge); box-shadow: inset 0 1px 0 var(--hairline); }
.site-legal-inner { display: grid; gap: 8px 32px; padding-block: 12px 24px; font: 400 .84rem/1.55 var(--font-body); color: var(--ink-3); }
.site-legal p { margin: 0; }
.site-legal b { color: var(--ink-2); font-weight: 500; }
.site-legal-note a { color: var(--ink-2); }
.site-legal-note a:hover { color: var(--ink); }
/* Phones: each group is a 44px row; an open list runs in two columns of 36px links. The
   lists keep display: block (columns, not grid) so [hidden] and hidden="until-found" work. */
@media (max-width: 47.99rem) {
  .site-dir-group { border-bottom: 1px solid var(--hairline); }
  .site-dir-title { padding: 12px 0 2px; }
  .site-dir[data-collapsible] .site-dir-title { padding: 0; }
  /* The button reaches 4px into the gutter, so its inset focus ring clears the text. */
  .site-dir-toggle { width: calc(100% + 8px); margin-inline: -4px; }
  .site-dir-group > ul { columns: 2; column-gap: 16px; }
  .site-dir-group > ul > li:last-child { padding-bottom: 6px; }
  .site-dir-group li, .site-dir-group a { min-height: 2.25rem; }
  .site-social-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
/* Before site-nav.js runs (it is deferred), a phone with scripting on already shows the closed
   rows, so a short page (/404, /contact) does not paint the open directory and then jump.
   Without scripting the media query fails and every list stays open. */
@media (scripting: enabled) and (max-width: 47.99rem) {
  .site-dir:not([data-collapsible]) .site-dir-title { display: flex; align-items: center; min-height: 2.75rem; padding: 0; }
  .site-dir:not([data-collapsible]) .site-dir-group:not([data-phone-open]) > ul { display: none; }
}
/* Columns: four from 48rem. Headings are plain h2s here. */
@media (min-width: 48rem) {
  .site-dir { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px 16px; padding-block: 18px 14px; }
  .site-dir-title { margin-bottom: 6px; padding-bottom: 4px; border-bottom: 1px solid var(--hairline); }
  .site-dir-label[hidden] { display: inline; }
  .site-dir-toggle, .site-dir-toggle:not([hidden]) { display: none; }
  .site-dir-group > ul[hidden] { display: block; content-visibility: visible; }
  .site-dir-bar { grid-column: 1 / -1; border-top: 1px solid var(--hairline); }
  .site-legal-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* From 72rem all eight in one row, weighted so the longest labels (Speed leveling, Start
   here) fit on one line and the short World and Systems lists take less. */
@media (min-width: 72rem) {
  .site-dir { grid-template-columns: repeat(2, minmax(0, 1fr)) minmax(0, 1.2fr) minmax(0, 1fr) repeat(2, minmax(0, .8fr)) minmax(0, 1fr) minmax(0, 1.2fr); }
}

/* ---------- Responsive ---------- */

@media (max-width: 760px) {
  .affiliate-creatives-grid { grid-template-columns: 1fr; }
  .affiliate-creatives--zygorguides .affiliate-creative-item { justify-content: center; }
  .partner-creative-float { float: none; width: min(100%, 320px); margin: 0 auto var(--s-6); }
  .sitemap-link { flex-direction: column; align-items: flex-start; gap: .15rem; }
}

@media (max-width: 300px) {
  .story h2,
  .story h3 {
    text-wrap: wrap;
  }
}

/* Motion stays on when the system asks for reduced motion (TJ, 2026-10-09); the footer's
   "Reduce motion" switch turns it off for this browser (the motion layer at the end of this file, html[data-motion="quiet"]). */

/* Figure zoom: the link opens the full-size image; image-zoom.js upgrades it to a dialog. */
.figure-zoom { display: block; cursor: zoom-in; }
.zoom-open, .zoom-open body { overflow: hidden; overscroll-behavior: contain; }
.zoom-dialog {
  position: fixed; inset: 0; border: 0; margin: 0; box-sizing: border-box;
  width: 100%; height: 100%; height: 100dvh; max-width: none; max-height: none;
  padding: calc(env(safe-area-inset-top) + 60px) max(var(--s-3), env(safe-area-inset-right)) max(var(--s-3), env(safe-area-inset-bottom)) max(var(--s-3), env(safe-area-inset-left));
  background: rgba(20, 18, 16, .94); color: var(--ink); overflow: hidden; overscroll-behavior: contain; touch-action: pan-y;
  font-family: var(--font-ui);
}
.zoom-dialog::backdrop { background: rgba(20, 18, 16, .94); }
.zoom-dialog[open] { display: grid; grid-template-rows: minmax(0, 1fr) auto; gap: var(--s-3); }
.zoom-stage { position: relative; display: grid; place-items: center; min-height: 0; overflow: hidden; touch-action: none; transition: opacity var(--t); }
.zoom-stage img { object-fit: contain; max-width: none; max-height: none; touch-action: none; user-select: none; -webkit-user-select: none; will-change: transform; transition: transform var(--t); }
.zoom-stage[aria-busy="true"] img, .zoom-stage img[hidden] { visibility: hidden; }
.zoom-stage.is-dragging, .zoom-stage.is-dragging img { transition: none; }
.zoom-status { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); margin: 0; padding: var(--s-3); background: var(--bg-2); color: var(--ink-2); pointer-events: none; }
.zoom-status:empty { display: none; }
.zoom-caption { text-align: center; min-width: 0; }
.zoom-caption p { margin: 0 auto; max-width: var(--measure); padding: var(--s-1); max-height: 25vh; max-height: 25dvh; overflow: auto; overflow-wrap: anywhere; font-size: .9rem; touch-action: pan-y; }
.zoom-tools { position: absolute; top: max(var(--s-3), env(safe-area-inset-top)); left: max(var(--s-3), env(safe-area-inset-left)); display: flex; gap: var(--s-2); }
.zoom-dialog button { width: 44px; height: 44px; padding: 0; border-radius: var(--r-ctl); border: 1px solid var(--line-strong); background: var(--bg-2); color: var(--ink); font: 400 1.25rem/1 var(--font-ui); cursor: pointer; }
.zoom-dialog button:hover { background: var(--bg-3); }
.zoom-dialog button:disabled { opacity: .5; cursor: default; }
.zoom-close { position: absolute; top: max(var(--s-3), env(safe-area-inset-top)); right: max(var(--s-3), env(safe-area-inset-right)); }
@media (prefers-reduced-motion: reduce) {
  .zoom-stage, .zoom-stage img { transition: none; }
}

/* ---------- Inline spell links and their tooltip ---------- */
/* A .spell-link is a plain anchor into the class page's talent calculator. The shared tooltip
   engine (public/components/tooltip.js) upgrades it; with no JS the link still works. Only the
   first mention on a page is an anchor; repeats are a <button type="button"> with this class
   (linkSpellsOnce) that pins the tooltip on tap, click, Enter or Space, and a repeat inside
   another control stays a <span>. The button keeps the running text's face, size and line,
   so it reads exactly like the anchor; the site's :focus-visible ring marks keyboard focus.
   :where() keeps the reset at class weight, so .spell-link's weight and table rules still win. */

:where(button).spell-link {
  appearance: none;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: inherit;
  cursor: help;
}
.spell-link {
  color: var(--gold);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-color: color-mix(in srgb, currentColor 50%, transparent);
  text-underline-offset: .18em;
}
.spell-link:hover, .spell-link.is-pinned { text-decoration-style: solid; text-decoration-color: currentColor; }
/* Without JavaScript a repeat has no tooltip to open, so it drops the link affordance and
   reads as emphasized text; the first mention's anchor still links. */
@media (scripting: none) {
  :is(button, span).spell-link, :is(button, span).spell-link:hover { cursor: auto; text-decoration: none; }
}
/* In tables and talent names the link sits in Expressway, which has one weight. */
.prose table .spell-link { font-weight: 400; }
/* ---------- Item, quest and NPC links (scripts/lib/core/entity-links.mjs) ---------- */
/* An anchor to the thing's Wowhead Forever page, upgraded by the shared tooltip engine with the
   game tooltip. An item's name takes its quality color (--q0 to --q7); a quest or NPC keeps the
   spell links' gold. Same dotted underline as a spell link, so every game-tooltip link reads
   alike. Two classes, so the color wins over .prose a and figure-caption link rules. */
.wh-link {
  color: var(--gold);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-color: color-mix(in srgb, currentColor 50%, transparent);
  text-underline-offset: .18em;
}
.wh-link:hover, .wh-link.is-pinned { text-decoration-style: solid; text-decoration-color: currentColor; }
.wh-link.q0 { color: var(--q0); }
.wh-link.q1 { color: var(--q1); }
.wh-link.q2 { color: var(--q2); }
.wh-link.q3 { color: var(--q3); }
.wh-link.q4 { color: var(--q4); }
.wh-link.q5 { color: var(--q5); }
.wh-link.q6 { color: var(--q6); }
.wh-link.q7 { color: var(--q7); }
.prose table .wh-link { font-weight: 400; }
/* An item's game icon before its name: the anchor's own background over a left padding, so
   the icon stays on the name's first line (the padding cannot wrap away from the text) and
   the underline starts at the name. */
.wh-item[style] {
  padding-left: 1.3em;
  background: var(--wh-i) no-repeat 0 50% / 1.05em 1.05em;
}
@media (forced-colors: active) {
  .wh-link { color: LinkText; }
  .wh-item[style] { padding-left: 0; }
}

.spell-link-icon {
  display: inline-block;
  width: 1.15em;
  height: 1.15em;
  margin-right: .25em;
  vertical-align: -.2em;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  overflow: hidden;
}
.spell-link-icon img { display: block; width: 100%; height: 100%; }

/* Class names carry their own text identity, independently of a page's accent. The inline
   span inherits typography and semantic text; Shaman uses the existing accessible blue. */
span.wow-class-name[data-wow-class] { display: inline; font: inherit; letter-spacing: inherit; text-transform: inherit; }
.wfg-sigil.is-dim .wow-class-name[data-wow-class] { color: inherit; }
.wow-class-name[data-wow-class="warrior"] { color: var(--class-warrior); }
.wow-class-name[data-wow-class="paladin"] { color: var(--class-paladin); }
.wow-class-name[data-wow-class="hunter"] { color: var(--class-hunter); }
.wow-class-name[data-wow-class="rogue"] { color: var(--class-rogue); }
.wow-class-name[data-wow-class="priest"] { color: var(--class-priest); }
.wow-class-name[data-wow-class="shaman"] { color: var(--class-shaman-text); }
.wow-class-name[data-wow-class="mage"] { color: var(--class-mage); }
.wow-class-name[data-wow-class="warlock"] { color: var(--class-warlock); }
.wow-class-name[data-wow-class="druid"] { color: var(--class-druid); }
.wow-class-name[data-wow-class="death-knight"] { color: var(--class-death-knight-text); }
.wow-class-name[data-wow-class="demon-hunter"] { color: var(--class-demon-hunter-text); }
.wow-class-name[data-wow-class="monk"] { color: var(--class-monk); }
.wow-class-name[data-wow-class="evoker"] { color: var(--class-evoker); }
/* Retain link recognition when class colors replace the ordinary link color. */
:where(main a:has(.wow-class-name)) { text-underline-offset: .18em; }
:where(main a:has(.wow-class-name):is(:hover, :focus-visible)) { text-decoration-thickness: 2px; }
@media (forced-colors: active) { .wow-class-name[data-wow-class] { color: inherit; } }

.article-figure--icon{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:var(--s-3)}
.article-figure--icon figcaption{margin-top:0}

/* ---------- The sitewide motion layer (card #909, TJ 2026-10-09; behavior in motion.js) ----------
   Motion is on for everyone by default, including readers whose system asks for reduced
   motion (TJ's decision). The footer "Reduce motion" button stores a choice in this browser;
   an inline head script sets html[data-motion="quiet"] from it before the first paint, and
   every rule below stands down under that attribute.

   SEO and layout contract (docs/brand/README.md, Motion):
   - Animate transform, opacity and filter only. Nothing here changes layout, so nothing
     here can shift the page (CLS).
   - Content is visible by default. motion.js hides a block for its reveal only after it has
     measured that the block starts below the first screen, so the first screen, the LCP
     element and any no-JS or failed-JS reader always see everything.
   - No text is added, removed or duplicated. */

/* ---------- Reading progress: a 2px line along the bottom of the sticky header ---------- */
.site-header::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--wfg-progress-color, var(--accent));
  transform-origin: 0 50%;
  transform: scaleX(var(--wfg-progress, 0));
  opacity: 0;
  pointer-events: none;
}
html[data-progress] .site-header::after { opacity: 1; }
@supports (animation-timeline: scroll()) {
  html[data-progress] .site-header::after { animation: wfg-progress linear both; animation-timeline: scroll(root); }
  @keyframes wfg-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
}

/* ---------- Header: settles into a raised bar once the page scrolls ---------- */
.site-header { transition: box-shadow 200ms ease, background-color 200ms ease; }
.site-header[data-scrolled] {
  background: color-mix(in srgb, var(--bg-2) 94%, transparent);
  box-shadow: inset 0 -1px 0 var(--edge), inset 0 -2px 0 var(--edge-inner), 0 6px 18px rgba(0, 0, 0, .45);
}
@supports (backdrop-filter: blur(1px)) {
  .site-header[data-scrolled] { backdrop-filter: blur(6px) saturate(1.1); }
}
.site-header .brand img { transition: transform 200ms ease; transform-origin: 0 50%; }
.site-header[data-scrolled] .brand img { transform: scale(.9); }

/* ---------- Scroll-triggered reveals and stagger ---------- */
/* motion.js marks blocks below the first screen with data-reveal and sets
   html[data-motion-ready]; a block shows the moment it scrolls in. */
html[data-motion-ready] [data-reveal] {
  transition: opacity 560ms cubic-bezier(.2, .7, .2, 1), transform 620ms cubic-bezier(.2, .7, .2, 1);
  transition-delay: calc(var(--wfg-i, 0) * 70ms);
}
html[data-motion-ready] [data-reveal]:not([data-revealed]) { opacity: 0; transform: translateY(28px) scale(.985); }

/* ---------- Buttons: pressed and disabled states, touch included ---------- */
.btn:active, .wfg-btn:active { transform: translateY(1px); filter: brightness(.9); }
.btn, .wfg-btn { transition: filter var(--t), color var(--t), box-shadow var(--t), transform 90ms ease; }
.btn:disabled, .btn[aria-disabled="true"], .wfg-btn:disabled, .wfg-btn[aria-disabled="true"] {
  filter: grayscale(.7) brightness(.65);
  cursor: not-allowed;
  transform: none;
}
@media (hover: none) {
  .btn:active svg, .wfg-btn:active svg { transform: translateX(3px); }
}

/* ---------- Disclosures: content eases in when a details element opens ---------- */
details.is-opening > :not(summary) { animation: wfg-open 220ms cubic-bezier(.2, .7, .2, 1); }
@keyframes wfg-open { from { opacity: 0; transform: translateY(-4px); } }

/* ---------- Talent filter: legend chips dim the other talents (motion.js) ---------- */
.class-legend .wfg-state.is-filter { cursor: pointer; border-radius: var(--r-ctl); padding: .1rem .35rem; margin: -.1rem 0; transition: background-color var(--t), box-shadow var(--t); }
.class-legend .wfg-state.is-filter:hover { background: var(--panel-2); }
.class-legend .wfg-state.is-filter[aria-pressed="true"] { background: var(--panel-2); box-shadow: inset 0 0 0 1px currentColor; }
[data-talent-frame] .wfg-talent { transition: opacity 200ms ease, filter 200ms ease; }
[data-talent-frame][data-filter="new"] .wfg-talent:not([data-state="new"]),
[data-talent-frame][data-filter="reworked"] .wfg-talent:not([data-state="reworked"]),
[data-talent-frame][data-filter="unchanged"] .wfg-talent:not([data-state="unchanged"]) { opacity: .22; filter: grayscale(1); }

/* ---------- Keyboard shortcuts sheet (motion.js; opens with ?) ---------- */
.wfg-keys { width: min(24rem, 100% - 1.5rem); padding: 0; border: 1px solid var(--edge); background: var(--bg-2); color: var(--ink-2); box-shadow: var(--panel-frame); }
.wfg-keys::backdrop { background: rgba(0, 0, 0, .55); }
.wfg-keys[open] { animation: wfg-open 180ms cubic-bezier(.2, .7, .2, 1); }
.wfg-keys-head { display: flex; align-items: center; gap: var(--s-2); padding: .4rem .5rem .4rem .75rem; border-bottom: 1px solid var(--edge); background: var(--panel-2); }
.wfg-keys-head h2 { margin: 0; font: var(--heading-weight) 1.1rem/1.2 var(--font-heading); color: var(--ink); }
.wfg-keys-close { margin-left: auto; width: 2.25rem; height: 2.25rem; border: 1px solid var(--line-control); border-radius: var(--r-ctl); background: var(--panel); color: var(--ink); font: 400 1.2rem/1 var(--font-ui); cursor: pointer; }
.wfg-keys dl { display: grid; grid-template-columns: auto 1fr; gap: .45rem .9rem; margin: 0; padding: .75rem; }
.wfg-keys dt, .wfg-keys dd { margin: 0; font: 400 var(--fs-s, .875rem)/1.4 var(--font-ui); }
.wfg-keys kbd { display: inline-block; min-width: 1.6rem; padding: .1rem .4rem; border: 1px solid var(--line-control); border-bottom-width: 2px; border-radius: var(--r-ctl); background: var(--panel); color: var(--ink); font: 400 .8rem/1.2 var(--font-ui); text-align: center; }

/* ---------- The footer's motion switch ---------- */
.motion-setting {
  margin-left: .35em;
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent-text, var(--accent));
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.motion-setting:hover { color: var(--ink); }

/* ---------- Quiet: the reader turned motion off ---------- */
html[data-motion="quiet"] { scroll-behavior: auto; }
html[data-motion="quiet"] *, html[data-motion="quiet"] *::before, html[data-motion="quiet"] *::after {
  transition: none !important;
  animation: none !important;
}
html[data-motion="quiet"] [data-reveal] { opacity: 1 !important; transform: none !important; }

@media print {
  [data-reveal] { opacity: 1 !important; transform: none !important; }
  .site-header::after { display: none; }
}

/* Neutral replacement for retired discount graphics; shared tokens and real link. */
.affiliate-creative-link--text { display: grid; align-content: center; justify-items: start; gap: var(--s-2); min-height: 5.5rem; padding: var(--s-4); border: 1px solid var(--line-strong); background: var(--bg-2); color: var(--ink); text-decoration: none; overflow-wrap: anywhere; }
.affiliate-creative-link--text strong { font-family: var(--font-heading); font-size: var(--fs-lg); font-weight: var(--heading-weight); }
.affiliate-creative-link--text span { color: var(--accent-text); text-decoration: underline; text-underline-offset: .15em; }
.affiliate-creative-link--text:hover { border-color: var(--accent-text); }
.affiliate-creative-link--text:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
