/* Rogue HQ — layout + components */

/* ── App shell: sidebar rail + content column ───────────── */
:root { --sb-w: 236px; }
.app-shell { display: grid; grid-template-columns: var(--sb-w) minmax(0, 1fr); min-height: 100vh; }

.brand { display: flex; align-items: center; gap: var(--s3); font-weight: 800; font-size: 1.05rem; letter-spacing: .2px; }
.brand .logo { width: 30px; height: 30px; border-radius: 9px; display: block; box-shadow: var(--shadow-2); }
.brand span { color: var(--accent); }   /* "Rogue" now takes the theme accent (was inheriting static white); the <b>HQ</b> below keeps the gradient, so both parts move with the theme */
.brand b { background: var(--accent-grad); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }

/* Sidebar — a sticky full-height rail on desktop, an off-canvas drawer on mobile (same DOM). */
.sidebar {
  position: sticky; top: 0; height: 100vh; z-index: 50;
  display: flex; flex-direction: column; gap: var(--s2);
  padding: var(--s4) var(--s3);
  background: rgba(11,12,20,.72); backdrop-filter: blur(14px) saturate(140%);
  border-right: 1px solid var(--border-soft); overflow: hidden;
}
.sb-brand { display: flex; align-items: center; justify-content: space-between; gap: var(--s2); padding: 2px var(--s2) var(--s3); }
.sb-collapse { flex: none; width: 30px; height: 30px; }
.sb-collapse svg { transition: transform var(--speed); }

.sb-search {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 9px 11px; margin-bottom: var(--s2);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--card); color: var(--muted); font: inherit; font-size: .85rem; cursor: pointer; transition: var(--speed);
}
.sb-search:hover { border-color: var(--accent); color: var(--text); }
.sb-search kbd { margin-left: auto; font-family: var(--mono); font-size: .68rem; border: 1px solid var(--border); border-radius: 5px; padding: 1px 5px; color: var(--muted); }

.sb-nav { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: var(--s4); padding-right: 2px; }
.sb-nav::-webkit-scrollbar { width: 7px; }
.sb-nav::-webkit-scrollbar-thumb { background: var(--border); border-radius: 8px; }
.sb-group { display: flex; flex-direction: column; gap: 1px; }
.sb-glabel { font-size: .6rem; font-weight: 800; text-transform: uppercase; letter-spacing: .7px; color: var(--muted); padding: 0 10px 3px; white-space: nowrap; }

.sb-link, .sb-sub {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: var(--r-md);
  color: var(--text-dim); font-weight: 600; font-size: .9rem; white-space: nowrap; transition: var(--speed);
}
.sb-link svg, .sb-sub svg { flex: none; }
.sb-link:hover, .sb-sub:hover { color: var(--text); background: var(--card); text-decoration: none; }
.sb-link.active, .sb-sub.active { color: var(--text); background: var(--card-2); box-shadow: inset 0 0 0 1px var(--border); }
.sb-hub { font-weight: 800; }
.sb-hub span { overflow: hidden; text-overflow: ellipsis; }
/* Guild switcher: which guild the tabs below describe. An inline expanding list rather than a popover,
   because the rail scrolls and an absolutely positioned menu inside a scroll container gets clipped. */
.sb-switch {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 10px; border: 0;
  border-radius: var(--r-md); background: var(--card); color: var(--text); font: inherit;
  font-weight: 800; font-size: .9rem; cursor: pointer; text-align: left; transition: var(--speed);
}
.sb-switch:hover { background: var(--card-2); }
/* Only the NAME flexes. This selector caught the applicant-count badge too, so the badge grew to the
   same share of the row as the guild name and "Lux Aeterna" ellipsised down to "Lux A...". */
.sb-switch > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-switch > span.badge { flex: none; }
.sb-switch svg:last-child { margin-left: auto; color: var(--text-dim); }
.sb-switchmenu { display: flex; flex-direction: column; gap: 1px; margin: 2px 0 0 19px;
  padding-left: 8px; border-left: 1px solid var(--border-soft); }
.sb-switch-opt {
  display: flex; align-items: baseline; gap: 6px; padding: 6px 10px; border-radius: var(--r-md);
  color: var(--text-dim); font-size: .85rem; font-weight: 600; white-space: nowrap;
}
.sb-switch-opt:hover { color: var(--text); background: var(--card); text-decoration: none; }
.sb-switch-opt.on { color: var(--text); background: var(--card-2); box-shadow: inset 0 0 0 1px var(--border); }
.sb-switch-opt span:first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.sb-ro { margin-left: auto; font-size: .65rem; font-weight: 700; color: var(--muted); }
/* A tab that guild has not shared. Greyed and explained on hover rather than hidden, so the rail keeps
   the same shape for every guild and the missing access reads as a permission, not a missing feature. */
.sb-off { opacity: .4; cursor: not-allowed; }
.sb-off:hover { background: none; color: var(--text-dim); }
.sb-subs { display: flex; flex-direction: column; gap: 1px; margin: 2px 0 0 19px; padding-left: 8px; border-left: 1px solid var(--border-soft); }
.sb-sub { padding: 6px 10px; font-size: .85rem; }
.sb-link .badge, .sb-sub .badge { margin-left: auto; font-size: .68rem; font-weight: 800; min-width: 18px; height: 18px; padding: 0 5px; display: grid; place-items: center; border-radius: 999px; background: var(--accent); color: #0b0c14; }

/* Top bar: spans the content column, holds the persistent top-right cluster (search, theme, Discord,
   install, refresh, profile). Burger + brand show only on mobile (desktop has them in the rail). */
.topbar {
  display: flex; align-items: center; gap: var(--s3);
  position: sticky; top: 0; z-index: 45; height: var(--header-h); padding: 0 var(--s5);
  background: rgba(11,12,20,.72); backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--border-soft);
}
.topbar .tb-burger, .topbar .brand { display: none; }
/* Previous page: far left of the bar. Disabled on home with nothing behind it, dimmed rather than
   hidden so the bar never shifts under the cursor. */
.tb-back { flex: none; }
.tb-back:disabled { opacity: .35; cursor: default; }   /* no pointer-events: none, so the "Nothing to go back to" tooltip can show */
.tb-right { display: flex; align-items: center; gap: 6px; }
.tb-right .profile { padding-left: 6px; }
.sb-scrim { display: none; }

/* Collapsed rail (desktop only) — icons stay, labels + groups + tools stack tight. */
@media (min-width: 761px) {
  body.sb-collapsed { --sb-w: 68px; }
  body.sb-collapsed .sb-brand { justify-content: center; }
  body.sb-collapsed .sb-brand .brand span { display: none; }
  body.sb-collapsed .sb-collapse svg { transform: rotate(180deg); }
  body.sb-collapsed .sb-search { justify-content: center; }
  body.sb-collapsed .sb-search span, body.sb-collapsed .sb-search kbd { display: none; }
  body.sb-collapsed .sb-glabel { display: none; }
  body.sb-collapsed .sb-link span, body.sb-collapsed .sb-sub span,
  body.sb-collapsed .sb-subs, body.sb-collapsed .sb-link .badge { display: none; }
  body.sb-collapsed .sb-link, body.sb-collapsed .sb-sub { justify-content: center; }
  /* The guild switcher collapses to an icon like every other rail row. Its menu is hidden outright:
     full guild names and "read only" chips do not fit a 68px rail, and the tabs are hidden anyway. */
  body.sb-collapsed .sb-switch { justify-content: center; padding: 8px 0; }
  body.sb-collapsed .sb-switch span, body.sb-collapsed .sb-switchmenu { display: none; }
  body.sb-collapsed .sb-switch svg { flex: none; }
  body.sb-collapsed .sb-switch svg:last-child { display: none; }   /* the caret has nothing to reveal */
  /* the guild head keeps its applicant badge even collapsed (as a corner dot) */
  body.sb-collapsed .sb-hub, body.sb-collapsed .sb-switch { position: relative; }
  body.sb-collapsed .sb-hub .badge,
  body.sb-collapsed .sb-switch .badge { display: grid; position: absolute; top: 2px; right: 8px; margin: 0; }
}

