/* ============================================================
 * disdex — Discord-native theme
 * ============================================================ */

:root {
  --blurple:        #7289da;
  --blurple-hover:  #677bc4;
  --bg-darkest:     #1e2124;
  --bg-dark:        #23272a;
  --bg:             #2c2f33;
  --bg-light:       #36393f;
  --bg-lighter:     #40444b;
  --border:         #40444b;
  --text:           #dcddde;
  --text-muted:     #99aab5;
  --text-dim:       #72767d;
  --green:          #43b581;
  --yellow:         #faa61a;
  --red:            #f04747;

  /* Locked topbar height — drives the sidebar's sticky offset so it doesn't
     "settle" when you start scrolling. Update if topbar contents change. */
  --topbar-h:       57px;
  --main-pad:       24px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg-dark);
  color: var(--text);
  color-scheme: dark; /* native dark form controls (date pickers, scrollbars) */
  font-family: "gg sans", "Whitney", -apple-system, BlinkMacSystemFont,
               "Segoe UI", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
}

a { color: inherit; text-decoration: none; }
a:hover { color: var(--blurple); }

/* ----- top bar ----- */

.topbar {
  border-bottom: 1px solid var(--border);
  background: var(--bg);
  position: sticky;
  top: 0;
  z-index: 10;
  min-height: var(--topbar-h);
  box-sizing: border-box;
}

.topbar-inner {
  /* Wider on big monitors so the data tables breathe — the previous 1280px
     cap was leaving ~1000px of empty page on a 2560px screen. 1800px keeps
     line lengths sane while letting tables actually use the room. Topbar
     follows main so the brand+nav stay aligned with the content below. */
  max-width: 1800px;
  margin: 0 auto;
  padding: 10px 24px;
  display: flex;
  align-items: center;
  gap: 24px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
.brand:hover { color: inherit; }

.brand-logo {
  width: 36px;
  height: 36px;
  display: block;
  filter: drop-shadow(0 2px 6px rgba(114, 137, 218, 0.35));
}

.brand-name { font-weight: 700; font-size: 19px; line-height: 1; }
.brand-sub  {
  font-size: 10px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-top: 3px;
}

.nav {
  display: flex;
  gap: 2px;
  flex: 1;
}

.nav a {
  padding: 8px 14px;
  border-radius: 6px;
  color: var(--text-muted);
  font-weight: 500;
  font-size: 14px;
  transition: background 0.12s, color 0.12s;
}

.nav a:hover {
  background: var(--bg-light);
  color: var(--text);
}

.nav a.active {
  background: var(--bg-lighter);
  color: var(--text);
}

.login-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  background: var(--blurple);
  color: #fff;
  font-weight: 600;
  font-size: 14px;
  border-radius: 6px;
  transition: background 0.12s;
  flex-shrink: 0;
}
.login-btn:hover { background: var(--blurple-hover); color: #fff; }
.login-btn svg { width: 18px; height: 18px; }

.user-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px 4px 4px;
  background: var(--bg-light);
  border: 1px solid var(--border);
  border-radius: 32px;
  flex-shrink: 0;
}
.user-pill-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 4px 0 0;
}
.user-pill-link:hover { color: inherit; }
.user-pill .icon {
  width: 28px;
  height: 28px;
  border-radius: 50%;
}
.user-pill-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
.logout-btn {
  font-size: 12px;
  color: var(--text-dim);
  padding: 2px 8px;
  border-radius: 4px;
  border-left: 1px solid var(--border);
  margin-left: 4px;
}
.logout-btn:hover { color: var(--red); background: var(--bg-lighter); }

/* ----- main layout ----- */

main {
  max-width: 1800px;
  margin: 0 auto;
  padding: 24px;
}

.page-h {
  font-size: 22px;
  font-weight: 700;
  margin: 0 0 4px;
  letter-spacing: -0.01em;
}
.page-sub { color: var(--text-muted); margin: 0 0 24px; font-size: 14px; }

/* ----- stats strip ----- */

.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin: 0 0 24px;
}

.stat {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px 18px;
  display: block;
  color: inherit;
  transition: background 0.12s, border-color 0.12s, transform 0.12s;
}
a.stat:hover {
  background: var(--bg-light);
  border-color: var(--blurple);
  color: inherit;
  transform: translateY(-1px);
}

