/* Self-hosted so the page's type owes nothing to a third party — Wowhead's tooltip widget stays the
   only outside request. Both are variable fonts: Inter carries the whole UI, Space Grotesk is display
   only. Latin + latin-ext, because character and realm names are full of accents. */
@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(fonts/inter-latin.woff2) format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(fonts/inter-latin-ext.woff2) format("woff2");
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(fonts/space-grotesk-latin.woff2) format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(fonts/space-grotesk-latin-ext.woff2) format("woff2");
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── The system ──────────────────────────────────────────────────────────────────────────────────
   This page is a ranked table of numbers, so it's built like a printed one: hairline rules do the
   separating, surfaces sit almost flat, and every corner is square. There is no radius token and no
   `border-radius` anywhere in this file — a rule that meets another rule at 90° is the whole look,
   and a 2px rounding just reads as a rounding someone gave up on halfway. Depth is spent, not
   sprinkled: exactly two elements glow (the verdict and the row it names), because they're the
   answer the page exists to give.

   One accent, a gold at roughly 39° — not the orange this started as. Orange that far around the
   wheel reads as a warning light against near-black, and it's the wrong association for a page whose
   accent means "this is where your token pays". Both themes hold that hue; only lightness moves.

   Gold is treated as the material rather than as a swatch: a thin line or a big struck statement,
   never the medium weight in between, which is where it stops looking like metal and starts looking
   like paint. See "struck, not painted" further down for the two lists that follow from that, and
   check any new gold against it — most things that want to be gold should get a gold rule instead.

   Raid and dungeon are told apart by a cool ink instead of a second brand colour, and the WoW
   item-quality colours stay canon since they mean something outside this app — note that legendary
   orange is deliberately *not* the accent, so a legendary item never reads as a recommendation.

   The three textural effects are each used once, where they earn their cost:
     · grain      — one fixed overlay across the whole viewport (see body::after)
     · frost      — the sticky masthead and the report menu, the two things that overlap content
     · glow       — the verdict, and the rank-1 row it points at
*/
:root {
  color-scheme: light dark;

  --paper: #f2efe8;
  --paper-deep: #e8e4da;
  --panel: #fbf9f5;
  --panel-2: #f4f1e9;
  --panel-3: #ece8dd;
  --ink: #191713;
  --muted: #65615a;
  --faint: #948e82;
  --rule: #ded8cb;
  --rule-strong: #c2bbaa;
  --accent: #956009;
  --accent-tint: #f6eddb;
  --accent-line: #c19a45;
  /* The two ends of the sheen. On paper both stay *darker* than the accent — a highlight lighter
     than #a86f0c drops the hero numeral under 3:1 against the panel, and a 52px figure nobody can
     read is not a stylish 52px figure. On ink the highlight goes properly bright, which is where
     the metal actually comes from. */
  --gold-hi: #a86f0c;
  --gold-lo: #5f3d02;
  --cool: #2b5f7e;
  --cool-tint: #e4edf3;
  --good: #2f6b3a;
  --danger: #a3271f;
  --q-rare: #1a5fb4;
  --q-epic: #7b2fb5;
  --q-legend: #b45309;

  /* Ambient light. --halo is the bloom thrown *outside* a box; --wash is the room light at the top
     of the page. Gold in both themes — the glow is the accent, not a neutral shadow. */
  --halo: rgba(200, 155, 50, 0.34);
  --halo-wide: rgba(200, 155, 50, 0.18);
  --wash: rgba(205, 165, 60, 0.11);
  --lift:
    0 14px 34px -16px rgba(30, 24, 12, 0.34),
    0 2px 6px -3px rgba(30, 24, 12, 0.18);

  /* Shine. A flat fill can only ever be yellow; what makes a surface read as *gold* is luminance
     moving across it, so the three elements that carry the accent as a surface get a rolled sheen
     instead of a colour. Defined once — the var()s inside resolve per theme at the point of use. */
  --sheen-y: linear-gradient(
    172deg,
    var(--gold-hi) 2%,
    var(--accent) 30%,
    var(--gold-lo) 62%,
    var(--accent) 86%,
    var(--gold-hi) 100%
  );
  --sheen-x: linear-gradient(
    90deg,
    var(--gold-lo),
    var(--accent) 18%,
    var(--gold-hi) 38%,
    var(--accent) 58%,
    var(--gold-lo) 82%,
    var(--accent)
  );

  /* Grain. Alpha-composited over paper it reads as fibre; multiply keeps it from hazing the whites. */
  --grain-blend: multiply;
  --grain-opacity: 0.05;

  /* One sans for everything readable; the display face is reserved for type big enough to show its
     character (≥19px) and never mixed into running text. Mono is kept for code and formulae only. */
  --font-ui:
    Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-display: "Space Grotesk", Inter, ui-sans-serif, system-ui, sans-serif;
  --font-mono:
    ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas,
    monospace;
}