/* Discord link chips (raid board + roster popup) — blurple */
.dchip {
  display: inline-flex; align-items: center; max-width: 170px; gap: 5px;
  padding: 3px 10px; border-radius: var(--r-pill);
  background: rgba(88,101,242,.16); color: #aeb6ff; font-size: .78rem; font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dchip-btn { border: 1px solid rgba(88,101,242,.38); cursor: pointer; }
.dchip-btn:hover { background: rgba(88,101,242,.28); color: #fff; }
.dlink {
  font-size: .76rem; font-weight: 700; color: var(--muted); background: transparent;
  border: 1px dashed var(--border); border-radius: var(--r-pill); padding: 3px 10px; cursor: pointer;
  transition: color var(--speed), border-color var(--speed);
}
.dlink:hover { color: #aeb6ff; border-color: rgba(88,101,242,.55); }

/* Discord link picker (username search) */
.dpick-lead { font-size: .82rem; color: var(--muted); margin: 0 0 10px; }
.dpick-results { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; max-height: 320px; overflow-y: auto; }
.dpick-row { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 8px 10px; border-radius: var(--r-md); background: var(--card-2); border: 1px solid var(--border); color: var(--text); cursor: pointer; transition: var(--speed); }
.dpick-row:hover { border-color: var(--accent); background: var(--card-hover); }
.dpick-av { width: 30px; height: 30px; border-radius: 50%; flex: none; display: grid; place-items: center; font-weight: 800; background: rgba(88,101,242,.2); color: #aeb6ff; }
.dpick-meta { flex: 1; min-width: 0; }
.dpick-name { font-weight: 700; font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dpick-user { font-size: .74rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dpick-status { font-size: .82rem; color: var(--muted); padding: 10px 2px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.dpick-status a { color: #aeb6ff; }
.dpick-remove { margin-top: 12px; background: none; border: none; color: var(--bad); font-size: .8rem; cursor: pointer; padding: 4px 0; }
.dpick-remove:hover { text-decoration: underline; }

/* Inline note pen — edit affordance next to a label (player popup) */
.notepen {
  background: none; border: none; cursor: pointer; color: var(--muted); opacity: .55;
  line-height: 0; padding: 2px; margin-left: 5px; vertical-align: middle; border-radius: 6px;
  display: inline-flex; align-items: center;
  transition: opacity var(--speed), color var(--speed), background var(--speed);
}
.notepen svg { width: 14px; height: 14px; display: block; }
.notepen:hover { opacity: 1; color: var(--text); background: color-mix(in srgb, currentColor 14%, transparent); }
.notepen.has { opacity: 1; color: var(--gold); }

/* Board "has a note" flag — a gold chip after a member's name, deliberately eye-catching so a single
   note on a 40-row board can't be missed (SVG icon = always renders, unlike the old pencil dingbat). */
.noteflag {
  display: inline-flex; align-items: center; vertical-align: middle; cursor: help;
  margin-left: 6px; padding: 2px 4px; line-height: 0; color: var(--gold); border-radius: 999px;
  background: color-mix(in srgb, var(--gold) 18%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 40%, transparent);
}
.noteflag svg { width: 13px; height: 13px; display: block; }

/* Per-player loading spinner (progressive raid board), sits after a member's name */
.mini-spin {
  display: inline-block; width: 11px; height: 11px; vertical-align: -1px; margin-left: 2px;
  border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Slacker Radar — tidy control strip (replaces the cramped right-aligned stack) */
.radar-controls {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--s3) var(--s4);
  background: var(--card-2); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 9px 14px; font-size: .82rem; color: var(--text-dim);
}
.radar-group { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s3); }
.radar-glabel { font-size: .6rem; font-weight: 800; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); }
.radar-div { width: 1px; align-self: stretch; min-height: 20px; background: var(--border); }
@media (max-width: 640px) { .radar-div { display: none; } }

/* profile chip */
.profile { display: flex; align-items: center; gap: var(--s3); cursor: pointer; padding: 4px 8px 4px 4px; border-radius: var(--r-pill); transition: var(--speed); }
.profile:hover { background: var(--card); }
.profile .pm-caret { color: var(--muted); display: inline-flex; margin-left: -2px; }
.profile:hover .pm-caret { color: var(--text); }
.avatar {
  width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center;
  background: var(--card-2); border: 1px solid var(--border); font-weight: 800; color: var(--accent);
}
.profile .who { display: flex; flex-direction: column; line-height: 1.15; }
.profile .who .name { font-weight: 700; font-size: .9rem; }
.profile .who .role { font-size: .72rem; }
.role-leader { color: var(--gold); } .role-vice { color: var(--accent-2); } .role-member { color: var(--muted); }
/* Top-right profile badge: a compact "small profile" card — gradient avatar + online dot, set apart
   from the flat utility icons by its own surface + border. Scoped to the top bar so other avatars
   (profile menu, self-profile) are untouched. */
.tb-right .profile { gap: 9px; padding: 4px 11px 4px 5px; margin-left: 2px; background: var(--card); border: 1px solid var(--border); border-radius: var(--r-md); }
.tb-right .profile:hover { background: var(--card-2); border-color: var(--accent); }
.tb-right .profile .avatar {
  position: relative; width: 34px; height: 34px; border: none; color: #0b0c14; font-size: .95rem;
  background: var(--accent-grad); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent);
}
.tb-right .profile .avatar::after {
  content: ''; position: absolute; right: -2px; bottom: -2px; width: 11px; height: 11px;
  border-radius: 50%; background: var(--good); border: 2px solid var(--card); transition: border-color var(--speed);
}
.tb-right .profile:hover .avatar::after { border-color: var(--card-2); }
/* profile dropdown (anchored to the chip) */
.profile-menu { position: fixed; z-index: 90; width: 300px; max-width: calc(100vw - 16px);
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-2); padding: 12px 13px; color: var(--text); }
.profile-menu .pm-act, .profile-menu .pm-row { display: flex; align-items: center; gap: 10px; width: 100%;
  background: none; border: none; color: var(--text); cursor: pointer; font-size: .85rem;
  font-family: inherit; padding: 8px; border-radius: var(--r-md); text-align: left; transition: var(--speed); }
.profile-menu .pm-act:hover, .profile-menu .pm-row:hover { background: var(--card-2); }
.profile-menu .pm-row { border-top: 1px solid var(--border-soft); border-radius: 0; padding: 10px 4px; }

.icon-btn {
  width: 38px; height: 38px; border-radius: var(--r-md); display: grid; place-items: center;
  background: var(--card); border: 1px solid var(--border); color: var(--text-dim);
  transition: var(--speed);
}
.icon-btn:hover { color: var(--text); border-color: var(--accent); }
.icon-btn.spin svg { animation: spin .7s linear infinite; }
.spinning svg { animation: spin .7s linear infinite; }   /* e.g. the Recalculate button while streaming */
/* Install-app CTA: accent it so it reads as actionable. Only shown when the app is installable. */
.install-btn { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.install-btn:hover { color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent); }
/* Community Discord link — blurple brand accent, matches the .dchip palette elsewhere. */
.discord-btn { color: #aeb6ff; border-color: rgba(88,101,242,.38); }
.discord-btn:hover { color: #fff; border-color: rgba(88,101,242,.7); box-shadow: 0 0 0 2px rgba(88,101,242,.25); }
.kofi-btn { color: #e6b34d; border-color: rgba(224,165,42,.38); }
.kofi-btn:hover { color: #ffd782; border-color: rgba(224,165,42,.72); box-shadow: 0 0 0 2px rgba(224,165,42,.22); }

/* ── Raid MVP card ─────────────────────────────────────── */
.mvp-card { border-color: color-mix(in srgb, var(--gold) 35%, var(--border)); background: linear-gradient(180deg, color-mix(in srgb, var(--gold) 8%, var(--card)), var(--card)); }
.mvp-head { display: flex; align-items: center; justify-content: space-between; gap: 8px 16px; flex-wrap: wrap; margin-bottom: var(--s3); }
.mvp-label { font-size: .78rem; font-weight: 800; letter-spacing: .04em; color: var(--gold); text-transform: uppercase; }
.mvp-timer { display: inline-flex; align-items: center; gap: 6px; font-size: .82rem; white-space: nowrap; color: var(--text-dim); }
.mvp-timer svg { opacity: .85; }
.mvp-timer-t { color: var(--muted); font-weight: 600; }
.mvp-timer-val { font-weight: 800; font-variant-numeric: tabular-nums; }
.mvp-empty { color: var(--muted); font-size: .9rem; padding: 2px 0; }
.mvp-hero { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); width: 100%; padding: 0; text-align: left; flex-wrap: wrap; }
.mvp-name { font-size: 1.25rem; font-weight: 800; }
.mvp-figs { display: flex; gap: var(--s5); }
.mvp-fig { text-align: right; }
.mvp-fig b { display: block; font-family: var(--mono); font-size: 1.15rem; font-weight: 700; color: var(--text); }
.mvp-fig span { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.mvp-daily { display: flex; align-items: center; gap: var(--s3); width: 100%; margin-top: var(--s4); padding: var(--s4) 0 0; border-top: 1px solid var(--border); }
.mvp-daily-k { font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.mvp-daily-n { font-weight: 600; }
.mvp-daily-v { margin-left: auto; font-family: var(--mono); font-weight: 700; color: var(--gold); }
.mvp-hero:hover .mvp-name, .mvp-daily:hover .mvp-daily-n { color: var(--accent-2); }

/* ── Compare players ───────────────────────────────────── */
.cmp-tbl { table-layout: fixed; }
.cmp-tbl th, .cmp-tbl td { width: 40%; vertical-align: middle; }
.cmp-tbl .cmp-mid { width: 20%; text-align: center; color: var(--muted); font-size: .75rem; font-weight: 600; }
.cmp-tbl thead th { font-size: .95rem; font-weight: 800; }
.cmp-tbl td:last-child, .cmp-tbl th:last-child { text-align: left; }
.cmp-tbl .cmp-sec td { width: auto; text-align: center; padding-top: var(--s4); font-size: .64rem; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); font-weight: 700; }

/* ── Guild insight panels (collapsible) ───────────────── */
.panel-head { display: flex; align-items: center; gap: var(--s3); width: 100%; text-align: left; padding: 0; }
.panel-caret { color: var(--muted); font-size: .85rem; width: 1em; flex: none; }
.panel-head:hover h2 { color: var(--accent-2); }
.diffwrap { display: flex; flex-wrap: wrap; gap: var(--s2); }
.diffchip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--card-2); font-size: .82rem; }
.diffchip-p { color: var(--muted); font-family: var(--mono); font-size: .72rem; }
.diffchip.in  { border-color: color-mix(in srgb, var(--good) 45%, var(--border)); }
.diffchip.out { border-color: color-mix(in srgb, var(--bad) 45%, var(--border)); opacity: .85; }
.diffchip.in:hover  { background: color-mix(in srgb, var(--good) 14%, var(--card-2)); }
.diffchip.out:hover { background: color-mix(in srgb, var(--bad) 14%, var(--card-2)); }

/* ── Raid sub-tabs ─────────────────────────────────────── */
.subtabs { display: inline-flex; gap: 4px; flex-wrap: wrap; background: var(--card); border: 1px solid var(--border); border-radius: 999px; padding: 4px; margin: var(--s4) 0 var(--s5); max-width: 100%; }
.subtab { display: inline-flex; align-items: center; gap: 7px; padding: 8px 16px; border-radius: 999px; color: var(--text-dim); font-weight: 600; font-size: .9rem; transition: var(--speed); }
.subtab:hover { color: var(--text); text-decoration: none; }
.subtab.active { background: var(--card-hover); color: var(--text); }
.subtab svg { opacity: .85; }

/* Patch Notes — editorial treatment: a violet "vine" grows down the left margin as you read (doubling as
   nav + scroll progress), carved small-caps serif headers, flowing sections, and leaf/stub verdict marks
   on change items. Recoloured entirely to the app's own tokens so it sits with the rest of Rogue HQ. */
:root{ --pn-serif:'Palatino Linotype',Palatino,'Book Antiqua','Iowan Old Style','URW Palladio L',Georgia,serif; }

/* At-a-glance TL;DR box */
.pn-glance{ margin:0 0 var(--s4); }
.pn-glance .pn-list{ margin-top:10px; }

/* Filter chips + expand-all */
.pn-controls{ display:flex; align-items:center; justify-content:space-between; gap:12px 16px; flex-wrap:wrap; margin:0 0 var(--s4); }
.pn-filter{ display:flex; gap:8px; flex-wrap:wrap; }
.pn-fchip{ font:600 .85rem/1 inherit; padding:7px 13px; border-radius:999px; cursor:pointer;
  border:1px solid var(--border); background:var(--card); color:var(--text-dim); transition:background .15s,color .15s,border-color .15s; }
.pn-fchip:hover{ color:var(--text); border-color:var(--accent); }
.pn-fchip.active{ background:var(--accent-grad); color:#10131f; border-color:transparent; font-weight:700; }
.pn-expand-all{ font:600 .82rem inherit; background:none; border:0; cursor:pointer; color:var(--accent-2); padding:6px 2px; white-space:nowrap; }
.pn-expand-all:hover{ text-decoration:underline; }

/* Topic groups */
.pn-group{ margin:0 0 var(--s4); }
.pn-group-head{ margin:0 0 10px; }
.pn-group-name{ font-family:var(--pn-serif); font-variant-caps:small-caps; letter-spacing:.03em; font-weight:700; font-size:1.3rem; margin:0; color:var(--text); }
.pn-group-note{ color:var(--muted); font-size:.9rem; line-height:1.5; margin:3px 0 0; max-width:74ch; }
.pn-topics{ display:flex; flex-direction:column; gap:8px; }

/* Collapsible topic row */
.pn-topic{ border:1px solid var(--border); border-radius:12px; background:var(--card); }
.pn-topic.open{ border-color:var(--accent); background:var(--card-2); }
.pn-topic-head{ width:100%; display:flex; align-items:flex-start; gap:12px; text-align:left; cursor:pointer; background:none; border:0; padding:13px 15px; font:inherit; color:inherit; }
.pn-tag{ flex:none; margin-top:1px; min-width:54px; text-align:center; font:700 .66rem/1 var(--mono); letter-spacing:.08em; text-transform:uppercase;
  padding:5px 8px; border-radius:6px; color:var(--tag,var(--accent)); border:1px solid var(--border); background:var(--card-2);
  border-color:color-mix(in srgb, var(--tag,var(--accent)) 42%, transparent); background:color-mix(in srgb, var(--tag,var(--accent)) 13%, transparent); }
.t-new{ --tag:var(--accent-2); } .t-nerf{ --tag:var(--bad); } .t-buff{ --tag:var(--good); }
.t-rework{ --tag:var(--warn); } .t-soon{ --tag:var(--rare); } .t-ref{ --tag:var(--muted); }
.t-fix{ --tag:var(--good); } .t-change{ --tag:var(--accent); }
.pn-topic-main{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:3px; }
.pn-topic-title{ font-weight:650; color:var(--text); line-height:1.3; }
.pn-topic-sum{ color:var(--text-dim); font-size:.9rem; line-height:1.45; }
.pn-chev{ flex:none; width:18px; height:18px; margin-top:3px; color:var(--muted); transition:transform .25s,color .15s; }
.pn-topic.open .pn-chev{ transform:rotate(180deg); color:var(--accent); }
.pn-topic-head:hover .pn-chev{ color:var(--text-dim); }
.pn-topic-body{ display:none; padding:2px 15px 15px; }
.pn-topic.open .pn-topic-body{ display:block; animation:pn-reveal .2s ease; }
@keyframes pn-reveal{ from{ opacity:0; transform:translateY(-4px); } to{ opacity:1; transform:none; } }
@media(prefers-reduced-motion:reduce){ .pn-topic.open .pn-topic-body{ animation:none; } }
.pn-secbody + .pn-secbody{ margin-top:14px; padding-top:14px; border-top:1px solid var(--border-soft); }

.pn-banner{ margin-bottom:var(--s5); }
.pn-banner-kick{ font:700 11px var(--mono); letter-spacing:.16em; text-transform:uppercase; color:var(--muted); }
.pn-banner-title{ font-family:var(--pn-serif); font-variant-caps:small-caps; letter-spacing:.035em; font-weight:700;
  font-size:clamp(1.9rem,4.6vw,2.5rem); line-height:1.06; margin:9px 0 8px; color:var(--text);
  text-shadow:-1px -1px 0 rgba(0,0,0,.34), 1px 1px 0 rgba(233,234,255,.05); }
.pn-tagline{ color:var(--text-dim); max-width:60ch; margin:0; line-height:1.55; font-size:1.02rem; }

.pn-eyebrow{ font:700 11px var(--mono); letter-spacing:.2em; text-transform:uppercase; margin:0 0 8px; }
.pn-title{ font-family:var(--pn-serif); font-variant-caps:small-caps; letter-spacing:.028em; font-weight:700;
  font-size:1.55rem; line-height:1.14; margin:0 0 10px; color:var(--text);
  text-shadow:-1px -1px 0 rgba(0,0,0,.28), 1px 1px 0 rgba(233,234,255,.04); }
.pn-lede{ color:var(--muted); line-height:1.62; max-width:70ch; margin:0 0 14px; font-size:.97rem; }
.pn-list{ list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:12px; max-width:80ch; }
.pn-list li{ position:relative; padding-left:22px; color:var(--text-dim); line-height:1.56; }
.pn-list li::before{ content:""; position:absolute; left:2px; top:8px; width:6px; height:6px; border-radius:2px; background:var(--accent); transform:rotate(45deg); opacity:.85; }
.pn-list li b{ color:var(--text); font-weight:650; }
/* verdict-marked change items: a leaf (buff), pruned stub (nerf), sprout (new), graft-scar (rework) */
.pn-list li.mark{ padding-left:27px; }
.pn-list li.mark::before{ display:none; }
.pn-mark{ position:absolute; left:0; top:1px; width:18px; height:18px; }
.pn-mark.buff{ color:var(--good); } .pn-mark.nerf{ color:var(--bad); } .pn-mark.new{ color:var(--gold); } .pn-mark.rework{ color:var(--accent-2); }
.pn-callout{ border:1px solid var(--border); border-radius:10px; padding:15px 17px; margin:2px 0 6px; background:var(--card-2); }
.pn-callout .pn-eyebrow{ margin-bottom:6px; }
.pn-chip{ display:inline-block; font-family:var(--mono); font-size:.78rem; padding:3px 9px; border-radius:6px; border:1px solid var(--border); color:var(--gold); background:var(--card); }

/* ── Gear upgrade guide ────────────────────────────────── */
/* visual upgrade tree — one continuous, content-width chart that mirrors the in-game gear screen.
   Rich jewel-tone bands that NEVER fade to black, soft seams, and glowing flourished headers, so the
   whole thing reads as a single guide rather than separate boxes stacked together. */
.gear-chart { width: fit-content; max-width: 100%; border: 1px solid rgba(255,255,255,.10); border-radius: var(--r-lg); overflow: hidden; margin-top: var(--s3); background: #1a1226; box-shadow: 0 8px 26px rgba(0,0,0,.45); }
.gear-chart-head { display: flex; align-items: center; gap: var(--s2); padding: 9px 13px; background: linear-gradient(180deg, #2a1f44, #1c1430); font-weight: 800; }
.gear-chart-head img { border-radius: var(--r-sm); }
/* each band keeps its rarity colour all the way down (deep jewel tone bottom, never black) */
.gear-sect-epic      { background: linear-gradient(180deg, #6a3fc0 0%, #3f2680 55%, #2e1c5e 100%); }
.gear-sect-legendary { background: linear-gradient(180deg, #927020 0%, #654918 55%, #4a360f 100%); }
.gear-sect-mythical  { background: linear-gradient(180deg, #9c3236 0%, #642025 55%, #4a161b 100%); }
/* header: a glowing title flanked by a fading flourish line + gem — no hard wall between sections */
.gear-banner { display: flex; align-items: center; justify-content: center; gap: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .18em; font-size: .86rem; padding: 9px 14px; }
.gear-banner-epic      { color: #efe4ff; text-shadow: 0 0 15px rgba(175,125,255,.95), 0 1px 2px rgba(0,0,0,.55); }
.gear-banner-legendary { color: #fff0c8; text-shadow: 0 0 15px rgba(248,202,90,.95), 0 1px 2px rgba(0,0,0,.55); }
.gear-banner-mythical  { color: #ffcccc; text-shadow: 0 0 15px rgba(248,110,110,.95), 0 1px 2px rgba(0,0,0,.55); }
.gear-line { flex: 1; height: 2px; max-width: 120px; border-radius: 2px; opacity: .85; }
.gear-line-l { background: linear-gradient(90deg, transparent, currentColor 94%); }
.gear-line-r { background: linear-gradient(90deg, currentColor 6%, transparent); }
.gear-gem { flex: none; width: 7px; height: 7px; transform: rotate(45deg); background: currentColor; border-radius: 1px; box-shadow: 0 0 8px currentColor, inset 0 0 2px rgba(255,255,255,.6); }
.gear-rows { display: grid; gap: 11px; padding: 9px 13px; }
.gear-row { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.gear-parts { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.gear-arrow { color: var(--text-dim); font-size: 1.2rem; margin: 0 2px; }
.gear-plus { color: var(--muted); font-weight: 700; font-size: 1rem; }
/* tile = star rarity cell + a big icon; the colored corner type badge overhangs top-left and is on top of everything */
/* Gear tile. Every plate in icons/gearbg is rendered on ONE shared canvas straight from the game's
   InventoryEquipmentItem prefab, so each layer just fills the box and no layer needs its own offset.
   The aspect and the icon box below are the prefab's, mirrored in gearbg/plates.json — do not tune them
   by eye, re-run scripts/build/build_gear_plates.py. The canvas is wider than the tile itself because
   the type badge overhangs the top-left and the +N badge hangs below the bottom edge. */
.gear-tile { position: relative; box-sizing: border-box; width: 68px; aspect-ratio: 619 / 654; flex: none; }
.gear-tile .gear-plate { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* the item art sits where the prefab puts it (Equipment_Icon/Icon), not centred by eye */
.gear-tile .gear-ico { position: absolute; left: 19.615%; top: 14.045%; width: 69.744%; height: 66.049%; object-fit: contain; filter: drop-shadow(0 1px 2px rgba(0,0,0,.5)); }
.gear-pick .gear-pick-s { position: absolute; bottom: -2px; left: -2px; width: 14px; height: 14px; object-fit: contain; pointer-events: none; }
.gear-legend { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s4); margin-top: var(--s3); font-size: .76rem; color: var(--muted); }
.gear-legend b { font-weight: 700; color: var(--text-dim); }
.gear-legend .gk { display: inline-block; width: 15px; height: 15px; border-radius: 4px; vertical-align: -3px; margin-right: 7px; background: #2a2440; box-shadow: 0 0 0 2px #e6b94e; }
.gear-legend .gk-img { width: 16px; height: 16px; vertical-align: -3px; margin-right: 6px; }
/* item picker grid */
.gear-grid-head { display: flex; align-items: center; gap: 7px; font-size: .8rem; font-weight: 800; color: var(--text-dim); margin: var(--s3) 0 var(--s2); }
.gear-grid-head img { opacity: .9; }
.gear-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)); gap: var(--s2); }
.gear-pick { position: relative; aspect-ratio: 1; border-radius: var(--r-md); border: 1px solid var(--border); background: var(--card-2); padding: 4px; cursor: pointer; transition: var(--speed); }
.gear-pick img { width: 100%; height: 100%; object-fit: contain; }
.gear-pick:hover { border-color: var(--accent); }
.gear-pick.sel { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); background: color-mix(in srgb, var(--accent) 12%, var(--card-2)); }
.gear-selected { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s4); font-weight: 800; }
.gear-selected img { border-radius: var(--r-sm); }

/* ── Self profile popup (boxed sections) ───────────────── */
.profile-fields { display: grid; gap: var(--s3); }
.profile-field { background: var(--card-2); border: 1px solid var(--border); border-radius: var(--r-md); padding: 10px var(--s4); }
.profile-field-label { font-size: .66rem; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); font-weight: 700; margin-bottom: 5px; }
.profile-field-value { font-size: .98rem; font-weight: 700; }

/* ── Layout shell ─────────────────────────────────────── */
.shell { max-width: var(--content-max); margin: 0 auto; padding: var(--s5) var(--s5) var(--s8); }
.view { animation: fade .25s ease; }
@keyframes fade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s4); margin-bottom: var(--s5); flex-wrap: wrap; }
.page-head h1 { font-size: 1.5rem; font-weight: 800; letter-spacing: -.01em; }
.page-head p { color: var(--muted); font-size: .9rem; margin-top: 2px; }

/* mobile bottom nav */
.tabbar { display: none; }

/* ── Cards ────────────────────────────────────────────── */
.card {
  background: var(--card); border: 1px solid var(--border-soft); border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
}
.card.pad { padding: var(--s5); }
.card-title { font-weight: 700; font-size: .95rem; display: flex; align-items: center; gap: var(--s2); }

/* ── Buttons ──────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  padding: 10px 18px; border-radius: var(--r-md); font-weight: 700; font-size: .9rem;
  border: 1px solid var(--border); background: var(--card-2); color: var(--text);
  transition: var(--speed); white-space: nowrap;
}
.btn:hover { border-color: var(--accent); background: var(--card-hover); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn.primary { background: var(--accent-grad); color: #0b0c14; border-color: transparent; }
.btn.primary:hover { filter: brightness(1.06); }
.btn.ghost { background: transparent; }
.btn.good { color: var(--good); border-color: rgba(61,220,151,.4); background: rgba(61,220,151,.1); }
.btn.bad  { color: var(--bad);  border-color: rgba(255,107,129,.4); background: rgba(255,107,129,.1); }
.btn.sm { padding: 6px 12px; font-size: .8rem; border-radius: var(--r-sm); }
.btn.block { width: 100%; }

/* ── Pills / badges ───────────────────────────────────── */
.pill {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: var(--r-pill);
  font-size: .72rem; font-weight: 800; letter-spacing: .2px; text-transform: uppercase;
  background: var(--card-2); border: 1px solid var(--border); color: var(--text-dim);
}
.pill.gold { color: var(--gold); border-color: rgba(255,206,107,.35); background: rgba(255,206,107,.1); }
.pill.good { color: var(--good); border-color: rgba(61,220,151,.35); background: rgba(61,220,151,.1); }
.pill.bad  { color: var(--bad);  border-color: rgba(255,107,129,.35); background: rgba(255,107,129,.1); }
.rar { padding: 3px 9px; border-radius: var(--r-pill); font-size: .68rem; font-weight: 800; }
/* One rule per rung, driven off the rarity tokens. The PLUS rungs matter: equipment rarities are
   `MythicPlusFour`, `LegendaryPlusThree` and so on, and while those had no rule they fell through to
   the bare .rar and rendered as plain white text next to correctly coloured rune pills. */
.rar-Common, .rar-CommonPlusOne, .rar-CommonPlusTwo { background: color-mix(in srgb, var(--common) 16%, transparent); color: var(--common); }
.rar-Great, .rar-GreatPlusOne, .rar-GreatPlusTwo { background: color-mix(in srgb, var(--great) 16%, transparent); color: var(--great); }
.rar-Rare, .rar-RarePlusOne, .rar-RarePlusTwo { background: color-mix(in srgb, var(--rare) 16%, transparent); color: var(--rare); }
.rar-Epic, .rar-EpicPlusOne, .rar-EpicPlusTwo { background: color-mix(in srgb, var(--epic) 16%, transparent); color: var(--epic); }
.rar-Legendary, .rar-LegendaryPlusOne, .rar-LegendaryPlusTwo, .rar-LegendaryPlusThree { background: color-mix(in srgb, var(--legendary) 16%, transparent); color: var(--legendary); }
.rar-Mythic { background: color-mix(in srgb, var(--mythic) 16%, transparent); color: var(--mythic); }
.rar-MythicPlusOne { background: color-mix(in srgb, var(--mythic-1) 16%, transparent); color: var(--mythic-1); }
.rar-MythicPlusTwo { background: color-mix(in srgb, var(--mythic-2) 16%, transparent); color: var(--mythic-2); }
.rar-MythicPlusThree { background: color-mix(in srgb, var(--mythic-3) 16%, transparent); color: var(--mythic-3); }
.rar-MythicPlusFour { background: color-mix(in srgb, var(--mythic-4) 16%, transparent); color: var(--mythic-4); }

/* ── Guild popup metric badges (icon-led, color per metric via --gc) ── */
.gstats { display: grid; grid-template-columns: repeat(auto-fill, minmax(134px, 1fr)); gap: 10px; margin-bottom: var(--s3); }
.gstat { display: flex; align-items: center; gap: 10px; background: var(--card-2); border: 1px solid var(--border-soft); border-radius: var(--r-md); padding: 8px 11px; min-width: 0; transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease; }
.gstat:hover { border-color: color-mix(in srgb, var(--gc, var(--accent)) 50%, var(--border-soft)); transform: translateY(-1px); box-shadow: 0 5px 16px rgba(0,0,0,.28); }
.gstat-ic { flex: none; width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center; color: var(--gc, var(--accent)); background: color-mix(in srgb, var(--gc, var(--accent)) 15%, transparent); }
.gstat-t { min-width: 0; overflow: hidden; }
.gstat-l { font-size: .58rem; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); font-weight: 700; line-height: 1; margin-bottom: 4px; white-space: nowrap; }
.gstat-v { font-weight: 800; font-size: 1.04rem; line-height: 1; color: var(--gc, var(--text)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gstat-copy { cursor: pointer; }
.gstat-copy:active { transform: translateY(0) scale(.98); }

/* ── Raid season-end timer card (board) ─────────────────── */
.raidtimer { display: grid; gap: 12px; margin-top: var(--s5); }
.rt-top { display: flex; align-items: center; gap: 13px; }
.rt-ic { flex: none; width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; color: var(--gold); background: color-mix(in srgb, var(--gold) 16%, transparent); }
.rt-label { font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 700; margin-bottom: 4px; }
.rt-count { font-size: 1.55rem; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; line-height: 1; transition: color .3s ease; }
.rt-bar { height: 6px; border-radius: 999px; background: var(--card-2); overflow: hidden; }
.rt-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, color-mix(in srgb, var(--gold) 65%, var(--accent)), var(--gold)); transition: width 1s linear; }
.rt-day { display: flex; align-items: center; gap: 6px; font-size: .8rem; color: var(--muted); }
.rt-day svg { color: var(--gold); opacity: .85; }

/* ── Stat grid (guild header) ─────────────────────────── */
.statgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s3); }
.stat { background: var(--card-2); border: 1px solid var(--border-soft); border-radius: var(--r-md); padding: var(--s3) var(--s4); }
.stat .k { font-size: .72rem; color: var(--muted); font-weight: 700; text-transform: uppercase; letter-spacing: .3px; }
.stat .v { font-size: 1.3rem; font-weight: 800; margin-top: 2px; }
.stat .sub { font-size: .74rem; color: var(--text-dim); margin-top: 1px; }

/* ── Guild hero banner (Raid view) ────────────────────── */
.banner { position: relative; overflow: hidden; border-radius: var(--r-xl); border: 1px solid var(--border-soft); background: var(--card); margin-bottom: var(--s5); }
.banner::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 160px; pointer-events: none;
  background:
    radial-gradient(620px 220px at 18% -50%, rgba(139,124,255,.40), transparent 70%),
    radial-gradient(520px 220px at 92% -60%, rgba(92,200,255,.30), transparent 70%);
}
.banner-inner { position: relative; padding: var(--s5); }
.banner-top { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s4); margin-bottom: var(--s4); }
.banner h1 { font-size: 1.7rem; font-weight: 900; letter-spacing: -.01em; line-height: 1.1; }
/* The banner title as a guild switcher. Styled as the heading it already was, so the only new signal is
   the caret: the name must not start looking like a button on a page where you have nothing to switch
   to, and guildTitle returns a plain h1 in that case. */
.gsw { position: relative; display: inline-block; }
.gsw-btn { display: inline-flex; align-items: center; gap: 8px; padding: 0; border: none; background: none;
  color: inherit; font: inherit; text-align: left; cursor: pointer; }
.gsw-btn h1 { display: inline; }
.gsw-caret { display: inline-flex; color: var(--muted); transition: color var(--speed); }
.gsw-btn:hover .gsw-caret, .gsw-btn[aria-expanded="true"] .gsw-caret { color: var(--text); }
/* FIXED and appended to the body, not absolute inside .banner: .banner is overflow:hidden, which
   clipped the first version so rows past its edge could not be clicked. Top and left are set in JS from
   the title's rect, and clamped to the viewport, so the cap here only has to leave room for that clamp. */
.gsw-menu { position: fixed; z-index: 60; min-width: 240px; display: flex;
  max-width: min(340px, calc(100vw - 16px)); flex-direction: column; gap: 2px; padding: 6px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); box-shadow: var(--shadow-2); }
.gsw-opt { display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 10px; border: none;
  background: none; border-radius: var(--r-md); color: var(--text-dim); font: inherit; font-size: .88rem;
  font-weight: 700; text-align: left; cursor: pointer; transition: var(--speed); }
.gsw-opt:hover { background: var(--card-2); color: var(--text); }
.gsw-opt.on { background: var(--card-2); color: var(--text); }
.gsw-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gsw-tag { flex: none; font-size: .64rem; font-weight: 800; letter-spacing: .03em; text-transform: uppercase; color: var(--muted); }
.gsw-tick { flex: none; display: inline-flex; color: var(--accent); }
.banner .sub { color: var(--text-dim); font-size: .85rem; margin-top: 3px; }
.league-pill {
  display: inline-flex; align-items: center; gap: 8px; padding: 7px 14px; border-radius: var(--r-pill);
  font-weight: 800; font-size: .82rem; margin-top: var(--s3);
  background: linear-gradient(135deg, rgba(255,206,107,.22), rgba(255,206,107,.08));
  border: 1px solid rgba(255,206,107,.4); color: var(--gold);
}
.league-pill.up   { background: linear-gradient(135deg, rgba(61,220,151,.22), rgba(61,220,151,.06)); border-color: rgba(61,220,151,.4); color: var(--good); }
.league-pill.down { background: linear-gradient(135deg, rgba(255,107,129,.22), rgba(255,107,129,.06)); border-color: rgba(255,107,129,.4); color: var(--bad); }

/* ── Skeleton table rows ──────────────────────────────── */
.skel-line { height: 13px; border-radius: 6px; }
table.tbl td .skel-line { width: 100%; }

/* ── Tables ───────────────────────────────────────────── */
/* The wrap is its own scroll region (both axes), so the header can stick to the TOP of the
   region (top:0) and the column labels stay visible on long lists, with no overlap. */
.tbl-wrap {
  overflow-x: auto;   /* horizontal scroll only when a table is too wide (mobile); rows use the page scrollbar */
  border-radius: var(--r-lg); border: 1px solid var(--border-soft);
}
table.tbl { width: 100%; border-collapse: collapse; font-size: .88rem; }
table.tbl thead th {
  background: var(--card-2); color: var(--text-dim);
  text-align: left; font-weight: 700; font-size: .76rem; text-transform: uppercase; letter-spacing: .4px;
  padding: 11px 14px; border-bottom: 1px solid var(--border); white-space: nowrap; cursor: pointer; user-select: none;
}
table.tbl thead th.r { text-align: right; }
table.tbl thead th .arr {
  display: inline-block; color: var(--accent); margin-left: 6px;
  font-size: 1.2em; font-weight: 900; line-height: 0; vertical-align: middle;
  text-shadow: 0 0 9px rgba(139,124,255,.6);
}
table.tbl thead th.sorted { color: var(--text); }
table.tbl tbody td { padding: 11px 14px; border-bottom: 1px solid var(--border-soft); }
table.tbl tbody tr { transition: background var(--speed); }
table.tbl tbody tr:hover { background: var(--card-2); }
table.tbl td.r { text-align: right; font-variant-numeric: tabular-nums; }
table.tbl td.name { font-weight: 600; }
/* per-account pin toggle in the guild name cell (gold = pinned) */
/* The pin star's own look, so it can be used outside a ranking row (the popup heads put one in the
   modal head). The table rule below keeps the float and spacing a name cell needs. */
.pin-star { background: none; border: 0; padding: 4px; cursor: pointer; color: var(--muted); line-height: 0; border-radius: 8px; opacity: .8; transition: color .15s, opacity .15s, background .15s; }
.pin-star:hover { color: var(--text); opacity: 1; background: var(--card-2); }
.pin-star.on { color: var(--gold); opacity: 1; }
.pin-star-lg svg { width: 18px; height: 18px; }
.modal-acts { display: flex; align-items: center; gap: 2px; flex: none; }
table.tbl td.name .pin-star { float: right; background: none; border: 0; padding: 2px 3px; margin-left: 6px; cursor: pointer; color: var(--muted); line-height: 0; border-radius: 6px; opacity: .5; transition: color var(--speed), opacity var(--speed), background var(--speed); }
table.tbl td.name .pin-star:hover { color: var(--text); opacity: 1; background: var(--card-2); }
table.tbl td.name .pin-star.on { color: var(--gold); opacity: 1; }
/* combined two-line cell: a main value with a small sub-line (power+growth, raid+rank) */
.cell-sub { display: block; font-size: .72rem; color: var(--muted); margin-top: 1px; font-weight: 500; }
/* Arena filler account. Deliberately loud enough to read at a glance in a list of names, because
   the whole point is answering "is the player in my bracket real?" — bot names collide with real
   ones. Not a link and not clickable on its own; the row still opens the profile. */
.bot-tag {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 6px;
  font-size: .62rem; font-weight: 800; letter-spacing: .5px; vertical-align: middle;
  color: var(--bad); background: color-mix(in srgb, var(--bad) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--bad) 38%, transparent);
}
/* dual-sort header: two clickable metrics in one column header */
.hsort {
  cursor: pointer; display: inline-flex; align-items: center; gap: 3px;
  padding: 3px 8px; border-radius: 7px; opacity: .5; font-weight: 700;
  transition: background var(--speed), color var(--speed), opacity var(--speed);
}
.hsort:hover { opacity: .9; background: var(--card-hover); }
.hsort.on { opacity: 1; color: var(--accent); background: rgba(139,124,255,.18); box-shadow: inset 0 0 0 1px rgba(139,124,255,.45); }
.hsort .arr { margin-left: 1px; font-size: 1.05em; }
.hsep { opacity: .2; margin: 0; }
/* page-size dropdown */
.psize { background: var(--card); border: 1px solid var(--border); border-radius: var(--r-md); padding: 8px 10px; color: var(--text-dim); font-size: .82rem; cursor: pointer; outline: none; }
.psize:focus { border-color: var(--accent); }
.role-tag { font-size: .64rem; font-weight: 800; padding: 1px 6px; border-radius: 5px; margin-left: 6px; vertical-align: middle; text-transform: uppercase; letter-spacing: .3px; }
.role-tag.role-leader { background: rgba(255,206,107,.16); color: var(--gold); }
.role-tag.role-vice { background: rgba(92,200,255,.16); color: var(--accent-2); }
.bulkbar {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  padding: 11px 16px; margin-bottom: var(--s4);
  background: var(--card); border: 1px solid var(--border-soft); border-radius: var(--r-md);
  font-size: .85rem;
}
.bulkbar input { width: 90px; padding: 7px 10px; }

/* ── Build version chip ───────────────────────────────── */
.verbar {
  position: fixed; bottom: 12px; left: calc(var(--sb-w) + 12px); z-index: 70;
  display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: var(--r-pill);
  background: var(--card); border: 1px solid var(--border-soft); color: var(--muted);
  font-family: var(--mono); font-size: .72rem; font-weight: 700; box-shadow: var(--shadow-1);
  transition: var(--speed);
}
.verbar .ver-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex: none; }
.verbar-help { display: inline-grid; place-items: center; width: 15px; height: 15px; border-radius: 50%;
  background: var(--border-soft); color: var(--text-dim); font-size: .62rem; font-weight: 800; flex: none; transition: var(--speed); }
.verbar-help:hover { background: var(--accent); color: #0b0c14; }
.verbar.current .ver-dot { background: var(--good); box-shadow: 0 0 8px var(--good); }
.verbar.outdated { color: var(--warn); border-color: rgba(255,180,84,.55); cursor: pointer; }
.verbar.outdated:hover { background: var(--card-2); }
.verbar.outdated .ver-dot { background: var(--warn); box-shadow: 0 0 8px var(--warn); animation: verpulse 1.3s infinite; }
@keyframes verpulse { 50% { opacity: .35; } }
/* clear the mobile tab bar INCLUDING the home indicator safe area, which only appears once the
   browser chrome hides on scroll (that is why the chip looked fine on load but rode onto the nav after scrolling) */
@media (max-width: 760px) { .verbar { bottom: calc(80px + env(safe-area-inset-bottom)); transform: translateY(calc(-1 * var(--vvb, 0px))); } }
/* Floating "refreshing" badge for the raid board: fixed bottom-center so it's prominent but OUT OF
   FLOW (never pushes the guild list down) and pointer-events:none (never blocks taps). Auto-removed
   when the fresh data re-renders the view (g._cached → false). Bottom-center is free — verbar owns
   the left corner, toasts the right. */
.refresh-badge {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%); z-index: 80;
  display: inline-flex; align-items: center; gap: 8px; padding: 9px 16px; border-radius: var(--r-pill);
  background: var(--card-2); border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--border));
  color: var(--text); font-size: .8rem; font-weight: 700; white-space: nowrap;
  box-shadow: 0 6px 22px rgba(0,0,0,.5); pointer-events: none; animation: rbadge-in .28s ease;
}
.refresh-badge .mini-spin { border-top-color: var(--accent); }
@keyframes rbadge-in { from { opacity: 0; transform: translate(-50%, 14px); } to { opacity: 1; transform: translate(-50%, 0); } }
@media (max-width: 760px) { .refresh-badge { bottom: calc(80px + env(safe-area-inset-bottom)); transform: translate(-50%, calc(-1 * var(--vvb, 0px))); } }
/* A second floating badge (applicants loading) stacks ABOVE the global refresh chip so they never overlap. */
.refresh-badge.rb-stack { bottom: 74px; }
@media (max-width: 760px) { .refresh-badge.rb-stack { bottom: calc(140px + env(safe-area-inset-bottom)); } }
/* Per-member load-in: a member row whose power isn't fresh yet — the whole board is still the cached
   snapshot, or this row is mid-stream — dims and carries a spinner, then clears the instant its fresh
   power lands, so the roster visibly fills in row by row (replaces the old whole-roster .is-cache grey). */
.tbl tr.row-loading { opacity: .5; transition: opacity var(--speed); }
/* Mobile Rankings tab — segmented Guilds | Players toggle above the reused ranking view. */
/* Scrolls sideways in its own box rather than squeezing: with four tabs, `flex: 1` (basis 0) let them
   shrink under their own label on a phone. `1 0 auto` keeps the desktop fill and stops the shrink. */
/* padding-bottom is deliberate: a scroll container clips to its padding box, and the global
   :focus-visible ring is a 3px box-shadow, so with 0 it gets sliced off the focused tab. */
.rankings-seg { display: flex; gap: 6px; padding: var(--s4) var(--s5) 4px; overflow-x: auto; scrollbar-width: none; }
.rankings-seg::-webkit-scrollbar { display: none; }
.rk-tab { flex: 1 0 auto; white-space: nowrap; max-width: 180px; padding: 9px 14px; border-radius: var(--r-pill); border: 1px solid var(--border);
  background: var(--card-2); color: var(--text-dim); font-weight: 700; font-size: .85rem; cursor: pointer; transition: var(--speed); }
.rk-tab.on { background: var(--accent); color: #fff; border-color: transparent; }
.rk-tab:not(.on):hover { color: var(--text); border-color: var(--accent); }

/* Guild header right column: persistent refresh chip above any header buttons. */
.banner-side { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; flex: none; }
.rfr-chip {
  display: inline-flex; align-items: center; gap: 7px; padding: 5px 12px; border-radius: var(--r-pill);
  font-size: .78rem; font-weight: 700; white-space: nowrap; transition: var(--speed);
  border: 1px solid var(--border); background: var(--card-2); color: var(--text-dim);
}
button.rfr-chip { cursor: pointer; }
button.rfr-chip:hover { color: var(--text); border-color: var(--accent); }
.rfr-chip.busy { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); color: var(--text); }
.rfr-chip .mini-spin { border-top-color: var(--accent); }
.rank-pill { display: inline-grid; place-items: center; min-width: 26px; height: 26px; padding: 0 6px; border-radius: 8px; font-weight: 800; font-size: .8rem; background: var(--card-hover); }
.rank-1 { background: rgba(255,206,107,.18); color: var(--gold); }
.rank-2 { background: rgba(200,210,230,.16); color: #cbd3e6; }
.rank-3 { background: rgba(205,127,75,.18); color: #e0a06a; }

/* ── Segmented control ────────────────────────────────── */
.seg { display: inline-flex; padding: 4px; gap: 2px; background: var(--card); border: 1px solid var(--border-soft); border-radius: var(--r-pill); }
.seg button { padding: 7px 16px; border-radius: var(--r-pill); font-weight: 600; font-size: .85rem; color: var(--text-dim); }
.seg button.active { background: var(--card-hover); color: var(--text); }

/* ── Inputs ───────────────────────────────────────────── */
/* .inp is the search box cgPicker builds (cluster invite, promote, the recruit picker), the cluster name
   input and the Share access textarea; it never had a rule of its own and rendered as a bare white
   field, so it shares this one. */
.input, .inp {
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 10px 14px; color: var(--text); outline: none; transition: var(--speed); width: 100%;
  box-sizing: border-box;   /* keep padding + border INSIDE width:100% so fields never overflow their container */
}
.input:focus, .inp:focus { border-color: var(--accent); box-shadow: var(--ring); }
.search { position: relative; }
.search svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--muted); }
.search input { padding-left: 38px; }

/* ── Jump-to-page input (pager) ── */
.pgjump { display: inline-flex; align-items: center; gap: 6px; }
.pgjump-in { width: 3.4em; text-align: center; padding: 5px 6px; border-radius: var(--r-md); border: 1px solid var(--border); background: var(--card-2); color: var(--text); font-size: .85rem; font-variant-numeric: tabular-nums; -moz-appearance: textfield; appearance: textfield; }
.pgjump-in:focus { outline: none; border-color: var(--accent); background: var(--card); }
.pgjump-in::-webkit-outer-spin-button, .pgjump-in::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ── Tappable power sparkline (opens the exact-values popup) ── */
.spark-btn { display: block; width: 100%; padding: 2px; background: none; border: 1px solid transparent; border-radius: var(--r-md); cursor: pointer; text-align: left; transition: var(--speed); }
.spark-btn:hover, .spark-btn:focus-visible { border-color: var(--border); background: var(--card-2); outline: none; }
.spark-hint { text-align: right; font-size: .66rem; color: var(--muted); margin-top: 1px; }
.spark-btn:hover .spark-hint, .spark-btn:focus-visible .spark-hint { color: var(--accent); }

/* ── Modal ────────────────────────────────────────────── */
.scrim {
  position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; padding: var(--s4);
  background: rgba(5,6,12,.6); backdrop-filter: blur(4px); animation: fade .2s;
}
.modal {
  width: min(560px, 100%); max-height: 88dvh; overflow: auto;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-xl); box-shadow: var(--shadow-3);
  animation: pop .22s ease;
}
.modal.modal-wide { width: min(640px, 100%); }   /* roomier form modals (e.g. Log a bug) so nothing scrolls */
.modal.modal-xwide { width: min(860px, 100%); }  /* data-table modals (guild roster: up to 6 columns) so the table fits without a sideways scroll */
@keyframes pop { from { opacity: 0; transform: scale(.96) translateY(8px); } to { opacity: 1; transform: none; } }
/* Sticky: .modal is the scroll container, so the head (title, pin star, close X) stays put however
   far a long popup scrolls. Livo: at the bottom of a player popup the X used to be off screen. */
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding: var(--s5) var(--s5) var(--s4); border-bottom: 1px solid var(--border-soft);
  position: sticky; top: 0; z-index: 4; background: var(--card); border-radius: var(--r-xl) var(--r-xl) 0 0; }
.modal-body { padding: var(--s5); }
/* tables inside a modal must NOT add their own scroll region — the modal scrolls as one unit */
.modal .tbl-wrap { max-height: none; overflow: visible; border: 0; border-radius: 0; }
.modal table.tbl thead th { position: static; }
/* Narrow screens: tighten cell padding so many-column modal tables (the guild roster) fit without a sideways scroll. */
@media (max-width: 640px) { .modal table.tbl tbody td, .modal table.tbl thead th { padding-left: 8px; padding-right: 8px; } }

/* Command palette (Ctrl/Cmd+K): jump-anywhere search */
.cmdk-scrim { position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,.5); display: flex; align-items: flex-start; justify-content: center; padding: 12vh 16px 16px; animation: pop .12s ease; }
.cmdk-box { width: min(560px, 100%); max-height: 70vh; display: flex; flex-direction: column; overflow: hidden; background: var(--card); border: 1px solid var(--border); border-radius: var(--r-xl); box-shadow: var(--shadow-3); }
.cmdk-input { box-sizing: border-box; width: 100%; padding: 15px 18px; border: 0; border-bottom: 1px solid var(--border-soft); background: transparent; color: var(--text); font-size: 1rem; outline: none; }
.cmdk-input::placeholder { color: var(--muted); }
.cmdk-list { overflow-y: auto; padding: 6px; }
.cmdk-item { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 10px 12px; border-radius: 8px; cursor: pointer; }
.cmdk-item.sel { background: var(--card-hover); }
.cmdk-label { color: var(--text); font-weight: 600; }
.cmdk-sub { flex: none; color: var(--muted); font-size: .72rem; text-transform: uppercase; letter-spacing: .4px; }
.cmdk-empty { padding: 16px; text-align: center; color: var(--muted); font-size: .9rem; }

/* ── Toasts ───────────────────────────────────────────── */
.toasts { position: fixed; bottom: var(--s5); right: var(--s5); z-index: 200; display: flex; flex-direction: column; gap: var(--s2); }
.toast {
  display: flex; align-items: center; gap: var(--s3); padding: 12px 16px; border-radius: var(--r-md);
  background: var(--card-2); border: 1px solid var(--border); box-shadow: var(--shadow-2); font-size: .88rem;
  animation: toastin .25s ease; max-width: 380px;
}
.toast.good { border-color: rgba(61,220,151,.4); } .toast.bad { border-color: rgba(255,107,129,.4); }
.toast.warn { border-color: rgba(255,180,84,.45); }
@keyframes toastin { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: none; } }

