/* ============================================================ Baghound — web theme
 *
 * Same charcoal + loot-gold palette as the desktop app, same Silkscreen +
 * VT323 fonts, with a touch more breathing room and modern motion than the
 * Swing version. Three core surfaces: BG (deepest, page), BG1 (raised
 * cards), BG2 (interactive chips / inputs).
 *
 * Layout is mobile-first; the .container helpers cap to readable widths.
 * Nothing depends on a framework — vanilla CSS, no preprocessor.
 * ========================================================================= */
@import url('https://fonts.googleapis.com/css2?family=VT323&family=Silkscreen:wght@400;700&display=swap');

:root {
  /* Tells browsers (Opera GX, Edge, some Chrome users) that the site is
     intentionally dark-themed, so their "force dark" or "auto dark mode"
     filters skip our content — otherwise those filters apply colour
     transforms to background-images and the sprite atlas comes out
     desaturated. Pure declarative — no JS or user-facing toggle. */
  color-scheme: dark;

  --bg:        #14121B;
  --bg1:       #1C1925;
  --bg2:       #262232;
  --bg3:       #322D40;
  --line:      #0A0810;
  --line-hi:   #3D3650;
  --ink:       #EFE6D2;
  --ink-dim:   #8A8295;
  --ink-faint: #5A546A;
  --gold:      #FFCC33;
  --gold-2:    #FFE066;
  --gold-d:    #B8861F;
  --red:       #D8423C;
  --blue:      #4AA8E8;
  --green:     #5FD16A;
  --purple:    #B86FDC;

  --font-pixel: 'Silkscreen', 'Courier New', monospace;
  --font-body:  'VT323', 'Courier New', monospace;
  --shadow-lift: 0 6px 24px rgba(0,0,0,.45);
  --shadow-hover: 0 8px 28px rgba(255,204,51,.08);
  --radius: 0; /* the whole design is square-edged on purpose */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background:
    radial-gradient(1200px 600px at 50% -200px, rgba(255,204,51,.06), transparent 60%),
    var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1.45;
  min-height: 100vh;
}
body { display: flex; flex-direction: column; }

.hidden { display: none !important; }
.muted  { color: var(--ink-dim); }
.accent { color: var(--gold); }

a { color: var(--blue); text-decoration: none; transition: color .12s ease; }
a:hover { color: var(--gold-2); }

/* ============================================================ Top bar */

#topbar {
  position: sticky; top: 0; z-index: 50;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 24px;
  padding: 14px 28px;
  background: rgba(28, 25, 37, .85);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.brand {
  display: inline-flex;
  align-items: center;
  height: 36px;
  cursor: pointer;
}
.brand svg { display: block; }
.brand:hover { opacity: 0.85; }

.search {
  position: relative;
  max-width: 420px;
  width: 100%;
  justify-self: center;
}
#searchInput {
  width: 100%;
  background: var(--bg2);
  border: 1px solid var(--line-hi);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  padding: 8px 12px;
  outline: none;
  transition: border-color .12s ease;
}
#searchInput:focus { border-color: var(--gold); }
#searchInput::placeholder { color: var(--ink-faint); }

.search-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--bg1);
  border: 1px solid var(--line-hi);
  max-height: 360px;
  overflow-y: auto;
  box-shadow: var(--shadow-lift);
}
.search-result {
  display: block;
  padding: 8px 14px;
  color: var(--ink);
  cursor: pointer;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-body);
  font-size: 16px;
  transition: background .08s ease;
}
.search-result:hover, .search-result.active {
  background: var(--bg2);
  color: var(--gold);
}
.search-empty {
  padding: 10px 14px;
  color: var(--ink-faint);
  font-size: 14px;
}

.topbar-right { justify-self: end; display: flex; align-items: center; gap: 12px; }
.topbar-right .display-name {
  font-family: var(--font-body);
  font-size: 17px;
  color: var(--ink);
}

/* ============================================================ Buttons */

.btn {
  display: inline-block;
  font-family: var(--font-pixel);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 12px 18px;
  border: 1px solid var(--line-hi);
  border-bottom-width: 2px;
  border-right-width: 2px;
  background: var(--bg2);
  color: var(--ink);
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  transition: transform .08s ease, background .12s ease, color .12s ease;
}
.btn:hover:not(:disabled) {
  background: var(--bg3);
  color: var(--gold);
  transform: translateY(-1px);
}
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn.primary {
  background: var(--gold);
  color: #000;
  border-color: var(--gold-d);
}
.btn.primary:hover:not(:disabled) {
  background: var(--gold-2);
  color: #000;
}

.btn.default {}
.btn.ghost {
  background: transparent;
  border-color: transparent;
  color: var(--ink-dim);
}
.btn.ghost:hover:not(:disabled) {
  background: var(--bg2);
  color: var(--ink);
  border-color: var(--line-hi);
}

/* ============================================================ Main + views */

main {
  flex: 1;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 28px 64px;
}

.view-card {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 80px;
}

.card {
  width: 100%;
  max-width: 460px;
  padding: 28px 28px 24px;
  background: var(--bg1);
  border: 1px solid var(--line-hi);
  box-shadow: var(--shadow-lift);
}

h1, h2, h3 {
  font-family: var(--font-pixel);
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  margin: 0 0 16px;
  color: var(--ink);
}
h1 { font-size: 20px; }
h2 { font-size: 16px; color: var(--gold); margin-bottom: 24px; }
h3 { font-size: 12px; color: var(--ink); }

label {
  display: block;
  font-size: 13px;
  color: var(--ink-dim);
  margin: 12px 0 4px;
}

input[type="text"],
input[type="email"],
input[type="search"],
input[inputmode="numeric"] {
  width: 100%;
  font-family: var(--font-body);
  font-size: 18px;
  background: var(--bg2);
  color: var(--ink);
  border: 1px solid var(--line-hi);
  padding: 10px 12px;
  outline: none;
  transition: border-color .12s ease;
}
input:focus { border-color: var(--gold); }

input[inputmode="numeric"] {
  font-size: 28px;
  letter-spacing: 8px;
  font-family: 'Courier New', monospace;
  text-align: center;
}

.row {
  display: flex; flex-wrap: wrap; gap: 8px;
  align-items: center; margin-top: 18px;
}
form .btn.primary { margin-top: 18px; }

.error {
  color: var(--red);
  min-height: 22px;
  margin: 14px 0 0;
  font-size: 16px;
}

/* ============================================================ Home — hero */

.hero {
  padding: 80px 0 60px;
  text-align: center;
}
.hero-mark { display: inline-block; margin-bottom: 32px; }
.hero-tagline {
  font-family: var(--font-pixel);
  font-size: 22px;
  line-height: 1.5;
  margin: 0 auto 18px;
  max-width: 720px;
  letter-spacing: 0.5px;
  text-transform: none;
  color: var(--ink);
}
.hero-sub {
  max-width: 580px;
  margin: 0 auto 32px;
  color: var(--ink-dim);
  font-size: 18px;
}
.hero-cta {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}