/* The dark palette is written twice — once for the OS preference, once for an explicit choice —
   because a custom property can't be aliased across an @media boundary. `:not([data-theme="light"])`
   is what lets the toggle override a dark OS. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #0d0d0f;
    --paper-deep: #08080a;
    --panel: #141416;
    --panel-2: #1b1b1f;
    --panel-3: #232328;
    --ink: #e8e4dc;
    --muted: #9a958b;
    --faint: #6b675f;
    --rule: #27272c;
    --rule-strong: #3c3c43;
    --accent: #fbc140;
    --accent-tint: #2e2210;
    --accent-line: #c2932f;
    --gold-hi: #ffe9a8;
    --gold-lo: #a3701a;
    --cool: #7db8dd;
    --cool-tint: #12222c;
    --good: #4fbb63;
    --danger: #ef6a5e;
    --q-rare: #4a9eff;
    --q-epic: #b57bff;
    --q-legend: #ff9d3c;
    --halo: rgba(251, 193, 64, 0.26);
    --halo-wide: rgba(251, 180, 64, 0.13);
    --wash: rgba(255, 180, 60, 0.055);
    --lift:
      0 18px 44px -18px rgba(0, 0, 0, 0.72), 0 2px 8px -3px rgba(0, 0, 0, 0.6);
    /* Screen, not multiply: grain has to *lift* out of near-black to be seen at all. */
    --grain-blend: screen;
    --grain-opacity: 0.05;
  }
}
:root[data-theme="dark"] {
  --paper: #0d0d0f;
  --paper-deep: #08080a;
  --panel: #141416;
  --panel-2: #1b1b1f;
  --panel-3: #232328;
  --ink: #e8e4dc;
  --muted: #9a958b;
  --faint: #6b675f;
  --rule: #27272c;
  --rule-strong: #3c3c43;
  --accent: #fbc140;
  --accent-tint: #2e2210;
  --accent-line: #c2932f;
  --gold-hi: #ffe9a8;
  --gold-lo: #a3701a;
  --cool: #7db8dd;
  --cool-tint: #12222c;
  --good: #4fbb63;
  --danger: #ef6a5e;
  --q-rare: #4a9eff;
  --q-epic: #b57bff;
  --q-legend: #ff9d3c;
  --halo: rgba(251, 193, 64, 0.26);
  --halo-wide: rgba(251, 180, 64, 0.13);
  --wash: rgba(255, 180, 60, 0.055);
  --lift:
    0 18px 44px -18px rgba(0, 0, 0, 0.72), 0 2px 8px -3px rgba(0, 0, 0, 0.6);
  --grain-blend: screen;
  --grain-opacity: 0.05;
}

* {
  box-sizing: border-box;
}
/* Anything render() hides stays hidden — a display:flex/grid on the same element would
   otherwise beat the UA's [hidden]{display:none} and show empty chrome on first visit. */
[hidden] {
  display: none !important;
}
html,
body {
  margin: 0;
}
body {
  font-family: var(--font-ui);
  font-size: 15px;
  color: var(--ink);
  line-height: 1.5;
  min-height: 100vh;
  background:
    radial-gradient(1100px 520px at 72% -14%, var(--wash), transparent 62%),
    var(--paper);
  font-optical-sizing: auto;
  letter-spacing: -0.005em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* The full-bleed masthead is 100vw wide, which overshoots by the scrollbar. `clip` rather than
     `hidden` on purpose: `hidden` would make this a scroll container and break the sticky masthead
     it exists to contain. */
  overflow-x: clip;
}

/* Gaussian-ish grain, generated in the stylesheet so it costs no request. One fixed layer over the
   whole viewport — including the frosted masthead and the toast — because grain that scrolls with
   the content reads as a texture *on* the boxes rather than on the screen.
   Above everything and inert: nothing here should ever intercept a click. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: var(--grain-opacity);
  mix-blend-mode: var(--grain-blend);
  background-size: 140px 140px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)'/%3E%3C/svg%3E");
}

.wrap {
  max-width: 1060px;
  margin: 0 auto;
  padding: 0 20px 100px;
}
h1,
h2,
h3 {
  margin: 0;
  text-wrap: balance;
  font-weight: 600;
  letter-spacing: -0.015em;
}
button {
  font-family: inherit;
  cursor: pointer;
  letter-spacing: inherit;
}
input,
select,
textarea {
  font-family: inherit;
  letter-spacing: inherit;
}
a {
  color: var(--ink);
  text-decoration-color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
.tnum {
  font-variant-numeric: tabular-nums;
}
/* Every figure that can change under the reader — scores, ilvls, EV — holds its column. */
.rank,
.iscore,
.ilvl,
.num-in,
.item-group .n,
.ev,
.trade b,
.vopt .couple b {
  font-variant-numeric: tabular-nums;
}

/* The page's one repeated typographic device: a tracked, uppercase micro-label naming the block
   under it. Inter, not mono — mono is reserved for code and formulae. */