/* ── Empty / loading states ───────────────────────────── */
.state { display: grid; place-items: center; gap: var(--s3); padding: var(--s8) var(--s4); color: var(--muted); text-align: center; }

/* ── Guide content (inline, replaces iframe) ──────────── */
/* `clip`, not `hidden`: hidden makes the host a scroll container, and a guide's sticky section bar
   then sticks to the host instead of the window (measured: -2464px at scrollY 3000). clip still clips
   to the rounded corners and creates no scroll container, so the bar rides under the topbar. */
.guide-host { background: var(--card); border: 1px solid var(--border-soft); border-radius: var(--r-lg); overflow: clip; }

/* ── Responsive ───────────────────────────────────────── */
@media (max-width: 760px) {
  /* Mobile nav: a thumb-reachable bottom tab bar for the primary destinations, plus a "More" (☰) tab
     that raises the FULL grouped nav as a bottom sheet (guild sub-pages, Patch Notes, Studio). Same
     sidebar DOM, re-anchored to the bottom edge right next to its trigger. */
  .app-shell { display: block; }
  body.drawer-open { overflow: hidden; }
  /* The desktop rail becomes an off-canvas bottom sheet. */
  .sidebar {
    position: fixed; inset: auto 0 0 0; height: auto; max-height: 82vh; width: auto; z-index: 70;
    border: none; border-top: 1px solid var(--border-soft); border-radius: 18px 18px 0 0;
    background: rgba(11,12,20,.97); box-shadow: 0 -14px 44px rgba(0,0,0,.55);
    padding: 16px var(--s3) calc(12px + env(safe-area-inset-bottom));
    transform: translateY(106%); transition: transform .3s cubic-bezier(.22,.61,.36,1);
  }
  body.drawer-open .sidebar { transform: translateY(0); }
  .sidebar::before {   /* grabber affordance */
    content: ''; position: absolute; top: 7px; left: 50%; transform: translateX(-50%);
    width: 40px; height: 4px; border-radius: 999px; background: var(--border);
  }
  .sidebar .sb-collapse { display: none; }   /* collapse is a desktop-rail control */
  .sidebar .sb-brand { padding-top: 6px; }
  .sb-scrim {
    display: block; position: fixed; inset: 0; z-index: 65; background: rgba(0,0,0,.55);
    opacity: 0; pointer-events: none; transition: opacity .25s;
  }
  body.drawer-open .sb-scrim { opacity: 1; pointer-events: auto; }
  /* gap 6px, and no brand logo in this bar any more (topbar() in app.js says why): with the back
     button in the left corner the signed-in cluster overflowed 375px by 14px and 360px by 19px. */
  .topbar { padding: 0 var(--s3); gap: 6px; z-index: 55; background: rgba(11,12,20,.85); }
  .topbar .tb-right { gap: 2px; }
  .topbar .tb-right .icon-btn, .topbar .tb-back { width: 34px; height: 34px; }   /* compact: back button + the whole cluster fit 360px with the install button showing */
  .topbar .profile .who { display: none; }
  .verbar { left: 12px; }
  /* enough bottom room that the last row + pagination clear BOTH the fixed tab bar and the version chip,
     plus the safe area that appears when the browser chrome hides on scroll */
  .shell { padding: var(--s4) var(--s4) calc(120px + env(safe-area-inset-bottom)); }
  .tabbar {
    display: flex; position: fixed; bottom: 0; left: 0; right: 0; z-index: 60;
    background: rgba(11,12,20,.9); backdrop-filter: blur(14px); border-top: 1px solid var(--border-soft);
    padding: 6px 6px calc(6px + env(safe-area-inset-bottom));
    /* track iOS Safari's dynamic bottom toolbar (see setupViewportNav) so the bar hugs the visible bottom */
    transform: translateY(calc(-1 * var(--vvb, 0px)));
    /* a few tabs fill the width; many (Livo's Studio group) scroll sideways instead of cramming */
    overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .tabbar::-webkit-scrollbar { display: none; }
  .tabbar a, .tabbar .tab-more { flex: 1 0 auto; min-width: 58px; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 6px 4px; color: var(--muted); font-size: .68rem; font-weight: 600; border-radius: var(--r-md); background: none; border: 0; cursor: pointer; font-family: inherit; }
  .tabbar a.active, .tabbar .tab-more:hover, .tabbar .tab-more:active { color: var(--accent); background: var(--card); }
  .tabbar a svg, .tabbar .tab-more svg { width: 22px; height: 22px; }
}

/* ── Guild chat dock (persistent, every page) ─────────────────────────── */
.cw { display: none; }
.cw.cw-on { display: block; }
.cw-fab {
  position: fixed; right: 20px; bottom: calc(20px + var(--vvb, 0px)); z-index: 70;
  width: 56px; height: 56px; border-radius: 999px; border: none; cursor: pointer; color: #0b0c14;
  background: var(--accent-grad, var(--accent)); display: grid; place-items: center;
  box-shadow: var(--shadow-2); transition: transform var(--speed, .15s);
}
.cw-fab:hover { transform: translateY(-2px); }
.cw.cw-open .cw-fab { display: none; }
.cw-badge {
  position: absolute; top: -3px; right: -3px; min-width: 20px; height: 20px; padding: 0 5px; box-sizing: border-box;
  border-radius: 999px; background: var(--bad); color: #fff; font-size: .66rem; font-weight: 800;
  display: grid; place-items: center; box-shadow: 0 0 0 2px #0b0c14;
}
.cw-panel {
  display: none; position: fixed; right: 20px; bottom: calc(20px + var(--vvb, 0px)); z-index: 71;
  width: 360px; max-width: calc(100vw - 32px); height: 520px; max-height: calc(100dvh - 96px);
  flex-direction: column; overflow: hidden;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-3, var(--shadow-2));
}
.cw.cw-open .cw-panel { display: flex; animation: pop .18s ease; }
.cw-head { display: flex; align-items: center; gap: 8px; padding: 11px 14px; border-bottom: 1px solid var(--border-soft, var(--border)); color: var(--text); }
.cw-head > svg { color: var(--accent); flex: none; }
.cw-title { font-weight: 800; font-size: .92rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cw-sp { flex: 1; }
.cw-x { background: none; border: none; color: var(--muted); cursor: pointer; display: inline-flex; padding: 3px; border-radius: 8px; }
.cw-x:hover { color: var(--text); background: var(--card-2); }
.cw-list { flex: 1; min-height: 0; overflow-y: auto; padding: 12px 12px 8px; display: flex; flex-direction: column; gap: 2px; }
.cw-empty { margin: auto; color: var(--muted); font-size: .85rem; text-align: center; }
.cw-sys { align-self: center; color: var(--muted); font-size: .72rem; padding: 4px 0; text-align: center; max-width: 90%; }
.cw-msg { display: flex; flex-direction: column; align-items: flex-start; margin-top: 7px; max-width: 86%; }
.cw-msg.me { align-self: flex-end; align-items: flex-end; }
.cw-name { font-size: .68rem; font-weight: 700; color: var(--accent-2, var(--accent)); margin: 0 0 2px 3px; }
.cw-bubble { background: var(--card-2); border: 1px solid var(--border); border-radius: 13px; padding: 6px 11px; font-size: .87rem; color: var(--text); line-height: 1.38; word-break: break-word; white-space: pre-wrap; }
.cw-msg.me .cw-bubble { background: color-mix(in srgb, var(--accent) 24%, var(--card-2)); border-color: color-mix(in srgb, var(--accent) 38%, var(--border)); }
.cw-time { font-size: .6rem; color: var(--muted); margin-left: 8px; }
.cw-err:not(:empty) { color: var(--bad); font-size: .73rem; padding: 5px 14px 0; }
.cw-bar { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); border-top: 1px solid var(--border-soft, var(--border)); }
.cw-input { flex: 1; resize: none; max-height: 110px; min-height: 38px; background: var(--card-2); border: 1px solid var(--border); border-radius: 12px; color: var(--text); padding: 9px 12px; font: inherit; font-size: .87rem; line-height: 1.35; }
.cw-input:focus { outline: none; border-color: var(--accent); }
.cw-send { flex: none; width: 38px; height: 38px; border-radius: 11px; border: none; cursor: pointer; background: var(--accent); color: #0b0c14; display: grid; place-items: center; }
.cw-send:hover { filter: brightness(1.06); }
.cw-send:disabled { opacity: .5; cursor: default; }
/* Opt-in gate (shown instead of the list+composer until the member turns chat on) */
.cw-optin { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 22px 22px 24px; }
.cw-optin-ico { width: 58px; height: 58px; border-radius: 999px; display: grid; place-items: center; color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); margin-bottom: 10px; }
.cw-optin-h { margin: 0; font-size: 1.04rem; font-weight: 800; color: var(--text); }
.cw-optin-p { margin: 6px 0 12px; font-size: .82rem; line-height: 1.5; color: var(--muted); max-width: 290px; }
.cw-optin-btn { width: 100%; max-width: 260px; padding: 11px 16px; border: none; border-radius: 12px; cursor: pointer; font: inherit; font-weight: 800; font-size: .9rem; color: #0b0c14; background: var(--accent-grad, var(--accent)); box-shadow: var(--shadow-2); transition: transform var(--speed, .15s), filter var(--speed, .15s); }
.cw-optin-btn:hover { transform: translateY(-1px); filter: brightness(1.05); }
.cw-optin-btn:disabled { opacity: .6; cursor: default; transform: none; }
.cw-optin-skip { background: none; border: none; color: var(--muted); cursor: pointer; font: inherit; font-size: .8rem; padding: 9px; margin-top: 4px; }
.cw-optin-skip:hover { color: var(--text); }
.cw-optin-err:not(:empty) { padding: 6px 0 0; text-align: center; }
.cw-prompt p { margin: 0; font-size: .9rem; line-height: 1.5; color: var(--text); }
/* Guild banner action buttons (Sheets export + Link Discord chat), stacked top-right */
.banner-btns { display: flex; flex-direction: column; gap: 6px; align-items: flex-end; flex: none; }
.banner-btns .btn { white-space: nowrap; }
/* Theme picker: the palette button in the app bar + the modal grid of theme swatches */
.theme-btn svg { color: var(--accent); }
.theme-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; max-width: 360px; }
.theme-card { display: flex; flex-direction: column; align-items: center; gap: 7px; padding: 12px 8px; cursor: pointer;
  background: var(--card-2); border: 1px solid var(--border); border-radius: var(--r-md); color: var(--text); font: inherit;
  transition: border-color var(--speed), transform var(--speed); }