.stat-num {
  font-size: 22px;
  font-weight: 700;
  color: var(--blurple);
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}

.stat-label {
  color: var(--text-dim);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  margin-top: 4px;
  font-weight: 600;
}

/* ----- live feed ----- */

.feed-h {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 24px 0 12px;
}

.feed-pulse {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 0 0 rgba(240, 71, 71, 0.6);
  animation: pulse 1.6s ease-out infinite;
}

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(240, 71, 71, 0.55); }
  70%  { box-shadow: 0 0 0 8px rgba(240, 71, 71, 0); }
  100% { box-shadow: 0 0 0 0 rgba(240, 71, 71, 0); }
}

.feed { display: flex; flex-direction: column; gap: 6px; }

.feed-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
}

.feed-item .icon {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--bg-light);
  object-fit: cover;
}
.feed-item .icon-fallback {
  display: grid;
  place-items: center;
  color: var(--text-muted);
  font-weight: 700;
}

.feed-body { flex: 1; min-width: 0; }
.feed-title {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.feed-title strong {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
}
.feed-meta { font-size: 12px; color: var(--text-dim); margin-top: 2px; }

.feed-arrow { color: var(--text-dim); margin: 0 4px; }
.feed-old { color: var(--text-dim); text-decoration: line-through; }
.feed-new { color: var(--text); font-weight: 600; }

.badge {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.badge-new       { background: rgba(67, 181, 129, 0.18); color: var(--green); }
.badge-renamed   { background: rgba(250, 166, 26, 0.18); color: var(--yellow); }
.badge-user      { background: rgba(114, 137, 218, 0.18); color: var(--blurple); }

.feed-time {
  color: var(--text-dim);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

/* ----- server / user / game cards (used on listing pages) ----- */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 10px;
}

.card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  transition: background 0.12s, border-color 0.12s, transform 0.12s;
}
.card:hover {
  background: var(--bg-light);
  border-color: var(--blurple);
  color: inherit;
  transform: translateY(-1px);
}

.icon {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  flex-shrink: 0;
  background: var(--bg-light);
  object-fit: cover;
}
.icon-fallback {
  display: grid;
  place-items: center;
  color: var(--text-muted);
  font-weight: 700;
  font-size: 20px;
  background: var(--bg-lighter);
}

.card-body { flex: 1; min-width: 0; }
.card-name {
  font-weight: 600;
  font-size: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ----- table view (with sidebar) ----- */

.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 18px;
  align-items: start;
}
@media (max-width: 760px) {
  .layout { grid-template-columns: 1fr; }
}

.sidebar {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px;
  position: sticky;
  /* Lock just under the topbar (no extra padding offset). The sidebar's
     natural in-flow position sits below this, so it scrolls naturally
     with the page for the first ~24px before sticky engages. Locking at
     exactly the natural position causes a sub-pixel "snap" the moment
     scroll leaves 0; this offset eliminates it. */
  top: var(--topbar-h);
}

.sidebar-h {
  font-size: 11px;
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  margin: 0 0 12px;
}

.filter-form { display: flex; flex-direction: column; gap: 14px; }

.field { display: flex; flex-direction: column; gap: 6px; }
.field > label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.field-row { display: flex; gap: 6px; }

.field input, .field select {
  width: 100%;
  padding: 7px 10px;
  background: var(--bg-light);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  outline: none;
}
.field input:focus, .field select:focus { border-color: var(--blurple); }
.field input[type=date]::-webkit-calendar-picker-indicator { filter: invert(0.7); cursor: pointer; }

.field label.check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 400;
  color: var(--text);
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
}
.field label.check input { width: auto; margin: 0; cursor: pointer; }

.filter-apply {
  padding: 8px 14px;
  background: var(--blurple);
  color: #fff;
  font-weight: 600;
  font-size: 13px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
}
.filter-apply:hover { background: var(--blurple-hover); }
.filter-reset {
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
  padding: 4px;
}
.filter-reset:hover { color: var(--blurple); }

/* ----- column toggles ----- */