.brand .sub,
.list-head h2,
.seg-label,
.verdict .label,
.cfg-row label,
.item-group,
.import .lead {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

/* ── masthead ────────────────────────────────────────────────────────────────────────────────────
   Sticky and frosted: the ranking below it is long, and the season label is the context you need
   while reading it. The amber hairline pinned to the top of the viewport is the page's signature. */
/* Full-bleed while its contents stay on the 1060px column: the margin pulls the box out to the
   viewport edges, the padding puts the text back where .wrap would have started it. Both rules —
   the amber one at the very top of the screen and the hairline under it — have to reach the edges
   or they read as a floating bar someone forgot to stretch. */
.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  margin: 0 calc(50% - 50vw) 26px;
  padding: 19px max(20px, calc(50vw - 510px)) 13px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--rule);
  background: color-mix(in srgb, var(--paper) 78%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter: blur(14px) saturate(1.4);
}
/* The gilt edge of the page. A border can't hold a gradient, so the top rule is its own bar — three
   pixels pinned to the top of the viewport with the light rolling along them. */
.masthead::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 3px;
  background: var(--sheen-x);
}
.brand {
  display: flex;
  align-items: center;
  gap: 13px;
}
.brand .mark {
  width: 26px;
  height: 26px;
  flex: 0 0 auto;
  color: var(--accent);
  filter: drop-shadow(0 0 10px var(--halo-wide));
}
.brand h1 {
  font-family: var(--font-display);
  font-size: 29px;
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
}
.brand .sub {
  display: block;
  color: var(--faint);
  margin-top: 6px;
}
.masthead-tools {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
}

/* ── buttons ─────────────────────────────────────────────────────────────────────────────────────
   Flat, ruled, square. The primary is a solid block of ink rather than a coloured pill — it reads
   as the one committed action on the page, and it leaves amber free to mean "this is the answer". */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--rule-strong);
  background: transparent;
  color: var(--ink);
  padding: 7px 12px;
  font-size: 13px;
  font-weight: 600;
  transition:
    background 0.12s,
    border-color 0.12s,
    color 0.12s;
}
.btn:hover {
  background: var(--panel-3);
  border-color: var(--ink);
}
.btn:active {
  transform: translateY(0.5px);
}
.btn.primary {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.btn.primary:hover {
  background: var(--sheen-x);
  border-color: var(--accent);
  color: #1a1408;
}
/* A fixed square, sized by the box rather than by whatever is inside it. These held emoji before,
   and an emoji is the one glyph the page can't measure: its advance width, baseline and side
   bearings come from a fallback font that differs per browser, so the button changed shape and the
   mark sat off-centre. Every icon is an SVG on the same 16px grid now, centred by the flexbox. */
.btn.icon {
  padding: 0;
  width: 36px;
  height: 36px;
  justify-content: center;
  flex: 0 0 auto;
}
.btn.icon svg {
  width: 16px;
  height: 16px;
  display: block;
}
.btn.ghost {
  border-color: transparent;
  color: var(--muted);
}
.btn.ghost:hover {
  background: var(--panel-3);
  color: var(--ink);
  border-color: var(--rule);
}
.btn.tiny {
  padding: 4px 9px;
  font-size: 12px;
}
.btn.danger:hover {
  border-color: var(--danger);
  color: var(--danger);
}

/* Theme toggle: show the theme you'd switch *to* — sun while dark, moon while light.
   Until the first click there's no data-theme, so the OS preference decides. */
#themeBtn .i-sun {
  display: none;
}
:root[data-theme="dark"] #themeBtn .i-sun {
  display: inline;
}
:root[data-theme="dark"] #themeBtn .i-moon {
  display: none;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) #themeBtn .i-sun {
    display: inline;
  }
  :root:not([data-theme]) #themeBtn .i-moon {
    display: none;
  }
}

/* ── shared surfaces ─────────────────────────────────────────────────────────────────────────────
   One box treatment, used by every panel on the page: hairline rule, near-square corner, a surface
   barely lifted off the paper. Nothing here casts a shadow — separation is the rule's job. */
.import,
.simc,
.simc-note,
.vault,
.legend,
.card,
.picker-menu {
  border: 1px solid var(--rule);
  background: var(--panel);
}

/* ── import bar ── */
.import {
  display: flex;
  gap: 9px;
  align-items: center;
  flex-wrap: wrap;
  padding: 13px 15px 14px;
  margin-bottom: 14px;
}
.import .lead {
  flex-basis: 100%;
  color: var(--faint);
  margin-bottom: -2px;
}
.import input {
  flex: 1 1 320px;
  min-width: 200px;
  border: 1px solid var(--rule-strong);
  background: var(--paper-deep);
  color: var(--ink);
  padding: 9px 11px;
  font-size: 14px;
}
.import input::placeholder {
  color: var(--faint);
}
.import input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--panel);
}
.import .hint {
  flex-basis: 100%;
  font-size: 12px;
  color: var(--faint);
  line-height: 1.55;
}
.import .hint b {
  color: var(--muted);
  font-weight: 650;
}