.theme-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.theme-card.sel { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
.theme-swatch { width: 46px; height: 46px; border-radius: 999px; display: grid; place-items: center; color: #0b0c14; box-shadow: var(--shadow-1); }
.theme-swatch svg { filter: drop-shadow(0 1px 1px rgba(0,0,0,.45)); }
.theme-name { font-size: .8rem; font-weight: 700; }
@media (max-width: 760px) { .theme-grid { grid-template-columns: repeat(2, 1fr); } }

@media (max-width: 760px) {
  .cw-fab { right: 14px; bottom: calc(74px + env(safe-area-inset-bottom) + var(--vvb, 0px)); width: 52px; height: 52px; }
  .cw-panel { right: 8px; left: 8px; top: 64px; width: auto; max-width: none; height: auto;
    bottom: calc(8px + env(safe-area-inset-bottom) + var(--vvb, 0px)); max-height: none; }
}

/* Account Statistics — Sticker Album section */
.rl-bar { height: 7px; border-radius: 999px; background: var(--card-2); border: 1px solid var(--border-soft); overflow: hidden; margin: 6px 0; }
.rl-bar > i { display: block; height: 100%; border-radius: 999px; transition: width var(--speed); }
.stk-grid { display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0 2px; }
.stk-chip { font-size: .7rem; line-height: 1.25; padding: 3px 7px; border-radius: 6px; border: 1px solid var(--border); border-left: 3px solid var(--muted); background: var(--card-2); color: var(--text); white-space: nowrap; }
.stk-chip.miss { opacity: .42; border-style: dashed; color: var(--muted); }
.stk-tiers { display: flex; flex-direction: column; gap: 3px; margin-top: 8px; }
.stk-tier { display: grid; grid-template-columns: 66px 1fr 18px; align-items: center; gap: 8px; font-size: .76rem; padding: 5px 9px; border-radius: 7px; background: var(--card-2); border: 1px solid var(--border-soft); opacity: .5; }
.stk-tier.done { opacity: 1; border-color: color-mix(in srgb, var(--good) 42%, transparent); }
.stk-packs { display: flex; flex-direction: column; gap: 2px; }
.stk-prow { display: grid; grid-template-columns: 1.5fr .6fr 1.5fr 1.1fr; gap: 8px; align-items: center; font-size: .76rem; padding: 5px 9px; border-radius: 6px; }
.stk-prow:not(.head):nth-child(even) { background: var(--card-2); }
.stk-prow.head { color: var(--muted); font-weight: 700; text-transform: uppercase; font-size: .64rem; letter-spacing: .3px; }

/* ── Account Statistics explorer (heroes/pets/gear tiles + the id-keyed tables) ───────── */
.ac-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); gap: var(--s2); }
.ac-tile { background: var(--card-2); border: 1px solid var(--border-soft); border-radius: var(--r-md); padding: 9px 11px; min-width: 0; }
.ac-tile.on { border-color: color-mix(in srgb, var(--gold) 45%, transparent); }
.ac-table { display: flex; flex-direction: column; gap: 2px; }
.ac-trow { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 8px; align-items: baseline; font-size: .78rem; padding: 5px 9px; border-radius: 6px; }
.ac-trow > span:first-child { font-weight: 600; }
.ac-trow > span:not(:first-child) { font-variant-numeric: tabular-nums; }
.ac-trow:not(.head):nth-child(even) { background: var(--card-2); }
.ac-trow.head { color: var(--muted); font-weight: 700; text-transform: uppercase; font-size: .64rem; letter-spacing: .3px; }
.ac-trow.head > span:first-child { font-weight: 700; }
.ac-table.c2 .ac-trow { grid-template-columns: 3fr 1fr; }
.ac-table.c3 .ac-trow { grid-template-columns: 2fr 1fr 1fr; }
.ac-scroll { max-height: 460px; overflow-y: auto; overscroll-behavior: contain; margin-top: 4px; }