.column-toggle {
  margin-top: 8px;
  border-top: 1px solid var(--border);
  padding-top: 12px;
}
.column-toggle-summary {
  cursor: pointer;
  font-size: 11px;
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  user-select: none;
  padding: 2px 0;
}
.column-toggle-summary::-webkit-details-marker { display: none; }
.column-toggle-summary::before {
  content: "▸";
  font-size: 9px;
  color: var(--text-muted);
  transition: transform 0.1s;
  display: inline-block;
}
.column-toggle[open] > .column-toggle-summary::before {
  transform: rotate(90deg);
}
.column-toggle-summary:hover { color: var(--text); }

.col-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
  padding-left: 2px;
}
.col-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  padding: 2px 0;
}
.col-toggle input { margin: 0; cursor: pointer; }
.col-toggle:hover { color: var(--blurple); }
.col-reset {
  margin-top: 4px;
  font-size: 11px;
  color: var(--text-dim);
  text-decoration: underline;
  cursor: pointer;
}
.col-reset:hover { color: var(--blurple); }

.table-area { min-width: 0; }
.table-meta { color: var(--text-muted); font-size: 13px; margin-bottom: 8px; }

.table-wrap {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}

table.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  /* Desktop intentionally uses `table-layout: auto`. With auto layout,
     setting `display: none` on every cell of a column physically removes
     those cells from the box tree, and the table re-runs its width
     algorithm to redistribute the freed space across remaining columns
     (which all have intrinsic content widths the algorithm can use).
     We tried `table-layout: fixed` here, but its column-width caching
     left empty bands on the right when columns were toggled off. Mobile
     still uses fixed layout — it needs the cap to prevent horizontal
     overflow on phone widths. */
}

table.table th {
  text-align: left;
  padding: 10px 12px;
  background: var(--bg-light);
  color: var(--text-dim);
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-bottom: 1px solid var(--border);
}

/* clickable sort header — link inherits th styling, hover hints */
table.table th.sort-th { padding: 0; }
table.table th.sort-th a {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 10px 12px;
  color: inherit;
  width: 100%;
  height: 100%;
  user-select: none;
}
table.table th.sort-th a:hover { background: var(--bg-lighter); color: var(--text); }
table.table th.sort-th .sort-arrow {
  color: var(--blurple);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0;
}

table.table td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  text-align: left;
}
table.table tr:last-child td { border-bottom: none; }
table.table tbody tr:hover { background: var(--bg-light); }
table.table td.tab-num {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
table.table td.online-num { color: var(--green); font-weight: 500; }
table.table .row-link { color: var(--text); font-weight: 600; }
table.table .row-link:hover { color: var(--blurple); }
.row-sub { font-size: 12px; color: var(--text-dim); }

td.icon-cell { width: 36px; padding-right: 0; }
.icon-xs {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--bg-light);
  object-fit: cover;
  display: block;
}
div.icon-xs.icon-fallback {
  display: grid;
  place-items: center;
  font-size: 12px;
  color: var(--text-muted);
  font-weight: 700;
}

.tags {
  display: flex;
  gap: 4px;
  flex-wrap: nowrap;
  overflow: hidden;
  max-width: 240px;
}
.tag {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 600;
  background: var(--bg-light);
  color: var(--text-muted);
  text-transform: lowercase;
  letter-spacing: 0.02em;
  white-space: nowrap;
  flex-shrink: 0;
}
/* Lock every row to exactly 56px regardless of cell contents.
   `height` on td is treated as min-height by browsers, so we also need
   max-height + overflow:hidden to actually clamp tall content (trait chips,
   badge icons, multiple wrapped lines). */
table.table tbody tr { height: 56px; }
table.table tbody td {
  height: 56px;
  max-height: 56px;
  overflow: hidden;
  vertical-align: middle;
  line-height: 1.3;
}
.tag-nsfw { background: rgba(240, 71, 71, 0.18); color: var(--red); }

/* user public_flags → real Discord badge icons (served from Discord's CDN) */
.badges {
  display: inline-flex;
  gap: 3px;
  flex-wrap: nowrap;
  vertical-align: middle;
}
.badge-icon {
  width: 18px;
  height: 18px;
  display: inline-block;
  vertical-align: middle;
}