/* ── /simc panel ── */
.simc {
  margin-bottom: 14px;
  overflow: hidden;
}
.simc summary {
  list-style: none;
  cursor: pointer;
  padding: 11px 15px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 9px;
}
.simc summary::-webkit-details-marker {
  display: none;
}
.simc summary:hover {
  color: var(--ink);
}
.simc summary .mk {
  color: var(--muted);
  transition: transform 0.18s;
  font-size: 10px;
}
.simc[open] summary .mk {
  transform: rotate(90deg);
}
.simc summary code,
.simc-actions code {
  font-family: var(--font-mono);
  font-size: 12px;
  background: var(--panel-3);
  padding: 1px 5px;
  color: var(--ink);
}
.simc[open] summary {
  border-bottom: 1px solid var(--rule);
}
.simc-body {
  padding: 13px 15px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.simc-body textarea {
  width: 100%;
  min-height: 92px;
  resize: vertical;
  border: 1px solid var(--rule-strong);
  background: var(--paper-deep);
  color: var(--ink);
  padding: 9px 11px;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.45;
}
.simc-body textarea:focus {
  outline: none;
  border-color: var(--accent);
}
.simc-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.simc-actions .hint {
  font-size: 12px;
  color: var(--faint);
  flex: 1 1 240px;
  line-height: 1.55;
}

/* ── notices ─────────────────────────────────────────────────────────────────────────────────────
   A marginal note in a printed manual: set in from a coloured rule, no icon, no tinted box. The
   rule carries the severity, so an alert changes one colour rather than the whole surface. */
.simc-note {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--muted);
  padding: 10px 14px;
  margin-bottom: 14px;
  border-left: 2px solid var(--rule-strong);
}
.simc-note b {
  color: var(--ink);
  font-weight: 650;
}
.simc-note.alert {
  border-left-color: var(--accent);
  background:
    linear-gradient(90deg, var(--accent-tint), transparent 70%), var(--panel);
}
.simc-note.alert b {
  color: var(--ink);
}
.simc-note.alert code {
  font-family: var(--font-mono);
  font-size: 11.5px;
  background: var(--panel-3);
  padding: 1px 5px;
}

/* ── vault ── */
.vault {
  padding: 15px 17px 16px;
  margin-bottom: 20px;
}
.vault h3 {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 3px;
}
.vault .vsub {
  font-size: 12.5px;
  color: var(--muted);
  margin-bottom: 13px;
  line-height: 1.55;
  max-width: 74ch;
}
/* Guaranteed item vs. the token that buys a roll — the choice the encounter ranking can't make. */
.trade {
  padding: 11px 0 11px 13px;
  margin-bottom: 13px;
  border-left: 2px solid var(--rule-strong);
}
.trade.roll {
  border-left-color: var(--accent);
}
.trade.keep {
  border-left-color: var(--cool);
}
.trade .tlead {
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 3px;
}
.trade.roll .tlead {
  color: var(--ink);
}
.trade.keep .tlead {
  color: var(--cool);
}
.trade .tbody {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.6;
  max-width: 78ch;
}
.trade .tbody b {
  color: var(--ink);
  font-weight: 650;
}
/* The cost of taking the item that outlives the week you take it in.
   Measure is held with padding-right rather than max-width, because the rule above it is a border
   and a border stops where the box does — a capped box draws a rule that ends mid-panel. */
.trade .tdrag {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.6;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--rule);
  padding-right: 18%;
}
.trade .tdrag b {
  color: var(--danger);
  font-weight: 650;
}
.vault-items {
  display: flex;
  flex-direction: column;
}
.vopt {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 12px;
  border-top: 1px solid var(--rule);
  padding: 10px 2px;
}
.vopt.taken {
  box-shadow: inset 2px 0 0 var(--accent);
  padding-left: 11px;
}
.vopt .vname {
  font-weight: 600;
  font-size: 14px;
}
.vopt.taken .vname {
  color: var(--ink);
}
.vopt .vmeta {
  font-size: 11.5px;
  color: var(--faint);
  margin-top: 2px;
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
}
.vopt .vmeta .warn {
  color: var(--danger);
}
.vopt .couple {
  font-size: 12px;
  color: var(--muted);
  margin-top: 5px;
}
.vopt .couple b {
  color: var(--ink);
  font-weight: 650;
}
/* Same reason as .tdrag above: padding, not max-width, so the rule spans the panel. */
.vault .note {
  font-size: 12.5px;
  color: var(--muted);
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
  line-height: 1.6;
  padding-right: 18%;
}
.vault .note b {
  color: var(--ink);
}