/* ── Event calendar (Mission Timeline + Departure Board tabs) ─────────────────────────── */
.cal-toolbar { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.cal-clockchip { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: .74rem; color: var(--text-dim); background: var(--card); border: 1px solid var(--border); border-radius: var(--r-pill); padding: 4px 11px; white-space: nowrap; }
.cal-tabs { display: inline-flex; gap: 4px; background: var(--card); border: 1px solid var(--border); border-radius: var(--r-pill); padding: 4px; }
.cal-tab { font: inherit; font-size: .76rem; font-weight: 700; color: var(--text-dim); background: none; border: none; border-radius: var(--r-pill); padding: 6px 14px; cursor: pointer; white-space: nowrap; }
.cal-tab.on { background: var(--accent); color: #fff; }
.cal-tab:not(.on):hover { color: var(--text); }
.cal-seccount { font-size: .64rem; font-weight: 800; min-width: 17px; height: 17px; padding: 0 5px; display: inline-grid; place-items: center; border-radius: 999px; background: var(--card-2); border: 1px solid var(--border); color: var(--text-dim); }
.cal-trow { display: flex; align-items: center; gap: 8px; min-width: 0; width: 100%; }
.cal-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--cc, var(--accent)); flex: none; }
.cal-dot.pulse { animation: calPulse 2.2s ease-out infinite; }
@keyframes calPulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--cc, var(--accent)) 45%, transparent); }
  70% { box-shadow: 0 0 0 7px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
/* Wraps rather than ellipsises, everywhere. An event name is the one thing on this row that must
   always be readable, and a fixed-width cell plus a growing number of pills cannot guarantee that. */
.cal-name { font-weight: 800; font-size: .92rem; overflow-wrap: anywhere; line-height: 1.25; min-width: 0; flex: 1 1 100%; }
/* The pills sit on their own line under the name once the name needs the width. */
.cal-trow { flex-wrap: wrap; row-gap: 3px; }
/* The status and cadence cells outgrew their columns when the wording got longer ("4d 7h run - no
   auto repeat", "1d on / 7d off - per player"). Let both wrap instead of spilling. */
.cal-bstat, .cal-bcad { white-space: normal; overflow-wrap: anywhere; }
.cal-bcad .cal-chip { white-space: normal; }
.cal-pill { flex: none; font-size: .62rem; font-weight: 800; text-transform: uppercase; letter-spacing: .3px; color: var(--cc, var(--muted)); border: 1px solid color-mix(in srgb, var(--cc, var(--muted)) 45%, transparent); border-radius: 6px; padding: 1px 6px; }
.cal-chips { display: flex; gap: 5px; flex-wrap: wrap; }
.cal-chip { font-size: .62rem; font-weight: 700; color: var(--text-dim); background: var(--card-2); border: 1px solid var(--border); border-radius: var(--r-pill); padding: 2px 8px; white-space: nowrap; }
.cal-chip.gold { color: var(--gold); border-color: rgba(255, 206, 107, .35); }
.cal-chip.myth { color: var(--mythic); border-color: color-mix(in srgb, var(--mythic) 40%, transparent); }
/* "Next prize not announced yet" — deliberately quiet, so an unknown never reads like a prediction. */
.cal-chip.cal-unk { color: var(--muted); border-style: dashed; font-weight: 600; }
/* the one status line both views share (LIVE · ends hh:mm:ss / in 2d 5h / ended 1d ago) */
.cal-status { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: .76rem; white-space: nowrap; }
.cal-status.live { color: var(--good); font-weight: 700; }
.cal-status.soon { color: var(--text-dim); }
.cal-status.done { color: var(--muted); }
.cal-status .warn { color: var(--warn); }
.cal-status .bad { color: var(--bad); }
/* Mission Timeline: sticky gutter + lanes over one axis; the wrap scrolls sideways when narrow */
.cal-tlwrap { overflow-x: auto; }
.cal-tl { position: relative; min-width: 1060px; }
.cal-tlgrid { position: absolute; top: 0; bottom: 0; left: 260px; right: 0; z-index: 0; pointer-events: none; overflow: hidden;
  background-image: linear-gradient(to right, color-mix(in srgb, var(--border) 60%, transparent) 1px, transparent 1px); }
.cal-tlwk { position: absolute; top: 0; bottom: 0; background: color-mix(in srgb, var(--text) 3%, transparent); }
.cal-tltoday { position: absolute; top: 0; bottom: 0; background: color-mix(in srgb, var(--accent) 7%, transparent); }
.cal-tlnow { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--bad); box-shadow: 0 0 6px color-mix(in srgb, var(--bad) 55%, transparent); }
.cal-tlnow span { position: absolute; top: 0; left: -14px; background: var(--bad); color: #fff; font-family: var(--mono); font-size: .56rem; font-weight: 700; text-transform: uppercase; padding: 1px 5px; border-radius: 0 0 4px 4px; }
.cal-tlane { display: flex; min-height: 58px; position: relative; z-index: 1; }
.cal-tlane + .cal-tlane { border-top: 1px solid var(--border); }
.cal-tlane.dim { opacity: .55; }
.cal-tlane.axis { min-height: 26px; }
.cal-tgut { position: sticky; left: 0; z-index: 3; width: 260px; flex: none; box-sizing: border-box; text-align: left; font: inherit; color: var(--text); background: var(--card); border: none; border-right: 1px solid var(--border); padding: 6px 10px; display: flex; flex-direction: column; justify-content: center; gap: 3px; cursor: pointer; min-width: 0; overflow: hidden; }
.cal-tgut:hover { background: var(--card-hover); }
.cal-tlane.axis .cal-tgut { cursor: default; }
.cal-tlane.axis .cal-tgut:hover { background: var(--card); }
.cal-ttrk { position: relative; flex: 1; overflow: hidden; min-width: 0; }
.cal-tlab { position: absolute; top: 6px; transform: translateX(-50%); font-family: var(--mono); font-size: .62rem; color: var(--muted); white-space: nowrap; }
.cal-tlhint { font-family: var(--mono); font-size: .58rem; color: var(--muted); }
.cal-tbar { position: absolute; top: 50%; transform: translateY(-50%); height: 20px; border-radius: 5px; box-sizing: border-box; padding: 0; cursor: pointer; overflow: hidden; font: inherit; display: flex; align-items: center; }
.cal-tbar.live { background: color-mix(in srgb, var(--cc, var(--accent)) 30%, transparent); border: 1.5px solid var(--cc, var(--accent)); }
.cal-tbar.fut { background: color-mix(in srgb, var(--cc, var(--accent)) 15%, transparent); border: 1px solid color-mix(in srgb, var(--cc, var(--accent)) 40%, transparent); }
.cal-tbar.past { background: color-mix(in srgb, var(--cc, var(--accent)) 8%, transparent); border: 1px solid color-mix(in srgb, var(--cc, var(--accent)) 22%, transparent); }
.cal-tbar:hover { filter: brightness(1.3); }
/* a member of a merged lane hidden in Site controls, as Livo previews it (the public never gets the bar) */
.cal-tbar.hid { opacity: .45; }
.cal-tbar.hid.live, .cal-tbar.hid.fut, .cal-tbar.hid.past { border-style: dashed; }
.cal-tbar .cal-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 4px 0 0 4px; background: color-mix(in srgb, var(--cc, var(--accent)) 30%, transparent); transition: width 1s linear; }
.cal-tbarlab { position: relative; z-index: 1; flex: 1; min-width: 0; font-size: .66rem; font-weight: 700; color: var(--text); padding: 0 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-tbar.fut .cal-tbarlab, .cal-tbar.past .cal-tbarlab { color: var(--cc, var(--text-dim)); }
/* Departure Board: one grid row per event, group headers between; identical templates keep columns aligned */
.cal-bwrap { overflow-x: auto; }
/* min-width covers the rows' hard column minimums (975px tracks + 50px gaps + 20px padding) */
.cal-board { min-width: 1045px; display: flex; flex-direction: column; }
.cal-brow { display: grid;
  grid-template-columns: minmax(300px, 2.1fr) 130px minmax(200px, 1.15fr) minmax(120px, .95fr) minmax(140px, 1fr);
  grid-template-areas: 'ev mini win cad rew' 'stat mini win cad rew';
  gap: 4px 10px; align-items: center; padding: 7px 10px; border: none; background: none; font: inherit; color: var(--text); text-align: left; cursor: pointer; border-radius: 8px; min-width: 0; }
.cal-bev  { grid-area: ev; min-width: 0; }
.cal-bstat{ grid-area: stat; min-width: 0; }
.cal-bmini{ grid-area: mini; } .cal-bwin { grid-area: win; }
.cal-bcad { grid-area: cad; }  .cal-brew { grid-area: rew; }
button.cal-brow:hover { background: var(--card-hover); }
.cal-brow.head { cursor: default; color: var(--muted); font-size: .6rem; font-weight: 800; text-transform: uppercase; letter-spacing: .5px; padding-bottom: 2px; }
.cal-brow.dim { opacity: .55; }
.cal-ghead { display: flex; align-items: center; gap: 7px; font-size: .66rem; font-weight: 800; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); padding: 12px 10px 4px; border-bottom: 1px solid var(--border); margin-bottom: 2px; }
.cal-ghead.liveh { color: var(--good); }
.cal-bwin { font-family: var(--mono); font-size: .68rem; color: var(--muted); white-space: nowrap; }
/* Also carries the "was: A, B, C" rotation history, which inherits the row's font size without an
   explicit one — on mobile that rendered at body size and spilled over the cadence chip. */