/* badge multi-select toggles in the filter sidebar */
.badge-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.badge-toggle {
  cursor: pointer;
  opacity: 0.35;
  padding: 3px;
  border-radius: 4px;
  transition: opacity 0.12s, background 0.12s;
  border: 1px solid transparent;
}
.badge-toggle:hover { opacity: 0.7; }
.badge-toggle:has(input:checked) {
  opacity: 1;
  background: var(--bg-lighter);
  border-color: var(--blurple);
}
.badge-toggle input { display: none; }
.badge-toggle .badge-icon { width: 22px; height: 22px; }

/* primary_guild "user tag" — clickable chip, links to source server */
.user-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 6px 1px 3px;
  background: var(--bg-light);
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text);
  vertical-align: middle;
}
.user-tag:hover {
  background: var(--bg-lighter);
  border-color: var(--blurple);
  color: var(--text);
}
.user-tag img {
  width: 14px;
  height: 14px;
  border-radius: 2px;
}

/* user-cell: compact two-line layout in the /users table */
.user-cell { line-height: 1.3; }
.user-display { font-weight: 600; font-size: 14px; }
.user-handle-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  flex-wrap: nowrap;
  overflow: hidden;
}
.user-handle { font-size: 12px; color: var(--text-dim); }

/* Compact user pill grid — used on the server detail page's "users with
   tag" section. Each pill packs avatar + name + badges into one chip and
   flex-wraps; many users fit per row so the section stays dense rather
   than one-per-line scrolling. */
.user-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 24px;
}
.user-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px 3px 3px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 13px;
  color: var(--text);
  max-width: 100%;
  text-decoration: none;
  transition: background 0.12s, border-color 0.12s;
}
.user-pill:hover {
  background: var(--bg-light);
  border-color: var(--blurple);
  color: inherit;
}
.user-pill .icon-xs {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
}
/* Two-line text stack inside a pill: display name on top, @handle below.
   `min-width: 0` lets the inner spans actually ellipsis-truncate inside
   the flex parent (without it, flex children won't shrink past content
   width). */
.user-pill-name {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  min-width: 0;
  max-width: 180px;
}
.user-pill-display,
.user-pill-handle {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.user-pill-display { font-size: 13px; }
.user-pill-handle  { font-size: 11px; color: var(--text-dim); }
.user-pill .badges { gap: 2px; }
.user-pill .badge-icon { width: 14px; height: 14px; }

.profile-handle-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  flex-wrap: wrap;
}

/* server's own clan tag — sits beside the h1 name on the detail page */
.server-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px 3px 5px;
  background: var(--bg-light);
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text);
  vertical-align: middle;
}
.server-tag img {
  width: 18px;
  height: 18px;
  border-radius: 3px;
}
.profile-handle-row .profile-handle { color: var(--text-muted); font-size: 14px; }
.profile-handle-row .badges { gap: 4px; }
.profile-handle-row .badge-icon { width: 22px; height: 22px; }

/* server profile_traits → chips (with optional emoji) */
.trait-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 0 7px;
  height: 20px;
  border-radius: 4px;
  background: var(--bg-light);
  color: var(--text);
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  flex-shrink: 0;
  max-width: 110px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* infinite-scroll sentinel row */
tr.loader td { padding: 0; }
.loading-row {
  text-align: center;
  padding: 14px;
  color: var(--text-dim);
  font-size: 12px;
  font-style: italic;
}

.empty-row { text-align: center; color: var(--text-dim); padding: 32px; }
.muted { color: var(--text-dim); }

.pagination {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 16px;
  font-size: 13px;
  color: var(--text-muted);
}
.pagination a { padding: 6px 12px; border-radius: 4px; }
.pagination a:hover { background: var(--bg-light); color: var(--text); }
.pg-disabled { padding: 6px 12px; color: var(--text-dim); opacity: 0.5; }
.pg-info { color: var(--text-dim); }

/* ----- feed flash animation (for SSE pushes) ----- */

.feed-item.flash {
  animation: feed-flash 0.7s ease-out;
}
@keyframes feed-flash {
  0%   { background: rgba(114, 137, 218, 0.32); }
  100% { background: var(--bg); }
}

/* ----- profile (user/server detail) ----- */

.profile {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 24px;
  /* The banner image and a dark scrim are layered behind everything else
     in this section. position:relative makes those absolute children
     anchor here. */
  position: relative;
}