/* ============================================================ Home — features */

.features { margin-top: 24px; }
.pro-features { margin-top: 64px; }
.features-sub {
  margin: -4px 0 16px;
  font-size: 15px;
  max-width: 640px;
}
.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
}
.feature {
  background: var(--bg1);
  border: 1px solid var(--line);
  padding: 24px;
  transition: transform .12s ease, box-shadow .15s ease, border-color .12s ease;
}
.feature:hover {
  transform: translateY(-3px);
  border-color: var(--line-hi);
  box-shadow: var(--shadow-hover);
}
.feature-icon {
  font-size: 28px;
  line-height: 1;
  margin-bottom: 12px;
}
.feature h3 { margin: 0 0 8px; font-size: 13px; }
.feature p  { margin: 0; color: var(--ink-dim); font-size: 17px; line-height: 1.5; }

/* ============================================================ Home — CTA band */

.cta-band {
  margin-top: 64px;
  padding: 48px 24px;
  background: linear-gradient(180deg, var(--bg1), var(--bg));
  border: 1px solid var(--line-hi);
  text-align: center;
}
.cta-band h2 { color: var(--ink); }
.cta-band p { margin: 16px 0 0; font-size: 16px; }

/* ============================================================ Profile */

.profile-header { padding: 56px 0 12px; }
.profile-header h1 {
  font-size: 28px;
  color: var(--gold);
  margin-bottom: 4px;
}
.profile-header .muted { font-size: 16px; }
/* Guild flair next to the page title — pixel-art display name is
   already huge gold; the flair sits as a smaller muted-blue tag
   to its right (e.g. 'SKYDRAE [Pluribus]'). Matches the per-card
   guild flair colour for consistency across the profile. */
.profile-header h1 .profile-guild {
  font-size: 18px;
  color: var(--blue, #6FC9DC);
  font-weight: 500;
  margin-left: 12px;
  vertical-align: middle;
}

.tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--line-hi);
  margin: 24px 0 24px;
}
.tab {
  font-family: var(--font-pixel);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 12px 18px;
  background: transparent;
  border: 0;
  color: var(--ink-dim);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color .12s ease, border-color .12s ease;
}
.tab:hover { color: var(--ink); }
.tab.active {
  color: var(--gold);
  border-bottom-color: var(--gold);
}

.profile-body { min-height: 280px; }

.placeholder {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 80px 24px;
  color: var(--ink-dim);
  text-align: center;
  background: var(--bg1);
  border: 1px dashed var(--line-hi);
}
.placeholder .ph-icon {
  font-size: 36px;
  margin-bottom: 14px;
}

/* ============================================================ Armory panel
 *
 * WoW-armory inspired layout for the profile page. Character chooser
 * dropdown sits above; below, the active character renders with its
 * 4 equipped slots flanking a central class label + stats grid.
 * ========================================================================= */

/* Owner-only membership card — sits between the profile header and the
   armoury. Lays out as: pill | text block | CTA button, gracefully
   wrapping on narrow viewports. Never rendered for visitors, so we
   don't worry about leaking license state via CSS inspection. */