.cal-bdim { color: var(--muted); font-size: .62rem; line-height: 1.5; min-width: 0; overflow-wrap: anywhere; }
.cal-brew { display: flex; gap: 5px; flex-wrap: wrap; min-width: 0; }
.cal-mini { position: relative; height: 12px; border-radius: 4px; background: var(--card-2); border: 1px solid var(--border); overflow: hidden; }
.cal-minibar { position: absolute; top: 2px; bottom: 2px; border-radius: 2px; background: color-mix(in srgb, var(--cc, var(--accent)) 45%, transparent); }
.cal-minibar.live { background: var(--cc, var(--accent)); }
.cal-mininow { position: absolute; left: 0; top: 0; bottom: 0; width: 2px; background: var(--bad); }
/* the 21-day rush strip (mobile board header card; same geometry as the old rail strip) */
.cal-mstriphead { font-size: .66rem; font-weight: 800; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); margin-bottom: 14px; }
.cal-rstrip { position: relative; height: 42px; }
.cal-rnow { position: absolute; left: 0; top: 13px; height: 12px; width: 2px; background: var(--accent); }
.cal-rnow span { position: absolute; top: -14px; left: -3px; font-family: var(--mono); font-size: .58rem; color: var(--accent); }
.cal-rblock { position: absolute; top: 14px; height: 10px; border-radius: 3px; background: var(--cc, var(--accent)); border: none; padding: 0; cursor: pointer; }
.cal-rlabel { position: absolute; top: 28px; font-family: var(--mono); font-size: .6rem; color: var(--muted); white-space: nowrap; }
.cal-foot { font-size: .72rem; margin-top: var(--s4); }
.cal-past td { opacity: .5; }
@media (max-width: 720px) {
  /* phones: the board IS the calendar (JS forces it) — tabs gone, rows reflow to two-line cards */
  .cal-tabs { display: none; }
  .cal-board { min-width: 0; }
  /* The rewards cell sits in the `auto` column, so once it carries the rotation history it grows
     until the 1fr column collapses to nothing — the event name and cadence chip were measuring 0px
     wide. Give the rewards their own full-width row instead of racing the cadence chip for space. */
  /* The status ("LIVE - ends 09:27:57") sat in the `auto` column next to the name's minmax(0,1fr),
     so it always won the space and the name ellipsised down to "Gems ..." / "LEG..." / "Wil...".
     Same failure as the rewards cell noted above, one column over. The name now gets a full width
     row of its own and the status sits under it, which is also the reading order that makes sense
     on a phone: what the event IS, then how long is left. */
  .cal-brow { grid-template-columns: minmax(0, 1fr); grid-template-areas: 'ev' 'stat' 'win' 'cad' 'rew'; row-gap: 4px; border-radius: 0; }
  /* ...and a name that cannot wrap will still ellipsise even in a full width row. */
  .cal-name { white-space: normal; overflow-wrap: anywhere; line-height: 1.25; }
  .cal-trow { flex-wrap: wrap; row-gap: 4px; }
  .cal-brow + .cal-brow { border-top: 1px solid var(--border-soft); }
  .cal-brow.head { display: none; }
  .cal-bstat { grid-area: stat; justify-self: start; }
  .cal-bev { grid-area: ev; min-width: 0; }
  .cal-bmini { display: none; }
  .cal-bwin { grid-area: win; }
  .cal-bcad { grid-area: cad; }
  .cal-brew { grid-area: rew; justify-self: stretch; justify-content: flex-start; max-width: 100%; }
  .cal-bcad { justify-self: start; }
  .cal-rlabel.alt { display: none; }
}

/* ── Build Planner (Tools) ──────────────────────────────────────────────────────────────────────
   Concept by Ebe; rebuilt on Rogue HQ's own shell and tokens. Tiles are square so a grid of 92 runes
   stays scannable, and the whole thing is one auto-fill grid so it reflows to any width. */
/* .card brings no padding of its own (only .card.pad does), so without this the step head, the count on
   its right, and the tile grid all sit flush against the card border. */