/* Banner image / colour fallback now stretches to cover the *entire*
   profile section — icon, name, description, AND stats all sit on top
   of it. Beats the old 120px banner-strip-then-flat-card look: way more
   visual impact and reuses dead space. */
.profile-banner {
  position: absolute;
  inset: 0;
  width: 100%;
  height: auto;
  background: var(--bg-light);
  overflow: hidden;
  z-index: 0;
}
.profile-banner img { width: 100%; height: 100%; object-fit: cover; }
.profile-banner .banner-color { width: 100%; height: 100%; }

/* Scrim: darkens whatever banner is behind it so the white text + stats
   read clearly regardless of banner brightness/contrast. The combo of
   a vertical gradient (subtle at top, strong at bottom) plus a flat
   tint guarantees legibility even on solid-white promotional banners
   (looking at you, generic Discord game cover art) without crushing
   already-dark banners to pure black. */
.profile::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, rgba(0,0,0,0.30), rgba(0,0,0,0.78)),
    rgba(0,0,0,0.18);
  z-index: 1;
  pointer-events: none;
}
/* All real content sits above the scrim. */
.profile > .profile-head,
.profile > .stats {
  position: relative;
  z-index: 2;
}

.profile-head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 24px;
}

.icon-lg {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  border: 6px solid var(--bg);
  background: var(--bg-light);
  object-fit: cover;
  display: block;
}
div.icon-lg.icon-fallback {
  display: grid;
  place-items: center;
  font-size: 36px;
  font-weight: 700;
  color: var(--text-muted);
}

.profile-name {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 10px;
}
.profile-handle { color: var(--text-muted); font-size: 14px; margin-top: 2px; }

.bot-tag {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 700;
  background: var(--blurple);
  color: #fff;
  letter-spacing: 0.04em;
}

.profile .stats {
  margin: 0;
  padding: 16px 24px 20px;
  /* Transparent so the banner shows through — the overlay scrim provides
     enough contrast for the white stat text. */
  background: transparent;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}
.profile .stat {
  /* Slight glassy tint so each stat card is still visually grouped on
     a busy banner. */
  background: rgba(0,0,0,0.22);
  border: 1px solid rgba(255,255,255,0.08);
}

/* Light text everywhere inside .profile so it reads against any banner.
   text-shadow gives a soft halo so even bright spots in the banner
   don't bleed into the type. */
.profile .profile-name,
.profile .profile-handle,
.profile .profile-handle-row,
.profile .profile-desc,
.profile .stat-num,
.profile .stat-label {
  color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.45);
}
.profile .profile-desc        { color: rgba(255,255,255,0.92); }
.profile .profile-handle,
.profile .profile-handle-row,
.profile .stat-label          { color: rgba(255,255,255,0.78); }

.profile-desc {
  margin: 8px 0 0;
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1.5;
  max-width: 720px;
}

.game-cards {
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.game-cards .card-stats { font-size: 12px; color: var(--text-muted); }

.guild-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 10px;
  margin: 8px 0 24px;
}
.guild-cards .card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.1s;
}
.guild-cards .card:hover { border-color: var(--blurple); }
.guild-cards .card-static { cursor: default; }
.guild-cards .card-static:hover { border-color: var(--border); }
.guild-cards .card-body { min-width: 0; flex: 1; }
.guild-cards .card-name {
  font-weight: 600;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.guild-cards .card-stats {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 2px;
}

/* tight variant for the /me header (no overlap, smaller padding) */
.profile-head-tight { padding: 14px 18px; gap: 12px; }
.profile-head-tight .icon-lg { width: 64px; height: 64px; border-width: 4px; }
.profile-head-tight div.icon-lg.icon-fallback { font-size: 24px; }
.profile-head-tight .profile-name { font-size: 18px; }

/* ----- inline-svg charts ----- */

.chart {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px 10px;
  margin: 8px 0 24px;
}
/* Plot box: positioning context for the SVG + the HTML axis-label overlays.
   Height includes ~14px at the bottom for the x-axis label row, so the SVG's
   data area + the visible label band exactly match across widths. */
.chart-plot {
  position: relative;
  height: 220px;
  padding-bottom: 14px;
}
.chart-svg {
  display: block;
  width: 100%;
  height: 100%;
}
.chart-grid {
  stroke: var(--border);
  stroke-width: 1;
  stroke-dasharray: 2 4;
}
/* Axis labels are now plain HTML positioned absolutely over the SVG. The
   percent coordinates set inline by the renderer line up with the same
   viewBox positions the path lives in, so labels track the data even when
   the SVG stretches horizontally — without inheriting the SVG's aspect-
   ratio distortion the way <text> would. */
.chart-y-label,
.chart-x-label {
  position: absolute;
  font-size: 10px;
  color: var(--text-dim);
  font-family: inherit;
  line-height: 1;
  pointer-events: none;
  white-space: nowrap;
}
.chart-y-label {
  /* `right:` set inline; vertically center on the gridline. */
  transform: translateY(-50%);
  text-align: right;
}
.chart-x-label {
  /* `left:` set inline; horizontally center under the sample point. */
  bottom: 0;
  transform: translateX(-50%);
}
.chart-legend {
  display: flex;
  gap: 16px;
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 8px;
}
.chart-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.chart-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
}
.chart-empty {
  background: var(--bg);
  border: 1px dashed var(--border);
  border-radius: 8px;
  padding: 32px;
  text-align: center;
  color: var(--text-dim);
  font-size: 13px;
  margin: 8px 0 24px;
}