/* ── board controls ── */
.controls {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.controls .who {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
select.board {
  border: 1px solid var(--rule-strong);
  background: var(--panel);
  color: var(--ink);
  padding: 7px 9px;
  font-size: 13.5px;
  font-weight: 600;
  min-width: 200px;
}
select.board.slim {
  min-width: 0;
  padding: 6px 8px;
  font-size: 13px;
}
.spec-badge {
  font-size: 12px;
  color: var(--faint);
}

/* report picker — a menu, not a <select>: the rows carry a class colour and a source line */
.picker {
  position: relative;
}
/* A square chip, not a dot — the name is historical. Blizzard's class colours are picked for a black
   UI, so on a light page Priest white and Rogue yellow vanish; there they're darkened toward the
   text colour instead of shown raw. Square reads a touch smaller than a circle of the same box, so
   it's a pixel larger. */
.cdot {
  width: 9px;
  height: 9px;
  flex: 0 0 auto;
  background: color-mix(in srgb, var(--cls) 74%, #2f2a3a);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .cdot {
    background: var(--cls);
  }
}
:root[data-theme="dark"] .cdot {
  background: var(--cls);
}
.picker-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: min(420px, 72vw);
  border: 1px solid var(--rule-strong);
  background: var(--panel);
  color: var(--ink);
  padding: 7px 10px;
  font-size: 13.5px;
  font-weight: 600;
  text-align: left;
}
.picker-btn:hover:not(:disabled) {
  border-color: var(--ink);
}
.picker-btn:disabled {
  opacity: 1;
  cursor: default;
}
.picker-btn:disabled .mk {
  display: none;
}
.picker-btn .nm {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.picker-btn .sp {
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}
.picker-btn .rl,
.pgroup .rl,
.popt .rl {
  font-weight: 400;
  color: var(--faint);
}
.picker-btn .rl {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.picker-btn .rl::before,
.picker-btn .sp::before {
  content: "· ";
  color: var(--faint);
}
.picker-btn .mk {
  margin-left: auto;
  padding-left: 4px;
  color: var(--faint);
  font-size: 10px;
}
/* The second and last place frost is used: this menu is the only thing besides the masthead that
   floats over content it needs to stay legible against. */
.picker-menu {
  position: absolute;
  z-index: 30;
  top: calc(100% + 5px);
  left: 0;
  min-width: max(100%, 250px);
  width: max-content;
  max-width: min(420px, 88vw);
  max-height: min(60vh, 420px);
  overflow: auto;
  padding: 4px;
  border-color: var(--rule-strong);
  box-shadow: var(--lift);
  background: color-mix(in srgb, var(--panel) 86%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  backdrop-filter: blur(16px) saturate(1.4);
}
.pgrp + .pgrp {
  border-top: 1px solid var(--rule);
  margin-top: 4px;
  padding-top: 4px;
}
.pgroup {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--faint);
  font-weight: 700;
  padding: 7px 9px 3px;
}
.pgroup .rl {
  text-transform: none;
  letter-spacing: 0;
}
.popt {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  color: var(--ink);
  padding: 7px 9px;
}
.popt:hover,
.popt:focus-visible {
  background: var(--panel-3);
  outline: none;
}
.popt.on {
  box-shadow: inset 2px 0 0 var(--accent);
}
.popt .ptext {
  min-width: 0;
  flex: 1 1 auto;
}
.popt .pl {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.popt .pm {
  display: block;
  font-size: 11.5px;
  color: var(--faint);
  margin-top: 1px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.popt .pcheck {
  color: var(--muted);
  font-size: 12px;
  opacity: 0;
}
.popt.on .pcheck {
  opacity: 1;
}
.spacer {
  flex: 1 1 auto;
}
.seg {
  display: inline-flex;
  border: 1px solid var(--rule-strong);
  overflow: hidden;
}
.seg button {
  border: 0;
  background: transparent;
  color: var(--muted);
  padding: 6px 12px;
  font-size: 12.5px;
  font-weight: 600;
}
.seg button:hover {
  color: var(--ink);
  background: var(--panel-3);
}
.seg button + button {
  border-left: 1px solid var(--rule-strong);
}
.seg button.on {
  background: var(--ink);
  color: var(--paper);
}
/* A unit with nothing to switch to. Reads as the value beside its label rather than as a control:
   the segmented box is dropped, because a bordered word that can't be pressed is a disabled button.
   The label above it still says what it is. */
.seg:has(.fixed) {
  border: 0;
}
.seg .fixed {
  padding: 6px 2px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
}
.seg-label {
  color: var(--faint);
  margin-right: 1px;
}
.toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
}
.toggle input {
  accent-color: var(--ink);
  width: 15px;
  height: 15px;
}

/* ── verdict ─────────────────────────────────────────────────────────────────────────────────────
   The answer. Everything else on the page is a way of checking it, so it gets the one thing nothing
   else gets: a halo. It's thrown by box-shadow rather than a blurred pseudo-element, which keeps the
   light strictly *outside* the box — the ambilight behaviour, and no extra compositing layer. */
.verdict {
  position: relative;
  border: 1px solid var(--accent-line);
  background:
    linear-gradient(168deg, var(--accent-tint), transparent 58%), var(--panel);
  padding: 20px 22px;
  margin-bottom: 22px;
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
  box-shadow:
    0 6px 26px -10px var(--halo),
    0 26px 70px -34px var(--halo-wide);
}
.verdict .label {
  color: var(--muted);
}
.verdict .target {
  font-family: var(--font-display);
  font-size: 27px;
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.12;
  margin-top: 6px;
}
.verdict .why {
  color: var(--muted);
  font-size: 13px;
  margin-top: 6px;
}
.verdict .big {
  margin-left: auto;
  text-align: right;
}
.verdict .big .ev {
  font-family: var(--font-display);
  font-size: 52px;
  font-weight: 700;
  line-height: 0.9;
  letter-spacing: -0.045em;
}

/* ── struck, not painted ─────────────────────────────────────────────────────────────────────────
   Gold behaves like the metal: it works as a thin line or as a big statement piece, and it is at
   its worst in between, where a flat mid-size fill reads as yellow paint. So every gold surface on
   this page is one or the other. The hairlines — the page's top rule, the verdict and rank-1
   borders, the 2px bars marking an owned item or the selected report, focus rings — are flat gold,
   because a 1px line has no room for a highlight anyway. Everything from 22px up is *struck*: the
   sheen runs across it, which is the luminance variation that makes a surface read as metal at all.

   Nothing between those two lives in gold. Small caps labels, 9.5px badges, "Own" buttons and
   13px links are ink or muted, and where one of them needs marking it gets a gold rule beside it
   rather than gold lettering. That is also why this list is short — three figures, all of them the
   answer or the row carrying it. `color` is set first and stays the fallback: without
   background-clip the figure is flat gold rather than invisible. */
.verdict .big .ev,
.card.rank-1 .rank,
.card.rank-1 .ev-cell .ev {
  color: var(--accent);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .verdict .big .ev,
  .card.rank-1 .rank,
  .card.rank-1 .ev-cell .ev {
    background: var(--sheen-y);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}
.verdict .big .ev-unit {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--faint);
  margin-top: 9px;
}
/* Nothing to recommend: the halo goes out, because there is no answer to point at. */
.verdict.empty {
  background: var(--panel);
  border-color: var(--rule);
  box-shadow: none;
}
.verdict.empty .label {
  color: var(--muted);
}
.verdict.empty .target {
  font-size: 18px;
  color: var(--muted);
}

.list-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 9px;
  padding-bottom: 7px;
  border-bottom: 1px solid var(--rule-strong);
}
.list-head h2 {
  color: var(--muted);
}
.list-head .hint {
  font-size: 11.5px;
  color: var(--faint);
}

/* ── the ranking ─────────────────────────────────────────────────────────────────────────────────
   One ruled table, not a stack of floating cards: rows share their borders (the -1px margin) so the
   list reads as a single object with lines through it. Only rows that need to escape the sheet —
   the leader, and whichever is open — are raised into their own layer. */
.sources {
  display: flex;
  flex-direction: column;
}
.card {
  margin-top: -1px;
  transition: border-color 0.15s;
}
.card:first-child {
  margin-top: 0;
}
.card.open,
.card.rank-1 {
  position: relative;
  z-index: 1;
}
.card:hover {
  border-color: var(--rule-strong);
}
/* The row the verdict named, lit from the same lamp — the only other glow on the page. */
.card.rank-1 {
  border-color: var(--accent-line);
  box-shadow:
    0 4px 20px -10px var(--halo),
    0 18px 50px -30px var(--halo-wide);
}
.card.rank-1::before {
  content: "";
  position: absolute;
  left: -1px;
  top: -1px;
  bottom: -1px;
  width: 2px;
  background: var(--sheen-y);
}
.card.depleted {
  opacity: 0.55;
}
.card-head {
  display: grid;
  grid-template-columns: 46px 1fr auto 22px;
  align-items: center;
  gap: 14px;
  padding: 13px 16px;
  cursor: pointer;
}
/* Editorial numbering: big, quiet, in the gutter — a position, not a badge. */
.rank {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  color: var(--faint);
  text-align: center;
  letter-spacing: -0.03em;
}
.card.rank-1 .rank {
  font-weight: 700;
}
.name-cell {
  min-width: 0;
}
.name-cell .name {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.025em;
  display: flex;
  align-items: center;
  gap: 9px;
}
.name-cell .name .txt {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.name-cell .meta {
  font-size: 11.5px;
  color: var(--faint);
  margin-top: 3px;
}
/* These tags sit inside the display-face headings — the heading gets the display face, the tag
   doesn't. At 10px the character reads as noise, and uppercase tracking wants the plainer face. */
.type-tag,
.pays,
.special {
  font-family: var(--font-ui);
}
.type-tag {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 2px 6px;
  border: 1px solid;
  white-space: nowrap;
}
.type-tag.raid {
  color: var(--muted);
  border-color: var(--rule-strong);
}
.type-tag.dungeon {
  color: var(--cool);
  border-color: color-mix(in srgb, var(--cool) 45%, transparent);
}
/* Upgrade track a roll on this encounter pays out at, when that isn't simply the drop. */
.pays {
  font-size: 10px;
  font-weight: 650;
  letter-spacing: 0.04em;
  color: var(--cool);
  white-space: nowrap;
}
/* End-of-raid encounter whose rewards are a tier apart — the one people bank a token for. */
.special {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
  border: 1px solid var(--accent-line);
  padding: 1px 5px;
  white-space: nowrap;
}
.swap-note.special-note b {
  color: var(--ink);
}
.ev-cell {
  text-align: right;
}
.ev-cell .ev {
  font-family: var(--font-display);
  font-size: 25px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.04em;
}
.card.rank-1 .ev-cell .ev {
  font-weight: 700;
}
.ev-cell .math {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--faint);
  margin-top: 5px;
  white-space: nowrap;
}
.chev {
  color: var(--faint);
  transition: transform 0.18s;
  font-size: 11px;
  text-align: center;
}
.card.open .chev {
  transform: rotate(90deg);
}

.card-body {
  border-top: 1px solid var(--rule);
  padding: 2px 16px 16px;
  display: none;
  background: var(--panel-2);
}
.card.open .card-body {
  display: block;
}
.cfg-row {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding: 13px 0 4px;
  font-size: 12.5px;
  color: var(--muted);
}
.cfg-row .field {
  display: flex;
  align-items: center;
  gap: 8px;
}
.cfg-row label {
  color: var(--faint);
}
.num-in {
  width: 56px;
  border: 1px solid var(--rule-strong);
  background: var(--panel);
  color: var(--ink);
  padding: 4px 6px;
  font-weight: 600;
  text-align: center;
}
.num-in:focus {
  outline: none;
  border-color: var(--accent);
}

/* ── item pools ──────────────────────────────────────────────────────────────────────────────────
   A spec table: no per-row box, no fill, just ruled lines and columns that hold. The pool is often
   twenty rows long, and twenty bordered chips is exactly the texture this redesign is getting rid
   of — the alignment does the work instead. */
.items {
  display: flex;
  flex-direction: column;
  margin-top: 6px;
  border-top: 1px solid var(--rule);
}
.item {
  display: grid;
  grid-template-columns: 74px 1fr auto 62px;
  align-items: center;
  gap: 12px;
  padding: 5px 4px;
  border-bottom: 1px solid var(--rule);
}
.item:hover {
  background: var(--panel-3);
}
.item.st-rolled {
  opacity: 0.45;
}
.item.st-rolled .iname {
  text-decoration: line-through;
  text-decoration-color: var(--faint);
}
.item.st-own {
  box-shadow: inset 2px 0 0 var(--accent-line);
}
.item.zero .iname {
  color: var(--muted);
}
.iname {
  font-size: 13.5px;
  font-weight: 500;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Item icons come from Blizzard's CDN; the link out is the only third-party request the app makes. */
img.icon,
.icon.blank {
  width: 21px;
  height: 21px;
  flex: 0 0 auto;
  border: 1px solid var(--rule-strong);
  display: block;
}
.icon.blank {
  background: var(--panel-3);
}
.icon-link {
  display: block;
  flex: 0 0 auto;
  line-height: 0;
}
.icon-link:hover .icon {
  border-color: var(--accent);
}
/* Underline on hover only — every item name being underlined at rest turned the pool into a wall of rules. */
.iname-link {
  color: inherit;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  text-decoration: none;
}
.iname-link.q4 {
  color: var(--q-epic);
}
.iname-link.q3 {
  color: var(--q-rare);
}
.iname-link.q5 {
  color: var(--q-legend);
}
.iname-link:hover {
  text-decoration: underline;
}

.iname .q4 {
  color: var(--q-epic);
}
.iname .q3 {
  color: var(--q-rare);
}
.iname .q5 {
  color: var(--q-legend);
}
/* The row's inline annotations, all cut from one pattern: tiny, tracked, ruled — never filled, so a
   name carrying three of them still reads as a name with notes rather than a row of buttons. */
.vr,
.have,
.only,
.item .why {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  border: 1px solid var(--rule-strong);
  padding: 1px 4px;
  color: var(--faint);
  white-space: nowrap;
  flex: 0 0 auto;
}
.vr {
  color: var(--muted);
  border-color: var(--accent-line);
}
.have.dupe {
  color: var(--muted);
  border-color: var(--accent-line);
}
.only {
  color: var(--cool);
  border-color: color-mix(in srgb, var(--cool) 40%, transparent);
  text-transform: none;
  letter-spacing: 0.03em;
}
.iscore {
  font-weight: 650;
  font-size: 13.5px;
  letter-spacing: -0.01em;
  text-align: right;
}
.item.zero .iscore {
  color: var(--faint);
  font-weight: 500;
}
.ilvl {
  font-size: 11px;
  color: var(--faint);
  font-weight: 500;
}
/* An item level the roll promotes above the drop's — not the number the report simmed. */
.ilvl .promoted {
  color: var(--cool);
  border-bottom: 1px dotted color-mix(in srgb, var(--cool) 50%, transparent);
}
/* Deliberately not amber. "Want" is the default state of nearly every row, so colouring it would
   put twenty accent-coloured boxes in a column and spend the one colour that means "this is the
   answer" on the least remarkable thing on screen. The two states you *changed* are the ones that
   carry colour; Want stays a quiet ruled control. */
.state-btn {
  border: 1px solid var(--rule-strong);
  background: transparent;
  padding: 3px 0;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  width: 100%;
  color: var(--muted);
  transition:
    color 0.12s,
    border-color 0.12s,
    background 0.12s;
}
.state-btn.want {
  color: var(--muted);
}
.state-btn.own {
  color: var(--muted);
  background: var(--panel-3);
}
.state-btn.rolled {
  color: var(--good);
  border-color: color-mix(in srgb, var(--good) 40%, transparent);
}
.state-btn:hover {
  color: var(--ink);
  border-color: var(--ink);
}
/* Divider between the tiers of a pool: upgrades, then filler, then what can't be awarded. */
.item-group {
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--faint);
  padding: 15px 2px 5px;
}
.item-group::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--rule);
}
.item-group .n {
  letter-spacing: 0;
}
.item-group.tap {
  cursor: pointer;
}
.item-group.tap:hover {
  color: var(--ink);
}

/* Items your loot spec can't be awarded: present so the pool reads honestly, dimmed so they never
   compete with what you can actually roll for. */
.item.blocked {
  opacity: 0.42;
}
.item.blocked .iname .q3,
.item.blocked .iname .q4,
.item.blocked .iname .q5 {
  color: var(--muted);
}
.state-btn.blocked {
  display: block;
  text-align: center;
  color: var(--faint);
  cursor: default;
}
.state-btn.blocked:hover {
  background: transparent;
}
.swap-note {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.6;
  padding: 9px 0 0 11px;
  margin-top: 9px;
  border-left: 2px solid var(--rule);
  max-width: 82ch;
}
.swap-note b {
  color: var(--ink);
  font-weight: 650;
}

/* ── legend & footer ── */
.legend {
  margin-top: 28px;
  overflow: hidden;
}
.legend summary {
  list-style: none;
  cursor: pointer;
  padding: 13px 17px;
  font-weight: 650;
  font-size: 14.5px;
  letter-spacing: -0.01em;
  display: flex;
  align-items: center;
  gap: 10px;
}
.legend summary::-webkit-details-marker {
  display: none;
}
.legend summary .mk {
  color: var(--muted);
  transition: transform 0.18s;
  font-size: 10px;
}
.legend[open] summary {
  border-bottom: 1px solid var(--rule);
}
.legend[open] summary .mk {
  transform: rotate(90deg);
}
.legend .body {
  padding: 16px 17px 18px;
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.65;
}
/* Set to a reading measure rather than the full 1060 — this is the one block of prose on the page. */
.legend .body p {
  margin: 0 0 11px;
  max-width: 76ch;
}
.legend .body strong {
  color: var(--ink);
}
.legend code {
  font-family: var(--font-mono);
  background: var(--panel-3);
  padding: 1px 6px;
  font-size: 12.5px;
  color: var(--ink);
}
.legend .states {
  display: grid;
  gap: 9px;
  margin: 13px 0;
  max-width: 76ch;
}
.legend .states div {
  display: flex;
  gap: 11px;
  align-items: baseline;
}
.legend .pill {
  flex: 0 0 auto;
  width: 56px;
  text-align: center;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  padding: 2px 0;
  border: 1px solid var(--rule-strong);
}
.legend .pill.w {
  color: var(--muted);
}
.legend .pill.o {
  color: var(--muted);
  background: var(--panel-3);
}
.legend .pill.r {
  color: var(--good);
  border-color: color-mix(in srgb, var(--good) 40%, transparent);
}

.empty-state {
  text-align: center;
  padding: 64px 20px;
  color: var(--muted);
  border: 1px dashed var(--rule-strong);
}
.empty-state .big {
  color: var(--faint);
  margin-bottom: 16px;
  line-height: 0;
}
.empty-state .big svg {
  width: 38px;
  height: 38px;
}
.empty-state .sub {
  font-size: 12.5px;
  color: var(--faint);
  margin-top: 8px;
}

.disclaimer {
  margin-top: 30px;
  padding-top: 16px;
  border-top: 1px solid var(--rule);
  font-size: 11px;
  line-height: 1.7;
  color: var(--faint);
  text-align: center;
  text-wrap: balance;
  max-width: 72ch;
  margin-inline: auto;
}

.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  opacity: 0;
  background: var(--ink);
  color: var(--paper);
  padding: 10px 18px;
  font-size: 13px;
  font-weight: 600;
  box-shadow: var(--lift);
  transition:
    opacity 0.2s,
    transform 0.2s;
  pointer-events: none;
  z-index: 50;
  max-width: 90vw;
  text-align: center;
}
.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

@media (max-width: 640px) {
  .masthead {
    position: static;
  }
  .brand h1 {
    font-size: 25px;
  }
  .card-head {
    grid-template-columns: 34px 1fr auto;
    grid-template-areas: "rank name ev" "rank meta chev";
    gap: 11px;
  }
  .chev {
    display: none;
  }
  .ev-cell .math {
    display: none;
  }
  .verdict {
    padding: 16px 17px;
    gap: 14px;
  }
  .verdict .big {
    margin-left: 0;
    text-align: left;
  }
  .verdict .big .ev {
    font-size: 42px;
  }
  .item {
    grid-template-columns: 62px 1fr auto 52px;
    gap: 9px;
  }
}
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }
}
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