.bp-step { margin-top: var(--s4); padding: var(--s4); }
.bp-stephead { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.bp-stepn { font-size: .6rem; font-weight: 800; letter-spacing: .7px; padding: 3px 7px;
  border-radius: 999px; background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.bp-steptitle { font-weight: 800; font-size: .95rem; text-transform: uppercase; letter-spacing: .5px; }
.bp-count { margin-left: auto; font-size: .78rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.bp-hint { font-size: .8rem; margin: 6px 0 10px; }
.bp-groups { display: flex; flex-direction: column; gap: var(--s4); }
.bp-grouphead { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; font-size: .82rem; font-weight: 700; }
.bp-grouphead .muted { margin-left: auto; font-variant-numeric: tabular-nums; }

.bp-grid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(76px, 1fr)); }
.bp-grid.bp-runes { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
.bp-tile {
  display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 7px 5px;
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--card);
  color: var(--text-dim); cursor: pointer; text-align: center; transition: var(--speed); position: relative;
}
.bp-tile:hover { background: var(--card-2); color: var(--text); border-color: var(--border-strong, var(--border)); }
.bp-tile.on { color: var(--text); border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent), 0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent); }
.bp-img { width: 40px; height: 40px; object-fit: contain; border-radius: 8px; flex: none; }
.bp-noimg { display: grid; place-items: center; background: var(--card-2); font-weight: 800; font-size: 1.1rem; }
.bp-name { font-size: .72rem; font-weight: 600; line-height: 1.2; overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.bp-sub { position: absolute; top: 3px; right: 4px; font-size: .58rem; font-weight: 800;
  min-width: 15px; padding: 1px 4px; border-radius: 999px; background: var(--accent); color: #0b0c14; }
/* Copy count. Top LEFT so it never fights the S-tier badge, which owns the right corner. */
.bp-qty { position: absolute; top: 3px; left: 4px; font-size: .58rem; font-weight: 800;
  min-width: 15px; padding: 1px 4px; border-radius: 999px; background: var(--accent); color: #0b0c14;
  font-variant-numeric: tabular-nums; }

/* One chip per filled slot. Tiles count copies rather than toggling (the game only forbids duplicate
   units and duplicate UNIQUE runes), so removing a specific copy needs its own target. For runes the
   chips are also the only legible view of the sockets, since every rune draws the same octagon. */
.bp-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }   /* under the grid, see the JS */
.bp-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 8px 4px 5px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--card-2);
  color: var(--text-dim); cursor: pointer; font-size: .74rem; transition: var(--speed); }
.bp-chip:hover { color: var(--text); border-color: var(--accent); }
.bp-chipart { position: relative; display: block; width: 20px; height: 20px; flex: none; }
.bp-chipimg { width: 20px; height: 20px; object-fit: contain; border-radius: 5px; flex: none; }
.bp-chipart .bp-chipimg { width: 100%; height: 100%; }
.bp-chipstar { position: absolute; left: -3px; bottom: -2px; width: 11px; height: 11px;
  object-fit: contain; pointer-events: none; }
.bp-chipslot { font-weight: 700; color: var(--muted); }
.bp-chipname { font-weight: 600; }
.bp-chipx { font-weight: 800; color: var(--muted); }
.bp-chip:hover .bp-chipx { color: var(--accent); }

.bp-pips { display: inline-flex; gap: 3px; }
.bp-pip { width: 7px; height: 7px; border-radius: 50%; background: var(--border); }
.bp-pip.on { background: var(--accent); }

/* ── Config Watch (Studio, Livo only) ───────────────────────────────────────────────────────────
   Rebuilt 2026-07-30: the first version printed raw JSON paths and 700-char blobs and was, in Livo's
   words, "not really readable". Rows are sentences now, so the styling only has to keep the eye on the
   left edge (label) and the right (value), and stay out of the way. */
.cw-stats { display: flex; flex-wrap: wrap; gap: var(--s4); }
.cw-stat { min-width: 92px; }
.cw-statn { font-size: 1.5rem; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.1; }
.cw-statl { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; }
.cw-meta { font-size: .76rem; margin: 10px 0 0; }
.cw-filter { width: 100%; }