/* ----- invite code chip (server list + server detail) ----- */

.invite-chip {
  display: inline-block;
  padding: 5px 10px;
  background: var(--bg-light);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  font-weight: 500;
  border-radius: 4px;
  text-decoration: none;
  white-space: nowrap;
  letter-spacing: 0;
  transition: border-color 0.1s, color 0.1s, background 0.1s;
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}
.invite-chip:hover {
  border-color: var(--blurple);
  color: var(--blurple);
  background: var(--bg-lighter);
}
.invite-chip-lg {
  padding: 8px 14px;
  font-size: 14px;
  max-width: none;
}

/* server-detail page: invite chip sits on the right edge of the header */
.profile-head .invite-chip-lg { margin-left: auto; align-self: center; }
td.invite-cell { width: 1%; white-space: nowrap; text-align: right; padding-right: 16px; }

/* a date in a stat (smaller than a giant number) */
.stat-num.stat-date { font-size: 18px; font-weight: 600; }

/* Dual date spans — the long form (Jun 03, 2025) is shown on desktop and
   the compact form (Jun '25) on phones. Swap is driven by media queries
   below. */
.date-full  { display: inline; }
.date-short { display: none; }

/* ----- placeholder for stub pages ----- */

.placeholder {
  background: var(--bg);
  border: 1px dashed var(--border);
  border-radius: 8px;
  padding: 60px 24px;
  text-align: center;
  color: var(--text-muted);
}
.placeholder h3 { color: var(--text); margin: 0 0 8px; }
.placeholder p { margin: 0; font-size: 14px; }

/* ============================================================
 * Responsive — phone & small-tablet adjustments.
 * ============================================================
 *
 * Strategy: stack the sidebar above content (already at 760px),
 * collapse the topbar (hide brand-sub + login text + user-pill name),
 * let tables scroll horizontally inside .table-wrap, and shrink
 * profile / stats / chart paddings so nothing clips off the screen.
 */