.membership-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  background: var(--bg1);
  border: 1px solid var(--line-hi);
  padding: 14px 20px;
  margin-bottom: 16px;
  box-shadow: var(--shadow-lift);
}
.membership-pill {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border: 1px solid var(--line-hi);
  background: var(--bg2);
  color: var(--fg);
}
.membership-pill-pro {
  background: var(--gold, #f3c44a);
  color: #1a1300;
  border-color: var(--gold, #f3c44a);
}
.membership-pill-trial {
  background: var(--bg2);
  color: var(--gold, #f3c44a);
  border-color: var(--gold, #f3c44a);
}
.membership-pill-free {
  background: var(--bg2);
  color: var(--fg-dim, #9aa);
}
.membership-text { flex: 1 1 220px; min-width: 0; }
.membership-headline {
  /* VT323 has no bold variant; setting font-weight:600 made the
     browser synthesize a janky pseudo-bold that read worse than
     plain. Bump the size instead so the headline still stands
     above the sub line. */
  font-size: 19px;
  color: var(--ink);
  line-height: 1.25;
}
.membership-sub { font-size: 15px; margin-top: 4px; line-height: 1.35; }
.membership-cta { flex: 0 0 auto; }
.membership-accountid {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  margin-bottom: 6px;
}
.membership-accountid code {
  background: var(--bg2);
  padding: 1px 6px;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--fg);
}
.membership-copy {
  padding: 2px 8px;
  font-size: 11px;
  line-height: 1.4;
}
.membership-discord {
  /* Sized to match .membership-sub so the row reads as a peer of the
     headline/sub copy rather than as fine-print muted text. The
     linked-state variant (with @handle + Unlink) stays inline-flex;
     the unlinked-state variant (two-line CTA) lays out as a block
     stack via the inner <div>s. */
  font-size: 15px;
  margin-top: 8px;
  line-height: 1.35;
  color: var(--fg);
}
.membership-discord code {
  background: var(--bg2);
  padding: 1px 6px;
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--fg);
  margin-left: 4px;
  margin-right: 4px;
}
.membership-discord-link {
  color: var(--gold, #f3c44a);
  text-decoration: underline;
}
.membership-discord-link:hover { text-decoration: none; }
.membership-discord-hint {
  color: var(--fg-dim, #9aa);
  font-size: 13px;
  margin-top: 2px;
}

/* Toast host: fixed bottom-right stack of transient messages. Each
   toast is click-to-dismiss; auto-dismiss is handled by JS. The
   ok/error variants borrow the membership pill palette so the visual
   language stays consistent across the site. */
.toast-host {
  position: fixed;
  bottom: 16px;
  right: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 1000;
  max-width: 360px;
  pointer-events: none;
}
.toast {
  background: var(--bg1);
  border: 1px solid var(--line-hi);
  padding: 10px 14px;
  font-size: 14px;
  color: var(--fg);
  box-shadow: var(--shadow-lift);
  cursor: pointer;
  pointer-events: auto;
}
.toast-ok    { border-left: 3px solid var(--gold, #f3c44a); }
.toast-error { border-left: 3px solid #e76f6f; }

.armory {
  background: var(--bg1);
  border: 1px solid var(--line-hi);
  padding: 20px 24px;
  margin-bottom: 24px;
  box-shadow: var(--shadow-lift);
}

.armory-chooser {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 18px;
  flex-wrap: wrap;
}
.armory-select {
  flex: 1;
  min-width: 220px;
  background: var(--bg2);
  border: 1px solid var(--line-hi);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 17px;
  padding: 8px 12px;
  outline: none;
  cursor: pointer;
  transition: border-color .12s ease;
}
.armory-select:focus { border-color: var(--gold); }
.armory-set-main { flex: 0 0 auto; }

.armory-body {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  padding: 12px 0 20px;
  border-bottom: 1px solid var(--line);
}
.armory-side {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
}
.armory-centre {
  text-align: center;
  padding: 0 20px;
  min-width: 200px;
}
.armory-class {
  font-family: var(--font-pixel);
  font-size: 22px;
  letter-spacing: 1px;
  color: var(--gold);
  margin-bottom: 6px;
}
.armory-level {
  font-family: var(--font-body);
  font-size: 18px;
  color: var(--ink);
  margin-bottom: 12px;
}
.armory-pet {
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--ink-dim);
  margin-bottom: 4px;
}
.armory-seen { font-size: 13px; }

.armory-slot {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Left side: label-on-left, sprite-on-right (mirrored), so the sprites
   face the central class block. Right side keeps the natural order. */
.armory-side:first-child .armory-slot { flex-direction: row-reverse; }
/* Fixed label width keeps the sprite columns vertically aligned across
   slots even when the label text differs in length (WEAPON vs ABILITY,
   ARMOR vs RING) — without this the longer label pushes its sprite
   sideways and the two slots no longer line up. */
.armory-slot-label {
  font-family: var(--font-pixel);
  font-size: 10px;
  letter-spacing: 1px;
  color: var(--ink-faint);
  text-transform: uppercase;
  min-width: 56px;
}
.armory-side:first-child .armory-slot-label { text-align: right; }
.armory-side:last-child  .armory-slot-label { text-align: left;  }
/* Width/height/background-size are set inline by BhData.spriteStyle so
   the same sprite atlas can render at multiple scales (24px collection
   tile vs 48px armory slot). Only chrome — background colour, border,
   crisp upscaling — lives in the CSS here. */
.armory-sprite {
  width: 48px;
  height: 48px;
  background-color: var(--bg2);
  border: 1px solid var(--line-hi);
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  flex: 0 0 48px;
}
.armory-sprite.empty {
  background-image:
    linear-gradient(45deg, transparent 47%, var(--line-hi) 47%, var(--line-hi) 53%, transparent 53%),
    linear-gradient(-45deg, transparent 47%, var(--line-hi) 47%, var(--line-hi) 53%, transparent 53%);
}
/* Bucket-colour borders on equipment sprites mirror the collection
   tile palette so eg. UT_LEGENDARY items glow the same shade. */
.armory-sprite.b-UT_LEGENDARY     { border-color: var(--gold);    }
.armory-sprite.b-UT_HIGH_RARE     { border-color: #FF8C3A;        }
.armory-sprite.b-UT_MEDIUM        { border-color: var(--blue);    }
.armory-sprite.b-UT_LOW           { border-color: var(--green);   }
.armory-sprite.b-ST               { border-color: var(--purple);  }
.armory-sprite.b-T14_WEAPON_ARMOR { border-color: #E6D14A;        }
.armory-sprite.b-T7_RING_ABILITY  { border-color: #6FC9DC;        }

.armory-stats {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 8px;
  padding-top: 16px;
}
.stat-cell {
  background: var(--bg2);
  border: 1px solid var(--line);
  padding: 8px 6px;
  text-align: center;
}
.stat-label {
  font-family: var(--font-pixel);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--ink-faint);
  margin-bottom: 2px;
}
.stat-val {
  font-family: var(--font-body);
  font-size: 20px;
  color: var(--ink);
}

.armory-empty {
  text-align: center;
  padding: 32px 24px 24px;
}
.armory-empty-icon {
  font-size: 36px;
  line-height: 1;
  margin-bottom: 12px;
  color: var(--gold);
}
.armory-empty h3 {
  font-size: 14px;
  color: var(--ink);
  margin-bottom: 10px;
}
.armory-empty p {
  margin: 0 auto;
  max-width: 540px;
  color: var(--ink-dim);
  font-size: 16px;
  line-height: 1.5;
}
.armory-empty strong { color: var(--gold); font-weight: 400; }

@media (max-width: 720px) {
  .armory-body { grid-template-columns: 1fr; }
  .armory-side { flex-direction: row; }
  .armory-side .armory-slot { flex-direction: row !important; }
  .armory-stats { grid-template-columns: repeat(4, 1fr); }
}

/* ============================================================ Legal pages

   Privacy + Terms — long-form prose pages with their own topbar /
   footer (no app shell). Inherit colours and typography from the
   site theme but use a comfortable measure for reading and bumped
   line-height so the documents don't feel like a wall of text. */

.legal {
  max-width: 760px;
  margin: 32px auto 80px;
  padding: 0 24px;
  line-height: 1.65;
}
.legal-header {
  border-bottom: 1px solid var(--line);
  padding-bottom: 16px;
  margin-bottom: 28px;
}
.legal-header h1 {
  font-size: 28px;
  font-weight: 700;
  margin-bottom: 6px;
}
.legal h2 {
  font-size: 18px;
  font-weight: 700;
  margin-top: 32px;
  margin-bottom: 8px;
  color: var(--ink);
}
.legal h3 {
  font-size: 15px;
  font-weight: 600;
  margin-top: 20px;
  margin-bottom: 6px;
  color: var(--ink);
}
.legal p { margin-bottom: 12px; }
.legal ul { padding-left: 22px; margin-bottom: 14px; }
.legal ul li { margin-bottom: 6px; }
.legal a { color: var(--gold, #f3c44a); }
.legal a:hover { text-decoration: underline; }
.legal code {
  background: var(--bg2);
  padding: 1px 6px;
  font-size: 0.95em;
  border-radius: 2px;
}
.legal .not-collected {
  background: var(--bg1);
  border-left: 3px solid var(--gold, #f3c44a);
  padding: 12px 16px;
  margin: 12px 0;
}
.legal .not-collected ul { margin-bottom: 0; }

/* ============================================================ Paywall + blur

   Shared by the collection, DPS, and dungeon-stats tabs when the
   viewer is on the free tier. The teaser (blurred grid, locked card
   shell, single DPS run) sits above the paywall card so the visitor
   sees both what's hidden and how to unlock it. */

.paywall-wrap {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.paywall-card {
  background: var(--bg1);
  border: 1px solid var(--line-hi);
  padding: 24px;
  text-align: center;
  box-shadow: var(--shadow-lift);
}
.paywall-headline { font-size: 16px; font-weight: 600; margin-bottom: 6px; }
.paywall-sub { font-size: 14px; margin-bottom: 16px; }
.paywall-cta { min-width: 220px; }

.collection-tile-blurred {
  /* Mirror tile dimensions so the silhouette matches a real grid. */
  min-height: 84px;
  background: linear-gradient(135deg, var(--bg2) 0%, var(--bg1) 100%);
  border: 1px solid var(--line);
  filter: blur(2px);
  pointer-events: none;
}
.collection-grid-blurred { opacity: 0.5; }

.dstats-card-blurred {
  min-height: 140px;
  background: linear-gradient(135deg, var(--bg2) 0%, var(--bg1) 100%);
  filter: blur(2px);
  pointer-events: none;
}
.dstats-view-blurred { opacity: 0.5; }

/* ============================================================ DPS date groups

   Pro viewers see runs grouped by local-time date with a collapsible
   header per day. Defaults to expanded; clicking the header collapses
   the section so a six-month history stays scrollable. */

.dps-grouped { display: flex; flex-direction: column; gap: 14px; }
.dps-date-group {}
.dps-date-header {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 12px;
  background: var(--bg2);
  border: 1px solid var(--line);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.dps-date-header:hover { background: var(--bg1); }
.dps-date-arrow {
  font-size: 12px;
  transition: transform 120ms ease;
}
.dps-date-group.collapsed .dps-date-arrow { transform: rotate(-90deg); }
.dps-date-title { flex: 1; font-weight: 600; font-size: 14px; }
.dps-date-count { font-size: 13px; }
.dps-date-runs { margin-top: 8px; }
.dps-date-group.collapsed .dps-date-runs { display: none; }

/* ============================================================ Collection grid */

.collection-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 8px;
}
.collection-tile {
  position: relative;
  background: var(--bg1);
  border: 1px solid var(--line);
  border-left-width: 3px;
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: border-color .12s ease, transform .08s ease, box-shadow .15s ease;
}
.collection-tile:hover {
  border-color: var(--line-hi);
  transform: translateY(-1px);
  box-shadow: 0 4px 16px rgba(0,0,0,.3);
}

/* Sprite + name row at the top of every tile. */
.collection-tile .tile-top {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 36px;
}
.collection-tile .sprite-slot {
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  background-color: var(--bg2);
  border: 1px solid var(--line);
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  background-repeat: no-repeat;
}
.collection-tile .tile-name-box { flex: 1; min-width: 0; }
.collection-tile .tile-name {
  font-family: var(--font-body);
  font-size: 17px;
  color: var(--ink);
  line-height: 1.15;
  word-break: break-word;
}
.collection-tile .tile-tier {
  font-family: var(--font-pixel);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--ink-faint);
  margin-top: 2px;
}
.collection-tile .counts {
  font-family: var(--font-body);
  font-size: 18px;
  color: var(--ink);
}
.collection-tile .live { font-size: 14px; color: var(--ink-dim); }
.collection-tile .live.none { color: var(--ink-faint); }

/* Bucket colours — mirror the desktop app's loot palette. */
.collection-tile.b-UT_LEGENDARY     { border-left-color: var(--gold);    }
.collection-tile.b-UT_HIGH_RARE     { border-left-color: #FF8C3A;        }
.collection-tile.b-UT_MEDIUM        { border-left-color: var(--blue);    }
.collection-tile.b-UT_LOW           { border-left-color: var(--green);   }
.collection-tile.b-ST               { border-left-color: var(--purple);  }
.collection-tile.b-T14_WEAPON_ARMOR { border-left-color: #E6D14A;        }
.collection-tile.b-T7_RING_ABILITY  { border-left-color: #6FC9DC;        }
.collection-tile.b-CHARACTER_SKIN   { border-left-color: #FF7AB6;        }
.collection-tile.b-PET_SKIN         { border-left-color: #FF7AB6;        }
.collection-tile.shiny::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(255,204,51,.45);
}

/* ============================================================ DPS history list
 *
 * One row per completed dungeon run. Click the header to expand a
 * per-boss breakdown table. Colour cues: green = cleared, red-left-bar
 * for died, gold-left-bar for nexused.
 * ========================================================================= */

.dps-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dps-row {
  background: var(--bg1);
  border: 1px solid var(--line);
  border-left: 3px solid var(--green);
  transition: border-color .12s ease, transform .08s ease;
}
.dps-row.died    { border-left-color: var(--red);  }
.dps-row.nexused { border-left-color: var(--gold); }
.dps-row:hover { border-color: var(--line-hi); }

.dps-row-head {
  display: grid;
  grid-template-columns: 110px 1.4fr 2fr auto;
  align-items: center;
  gap: 16px;
  padding: 12px 14px;
  cursor: pointer;
}
.dps-class {
  font-family: var(--font-pixel);
  font-size: 10px;
  letter-spacing: 1px;
  color: var(--ink);
  text-transform: uppercase;
  background: var(--bg2);
  border: 1px solid var(--line-hi);
  padding: 6px 8px;
  text-align: center;
}
.dps-dungeon {
  font-family: var(--font-body);
  font-size: 18px;
  color: var(--ink);
}
.dps-meta {
  font-size: 14px;
  color: var(--ink-dim);
}
.dps-damage {
  text-align: right;
  display: flex; flex-direction: column; align-items: flex-end;
  line-height: 1.1;
}
.dps-damage .big {
  font-family: var(--font-body);
  font-size: 22px;
  color: var(--ink);
}
.dps-damage .small {
  font-family: var(--font-pixel);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--ink-faint);
  text-transform: uppercase;
  margin-top: 2px;
}

.dps-detail {
  border-top: 1px solid var(--line-hi);
  padding: 12px 14px;
  display: none;
}
.dps-row.open .dps-detail { display: block; }

/* Per-boss block inside an expanded run — boss heading then a table
   of every player who hit the boss, sorted by damage. The owner's row
   is highlighted so they can spot themselves in the party at a glance. */
.dps-boss-block {
  margin-top: 12px;
  border-top: 1px solid var(--line-hi);
  padding-top: 10px;
}
.dps-boss-block:first-child { margin-top: 0; border-top: 0; padding-top: 0; }
.dps-boss-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 6px;
}
.dps-boss-name {
  font-family: var(--font-pixel);
  font-size: 11px;
  letter-spacing: 1px;
  color: var(--gold);
  text-transform: uppercase;
}
.dps-boss-time { font-size: 14px; }

.dps-players {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-body);
  font-size: 15px;
}
.dps-players thead th {
  font-family: var(--font-pixel);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--ink-faint);
  text-transform: uppercase;
  text-align: left;
  padding: 4px 6px;
  border-bottom: 1px solid var(--line-hi);
}
.dps-players tbody td {
  padding: 4px 6px;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
}
.dps-players tbody tr:last-child td { border-bottom: 0; }
.dps-players tbody tr.is-user td {
  background: rgba(255, 204, 51, .08);
  color: var(--gold-2);
}
.dps-players .died    { color: var(--red);   }
.dps-players .nexused { color: var(--gold);  }

/* Gear strip in the per-boss player table — 4 worn-equipment sprites
   per row. Mirrors the sniffer's MiniBoard gear column so the data
   matches across surfaces. Empty slots collapse rather than reserving
   space so legacy runs without equipment data don't render a row of
   placeholder boxes. */
.dps-gear {
  display: inline-flex;
  gap: 3px;
  vertical-align: middle;
}
.dps-gear-slot {
  display: inline-block;
  width: 22px;
  height: 22px;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  background-repeat: no-repeat;
  background-size: 22px 22px;
}
.dps-gear-slot.empty { display: none; }
.dps-players .cleared { color: var(--green); }

/* Owner-only DPS settings (top of the DPS tab) — two toggle switches
   for dps_public and dps_full_party. Lives here instead of a separate
   settings page until that's built. */
.dps-owner-controls {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 16px;
  background: var(--bg1);
  border: 1px solid var(--line);
  margin-bottom: 16px;
}
.dps-owner-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}
.dps-owner-hint {
  margin: 0;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink-dim);
  line-height: 1.5;
}
.dps-owner-hint strong { color: var(--gold); font-weight: 400; }
.toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  user-select: none;
}
.toggle.disabled { opacity: .45; cursor: not-allowed; }
.toggle input { position: absolute; opacity: 0; pointer-events: none; }
.toggle-slider {
  width: 36px;
  height: 18px;
  background: var(--bg2);
  border: 1px solid var(--line-hi);
  position: relative;
  flex: 0 0 36px;
  transition: background .12s ease;
}
.toggle-slider::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  background: var(--ink-faint);
  transition: left .12s ease, background .12s ease;
}
.toggle input:checked + .toggle-slider {
  background: var(--gold-d);
  border-color: var(--gold);
}
.toggle input:checked + .toggle-slider::after {
  left: 20px;
  background: var(--gold-2);
}
.toggle-label {
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--ink);
}

@media (max-width: 720px) {
  .dps-row-head { grid-template-columns: 1fr; gap: 4px; }
  .dps-damage { align-items: flex-start; }
  .dps-players { font-size: 13px; }
}

/* ============================================================ Fame tab
 *
 * Scope chips up top toggle the data set (all-time across every
 * character, or scoped to one). Four stat tiles summarise the slice.
 * SVG chart fills the rest — gold curve with subtle area fill,
 * gridlines at five y-stops, four x-axis date ticks.
 * ========================================================================= */

.fame-view {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.fame-scope {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding-bottom: 4px;
}
.fame-chip {
  font-family: var(--font-pixel);
  font-size: 10px;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 8px 14px;
  background: var(--bg2);
  border: 1px solid var(--line-hi);
  color: var(--ink-dim);
  cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.fame-chip:hover { color: var(--ink); }
.fame-chip.on {
  background: var(--gold);
  color: #000;
  border-color: var(--gold-d);
}

.fame-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.fame-tile {
  background: var(--bg1);
  border: 1px solid var(--line-hi);
  padding: 14px 16px;
  text-align: center;
}
.fame-tile-label {
  font-family: var(--font-pixel);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--ink-faint);
  text-transform: uppercase;
  margin-bottom: 6px;
}
.fame-tile-value {
  font-family: var(--font-body);
  font-size: 24px;
  color: var(--gold);
}

.fame-chart {
  background: var(--bg1);
  border: 1px solid var(--line-hi);
  padding: 16px;
}
.fame-chart svg {
  width: 100%;
  height: auto;
  display: block;
}
.fame-chart .fame-tick {
  font-family: var(--font-body);
  font-size: 11px;
  fill: var(--ink-faint);
}

@media (max-width: 720px) {
  .fame-stats { grid-template-columns: repeat(2, 1fr); }
}

/* ============================================================ Dungeon stats
 *
 * Top three dungeons get podium cards (favourite = larger, gold badge;
 * #2 + #3 = lean side-by-side cards). Everything else falls into the
 * "Other dungeons" table with a relative-frequency damage bar.
 * ========================================================================= */

.dstats-view {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.dstats-podium {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.dstats-podium-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.dstats-card {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 20px;
  background: var(--bg1);
  border: 1px solid var(--line-hi);
}
.dstats-card.rank-1 {
  border-color: var(--gold);
  background: linear-gradient(135deg,
      rgba(255,204,51,0.08) 0%, var(--bg1) 60%);
}
.dstats-sprite {
  flex: 0 0 auto;
  background-color: var(--bg2);
  border: 1px solid var(--line);
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  background-repeat: no-repeat;
  width: 48px;
  height: 48px;
  /* <img>-tag styling: scale the native 8x8 PNG to fill the slot
     with nearest-neighbour, just like the background-image path did
     before crowd-sourcing. */
  object-fit: contain;
  display: block;
}
.dstats-card.rank-1 .dstats-sprite {
  width: 64px;
  height: 64px;
}
.dstats-sprite.empty {
  background-image:
    linear-gradient(45deg, transparent 47%, var(--line-hi) 47%, var(--line-hi) 53%, transparent 53%),
    linear-gradient(-45deg, transparent 47%, var(--line-hi) 47%, var(--line-hi) 53%, transparent 53%);
}
.dstats-card-body {
  flex: 1;
  min-width: 0;
}
.dstats-badge {
  font-family: var(--font-pixel);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--gold);
  text-transform: uppercase;
  margin-bottom: 4px;
}
.dstats-rank {
  font-family: var(--font-pixel);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--ink-faint);
  text-transform: uppercase;
  margin-bottom: 4px;
}
.dstats-name {
  font-family: var(--font-body);
  font-size: 20px;
  color: var(--ink);
  margin-bottom: 8px;
}
.dstats-card.rank-1 .dstats-name {
  font-size: 26px;
  color: var(--gold-2);
}
.dstats-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}
.dstats-stat {
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.dstats-stat-label {
  font-family: var(--font-pixel);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--ink-faint);
  text-transform: uppercase;
}
.dstats-stat-value {
  font-family: var(--font-body);
  font-size: 17px;
  color: var(--ink);
}

.dstats-others-heading {
  font-family: var(--font-pixel);
  font-size: 11px;
  letter-spacing: 1px;
  color: var(--ink-dim);
  text-transform: uppercase;
  margin: 0 0 8px;
}
.dstats-table {
  background: var(--bg1);
  border: 1px solid var(--line);
}
.dstats-row {
  display: grid;
  grid-template-columns: 1.6fr 70px 1fr 100px;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--ink);
}
.dstats-row:last-child { border-bottom: 0; }
.dstats-row-head {
  font-family: var(--font-pixel);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--ink-faint);
  text-transform: uppercase;
  border-bottom: 1px solid var(--line-hi);
}
.dstats-row .col-dungeon {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dstats-row-sprite {
  display: inline-block;
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  background-color: var(--bg2);
  border: 1px solid var(--line);
  background-repeat: no-repeat;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  /* When the sprite is an <img>, fill the slot via object-fit. */
  object-fit: contain;
  vertical-align: middle;
}
.dstats-row-sprite.empty-inline {
  background-image:
    linear-gradient(45deg, transparent 47%, var(--line-hi) 47%, var(--line-hi) 53%, transparent 53%),
    linear-gradient(-45deg, transparent 47%, var(--line-hi) 47%, var(--line-hi) 53%, transparent 53%);
}
.dstats-row .col-runs { text-align: right; }
.dstats-row .col-peak { text-align: right; }
.dstats-row .col-bar { height: 8px; background: var(--bg2); }
/* The header row borrows the same grid as data rows so the columns
   line up, but it shouldn't render the empty bar track between Runs
   and Peak DPS — that looks like a stray rectangle floating in the
   header. Drop the background + height to render a transparent
   spacer instead. */
.dstats-row-head .col-bar { background: transparent; height: auto; }
.dstats-row .col-bar .dstats-bar {
  display: block;
  height: 100%;
  background: var(--gold);
  max-width: 100%;
}

@media (max-width: 720px) {
  .dstats-podium-row { grid-template-columns: 1fr; }
  .dstats-row { grid-template-columns: 1.5fr 50px 80px; }
  .dstats-row .col-bar { display: none; }
}

/* ============================================================ Footer */

.footer {
  border-top: 1px solid var(--line);
  background: var(--bg1);
  padding: 24px 28px;
}
.footer-inner {
  max-width: 1080px; margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  flex-wrap: wrap;
}
.footer-brand { display: flex; align-items: center; gap: 14px; font-size: 15px; }
.footer-links { display: flex; gap: 18px; }
.footer-links a { color: var(--ink-dim); font-size: 15px; }
.footer-links a:hover { color: var(--gold); }

/* ============================================================ Small screens */

@media (max-width: 720px) {
  #topbar { grid-template-columns: auto 1fr; gap: 12px; padding: 12px 16px; }
  .search { grid-column: 1 / -1; order: 3; max-width: none; }
  main { padding: 0 16px 48px; }
  .hero { padding-top: 48px; }
  .hero-tagline { font-size: 18px; }
}

/* --- Graveyard tab ---------------------------------------------------
   One row per detected death. Three-column layout so 100 deaths stack
   as scannable rows: identity (left) | equipped gear (middle) | total
   fame earned (right). Mirrors the in-game roster list density rather
   than the popup's centred-block design — the popup is one-at-a-time,
   the Graveyard is a long history. */
.graveyard-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
}
.graveyard-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* The main row — identity / fame / maxed / gear — sits inside the
   card. The card itself is a column so we can append a second row
   (the expanded carried-items strip) underneath without breaking
   the main-row's column alignment. */
.graveyard-row {
  display: flex;
  align-items: center;
  gap: 20px;
}
/* Identity column — name on top, killer/date below. Min-width keeps
   the column stable when the killer string is short ("Oryx" vs
   "Hadopelagic Jellyfish") so the gear-strip column doesn't dance. */
.graveyard-left {
  flex: 1 1 auto;
  min-width: 0;
}
.graveyard-ident {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.2;
}
.graveyard-name {
  color: var(--gold);
}
.graveyard-ident-meta {
  font-weight: 500;
  color: var(--muted);
  margin-left: 6px;
}
/* Guild flair — small bracketed tag in muted blue next to the
   character name. Reads as a faction badge without competing with
   the name for attention. */
.graveyard-guild {
  font-weight: 500;
  font-size: 13px;
  color: var(--blue, #6FC9DC);
  margin-left: 2px;
}
.graveyard-sub {
  font-size: 13px;
  color: var(--muted);
  margin-top: 4px;
}
.graveyard-killer {
  color: var(--text);
  font-weight: 500;
}
/* Middle: alive + on-death fame stats laid out as two columns,
   sitting in the previous empty gap between identity and gear so
   the row carries visible information all the way across. */
.graveyard-fame {
  flex: 0 0 auto;
  display: flex;
  gap: 24px;
  align-items: center;
}
.graveyard-fame-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.1;
  min-width: 60px;
}
.graveyard-fame-value {
  color: var(--gold, #ffae3a);
  font-weight: 700;
  font-size: 22px;
}
/* Alive fame uses a muted colour so 'fame on death' (the bonus-
   multiplied number that gets credited to the account) stays the
   visually dominant value. */
.graveyard-fame-alive-value {
  color: var(--muted);
  opacity: 0.85;
}
.graveyard-fame-label {
  color: var(--muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-top: 3px;
  white-space: nowrap;
}
/* Maxed-stat badge ("5/8 STATS MAXED") sits between the fame block
   and the gear strip. margin-left:auto here pushes it (and the gear
   that follows) to the right end of the row, so the empty space
   between Fame on Death and the badge fills naturally. */
.graveyard-maxed {
  flex: 0 0 auto;
  margin-left: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.1;
  min-width: 60px;
}
.graveyard-maxed-value {
  color: var(--text);
  font-weight: 700;
  font-size: 22px;
}
/* 8/8 maxed gets the achievement treatment — same gold as the
   Baghound brand mark and the character name above, so a fully-
   maxed death reads as a 'these characters were end-game' signal
   when scanning a long Graveyard. */
.graveyard-maxed-value-full {
  color: var(--gold);
}
.graveyard-maxed-label {
  color: var(--muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-top: 3px;
  white-space: nowrap;
}
/* Right: gear strip. */
.graveyard-gear {
  flex: 0 0 auto;
  display: flex;
  gap: 6px;
}
/* Override .armory-sprite's flex-basis (48px, which leaves an 8px
   off-centre gap when the background tile is 40px) so the sprite
   tile sits flush with the box edge. */
.graveyard-gear-sprite {
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  box-sizing: content-box;
}
/* '+N' chip after the worn 4. Compact pill that hints at extra
   loot below without taking the visual weight of another sprite
   tile. Hovers + focus get a subtle brightening so it reads as
   interactive even before you click. */
.graveyard-gear-more {
  flex: 0 0 auto;
  align-self: center;
  height: 28px;
  min-width: 34px;
  padding: 0 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  margin-left: 6px;
  transition: background 120ms, color 120ms, border-color 120ms;
}
/* Invisible same-size placeholder rendered when a death had no
   carried items, so the gear strip's right edge lines up across
   every row regardless of whether a "+N" chip is shown. */
.graveyard-gear-more.placeholder {
  visibility: hidden;
  pointer-events: none;
  border-color: transparent;
}
.graveyard-gear-more:hover,
.graveyard-gear-more:focus-visible {
  background: var(--bg2);
  color: var(--text);
  border-color: var(--line-hi);
}
.graveyard-card.expanded .graveyard-gear-more {
  background: var(--bg2);
  color: var(--text);
  border-color: var(--line-hi);
}

/* Expanded second row revealing every carried equippable. Hidden
   by default so a long Graveyard stays scannable; shown when the
   card has the .expanded class (toggled by the +N chip). Right-
   aligned so the label + items sit directly under the equipped
   gear strip rather than under the character name on the left.
   The right padding offsets the '+N' chip so the rightmost carried
   item lines up with the ring (rightmost equipped slot) rather than
   the chip's edge. */
.graveyard-carried {
  display: none;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  padding-top: 8px;
  padding-right: 46px;
  border-top: 1px solid var(--border);
}
.graveyard-card.expanded .graveyard-carried {
  display: flex;
}
.graveyard-carried-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
  text-align: right;
}
.graveyard-carried-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
}

/* --- Enchant overlays --------------------------------------------------
   Mirrors the in-game and Discord-webhook treatment for an enchanted
   item: a tinted border / glow around the sprite, plus 1-4 small
   diamond glyphs at the bottom-left corner. Palette matches the Java
   ParsePanelGUI.getGlowColor switch — same colours the Discord posts
   use so the website is visually consistent with the bot output.
     1 enchant  → green   (0,255,0)
     2 enchants → cyan    (0,200,255)
     3 enchants → purple  (200,0,255)
     4 enchants → gold    (255,215,0)

   Applied to .armory-sprite (armory + graveyard share this base class).
   Position:relative on the sprite so the diamond overlay can absolute-
   position itself within the sprite tile. Glow is a soft outer box-
   shadow + same-colour border so it reads even on the dark profile
   background. */
.armory-sprite.enchant-1,
.armory-sprite.enchant-2,
.armory-sprite.enchant-3,
.armory-sprite.enchant-4 {
  position: relative;
}
.armory-sprite.enchant-1 { border-color: #00ff00; box-shadow: 0 0 6px rgba(  0,255,  0,0.55); }
.armory-sprite.enchant-2 { border-color: #00c8ff; box-shadow: 0 0 6px rgba(  0,200,255,0.55); }
.armory-sprite.enchant-3 { border-color: #c800ff; box-shadow: 0 0 6px rgba(200,  0,255,0.55); }
.armory-sprite.enchant-4 { border-color: #ffd700; box-shadow: 0 0 7px rgba(255,215,  0,0.65); }

/* Diamond cluster, bottom-left corner of the sprite tile. flex-row so
   the 1-4 diamonds line up horizontally. The container is small and
   absolutely positioned so it doesn't push other layout around — the
   sprite tile itself stays the visual anchor. */
.enchant-diamonds {
  position: absolute;
  left: 2px;
  bottom: 2px;
  display: flex;
  gap: 1px;
  pointer-events: none;  /* let title-tooltip hover hit the sprite */
}
.enchant-diamond {
  width: 5px;
  height: 5px;
  transform: rotate(45deg);
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
}
/* Diamond colour is keyed off the total enchant count (all diamonds
   on one item share a colour, matching the in-game treatment where
   the glow tier is the single signal). */
.enchant-diamond-1 { background: #00ff00; }
.enchant-diamond-2 { background: #00c8ff; }
.enchant-diamond-3 { background: #c800ff; }
.enchant-diamond-4 { background: #ffd700; }

/* --- Collection: grouped + searchable layout --------------------------
   Per user feedback, the flat 2000-tile grid was overwhelming. Items
   are now grouped by loot-filter bucket inside collapsed <details>
   sections (one per bucket), with an overall "Collected / N" header
   and a search bar that filters tiles in-place. */
.collection-grouped {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.collection-search-wrap {
  margin-bottom: 4px;
}
.collection-search {
  width: 100%;
  font-family: var(--font-body);
  font-size: 16px;
  padding: 10px 14px;
  background: var(--bg2);
  border: 1px solid var(--line-hi);
  color: var(--ink);
  outline: none;
  transition: border-color .12s ease;
}
.collection-search:focus { border-color: var(--gold); }
.collection-progress {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 12px 14px;
  background: var(--bg1);
  border: 1px solid var(--line-hi);
}
.collection-progress-label {
  font-family: var(--font-pixel);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.collection-progress-value {
  font-family: var(--font-pixel);
  font-size: 18px;
  color: var(--gold);
  font-weight: 700;
}
.collection-progress-total {
  color: var(--ink-dim);
  font-weight: 400;
}
.collection-bucket {
  background: var(--bg1);
  border: 1px solid var(--line-hi);
}
.collection-bucket-summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 14px;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-pixel);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--ink);
  user-select: none;
  transition: background .12s ease;
}
.collection-bucket-summary::-webkit-details-marker { display: none; }
.collection-bucket-summary::before {
  content: '▸';
  display: inline-block;
  margin-right: 8px;
  color: var(--gold);
  transition: transform .12s ease;
}
.collection-bucket[open] > .collection-bucket-summary::before {
  transform: rotate(90deg);
}
.collection-bucket-summary:hover { background: var(--bg2); }
.collection-bucket-label { flex: 1; }
.collection-bucket-count {
  color: var(--ink);
  font-weight: 700;
}
.collection-bucket-count-total {
  color: var(--ink-dim);
  font-weight: 400;
}
.collection-bucket .collection-grid {
  padding: 12px 14px 16px;
  margin-top: 0;
}

/* --- Armoury hover card ----------------------------------------------
   Tooltip shown on hover over any equipped slot in the armoury.
   Mirrors the in-game item card layout: sprite + name/tier header,
   then damage / on-equip bonuses / mp / feed power / xp / soulbound /
   description. Positioned by JS (fixed, viewport-anchored) so the
   stylesheet only needs to handle look + feel. */
.armory-hover-card {
  position: fixed;
  z-index: 1000;
  width: 340px;
  background: var(--bg1);
  border: 1px solid var(--line-hi);
  box-shadow: var(--shadow-lift);
  padding: 14px;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.35;
  color: var(--ink);
  pointer-events: none;  /* hover stays on the slot, not the card */
}
.ahc-head {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  margin-bottom: 10px;
}
.ahc-sprite-wrap {
  flex: 0 0 auto;
}
.ahc-sprite {
  position: relative;
  width: 56px;
  height: 56px;
  background-color: var(--bg2);
  border: 1px solid var(--line-hi);
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
/* Shiny treatment: five gold sparkles scattered around the sprite,
   mirroring the Discord loot-card drawShinyStars(). Baked into a
   single SVG data URI so the overlay paints in one CSS layer with
   zero JS. Positions/sizes mirror the Java renderer (5 sparkles, 4-
   point star with a white core and a soft gold halo). Applied to
   every sprite renderer — armoury slots, graveyard equipped /
   carried, and the hover card. The wrap needs position:relative so
   the absolute overlay anchors to the sprite tile rather than the
   nearest positioned ancestor. */
.armory-sprite.shiny,
.graveyard-gear-sprite.shiny,
.ahc-sprite.shiny {
  position: relative;
}
.armory-sprite.shiny::after,
.graveyard-gear-sprite.shiny::after,
.ahc-sprite.shiny::after {
  content: '';
  position: absolute;
  inset: -3px;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 60'><defs><radialGradient id='h'><stop offset='0%' stop-color='%23FFF3C0' stop-opacity='0.9'/><stop offset='100%' stop-color='%23FFF3C0' stop-opacity='0'/></radialGradient></defs><g fill='%23FFF3C0'><circle cx='10' cy='10' r='11' fill='url(%23h)'/><polygon points='10,0 13,8 21,10 13,12 10,21 7,12 -1,10 7,8'/><circle cx='10' cy='10' r='2.5' fill='white'/><circle cx='50' cy='13' r='7' fill='url(%23h)'/><polygon points='50,7 52,12 57,13 52,14 50,19 48,14 43,13 48,12'/><circle cx='50' cy='13' r='2' fill='white'/><circle cx='47' cy='47' r='9' fill='url(%23h)'/><polygon points='47,37 49,45 57,47 49,49 47,57 45,49 37,47 45,45'/><circle cx='47' cy='47' r='2.5' fill='white'/><circle cx='14' cy='50' r='6' fill='url(%23h)'/><polygon points='14,44 15,48 19,50 15,51 14,55 13,51 9,50 13,48'/><circle cx='14' cy='50' r='1.8' fill='white'/><circle cx='35' cy='4' r='4' fill='url(%23h)'/><polygon points='35,0 36,3 39,4 36,5 35,8 34,5 31,4 34,3'/><circle cx='35' cy='4' r='1.2' fill='white'/></g></svg>");
  background-size: 100% 100%;
  background-repeat: no-repeat;
}
/* Reuse the bucket-border palette already defined for .armory-sprite. */
.ahc-sprite.b-UT_LEGENDARY     { border-color: var(--gold);    }
.ahc-sprite.b-UT_HIGH_RARE     { border-color: #FF8C3A;        }
.ahc-sprite.b-UT_MEDIUM        { border-color: var(--blue);    }
.ahc-sprite.b-UT_LOW           { border-color: var(--green);   }
.ahc-sprite.b-ST               { border-color: var(--purple);  }
.ahc-sprite.b-T14_WEAPON_ARMOR { border-color: #E6D14A;        }
.ahc-sprite.b-T7_RING_ABILITY  { border-color: #6FC9DC;        }
/* Reuse enchant-N glow classes from the armoury sprite — same border
   + box-shadow palette so the hover card matches the in-app card. */
.ahc-head-text {
  flex: 1 1 auto;
  min-width: 0;
}
.ahc-name {
  font-family: var(--font-pixel);
  font-size: 16px;
  letter-spacing: 0.5px;
  line-height: 1.15;
  color: var(--ink);
}
.ahc-name.b-UT_LEGENDARY,
.ahc-name.b-T14_WEAPON_ARMOR { color: var(--gold);   }
.ahc-name.b-UT_HIGH_RARE     { color: #FF8C3A;       }
.ahc-name.b-UT_MEDIUM        { color: var(--blue);   }
.ahc-name.b-UT_LOW           { color: var(--green);  }
.ahc-name.b-ST               { color: var(--purple); }
.ahc-name.b-T7_RING_ABILITY  { color: #6FC9DC;       }
.ahc-sub {
  margin-top: 4px;
  font-size: 13px;
  color: var(--ink-dim);
}
.ahc-feed {
  color: var(--ink-dim);
}
.ahc-enchant-pill {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.5px;
  color: #000;
}
.enchant-pill-1 { background: #00ff00; color: #002800; }
.enchant-pill-2 { background: #00c8ff; color: #002030; }
.enchant-pill-3 { background: #c800ff; color: #200030; }
.enchant-pill-4 { background: #ffd700; color: #2a1f00; }

.ahc-row {
  display: flex;
  gap: 10px;
  font-size: 14px;
  margin-top: 6px;
  align-items: baseline;
}
.ahc-row-label {
  color: var(--ink-dim);
  width: 70px;
  flex: 0 0 70px;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.5px;
  padding-top: 2px;
}
.ahc-row-value {
  flex: 1 1 auto;
}
.ahc-damage {
  color: var(--gold-2);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.5px;
}
.ahc-bonuses {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.ahc-bonus {
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--bg2);
  font-size: 13px;
}
.ahc-bonus.pos { color: var(--green); }
.ahc-bonus.neg { color: var(--red);   }
.ahc-small {
  margin-top: 8px;
  color: var(--ink-dim);
  font-size: 13px;
}
.ahc-soulbound {
  margin-top: 8px;
  color: var(--ink-dim);
  font-style: italic;
  font-size: 13px;
}
.ahc-desc {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  color: var(--ink-dim);
  font-size: 13px;
  font-style: italic;
}

/* Enchantments list on the armoury hover card. Mirrors the in-game
   "Enchantments" section: header, then one line per enchant in the
   tier colour (green / cyan / purple / gold). No glyph icons in v1
   — the names alone are enough signal for now. */
.ahc-enchants-section {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}
.ahc-enchants-header {
  font-family: var(--font-pixel);
  font-size: 11px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 6px;
}
.ahc-enchant-line {
  font-size: 14px;
  padding: 2px 0;
  color: var(--ink);
}
.ahc-enchant-line.enchant-tier-1 { color: #6BE56B; }
.ahc-enchant-line.enchant-tier-2 { color: #6FC9DC; }
.ahc-enchant-line.enchant-tier-3 { color: #C76FE5; }
.ahc-enchant-line.enchant-tier-4 { color: var(--gold); }

/* Enchantment description sub-line — sits under each prettified
   enchant name. Smaller, muted, slight indent so it visually
   groups with the line above. Matches the in-game card's two-line
   layout (name in colour, description in dimmer text below). */
.ahc-enchant-entry {
  margin-bottom: 8px;
}
.ahc-enchant-entry:last-child {
  margin-bottom: 0;
}
.ahc-enchant-desc {
  font-size: 12px;
  color: var(--ink-dim);
  line-height: 1.3;
  margin-top: 2px;
  padding-left: 0;
}