.cw-hls { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.cw-hl { display: flex; gap: 9px; padding: 9px 11px; border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong, var(--border)); border-radius: var(--r-md); background: var(--card-2); }
.cw-hl.good { border-left-color: var(--good, #3fb950); }
.cw-hl.warn { border-left-color: var(--warn, #e0a92a); }
.cw-hl.bad  { border-left-color: var(--bad, #e8556a); }
.cw-hlkind { font-size: .56rem; font-weight: 800; letter-spacing: .6px; text-transform: uppercase;
  color: var(--muted); padding-top: 3px; width: 62px; flex: none; }
.cw-hltitle { font-size: .84rem; font-weight: 700; }
.cw-hldetail { font-size: .76rem; margin-top: 2px; }

/* A wave = one version's worth of staged content, the unit Livo actually asks about. */
.cwwave { margin-top: var(--s4); }
.cwwave-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.cwwave-tag { font-size: .58rem; font-weight: 800; letter-spacing: .8px; padding: 3px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--warn, #e0a92a) 22%, transparent); color: var(--warn, #e0a92a); }
.cwwave-tag.live { background: color-mix(in srgb, var(--good, #3fb950) 22%, transparent); color: var(--good, #3fb950); }
.cwwave-title { font-size: 1.15rem; font-weight: 800; margin: 0; }
.cwwave-sub { font-size: .76rem; }

.cwns { border: 1px solid var(--border); border-radius: var(--r-md); background: var(--card);
  padding: 10px 12px; margin-bottom: 8px; }
.cwns-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }
.cwns-name { font-weight: 800; font-size: .9rem; }
.cwns-scope { font-size: .72rem; }
.cwns-sub { font-size: .72rem; margin-left: auto; text-align: right; }

.cwr { padding: 6px 0; border-top: 1px solid var(--border); }
.cwr:first-of-type { border-top: 0; }
.cwr-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: .84rem; }
.cwr-op { font-size: .55rem; font-weight: 800; letter-spacing: .5px; padding: 2px 6px; border-radius: 4px;
  background: var(--card-2); color: var(--muted); flex: none; }
.op-added { background: color-mix(in srgb, var(--good, #3fb950) 22%, transparent); color: var(--good, #3fb950); }
.op-removed { background: color-mix(in srgb, var(--bad, #e8556a) 22%, transparent); color: var(--bad, #e8556a); }
.op-changed, .op-list_rewritten, .op-appended {
  background: color-mix(in srgb, var(--warn, #e0a92a) 22%, transparent); color: var(--warn, #e0a92a); }
.cwr-label { font-weight: 700; }
.cwr-from { color: var(--muted); text-decoration: line-through; }
.cwr-arrow { color: var(--muted); }
.cwr-to { color: var(--text); }
.cwr-items { display: flex; flex-wrap: wrap; gap: 4px; margin: 5px 0 0 46px; }
.cwr-item { font-size: .68rem; padding: 1px 7px; border-radius: 999px; background: var(--card-2); color: var(--muted); }
.cwr-flag { margin: 4px 0 0 46px; font-size: .7rem; color: var(--warn, #e0a92a); }
.cwr-explain, .cwr-raw { margin: 5px 0 0 46px; }
.cwr-explain summary, .cwr-raw summary { cursor: pointer; font-size: .68rem; color: var(--muted); }
.cwr-explain summary { color: var(--accent); }
.cwr-detail { margin-top: 5px; padding: 7px 10px; background: var(--card-2); border-radius: var(--r-sm, 6px);
  max-height: 420px; overflow: auto; }
.cwr-dline { font-size: .76rem; line-height: 1.5; }
.cwr-dline:not(:last-child) { border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent); }
.cwr-raw pre { margin: 4px 0 0; padding: 7px 9px; background: var(--card-2); border-radius: var(--r-sm, 6px);
  font-family: var(--mono); font-size: .68rem; white-space: pre-wrap; word-break: break-all; max-height: 260px; overflow: auto; }
.cwr-trunc { font-size: .72rem; margin: 6px 0 0; }
@media (max-width: 640px) {
  .cwns-sub { margin-left: 0; text-align: left; width: 100%; }
  .cwr-items, .cwr-flag, .cwr-raw { margin-left: 0; }
}

.bp-share { margin-top: var(--s4); padding: var(--s4); }
.bp-sharerow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bp-sharerow + .bp-sharerow { margin-top: 8px; }
.bp-nameinput { flex: 1; min-width: 160px; }
.bp-string { flex: 1; min-width: 200px; font-family: var(--mono); font-size: .74rem; }
.bp-strlabel { font-size: .74rem; white-space: nowrap; }
.bp-saved { display: flex; flex-direction: column; gap: 6px; }
.bp-savedrow { display: flex; align-items: center; gap: 8px; padding: 7px 9px;
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--card); }
.bp-savedname { flex: 1; min-width: 0; font-weight: 700; font-size: .86rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Second row of the one bar: open a saved build on the left, the build string on the right. The picker
   keeps its natural width so it does not dominate the row the way a flex:1 select did. */
.bp-openrow { margin-top: 8px; }
.bp-openpick { width: auto; min-width: 170px; max-width: 240px; cursor: pointer;
  padding: 6px 10px; font-size: .82rem; }
/* The preview board inside a modal. .modal-body already pads, and the board is a fixed 440px, so it
   only needs centring. */
.bp-preview { align-items: center; }
.bp-preview .bp-livehead { width: auto; justify-content: center; }
/* Rune tile art: one shared octagon for every rune, with the unique star overlaid bottom-left, which
   is how the game itself draws them (all runes identical, only uniques starred). */
.bp-runeart { position: relative; display: block; width: 40px; height: 40px; flex: none; }
.bp-runeart .bp-img { width: 100%; height: 100%; }
.bp-star { position: absolute; left: -5px; bottom: -3px; width: 19px; height: 19px;
  object-fit: contain; pointer-events: none; filter: drop-shadow(0 1px 1px rgba(0,0,0,.5)); }

/* Live editor. Sits between the share bar and Step 1, so the build you are assembling is visible
   without scrolling back up past five steps of tiles. Not numbered as a step: the steps are a
   sequence you work through, this is a readout of where you got to.
   Laid out like the in-game equipment screen: gear down both edges, units up the middle. */
/* .card carries no padding of its own (only .card.pad does), which centring used to disguise. Now the
   board is left-aligned it would otherwise sit flush against the border, so pad it here. */
.bp-live { margin-top: var(--s4); padding: var(--s4); }
/* One head per board, so each side is labelled and countable on its own. 440px matches the board
   width, which keeps the count hard against the board's right edge instead of the card's. */
.bp-livehead { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; width: 440px; max-width: 100%; }
.bp-livetitle { font-weight: 800; font-size: .95rem; text-transform: uppercase; letter-spacing: .5px; }
/* Sized to fit TWO of these side by side in one row, so a later compare view can put two builds up
   against each other with no second layout. Every track is fixed and the whole board is
   width:max-content centred in its card: a fluid middle stretched the units halfway across a desktop
   card and left the gear columns marooned at the edges, which is the sprawl this replaces.
   The middle track is 224px because the widest thing in it is a row of three 66px unit cells plus two
   8px gaps = 214px; the spare 10px is deliberate headroom, since that row is the first thing to wrap
   if a gap or cell width is ever nudged.
   Total: 96 + 224 + 96 + two 12px gaps = 440px, so two fit inside a 900px content column. */
/* Your board is a fixed 440px on the left; the second track takes everything left over. Compare off,
   that track holds the button and so the button spans the full right side. Compare on, it holds the
   second board, which right-aligns inside it (see .cmp below) so the two builds sit at the container's
   two edges instead of one floating in the middle. */
.bp-livewrap { display: grid; grid-template-columns: 440px minmax(0, 1fr);
  gap: var(--s4); align-items: start; }
.bp-lside { display: flex; flex-direction: column; gap: var(--s4); }
/* Compare on: the right board hugs the right edge. Only the second .bp-lside is targeted, so your own
   board keeps the left edge. */
.bp-live.cmp .bp-lside + .bp-lside { align-items: flex-end; }
.bp-lcompare { align-self: stretch; min-height: 220px; display: flex; align-items: center;
  justify-content: center; gap: var(--s3); flex-wrap: wrap; padding: var(--s4);
  border-radius: var(--r-lg); border: 1px dashed var(--border);
  background: color-mix(in srgb, var(--card-2) 35%, transparent); }
/* The link is the secondary action here, so it stays quieter than Compare. */
.bp-lcompare a.btn { color: var(--text-dim); }
.bp-lcompare a.btn:hover { color: var(--text); text-decoration: none; }
.bp-lsidefoot { display: flex; gap: var(--s2); }
.bp-livebody { display: grid; grid-template-columns: 96px 224px 96px;
  gap: var(--s3); justify-items: center; align-items: start; width: max-content; }
.bp-lcol { display: flex; flex-direction: column; gap: var(--s3); }
.bp-lmid { display: flex; flex-direction: column; align-items: center; gap: var(--s2); }
.bp-lrow { display: flex; justify-content: center; gap: var(--s2); }
.bp-lcell { display: flex; flex-direction: column; align-items: center; gap: 4px; width: 96px; }
.bp-lcell.unit { width: 66px; }

/* The frame carries the rarity colour via --lr, set inline from the catalog entry. */
.bp-lframe { position: relative; width: 64px; height: 64px; border-radius: var(--r-md);
  border: 2px solid var(--lr, var(--border)); background: color-mix(in srgb, var(--lr, var(--card-2)) 14%, var(--card-2));
  display: grid; place-items: center; }
.bp-lcell.main .bp-lframe { width: 84px; height: 84px; }
.bp-lframe.empty { border-style: dashed; border-color: var(--border);
  background: color-mix(in srgb, var(--card-2) 60%, transparent); }
.bp-lempty { color: var(--muted); font-size: 1.3rem; font-weight: 800; line-height: 1; }
.bp-limg { width: 52px; height: 52px; object-fit: contain; }
.bp-lcell.main .bp-limg { width: 70px; height: 70px; }
.bp-lcell .bp-noimg { border-radius: 8px; }
/* Slot label + S-tier badge overhang the frame corners, the way the gear guide draws its type badge. */
.bp-lslot { position: absolute; top: -7px; left: -5px; max-width: 92%; padding: 1px 5px;
  border-radius: 999px; background: var(--card); border: 1px solid var(--lr, var(--border));
  color: var(--text-dim); font-size: .52rem; font-weight: 800; letter-spacing: .3px;
  text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* S-tier mark: the game's own gold S, the same file the gear upgrade tree uses. Both the picker tile and
   the live board draw it, so one item reads the same in both places. No box, no border, no letter, the
   badge is already a finished piece of art. */
.bp-stier { position: absolute; top: 2px; right: 3px; width: 15px; height: 15px;
  object-fit: contain; pointer-events: none;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .55)); }
.bp-lstier { position: absolute; bottom: -4px; left: -4px; width: 16px; height: 16px;
  object-fit: contain; pointer-events: none;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .55)); }

/* Five socket dots under a gear tile, each the rune's own art. Fixed 5-up grid so an empty socket
   holds its place and the row never reflows as runes go in. */
.bp-lsocks { display: grid; grid-template-columns: repeat(5, 16px); gap: 3px; }
.bp-lsock { position: relative; width: 16px; height: 16px; border-radius: 50%; }
.bp-lsock.empty { border: 1px dashed var(--border); background: color-mix(in srgb, var(--card-2) 55%, transparent); }
.bp-lsockimg { width: 100%; height: 100%; object-fit: contain; }
.bp-lsockstar { position: absolute; left: -3px; bottom: -2px; width: 9px; height: 9px;
  object-fit: contain; pointer-events: none; filter: drop-shadow(0 1px 1px rgba(0,0,0,.5)); }

.bp-lname { font-size: .72rem; font-weight: 600; line-height: 1.2; text-align: center;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Below ~960px two 440px boards stop fitting side by side, so the right side drops under the left
   rather than forcing a horizontal scrollbar on the whole page. Stacked, it left-aligns again: an
   edge-hugging board reads as "paired with the thing beside it", and there is nothing beside it now. */
@media (max-width: 960px) {
  .bp-livewrap { grid-template-columns: 440px; }
  .bp-live.cmp .bp-lside + .bp-lside { align-items: flex-start; }
  .bp-lcompare { min-height: 0; }   /* the buttons set the height once it is not beside a board */
}
/* Below ~470px the 440px board plus the card's own padding stops fitting. Stack to one column: units
   first, since the middle is what the build is built around, then gear as a single flowing grid.
   max-content has to be dropped here or the single column keeps the widest child's width. */
@media (max-width: 470px) {
  .bp-livewrap { grid-template-columns: 1fr; }
  .bp-livebody { grid-template-columns: 1fr; gap: var(--s5); width: auto; }
  .bp-lcol { flex-direction: row; flex-wrap: wrap; justify-content: center; }
  .bp-lmid { order: -1; }
}

/* ── Popular Builds (Tools) ─────────────────────────────────────────────────────────────────────
   A ranked list of community loadouts. One row per build, fixed height, so the ranking stays
   scannable however long the list gets. */
.pb-head { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s3); }
.pb-head svg { color: var(--bad); }
.pb-top { font-size: .72rem; font-weight: 900; letter-spacing: .1em; text-transform: uppercase; color: var(--bad); }
.pb-headsub { font-size: .78rem; }
.pb-list { display: flex; flex-direction: column; gap: var(--s3); }
.pb-row { display: flex; align-items: center; gap: var(--s3); padding: var(--s3) var(--s4); }
/* Highlighting is three tiers. Everything published is listed; these only say where it placed.
   Top N: a faint accent edge. Podium: the same plus a wash, so it reads before you parse any number. */
.pb-row.top { border-color: color-mix(in srgb, var(--accent) 24%, var(--border-soft)); }
.pb-row.top.podium {
  background: linear-gradient(100deg, color-mix(in srgb, var(--accent) 7%, var(--card)), var(--card) 60%); }
/* Where the top N ends, marked only when there is actually something below it (see the JS). A bare gap
   read as a loading seam, so the boundary says what it is. */
.pb-cut { display: flex; align-items: center; gap: var(--s3); margin: var(--s2) 0; }
.pb-cut::before, .pb-cut::after { content: ''; flex: 1; height: 1px; background: var(--border-soft); }
.pb-cutlabel { font-size: .58rem; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--muted); white-space: nowrap; }
.pb-row.bad { border-color: color-mix(in srgb, var(--bad) 35%, var(--border-soft)); }

.pb-rank { flex: none; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  font-size: .76rem; font-weight: 800; color: var(--muted); background: var(--card-2);
  border: 1px solid var(--border-soft); font-variant-numeric: tabular-nums; }
/* Medal colours for the podium, matching the ranking colours used elsewhere in the app. */
.pb-rank.medal { color: #0b0c14; border-color: transparent; }
.pb-rank.m1 { background: var(--gold); }
.pb-rank.m2 { background: var(--silver); }
.pb-rank.m3 { background: var(--bronze); }

.pb-arts { flex: none; display: flex; gap: 6px; }
.pb-art { display: grid; place-items: center; width: 42px; height: 42px; border-radius: var(--r-md);
  background: var(--card-2); border: 1px solid var(--border); overflow: hidden; }
.pb-art.empty { border-style: dashed; }
.pb-artimg { width: 36px; height: 36px; object-fit: contain; }
.pb-noart { display: grid; place-items: center; font-weight: 800; color: var(--muted); }

.pb-main { flex: 1; min-width: 0; }
.pb-title { font-weight: 700; font-size: .92rem; display: flex; align-items: center; gap: 6px;
  flex-wrap: wrap; }
.pb-by { font-weight: 600; }
.pb-mine { font-size: .58rem; padding: 2px 7px; color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  background: color-mix(in srgb, var(--accent) 14%, transparent); }
.pb-sub { font-size: .76rem; margin-top: 2px; }

.pb-acts { flex: none; display: flex; align-items: center; gap: 6px; }
.pb-act { width: 34px; height: 34px; }
/* The count sits inside the button, so the press target and the number that changes are one thing. */
.pb-vote { gap: 6px; font-variant-numeric: tabular-nums; min-width: 68px; }
.pb-vote.on { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 45%, var(--border));
  background: color-mix(in srgb, var(--bad) 12%, transparent); }
.pb-vote.on svg { fill: var(--bad); }

/* Narrow: the actions drop under the name rather than squeezing it to nothing. */
@media (max-width: 640px) {
  .pb-row { flex-wrap: wrap; }
  .pb-main { flex-basis: 100%; order: 3; }
  .pb-acts { order: 4; margin-left: auto; }
}
/* "Yours" — published builds that rank below the top N. Separated by a rule so it reads as a footnote
   to the ranking rather than as part of it. */

/* Build diff — the +/- readout under a compare. Two columns per group: gains on the left in green,
   losses on the right in red, so a long list on one side never pushes the other side down. */
.bp-diff { margin-top: var(--s4); padding: var(--s4); background: var(--card-2); }
.bp-dtotals { font-weight: 800; font-variant-numeric: tabular-nums; }
.bp-dpos { color: var(--good); }
.bp-dneg { color: var(--bad); }
.bp-dlede { font-size: .8rem; margin-top: 4px; }
.bp-dgroups { display: grid; gap: var(--s3); margin-top: var(--s3); }
.bp-dgroup { border-top: 1px solid var(--border-soft); padding-top: var(--s2); }
.bp-dghead { font-size: .62rem; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--muted); margin-bottom: 6px; }
.bp-dcols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); align-items: start; }
.bp-dlist { list-style: none; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.bp-ditem { display: flex; align-items: center; gap: 7px; padding: 4px 8px; border-radius: var(--r-sm);
  font-size: .8rem; font-weight: 600; min-width: 0; }
.bp-ditem.add { color: var(--good); background: color-mix(in srgb, var(--good) 11%, transparent); }
.bp-ditem.rem { color: var(--bad);  background: color-mix(in srgb, var(--bad) 11%, transparent); }
.bp-dsign { flex: none; width: 12px; font-weight: 900; text-align: center; }
.bp-dart { position: relative; display: block; width: 22px; height: 22px; flex: none; }
.bp-dimg { width: 22px; height: 22px; object-fit: contain; border-radius: 5px; flex: none; }
.bp-dart .bp-dimg { width: 100%; height: 100%; }
.bp-dstar { position: absolute; left: -3px; bottom: -2px; width: 11px; height: 11px;
  object-fit: contain; pointer-events: none; filter: drop-shadow(0 1px 1px rgba(0,0,0,.5)); }
/* The name truncates rather than wrapping, so every row stays one line and the list scans vertically. */
.bp-dname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--text); }
.bp-dqty { flex: none; font-size: .7rem; font-weight: 800; opacity: .85; }
@media (max-width: 640px) { .bp-dcols { grid-template-columns: 1fr; } }

/* ── Skill priorities (Build Planner step 6) ────────────────────────────────────────────────────
   A tier list: one row per priority, skills dragged between them. Rows keep their height when empty so
   the list does not jump around mid-drag, which is exactly when a moving target is worst. */
.bp-skrows { display: flex; flex-direction: column; gap: 6px; }
.bp-skrow { display: flex; align-items: flex-start; gap: var(--s3); padding: 7px 9px; min-height: 58px;
  border: 1px solid var(--border-soft); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--card-2) 45%, transparent); transition: var(--speed); }
.bp-skrow.over { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* The default bucket is visually quieter: it is a holding pen, not a ranking. */
.bp-skrow.unranked { background: transparent; border-style: dashed; }
.bp-skrowhead { flex: none; width: 74px; display: flex; align-items: center; gap: 6px; padding-top: 3px; }
.bp-skprio { font-size: .68rem; font-weight: 800; letter-spacing: .3px; padding: 3px 8px;
  border-radius: 999px; background: var(--card-2); color: var(--muted); }
.bp-skprio.on { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.bp-skcount { font-size: .72rem; font-variant-numeric: tabular-nums; }
.bp-skcells { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: 6px; align-items: flex-start; }
.bp-skempty { font-size: .74rem; padding: 6px 2px; }

.bp-sktile { position: relative; display: flex; align-items: center; gap: 7px; padding: 5px 8px 5px 5px;
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--card);
  cursor: grab; max-width: 230px; transition: var(--speed); }
.bp-sktile:hover { border-color: var(--accent); }
.bp-sktile:active { cursor: grabbing; }
/* The rarity plate behind the art is the GAME's own image, set per tile as a background-image, the same
   way the gear upgrade tree draws its tiles. A plate rather than another pill: the panel shows 163
   skills at once, so a colour you can scan beats a label you have to read. Padding keeps the skill art
   off the plate's border, and `contain` keeps a non-square plate from stretching. */
.bp-skimg { width: 30px; height: 30px; object-fit: contain; border-radius: 7px; flex: none;
  padding: 3px; background-size: 100% 100%; background-repeat: no-repeat; }
.bp-skimg.bp-noimg { display: grid; place-items: center; background: var(--card-2); font-weight: 800; }
.bp-skname { font-size: .78rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; min-width: 0; }
/* Keyboard and touch route: HTML5 drag events never fire on touch, so the select is the real control
   there, not a fallback. */
.bp-skmove { flex: none; background: var(--card-2); border: 1px solid var(--border-soft);
  border-radius: 6px; color: var(--text-dim); font-size: .66rem; font-weight: 700; padding: 2px 3px;
  cursor: pointer; }
.bp-skmove:focus { border-color: var(--accent); outline: none; }
.bp-skinfo { position: absolute; top: -6px; right: -6px; width: 17px; height: 17px; border-radius: 50%;
  display: grid; place-items: center; background: var(--card-2); border: 1px solid var(--border);
  color: var(--muted); font-size: .64rem; font-weight: 900; line-height: 1; }
.bp-skinfo:hover { background: var(--accent); border-color: var(--accent); color: #0b0c14; }

/* Detail modal */
.bp-skdetail { display: flex; flex-direction: column; gap: var(--s3); }
.bp-skhead { display: flex; align-items: flex-start; gap: var(--s3); }
.bp-skdimg { width: 56px; height: 56px; object-fit: contain; border-radius: var(--r-md); flex: none;
  background: var(--card-2); }
.bp-skmeta { display: flex; flex-wrap: wrap; gap: 6px; }
.bp-skhint { font-size: .76rem; margin-top: 6px; }
.bp-sklevel { border-top: 1px solid var(--border-soft); padding-top: var(--s2); }
.bp-sklabel { font-size: .6rem; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--muted); margin-bottom: 3px; }
.bp-sktext { font-size: .86rem; line-height: 1.55; }
/* The game's own markup: a value going up, a value going down, and a keyword. */
.bp-skup { color: var(--good); font-weight: 700; }
.bp-skdown { color: var(--bad); font-weight: 700; }
.bp-skkey { color: var(--accent-2); font-weight: 600; }

@media (max-width: 640px) {
  .bp-skrow { flex-direction: column; gap: 6px; }
  .bp-skrowhead { width: auto; padding-top: 0; }
  .bp-sktile { max-width: none; }
}
/* Moved skill priorities in the diff. Not a +/- list: the same skill sits at a different rank in each
   build, so the row shows yours -> theirs. */
.bp-dmoved { list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 4px; }
.bp-dmove { display: flex; align-items: center; gap: 7px; padding: 4px 8px; border-radius: var(--r-sm);
  font-size: .8rem; font-weight: 600; background: var(--card); min-width: 0; }
.bp-dprio { flex: none; font-size: .7rem; font-weight: 800; padding: 1px 6px; border-radius: 999px;
  background: var(--card-2); color: var(--muted); font-variant-numeric: tabular-nums; }
.bp-dprio.up { color: var(--good); background: color-mix(in srgb, var(--good) 14%, transparent); }
.bp-dprio.down { color: var(--bad); background: color-mix(in srgb, var(--bad) 14%, transparent); }
.bp-darrow { flex: none; color: var(--muted); font-size: .72rem; }

/* ─── Data provenance LEDs ───────────────────────────────────────────────────────
   Green = live from the game this request, orange = our DB and recent, grey = old
   or cached. NO DOT MEANS NOT INSTRUMENTED, never "live" — see docs/DATA_PROVENANCE.md. */
.prov { display: inline-flex; align-items: center; gap: 5px; vertical-align: middle; margin-left: 8px; }
.prov-led { width: 8px; height: 8px; border-radius: 50%; flex: none; display: inline-block; }
.prov-age { font-size: .62rem; font-weight: 700; color: var(--muted); letter-spacing: .02em; }
.prov-live  .prov-led { background: var(--good, #57d9a3); box-shadow: 0 0 6px color-mix(in srgb, var(--good, #57d9a3) 65%, transparent); }
.prov-db    .prov-led { background: var(--gold, #ffce6b); }
.prov-stale .prov-led { background: var(--muted, #767ca0); }