@media (max-width: 760px) {
  /* layout shell */
  main { padding: 14px; }
  .topbar-inner { padding: 8px 14px; gap: 10px; flex-wrap: wrap; }
  .brand-sub { display: none; }
  .brand-name { font-size: 16px; }
  .brand-logo { width: 30px; height: 30px; }

  .nav {
    order: 3;
    flex-basis: 100%;
    overflow-x: auto;
    white-space: nowrap;
    scrollbar-width: none;
    gap: 0;
  }
  .nav::-webkit-scrollbar { display: none; }
  .nav a { padding: 6px 10px; font-size: 13px; }

  .login-btn { padding: 6px 10px; font-size: 13px; gap: 6px; }
  .login-btn svg { width: 16px; height: 16px; }
  .user-pill-name { display: none; }
  .logout-btn { font-size: 12px; }

  /* table view: sidebar on top, full width, not sticky */
  .layout { gap: 12px; }
  .sidebar {
    position: static;
    padding: 12px;
  }
  .filter-form { gap: 10px; }

  /* tables on phones: switch to `table-layout: fixed` so column widths
     respect the col[data-col=…] rules below — without that, content
     widths force the table to overflow horizontally. Desktop uses auto
     layout (see the base .table block) which redistributes naturally
     when columns are toggled. */
  .table-wrap { overflow-x: hidden; }
  table.table {
    table-layout: fixed;
    font-size: 11px;
  }
  table.table th, table.table td {
    padding: 5px 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  table.table th { font-size: 9px; padding: 5px 4px; letter-spacing: 0.04em; }
  table.table th.sort-th a { padding: 5px 4px; gap: 2px; }
  table.table th.sort-th .sort-arrow { font-size: 10px; }
  table.table tbody tr,
  table.table tbody td { height: 38px; max-height: 38px; line-height: 1.2; }

  /* Mobile column widths: shrink everything proportionally so cols fit
     a phone width. Override the desktop widths in the base .table block.
     The unsized name/traits/invite cols still auto-distribute remaining
     space, and `visibility: collapse` on toggled-off cols redistributes. */
  table.table col:first-child            { width: 28px; }
  table.table col[data-col="members"]    { width: 56px; }
  table.table col[data-col="online"]     { width: 50px; }
  table.table col[data-col="created"]    { width: 70px; }
  table.table col[data-col="boosts"]     { width: 44px; }
  table.table col[data-col="tag_users"]  { width: 56px; }
  table.table col[data-col="invites"]    { width: 56px; }
  table.table col[data-col="servers"]    { width: 56px; }
  table.table col[data-col="appid"]      { width: 110px; }
  table.table col[data-col="score"]      { width: 80px; }
  table.table th:first-child,
  table.table td:first-child { padding-left: 6px; padding-right: 0; }

  /* Compact icons */
  .icon-xs { width: 22px; height: 22px; }
  div.icon-xs.icon-fallback { font-size: 10px; }

  /* Drop secondary inline text (vanity hint under name) — no room */
  .row-sub { display: none; }

  /* Compact chips */
  .invite-chip { padding: 2px 5px; font-size: 10px; max-width: 100%; }
  .trait-chip { font-size: 8px; padding: 0 3px; height: 14px; max-width: 100%; }
  .tags { gap: 2px; max-width: 100%; }
  /* one chip per cell — the rest get clipped */
  .tags .trait-chip:nth-child(n+2) { display: none; }

  /* swap to compact dates inside table cells (e.g. "Jun '25") */
  .date-full  { display: none; }
  .date-short { display: inline; }

  /* stats strip: condensed grid */
  .stats { gap: 8px; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
  .stat { padding: 10px 12px; }
  .stat-num { font-size: 22px; }
  .stat-num.stat-date { font-size: 15px; }

  /* profile (server / user / game detail) */
  .profile-banner { height: 90px; }
  .profile-head {
    flex-wrap: wrap;
    padding: 12px;
    gap: 10px;
    align-items: flex-start;
  }
  .profile-head .icon-lg { width: 64px; height: 64px; border-width: 4px; }
  .profile-head div.icon-lg.icon-fallback { font-size: 22px; }
  .profile-name { font-size: 18px; flex-wrap: wrap; }
  .profile-handle-row { gap: 6px; }
  .profile-head .invite-chip-lg {
    margin-left: 0;
    align-self: stretch;
    text-align: center;
    flex-basis: 100%;
  }

  /* charts: shorter on phones so the legend + title fit one screen */
  .chart { padding: 10px 10px 6px; }
  .chart-plot { height: 180px; }

  /* card grids on /me + game-cards on server detail */
  .game-cards, .guild-cards { grid-template-columns: 1fr; gap: 8px; }

  /* feed entries: compact spacing */
  .feed-h { font-size: 14px; }

  /* page heading */
  .page-h { font-size: 18px; }
}

@media (max-width: 420px) {
  /* tighter still on small phones */
  main { padding: 10px; }
  .topbar-inner { padding: 6px 10px; }
  .stats { grid-template-columns: 1fr 1fr; }
  .stat-num { font-size: 18px; }
  .stat-num.stat-date { font-size: 13px; }
  .profile-banner { height: 70px; }
  .chart-plot { height: 160px; }
}
