/* ==========================================================================
   ReforgerMods page kit
   --------------------------------------------------------------------------
   The single source of truth for page-level layout and for the data-display
   primitives shared by the mod and server browsers.

   Before this file each page family invented its own shell: .tool-page on the
   config tools, .mod-browser-page on the mod browser, .docs-hero-split on the
   API quickstart, .billing-page-hero on pricing, .guide-header on guides. They
   drifted in padding, type scale, and hero treatment. Everything now composes
   from .rm-page.

   Legacy class names are kept as selector aliases so a page can be converted
   independently of the others. New markup should only use the .rm-* names.

   Loaded after index.css and tools.css, so the kit wins on page-level layout
   while the per-tool component CSS in tools.css keeps its own rules.
   ========================================================================== */

/* -- Kit tokens -------------------------------------------------------------
   Derived from the existing --site-* palette rather than replacing it, so a
   theme change in index.css still propagates everywhere. */
:root {
  --rm-gutter: clamp(1.5rem, 4vw, 3rem);
  --rm-radius: 2px;
  --rm-radius-lg: 6px;
  --rm-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
  --rm-micro-size: 0.68rem;
  --rm-micro-spacing: 0.06em;

  /* Two h1 scales, not five. --rm-h1 titles a working page (tools, browsers,
     detail, policy). --rm-h1-display titles a page whose job is to introduce
     something (home, pricing, API reference). Before this the billing hero,
     the API docs, and the kit hero each had their own clamp and weight, so
     the same site shouted at three different volumes. */
  --rm-h1: clamp(1.45rem, 2.2vw, 1.75rem);
  --rm-h1-display: clamp(1.85rem, 3vw, 2.6rem);
  --rm-h1-weight: 800;
  --rm-scrim: 15, 18, 23;

  /* Trend semantics. Deliberately not red/green alone: the up/down arrow and
     the sign carry the meaning for anyone who cannot separate the hues. */
  --rm-rise: #7fae63;
  --rm-fall: #c2705a;
  --rm-flat: #8b96a0;
}

[data-bs-theme="light"] {
  --rm-rise: #4c7a37;
  --rm-fall: #9e4a34;
  --rm-flat: #6c777f;
}

/* ==========================================================================
   1. Page shell
   ========================================================================== */

/* Full-bleed page that escapes the .docs-content column. */
.rm-page {
  color: var(--site-text-strong);
  margin: -0.5rem calc(50% - 50vw) 0;
  width: 100vw;
}

/* -- Hero ------------------------------------------------------------------
   One hero, three variants. Default is the flat band used by content and
   account pages; -dashboard and -cinematic add imagery. */
.rm-page-hero {
  align-items: flex-start;
  background: var(--site-band);
  border-bottom: 1px solid var(--site-muted-border);
  display: flex;
  gap: 1.5rem;
  justify-content: space-between;
  padding: clamp(2rem, 4vw, 3rem) var(--rm-gutter) 1.45rem;
}

.rm-page-hero > .rm-hero-main {
  min-width: 0;
}

.rm-page-hero h1 {
  color: var(--site-text-strong);
  font-size: var(--rm-h1);
  font-weight: var(--rm-h1-weight);
  letter-spacing: 0;
  margin: 0 0 0.45rem;
}

.rm-page-hero .rm-lede {
  color: var(--site-text-muted) !important;
  font-size: 0.92rem;
  line-height: 1.45;
  margin: 0;
  max-width: 74ch;
}

.rm-kicker {
  color: var(--site-accent);
  font-family: var(--rm-mono);
  font-size: 0.67rem;
  letter-spacing: var(--rm-micro-spacing);
  margin-bottom: 0.7rem;
  text-transform: uppercase;
}

.rm-hero-aside {
  color: var(--site-text-faint);
  flex: 0 0 auto;
  font-family: var(--rm-mono);
  font-size: 0.72rem;
  padding-top: 0.7rem;
}

/* Dashboard variant: image bleeds in from the right, copy stays on a scrim
   dark enough to hold AA contrast regardless of the photo behind it. */
.rm-page-hero-dashboard {
  background:
    linear-gradient(90deg, rgba(var(--rm-scrim), 0.97) 0%, rgba(var(--rm-scrim), 0.88) 34%, rgba(var(--rm-scrim), 0.58) 70%, rgba(var(--rm-scrim), 0.8) 100%),
    linear-gradient(180deg, rgba(var(--rm-scrim), 0.24) 0%, rgba(var(--rm-scrim), 0.6) 62%, rgba(var(--rm-scrim), 0.97) 100%),
    url("/static/assets/web/dashboard-hero.jpg") right 24% / cover no-repeat;
  /* No rule here: see the note above. The closing scrim is the separator. */
  border-bottom: 0;
  min-height: 260px;
  padding: clamp(1.9rem, 4vw, 2.7rem) var(--rm-gutter) 1.6rem;
}

.rm-page-hero-dashboard h1 {
  color: #eef2f5;
  max-width: 28ch;
}

.rm-page-hero-dashboard .rm-lede {
  color: rgba(216, 224, 230, 0.84) !important;
  max-width: 52ch;
}

.rm-page-hero-dashboard .rm-kicker {
  color: var(--site-accent-strong);
}

/* Over a photo the aside cannot rely on the page background for contrast, so
   it carries its own scrim. tools.css used to hide it outright on this hero
   variant; the kit puts real content there (the live badge, the index note,
   the last-updated date), so it stays visible and stays readable instead. */
.rm-page-hero-dashboard .rm-hero-aside {
  align-self: flex-start;
  background: rgba(var(--rm-scrim), 0.62);
  border: 1px solid rgba(216, 224, 230, 0.16);
  border-radius: var(--rm-radius);
  color: rgba(226, 233, 238, 0.88);
  padding: 0.35rem 0.6rem;
}

.rm-page-hero-dashboard .rm-hero-aside .bi {
  color: var(--site-accent-strong);
}

.rm-page-hero-dashboard .rm-hero-aside a {
  color: var(--site-accent-strong);
}

/* Cinematic variant: tall, centred, fades into the page background. */
.rm-page-hero-cinematic {
  align-items: center;
  background:
    linear-gradient(180deg, rgba(var(--rm-scrim), 0.35) 0%, rgba(var(--rm-scrim), 0.3) 45%, var(--site-bg) 98%),
    url("/static/assets/web/guides-hero.jpg") center 62% / cover no-repeat;
  border-bottom: 0;
  flex-direction: column;
  justify-content: center;
  min-height: min(52vh, 480px);
  padding: clamp(4rem, 9vw, 6rem) var(--rm-gutter) clamp(2.5rem, 6vw, 4rem);
  text-align: center;
}

.rm-page-hero-cinematic h1 {
  color: #eef2f5;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.55);
}

.rm-page-hero-cinematic .rm-lede {
  color: rgba(222, 229, 235, 0.92) !important;
  margin: 0 auto;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);
}

.rm-page-hero-cinematic .rm-hero-chips {
  justify-content: center;
}

.rm-page-hero-cinematic .rm-hero-aside {
  color: rgba(216, 224, 230, 0.55);
  padding-top: 1rem;
}

/* Hero facts: short, factual capability chips. Kept to a single row so the
   hero cannot grow into a feature wall. */
.rm-hero-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 1rem;
}

.rm-hero-chips > * {
  align-items: center;
  background: var(--site-band);
  border: 1px solid var(--site-muted-border);
  border-radius: var(--rm-radius);
  color: var(--site-text-muted);
  display: inline-flex;
  font-family: var(--rm-mono);
  font-size: var(--rm-micro-size);
  gap: 0.35rem;
  letter-spacing: var(--rm-micro-spacing);
  padding: 0.3rem 0.55rem;
  text-transform: uppercase;
}

.rm-hero-chips .bi {
  color: var(--site-accent-strong);
  font-size: 0.8rem;
}

.rm-page-hero-dashboard .rm-hero-chips > *,
.rm-page-hero-cinematic .rm-hero-chips > * {
  background: rgba(var(--rm-scrim), 0.55);
  border-color: rgba(216, 224, 230, 0.18);
  color: rgba(222, 229, 235, 0.86);
}

/* -- Bands ----------------------------------------------------------------- */
.rm-band {
  background: var(--site-bg);
  padding: 1.5rem var(--rm-gutter) 1.9rem;
}

/* The browsers want their working surface flush with the hero, not floating
   in a padded band. */
.rm-band-flush {
  background: var(--site-band);
  border-bottom: 1px solid var(--site-muted-border);
  padding: 1.4rem var(--rm-gutter) 1.5rem;
}

.rm-band-wide {
  padding-bottom: 2.5rem;
}

.rm-band > .rm-panel {
  background: var(--site-band);
  border: 1px solid var(--site-muted-border);
  border-radius: var(--rm-radius-lg);
  margin: 0;
}

/* -- Supporting prose grid ------------------------------------------------- */
/* The supporting prose at the foot of a page. It was reading as a solid slab
   of small text: two tight columns, full-viewport measure, and no separation
   from the FAQ below it. The fix is air rather than fewer words -- a real
   column gap, a capped measure so lines stay scannable on a wide monitor, and
   a hairline between rows so each section reads as its own block. */
.rm-info-grid {
  background: var(--site-band);
  border-top: 1px solid var(--site-muted-border);
  column-gap: clamp(2.5rem, 6vw, 6rem);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  padding: clamp(2.5rem, 5vw, 3.75rem) var(--rm-gutter);
  row-gap: 0;
}

/* Rows are separated by a rule on the section itself, so the last row in each
   column does not trail one. */
.rm-info-grid section {
  border-bottom: 1px solid var(--site-muted-border);
  margin-bottom: 1.6rem;
  max-width: 46rem;
  padding-bottom: 1.6rem;
}

.rm-info-grid section:last-child,
.rm-info-grid section:nth-last-child(2):nth-child(odd) {
  border-bottom: 0;
  margin-bottom: 0;
  padding-bottom: 0;
}

.rm-info-grid h2 {
  border: 0;
  color: var(--site-text-strong);
  font-size: 0.95rem;
  letter-spacing: -0.005em;
  margin: 0 0 0.5rem;
  padding: 0;
}

.rm-info-grid p,
.rm-info-grid li {
  color: var(--site-text-muted);
  font-size: 0.875rem;
  line-height: 1.7;
  max-width: 64ch;
}

.rm-info-grid p {
  margin: 0 0 0.6rem;
}

.rm-info-grid p:last-child {
  margin-bottom: 0;
}

/* ==========================================================================
   2. Surfaces
   ========================================================================== */

.rm-panel {
  background: var(--site-panel);
  border: 1px solid var(--site-border);
  border-radius: var(--rm-radius-lg);
  padding: 1.25rem;
  width: 100%;
}

@media (min-width: 992px) {
  .rm-panel {
    padding: 1.75rem 2rem;
  }
}

.rm-panel-head {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: space-between;
  margin-bottom: 0.9rem;
}

.rm-panel-title {
  color: var(--site-text-strong);
  font-size: 0.95rem;
  font-weight: 700;
  margin: 0;
}

/* Uppercase monospace micro-label. The site's most reused text treatment;
   it existed as .tool-col-head, .sb-field-label, .guide-index-section-title,
   and three more one-offs. */
.rm-label {
  color: var(--site-text-faint);
  font-family: var(--rm-mono);
  font-size: var(--rm-micro-size);
  letter-spacing: var(--rm-micro-spacing);
  text-transform: uppercase;
}

.rm-label .bi {
  color: var(--site-accent-strong);
  margin-right: 0.3rem;
}

.rm-status,
.tool-status {
  color: var(--site-text-muted);
  font-size: 0.9rem;
  margin: 0.5rem 0;
  min-height: 1.5rem;
}

.rm-status .spinner-border,
.tool-status .spinner-border {
  border-width: 0.14em;
  height: 0.9rem;
  margin-right: 0.4rem;
  vertical-align: -0.1em;
  width: 0.9rem;
}

.rm-status-error,
.tool-status-error {
  color: var(--bs-danger-text-emphasis);
}

.rm-empty {
  align-items: center;
  border: 1px dashed var(--site-border);
  border-radius: var(--rm-radius-lg);
  color: var(--site-text-faint);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  font-size: 0.9rem;
  justify-content: center;
  padding: 2.5rem 1.5rem;
  text-align: center;
}

.rm-empty .bi {
  font-size: 1.5rem;
  opacity: 0.55;
}

.rm-breadcrumb {
  color: var(--site-text-muted);
  font-size: 0.85rem;
  margin-bottom: 0.75rem;
}

/* ==========================================================================
   3. Data display primitives

   These back the "how is this trending / how much is this used" surfaces in
   the mod and server browsers. Every one of them renders nothing when its
   data is absent, so a browser row degrades to exactly what it shows today
   until the corresponding API field starts being populated.
   ========================================================================== */

/* -- Stat tile ------------------------------------------------------------- */
.rm-stat-grid {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
}

.rm-stat {
  background: var(--site-inset);
  border: 1px solid var(--site-muted-border);
  border-radius: var(--rm-radius);
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
  padding: 0.6rem 0.7rem;
}

.rm-stat-label {
  color: var(--site-text-faint);
  font-family: var(--rm-mono);
  font-size: var(--rm-micro-size);
  letter-spacing: var(--rm-micro-spacing);
  text-transform: uppercase;
  white-space: nowrap;
}

.rm-stat-value {
  align-items: baseline;
  color: var(--site-text-strong);
  display: flex;
  flex-wrap: wrap;
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  gap: 0.35rem;
  line-height: 1.2;
}

.rm-stat-unit {
  color: var(--site-text-faint);
  font-size: 0.72rem;
  font-weight: 500;
}

.rm-stat-sub {
  color: var(--site-text-faint);
  font-size: 0.72rem;
  line-height: 1.3;
}

/* Compact inline variant for card and row footers. */
.rm-stat-inline {
  background: none;
  border: 0;
  flex-direction: row;
  align-items: baseline;
  gap: 0.3rem;
  padding: 0;
}

.rm-stat-inline .rm-stat-value {
  font-size: 0.85rem;
}

/* -- Trend chip ------------------------------------------------------------
   Direction is encoded three ways (arrow glyph, sign, colour) so it survives
   colour-blindness and greyscale printing. */
.rm-trend {
  align-items: center;
  border-radius: var(--rm-radius);
  display: inline-flex;
  font-family: var(--rm-mono);
  font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
  gap: 0.2rem;
  line-height: 1;
  padding: 0.16rem 0.3rem;
  white-space: nowrap;
}

.rm-trend .bi {
  font-size: 0.72rem;
}

.rm-trend.is-up {
  background: color-mix(in srgb, var(--rm-rise) 14%, transparent);
  color: var(--rm-rise);
}

.rm-trend.is-down {
  background: color-mix(in srgb, var(--rm-fall) 14%, transparent);
  color: var(--rm-fall);
}

.rm-trend.is-flat {
  background: color-mix(in srgb, var(--rm-flat) 12%, transparent);
  color: var(--rm-flat);
}

/* First time this entity has been seen in the index. */
.rm-trend.is-new {
  background: var(--site-accent-dim);
  color: var(--site-accent-strong);
}

/* -- Sparkline -------------------------------------------------------------
   Inline SVG, no chart library. Sized by its container so it can sit in a
   table cell, a card footer, or a stat tile without per-site overrides. */
.rm-spark {
  color: var(--site-accent);
  display: block;
  height: 1.6rem;
  overflow: visible;
  width: 100%;
}

.rm-spark-line {
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}

.rm-spark-area {
  fill: currentColor;
  opacity: 0.13;
  stroke: none;
}

.rm-spark-last {
  fill: currentColor;
  stroke: none;
}

.rm-spark.is-up { color: var(--rm-rise); }
.rm-spark.is-down { color: var(--rm-fall); }
.rm-spark.is-flat { color: var(--rm-flat); }

/* -- Meter -----------------------------------------------------------------
   Population / capacity / storage fill. */
.rm-meter {
  background: var(--site-muted-border);
  border-radius: 999px;
  height: 4px;
  overflow: hidden;
  width: 100%;
}

.rm-meter-fill {
  background: var(--site-accent);
  border-radius: inherit;
  display: block;
  height: 100%;
  transition: width 0.25s ease;
}

.rm-meter-fill.is-full { background: var(--rm-fall); }
.rm-meter-fill.is-high { background: var(--site-accent-strong); }

/* -- Rank movement ---------------------------------------------------------
   Position change since the previous ranking window. */
.rm-rankmove {
  align-items: center;
  display: inline-flex;
  font-family: var(--rm-mono);
  font-size: 0.63rem;
  font-variant-numeric: tabular-nums;
  gap: 0.1rem;
  line-height: 1;
}

.rm-rankmove.is-up { color: var(--rm-rise); }
.rm-rankmove.is-down { color: var(--rm-fall); }
.rm-rankmove.is-flat { color: var(--rm-flat); }

/* -- Chip / tag ------------------------------------------------------------ */
.rm-chip {
  align-items: center;
  background: var(--site-hover-bg);
  border: 1px solid var(--site-muted-border);
  border-radius: var(--rm-radius);
  color: var(--site-text-muted);
  display: inline-flex;
  font-size: 0.72rem;
  gap: 0.25rem;
  line-height: 1.2;
  padding: 0.2rem 0.4rem;
  white-space: nowrap;
}

.rm-chip-accent {
  background: var(--site-accent-dim);
  border-color: transparent;
  color: var(--site-accent-strong);
}

/* -- Meta line -------------------------------------------------------------
   The icon+value run used under card and row titles. */
.rm-metaline {
  align-items: center;
  color: var(--site-text-muted);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.78rem;
  gap: 0.25rem 0.7rem;
  min-width: 0;
}

.rm-metaline > span {
  align-items: center;
  display: inline-flex;
  gap: 0.28rem;
  min-width: 0;
}

.rm-metaline .bi {
  color: var(--site-text-faint);
  font-size: 0.8rem;
}

.rm-metaline .rm-num {
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   4. Utilities
   ========================================================================== */

.rm-num {
  font-variant-numeric: tabular-nums;
}

/* Single-line truncation that still shrinks inside a flex row. */
.rm-truncate {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rm-sr-only {
  clip: rect(0, 0, 0, 0);
  border: 0;
  height: 1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

@media (prefers-reduced-motion: reduce) {
  .rm-meter-fill {
    transition: none;
  }
}

/* ==========================================================================
   5. Responsive
   ========================================================================== */

@media (max-width: 991.98px) {
  .rm-page-hero {
    flex-direction: column;
    gap: 1rem;
  }

  .rm-hero-aside {
    padding-top: 0;
  }
}

@media (max-width: 767.98px) {
  .rm-info-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .rm-stat-grid {
    grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  }
}

/* ==========================================================================
   6. Hero imagery
   --------------------------------------------------------------------------
   Per-page art as modifiers on the dashboard hero, so every page shares one
   scrim, one type scale, and one set of paddings and only swaps the photo.
   Previously each page redeclared the whole background shorthand.
   ========================================================================== */

/* Taller than the other photo heroes: this one is carrying the artwork, not
   just tinting behind two lines of copy. */
.rm-page-hero-dashboard.rm-hero-mods {
  background-image:
    linear-gradient(90deg, rgba(var(--rm-scrim), 0.96) 0%, rgba(var(--rm-scrim), 0.84) 34%, rgba(var(--rm-scrim), 0.44) 70%, rgba(var(--rm-scrim), 0.62) 100%),
    linear-gradient(180deg, rgba(var(--rm-scrim), 0.18) 0%, rgba(var(--rm-scrim), 0.5) 62%, rgba(var(--rm-scrim), 0.97) 100%),
    url("/static/assets/web/mod-browser-hero.jpg");
  background-position: center 26%;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 360px;
}

/* A squad on the move past a line of hueys: a populated server, which is what
   the page is for. Positioned low so the group stays in the crop. */
.rm-page-hero-dashboard.rm-hero-servers {
  background-image:
    linear-gradient(90deg, rgba(var(--rm-scrim), 0.96) 0%, rgba(var(--rm-scrim), 0.85) 34%, rgba(var(--rm-scrim), 0.46) 70%, rgba(var(--rm-scrim), 0.66) 100%),
    linear-gradient(180deg, rgba(var(--rm-scrim), 0.2) 0%, rgba(var(--rm-scrim), 0.52) 62%, rgba(var(--rm-scrim), 0.97) 100%),
    url("/static/assets/web/helicopters-soldiers.jpg");
  background-position: center 62%;
  background-repeat: no-repeat;
  background-size: cover;
}

.rm-page-hero-dashboard.rm-hero-barracks {
  background-image:
    linear-gradient(90deg, rgba(var(--rm-scrim), 0.97) 0%, rgba(var(--rm-scrim), 0.88) 34%, rgba(var(--rm-scrim), 0.58) 70%, rgba(var(--rm-scrim), 0.82) 100%),
    linear-gradient(180deg, rgba(var(--rm-scrim), 0.2), rgba(var(--rm-scrim), 0.58)),
    url("/static/assets/web/barracks.jpg");
  background-position: right 18% bottom 35%;
  background-repeat: no-repeat;
  background-size: cover;
}

/* ==========================================================================
   7. Browser layout
   --------------------------------------------------------------------------
   The mod and server browsers are the same shape: a sticky-ish control band,
   a result summary line, results, then a pager. Shared here so the two stay
   aligned as statistical columns are added to either one.
   ========================================================================== */

.rm-browser-controls {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* Primary control row: search grows, everything else keeps its size. */
.rm-browser-search-row {
  align-items: stretch;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.rm-browser-search-row > .rm-browser-search-group {
  display: flex;
  flex: 1 1 22rem;
  min-width: 0;
}

.rm-browser-search-row > .rm-browser-actions {
  display: flex;
  flex: 0 0 auto;
  gap: 0.75rem;
}

/* Filter row: a label column and a wrapping run of toggles. */
.rm-browser-filter-row {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.9rem;
}

/* Inline in a control row, not heading a panel, so it drops the block
   label's bottom margin and stays on the row's centre line. */
.rm-browser-filter-row > .rm-label {
  flex: 0 0 auto;
  margin-bottom: 0;
}

/* Summary strip between the controls and the results: counts on the left,
   result-scope switches on the right. */
.rm-browser-summary {
  align-items: center;
  border-bottom: 1px solid var(--site-muted-border);
  color: var(--site-text-muted);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.82rem;
  gap: 0.5rem 1rem;
  justify-content: space-between;
  min-height: 2.25rem;
  padding-bottom: 0.6rem;
}

.rm-browser-summary strong {
  color: var(--site-text-strong);
  font-variant-numeric: tabular-nums;
}

.rm-browser-summary-note {
  align-items: center;
  color: var(--site-text-faint);
  display: inline-flex;
  font-size: 0.76rem;
  gap: 0.3rem;
}

/* ==========================================================================
   8. Mod result card
   --------------------------------------------------------------------------
   Consolidates the card rules that were previously spread over three blocks
   in tools.css (base at :80, hover at :1002, theme at :1923) and adds the
   measurement rail. Loading last, this section is the one that wins.
   ========================================================================== */

.mod-grid {
  display: grid;
  gap: 0.9rem;
  /* Wider than the old 320px floor: the stat rail needs two tiles per row
     without the labels wrapping. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  margin: 0.75rem 0 1.25rem;
}

.mod-card {
  background: var(--site-card);
  border: 1px solid var(--site-muted-border);
  border-radius: var(--rm-radius-lg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.mod-card:hover,
.mod-card:focus-within {
  border-color: var(--site-accent);
  box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.18), 0 0 0 1px var(--site-accent-dim);
  transform: translateY(-2px);
}

.mod-card-media {
  display: block;
  line-height: 0;
}

.mod-card-body {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  gap: 0.5rem;
  padding: 0.8rem 0.85rem 0.85rem;
}

.mod-card .mod-card-title {
  font-size: 0.98rem;
  font-weight: 700;
  line-height: 1.25;
  margin: 0;
  overflow-wrap: anywhere;
}

.mod-card-meta {
  gap: 0.2rem 0.75rem;
}

/* Tags sit between identity and measurement so the eye reads
   "what is it" -> "what kind" -> "how big / how used". */
.mod-card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

/* Three across on one line, and light enough to read as a measurement strip
   rather than three cards nested inside the card. */
.mod-card .rm-stat-grid {
  border-block: 1px solid var(--site-muted-border);
  gap: 0;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.mod-card .rm-stat {
  background: none;
  border: 0;
  border-left: 1px solid var(--site-muted-border);
  gap: 0.05rem;
  padding: 0.4rem 0.55rem;
}

.mod-card .rm-stat:first-child {
  border-left: 0;
  padding-left: 0;
}

.mod-card .rm-stat-label {
  font-size: 0.575rem;
  opacity: 0.8;
}

.mod-card .rm-stat-value {
  font-size: 0.88rem;
}

.mod-card .rm-stat-sub {
  font-size: 0.66rem;
}

.mod-card-spark {
  margin-top: -0.1rem;
}

.mod-card-spark .rm-spark {
  height: 1.35rem;
}

.mod-card-id {
  align-items: center;
  display: flex;
  font-size: 0.75rem;
  gap: 0.35rem;
}

.mod-card-id code {
  color: var(--site-accent);
  overflow-wrap: anywhere;
}

.mod-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: auto;
  padding-top: 0.55rem;
}

.mod-card-actions .btn {
  --bs-btn-padding-y: 0.2rem;
  --bs-btn-padding-x: 0.5rem;
  --bs-btn-font-size: 0.78rem;
}

/* -- List view -------------------------------------------------------------
   A list should read as a list, not as a card turned on its side. The cover
   art goes (it is the single biggest thing making the old list view as tall
   as the grid), the body becomes one row, and everything that only earns its
   place at card size -- tags, the sparkline, the size sub-label -- drops out.
   What is left is title and author on the left, the numbers in a fixed rail,
   then the ID and actions. */
/* No container border: the rows already read as a list, and a box around
   them sat inside the band's own frame as a second, redundant outline. Rows
   are separated by a small gap instead of a rule, the way the server browser
   list is. */
.mod-grid-list {
  border: 0;
  gap: 0.2rem;
  grid-template-columns: minmax(0, 1fr);
  margin: 0.25rem 0 1.25rem;
}

/* Every row is exactly the same height. Nothing inside is allowed to grow it:
   the thumbnail stretches to fill, the text truncates, and the stat rail is
   fixed width. Uneven rows were the main thing making the list feel unruly. */
.mod-grid-list .mod-card {
  align-items: stretch;
  background: var(--site-card);
  border: 1px solid transparent;
  border-radius: 3px;
  cursor: pointer;
  display: flex;
  flex-direction: row;
  height: 3.6rem;
  overflow: hidden;
  transform: none;
}

.mod-grid-list .mod-card:hover,
.mod-grid-list .mod-card:focus-within {
  background: var(--site-card-hover);
  border-color: var(--site-muted-border);
  box-shadow: none;
  transform: none;
}

/* Controls inside the row keep the default pointer and their own click
   target; only the dead space navigates. */
.mod-grid-list .mod-card button,
.mod-grid-list .mod-card a,
.mod-grid-list .mod-card code {
  cursor: auto;
}

.mod-grid-list .mod-card button,
.mod-grid-list .mod-card a {
  cursor: pointer;
}

.mod-grid-list .mod-card-tags,
.mod-grid-list .mod-card-spark {
  display: none;
}

/* A thumbnail earns its place as a recognition cue, but it must never drive
   the row height: fixed box, fixed ratio, image cropped to fit inside it. */
.mod-grid-list .mod-card-media {
  align-self: stretch;
  background: var(--site-inset);
  border-radius: 0;
  display: block;
  flex: 0 0 auto;
  overflow: hidden;
  width: 4.5rem;
}

.mod-grid-list .mod-card-media .mod-card-image,
.mod-grid-list .mod-card-media .mod-card-image-placeholder {
  aspect-ratio: auto;
  height: 100%;
  object-fit: cover;
  width: 100%;
}

.mod-grid-list .mod-card-media .mod-card-image-placeholder {
  font-size: 0.6rem;
}

.mod-grid-list .mod-card-media .mod-card-image-placeholder::after {
  content: "";
}

.mod-grid-list .mod-card-media .mod-card-image-placeholder .bi {
  display: block;
  font-size: 0.8rem;
}

.mod-grid-list .mod-card-body {
  align-items: center;
  display: flex;
  flex-direction: row;
  gap: 0.85rem;
  height: 100%;
  min-height: 0;
  min-width: 0;
  padding: 0 0.75rem;
  width: 100%;
}

/* The title is the thing you scan a list for, so it takes the slack and is
   the largest text in the row. Everything after it is fixed width. */
.mod-grid-list .mod-card-title {
  align-items: center;
  display: flex;
  flex: 1 1 16rem;
  font-size: 1rem;
  min-width: 0;
}

.mod-grid-list .mod-card-title a {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Author, age, and version get their own fixed columns rather than a wrapping
   flex run, so they line up down the list instead of shifting with the length
   of whatever is beside them. Each truncates rather than wrapping to a second
   line, which is what made rows uneven. */
.mod-grid-list .mod-card-meta {
  align-items: center;
  display: grid;
  flex: 0 0 auto;
  font-size: 0.76rem;
  gap: 0 0.85rem;
  grid-template-columns: 9rem 5rem 5rem;
}

.mod-grid-list .mod-card-meta > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The stat rail flattens into a fixed run of label-over-value pairs so the
   numbers line up down the list instead of ragging with the titles. */
.mod-grid-list .mod-card .rm-stat-grid {
  border-block: 0;
  display: flex;
  flex: 0 0 auto;
  gap: 0.35rem;
}

.mod-grid-list .mod-card .rm-stat {
  border-left: 0;
  padding-left: 0;
}

.mod-grid-list .mod-card .rm-stat.is-empty .rm-stat-value {
  color: var(--site-text-faint);
  font-weight: 400;
}

.mod-grid-list .mod-card .rm-stat {
  background: none;
  border: 0;
  gap: 0;
  padding: 0;
  text-align: right;
  width: 4.75rem;
}

.mod-grid-list .mod-card .rm-stat-label {
  font-size: 0.56rem;
  opacity: 0.75;
}

.mod-grid-list .mod-card .rm-stat-value {
  font-size: 0.78rem;
  font-weight: 600;
  justify-content: flex-end;
}

/* The full-precision sub-line is card-only; the rail is too tight for it. */
.mod-grid-list .mod-card .rm-stat-sub {
  display: none;
}

.mod-grid-list .mod-card-id {
  flex: 0 0 auto;
  font-size: 0.7rem;
}

.mod-grid-list .mod-card-actions {
  flex: 0 0 auto;
  flex-wrap: nowrap;
  margin-top: 0;
  padding-top: 0;
}

/* Below the point where the rail and the title can share a line, the row
   stacks rather than crushing the title to nothing. */
@media (max-width: 991.98px) {
  .mod-grid-list .mod-card-body {
    align-items: flex-start;
    flex-direction: column;
    gap: 0.45rem;
    padding: 0.7rem 0.75rem;
  }

  .mod-grid-list .mod-card-title,
  .mod-grid-list .mod-card-meta {
    flex: 0 0 auto;
    white-space: normal;
  }

  .mod-grid-list .mod-card .rm-stat {
    text-align: left;
    width: auto;
  }

  .mod-grid-list .mod-card .rm-stat-value {
    justify-content: flex-start;
  }
}

/* ==========================================================================
   9. Server browser: trend column
   --------------------------------------------------------------------------
   The list grid is declared in three places in tools.css (base, dense, and
   the two mobile breakpoints). Rather than editing all of them, the column is
   added here, where the kit's later cascade position lets one declaration
   cover every case.
   ========================================================================== */

.sb-list-head,
.sb-row {
  grid-template-columns: 96px minmax(0, 1fr) 86px 128px 82px 116px 76px 58px;
}

.sb-h-trend {
  text-align: center;
}

.sb-row-trend {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  justify-content: center;
  min-width: 0;
  overflow: hidden;
}

.sb-row-trend .rm-spark {
  height: 1.1rem;
  /* Muted until hover: the trend is supporting evidence, not the thing you
     scan the list for. */
  opacity: 0.75;
}

.sb-row:hover .sb-row-trend .rm-spark,
.sb-row[aria-current="true"] .sb-row-trend .rm-spark {
  opacity: 1;
}

.sb-trend-none {
  color: var(--site-text-faint);
  font-size: 0.8rem;
  opacity: 0.4;
}

.sb-row.is-offline .sb-row-trend {
  opacity: 0.45;
}

@media (max-width: 1199.98px) {
  /* Squeeze the ping-region column before the trend column: region is a
     filter you have already applied, the trend is new information. */
  .sb-list-head,
  .sb-row {
    grid-template-columns: 84px minmax(0, 1fr) 78px 104px 76px 106px 68px 52px;
  }
}

@media (max-width: 900px) {
  /* Stacked rows have no room for a chart; the numeric chip still fits and
     carries the same information. */
  .sb-list-head,
  .sb-row {
    grid-template-columns: 42px minmax(0, 1fr) auto;
  }

  .sb-row-trend {
    grid-area: trend;
    flex-direction: row;
    justify-self: start;
  }

  .sb-row-trend .rm-spark {
    display: none;
  }

  .sb-trend-none {
    display: none;
  }

  .sb-row {
    grid-template-areas:
      "rank name players"
      "rank plat ping"
      "rank region mods"
      "rank trend trend";
  }

  /* Nothing to show means no fourth row at all. */
  .sb-row:not(:has(.sb-row-trend .rm-trend)) {
    grid-template-areas:
      "rank name players"
      "rank plat ping"
      "rank region mods";
  }

  .sb-row:not(:has(.sb-row-trend .rm-trend)) .sb-row-trend {
    display: none;
  }
}

@media (max-width: 900px) {
  .rm-page-hero-dashboard {
    background-position: center;
    min-height: auto;
    padding: 1.35rem 1rem 1.05rem;
  }

  .rm-page-hero-dashboard h1 {
    font-size: 1.45rem;
    max-width: none;
  }

  .rm-page-hero-dashboard .rm-lede {
    font-size: 0.9rem;
    line-height: 1.5;
    max-width: none;
  }

  .rm-hero-chips {
    gap: 0.35rem;
    margin-top: 0.75rem;
  }

  .rm-hero-chips > * {
    font-size: 0.63rem;
    padding: 0.22rem 0.42rem;
  }
}

/* ==========================================================================
   10. Full-bleed pages inside the content column
   --------------------------------------------------------------------------
   .tool-content-full caps its children at 76rem to keep prose readable. A
   kit page opts out: it manages its own measure per band, and the cap would
   otherwise fight the 100vw + negative-margin full-bleed. tools.css already
   carved out .mod-browser-page and .api-docs-page individually; this is the
   general rule for every kit page.
   ========================================================================== */

.tool-content-full > .rm-page {
  max-width: none;
}

/* Long-form prose inside a full-bleed band still needs a measure. */
.rm-prose {
  margin: 0 auto;
  max-width: 76rem;
}

.rm-prose > h2 {
  margin-top: 2rem;
}

.rm-prose > h2:first-child {
  margin-top: 0;
}

/* -- Micro-label colour modifiers ------------------------------------------
   .rm-label carries the typography; these carry the emphasis. Before this
   the same four declarations were repeated as .guide-index-section-title,
   .billing-kicker, .changelog-kicker, .api-docs-kicker, and .api-code-title,
   each with its own font-size and letter-spacing drift. */
.rm-label-accent {
  color: var(--site-accent-strong);
}

.rm-label-blue {
  color: var(--site-blue);
}

/* -- Hero details ---------------------------------------------------------- */

/* A breadcrumb inside the hero sits above the kicker, not in its own band. */
.rm-page-hero .rm-breadcrumb {
  margin-bottom: 0.55rem;
}

.rm-hero-aside code {
  color: var(--site-accent);
  font-size: inherit;
}

/* ==========================================================================
   11. Inline hero
   --------------------------------------------------------------------------
   The sidebar layouts (changelog, API reference) cannot use the full-bleed
   hero: their heading sits inside a content column next to a nav rail. This
   is the same kicker / title / lede / aside vocabulary at the same type
   scale, separated by a rule instead of a band. It replaces .changelog-hero
   and the ad-hoc first section of each API reference page.
   ========================================================================== */

.rm-hero-inline {
  border-bottom: 1px solid var(--site-border);
  margin-bottom: 2rem;
  padding-bottom: 1.75rem;
}

.rm-hero-inline > .rm-kicker {
  margin-bottom: 0.55rem;
}

.rm-hero-inline .rm-lede {
  color: var(--site-text-muted);
  font-size: 0.96rem;
  line-height: 1.7;
  margin: 0.85rem 0 0;
  max-width: 72ch;
}

/* "Last updated" and similar provenance lines. */
.rm-hero-meta {
  color: var(--site-text-faint);
  font-family: var(--rm-mono);
  font-size: 0.74rem;
  margin: 0.5rem 0 0;
}

/* ==========================================================================
   12. Page FAQ
   --------------------------------------------------------------------------
   Emitted by the site template beneath the info grid. It used to inherit the
   surrounding prose styles and render as an undifferentiated ribbon of h3/p
   pairs at full page width, directly under an already dense two-column slab.
   It now gets its own band, its own measure, and a two-column layout so it
   reads as a distinct, finite section rather than more of the same.
   ========================================================================== */

.page-faq {
  background: var(--site-bg);
  border-top: 1px solid var(--site-muted-border);
  padding: clamp(2.5rem, 5vw, 3.75rem) var(--rm-gutter);
}

.page-faq-inner {
  margin: 0 auto;
  max-width: 76rem;
}

.page-faq h2 {
  border: 0;
  color: var(--site-text-strong);
  font-size: 1.05rem;
  margin: 0 0 1.5rem;
  padding: 0;
}

.page-faq-list {
  column-gap: clamp(2.5rem, 6vw, 6rem);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  row-gap: 1.6rem;
}

.page-faq-item h3 {
  border: 0;
  color: var(--site-text-strong);
  font-size: 0.92rem;
  font-weight: 700;
  margin: 0 0 0.4rem;
  padding: 0;
}

.page-faq-item p {
  color: var(--site-text-muted);
  font-size: 0.875rem;
  line-height: 1.7;
  margin: 0;
  max-width: 64ch;
}

@media (max-width: 767.98px) {
  .page-faq-list {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ==========================================================================
   13. Server detail: masthead and vitals
   --------------------------------------------------------------------------
   The page used to open with a banner and then a separate card floating
   underneath carrying the name, badges, and player count -- the shape every
   general-purpose server tracker uses. The banner is kept, and kept large,
   because it is what makes a server read as a place. What changed is what
   sits on it and under it:

     - the identity moved onto the banner, as a masthead rather than a
       detached card, using the same scrim treatment as the site's other
       photo heroes;
     - a vitals rail is fused to the bottom of the masthead, so the numbers
       a visitor actually came for are answered in the first view;
     - the mod manifest now leads the main column and population history
       follows it, rather than the chart taking the hero slot.

   Nothing was dropped. Every figure still has its full context in the panels
   below; the rail hoists the existing elements rather than duplicating them.
   ========================================================================== */

.sd-masthead {
  border-radius: var(--rm-radius-lg) var(--rm-radius-lg) 0 0;
  height: clamp(300px, 32vw, 440px);
  overflow: hidden;
  position: relative;
}

/* Absolutely positioned, so the scrim and the identity block stack on top of
   it rather than being pushed below it and clipped by the masthead's
   overflow -- which is what hid the server name entirely. */
.sd-masthead .sd-banner-img {
  display: block;
  height: 100%;
  inset: 0;
  object-fit: cover;
  position: absolute;
  width: 100%;
}

/* Two scrims: one bottom-up so the title always has contrast, one from the
   left so the status chip does too, whatever the scenario art happens to be. */
.sd-masthead-scrim {
  background:
    linear-gradient(180deg, rgba(var(--rm-scrim), 0.1) 0%, rgba(var(--rm-scrim), 0.5) 42%, rgba(var(--rm-scrim), 0.94) 100%),
    linear-gradient(90deg, rgba(var(--rm-scrim), 0.72) 0%, rgba(var(--rm-scrim), 0.24) 58%, rgba(var(--rm-scrim), 0) 100%);
  inset: 0;
  position: absolute;
}

.sd-masthead-body {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  height: 100%;
  justify-content: flex-end;
  padding: 1.2rem clamp(1rem, 2.5vw, 1.6rem);
  position: relative;
}

.sd-masthead-topline {
  align-items: flex-start;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  left: clamp(1rem, 2.5vw, 1.6rem);
  position: absolute;
  right: clamp(1rem, 2.5vw, 1.6rem);
  top: 1.1rem;
}

.sd-masthead .sd-title {
  color: #f2f6f8;
  font-size: clamp(1.5rem, 3vw, 2.1rem);
  margin: 0;
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.62);
}

.sd-masthead .sd-subline {
  color: rgba(226, 233, 238, 0.9);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.6);
}

.sd-masthead .sd-hero-badges {
  margin-top: 0.15rem;
}

/* -- Vitals rail ----------------------------------------------------------
   Fused to the masthead, so the two read as one unit rather than as a card
   dropped below an image. */
.sd-vitals {
  background: var(--site-band);
  border: 1px solid var(--site-muted-border);
  border-radius: 0 0 var(--rm-radius-lg) var(--rm-radius-lg);
  border-top: 0;
  display: grid;
  /* Players carries a count, a percentage, a queue warning, and the capacity
     bar, so it takes roughly three times the room. The four figure tiles are
     identical widths, so the rail reads as a regular measure. */
  grid-template-columns: 3fr repeat(4, minmax(0, 1fr));
  margin-bottom: 1.5rem;
}

.sd-vital {
  border-left: 1px solid var(--site-muted-border);
  display: grid;
  gap: 0.25rem;
  grid-template-rows: 1rem auto auto;
  min-width: 0;
  padding: 0.9rem clamp(0.75rem, 1.4vw, 1.15rem) 1rem;
  position: relative;
}

.sd-vital > .rm-stat-label {
  align-self: start;
  line-height: 1;
}

.sd-vital:first-child {
  border-left: 0;
}

/* .sd-vital sets a display, which would otherwise beat the UA rule for the
   hidden attribute that server-detail.js toggles on the runtime tile. */
.sd-vital[hidden],
.sd-runtime-stat[hidden] {
  display: none;
}

.sd-vital-value {
  align-items: baseline;
  align-self: start;
  color: var(--site-text-strong);
  display: flex;
  flex-wrap: wrap;
  font-size: 1.25rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  gap: 0.3rem;
  line-height: 1.2;
  min-height: 1.5rem;
  min-width: 0;
}

.sd-vital-value strong {
  font-weight: inherit;
}

.sd-vital-sub {
  color: var(--site-text-faint);
  font-size: 0.72rem;
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sd-vital .sd-players-max {
  color: var(--site-text-faint);
  font-size: 0.85rem;
  font-weight: 500;
}

.sd-vital .sd-capbar {
  margin-top: 0.15rem;
}

.sd-vital-runtime .sd-vital-value {
  gap: 0.75rem;
}

/* -- Panels ---------------------------------------------------------------
   The old stack was boxes inside boxes with 1rem of air. Flattening the cards
   and widening the gutters is most of what makes the page feel calm. */
.sd-serverdetail-panels,
.sd-columns {
  gap: 1.5rem;
}

.rm-serverdetail-page .sd-card {
  border-color: var(--site-muted-border);
  padding: 1.15rem 1.35rem 1.3rem;
}

.rm-serverdetail-page .sd-col-main,
.rm-serverdetail-page .sd-col-side {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.rm-serverdetail-page .sd-card-head {
  margin-bottom: 0.9rem;
}

@media (max-width: 1100px) {
  .sd-vitals {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 900px) {
  .sd-vitals {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .sd-vital {
    border-top: 1px solid var(--site-muted-border);
  }

  .sd-vital:nth-child(-n + 2) {
    border-top: 0;
  }

  .sd-vital:nth-child(odd) {
    border-left: 0;
  }

  .sd-masthead-topline {
    position: static;
    padding-bottom: 0.3rem;
  }

  .sd-masthead-body {
    justify-content: space-between;
  }
}

/* ==========================================================================
   14. Buttons
   --------------------------------------------------------------------------
   .btn-primary was still Bootstrap's stock #0d6efd. Against an olive-accented
   dark theme every primary action on the site -- Search, Add to Config, the
   planner actions -- read as borrowed chrome. Bootstrap drives its buttons
   from --bs-btn-* custom properties, so remapping those is enough; no
   !important and no per-page overrides.
   ========================================================================== */

.btn-primary {
  --bs-btn-color: #0b1116;
  --bs-btn-bg: var(--site-accent);
  --bs-btn-border-color: var(--site-accent);
  --bs-btn-hover-color: #0b1116;
  --bs-btn-hover-bg: var(--site-accent-strong);
  --bs-btn-hover-border-color: var(--site-accent-strong);
  --bs-btn-active-color: #0b1116;
  --bs-btn-active-bg: var(--site-accent-strong);
  --bs-btn-active-border-color: var(--site-accent-strong);
  --bs-btn-disabled-color: #0b1116;
  --bs-btn-disabled-bg: var(--site-accent);
  --bs-btn-disabled-border-color: var(--site-accent);
  --bs-btn-focus-shadow-rgb: 139, 157, 99;
  font-weight: 600;
}

[data-bs-theme="light"] .btn-primary {
  --bs-btn-color: #ffffff;
  --bs-btn-hover-color: #ffffff;
  --bs-btn-active-color: #ffffff;
  --bs-btn-disabled-color: #ffffff;
}

.btn-outline-secondary {
  --bs-btn-color: var(--site-text-muted);
  --bs-btn-border-color: var(--site-input-border);
  --bs-btn-hover-color: var(--site-text-strong);
  --bs-btn-hover-bg: var(--site-hover-bg);
  --bs-btn-hover-border-color: var(--site-strong-border);
  --bs-btn-active-color: var(--site-text-strong);
  --bs-btn-active-bg: var(--site-hover-bg);
  --bs-btn-active-border-color: var(--site-accent);
  --bs-btn-focus-shadow-rgb: 139, 157, 99;
}

.btn-link {
  --bs-btn-color: var(--site-accent-strong);
  --bs-btn-hover-color: var(--site-accent);
}

/* ==========================================================================
   15. Browser control bar
   --------------------------------------------------------------------------
   One control height for the whole bar. min-height was not enough: Bootstrap's
   own padding plus the stretch alignment on the row let the search field grow
   well past the buttons beside it, which is what made the bar look out of
   proportion to the category row underneath.
   ========================================================================== */

:root {
  --rm-control-h: 2.35rem;
}

.rm-browser-search-row {
  align-items: center;
  gap: 0.6rem;
}

.rm-browser-search-row .form-control,
.rm-browser-search-row .form-select,
.rm-browser-search-row .btn {
  height: var(--rm-control-h);
  line-height: 1.2;
  min-height: 0;
  padding-bottom: 0;
  padding-top: 0;
}

/* Search field, sort, and submit read as one control, not two clusters
   separated by whatever space happened to be left over. */
.rm-browser-search-row > .rm-browser-search-group {
  flex: 1 1 20rem;
}

.rm-browser-search-row > .rm-browser-actions {
  gap: 0;
}

/* The snapshot tile has no headline figure of its own, so its one line is
   sized to sit level with the values beside it rather than reading as a
   stranded caption. */
.sd-vital .sd-updated {
  color: var(--site-text-muted);
  font-size: 0.92rem;
  line-height: 1.2;
}

/* Queue and capacity warnings can be long; let the players tile wrap them
   under the count instead of stretching the whole rail. */
.sd-vital-players .sd-vital-value {
  row-gap: 0.3rem;
}

/* -- Mod browser control bar ----------------------------------------------
   The search group is one joined control (view toggle, field, submit); sort
   is a separate, shrinkable control beside it. Everything can shrink, so the
   bar cannot overflow its band and push the submit off screen. */
.mod-browser-search-group {
  flex: 1 1 20rem;
  min-width: 0;
}

.mod-browser-search-group .mod-browser-search {
  font-size: 0.9rem;
}

.mod-browser-search-controls {
  flex: 0 1 auto;
  min-width: 0;
}

@media (max-width: 767.98px) {
  /* Icon-only submit once the label stops earning its width. */
  .mod-browser-submit-text {
    display: none;
  }
}

/* -- Masthead state -------------------------------------------------------- */
.sd-masthead-state {
  align-items: flex-start;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.sd-masthead-state .sd-updated {
  background: rgba(var(--rm-scrim), 0.55);
  border-radius: var(--rm-radius);
  color: rgba(226, 233, 238, 0.78);
  font-family: var(--rm-mono);
  font-size: 0.68rem;
  padding: 0.16rem 0.4rem;
}

.sd-masthead-state .sd-updated:empty {
  display: none;
}

/* -- Estimated ping tile ---------------------------------------------------
   The whole ping affair now lives in one tile: the figure, the region, a
   minimal control to change it, and the picker as a popover anchored here.
   The sidebar card it replaced held one number and a button. */
.sd-vital-ping .sd-ping-line {
  align-items: center;
  display: flex;
  gap: 0.3rem;
  min-width: 0;
}

.sd-ping-region-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sd-ping-change {
  align-items: center;
  background: none;
  border: 0;
  border-radius: 3px;
  color: var(--site-text-faint);
  display: inline-flex;
  flex: 0 0 auto;
  font-size: 0.62rem;
  justify-content: center;
  line-height: 1;
  opacity: 0.65;
  padding: 0.2rem;
  transition: color 0.14s ease, background 0.14s ease, opacity 0.14s ease;
}

.sd-vital-ping:hover .sd-ping-change,
.sd-ping-change:focus-visible {
  opacity: 1;
}

.sd-ping-change:hover,
.sd-ping-change:focus-visible {
  background: var(--site-hover-bg);
  color: var(--site-accent-strong);
}

.sd-ping-extra {
  color: var(--site-text-faint);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.68rem;
  gap: 0.15rem 0.6rem;
}

.sd-ping-extra:not(:has(> span:not([hidden]))) {
  display: none;
}

.sd-ping-extra b {
  color: var(--site-text-muted);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* Popover, not an inline panel: opening it must not resize the rail. */
.sd-vital-ping .sd-ping-picker {
  left: clamp(0.75rem, 1.4vw, 1.15rem);
  position: absolute;
  top: calc(100% - 0.5rem);
  z-index: 40;
}

.sd-vital-ping .sd-ping-menu {
  background: var(--site-panel);
  border: 1px solid var(--site-border);
  border-radius: var(--rm-radius-lg);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  max-height: 60vh;
  min-width: 17rem;
  overflow: auto;
  padding: 0.6rem;
}

.sd-vital-ping .sd-ping-picker[hidden] {
  display: none;
}

/* -- Mods tile ------------------------------------------------------------
   Count and download size read as one statement, "7 mods · 4.22 MiB", rather
   than a figure with a caption stacked beneath it. */
.sd-vital-unit {
  color: var(--site-text-faint);
  font-size: 0.78rem;
  font-weight: 500;
}

.sd-vital-bytes {
  color: var(--site-accent-strong);
  font-weight: 600;
}

/* The caveat line, when present, follows the size on its own row. */
.sd-vital .sd-mod-sub:not(:empty) {
  display: block;
}

.sd-vital-tracked .sd-vital-value {
  font-size: 1.05rem;
}

/* -- Masthead actions -----------------------------------------------------
   Over a photo these read as dark-on-dark. A scrim plus an accent edge lifts
   them off whatever is behind, and the accent wash on hover makes the target
   obvious without turning three utilities into primary actions. */
.sd-masthead .sd-hero-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
}

.sd-masthead .sd-hero-tools .sd-btn {
  backdrop-filter: blur(6px);
  background: rgba(var(--rm-scrim), 0.6);
  border: 1px solid rgba(164, 184, 115, 0.42);
  border-radius: var(--rm-radius);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
  color: #eaf0e6;
  font-weight: 600;
  padding: 0.45rem 0.8rem;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.sd-masthead .sd-hero-tools .sd-btn .bi {
  color: var(--site-accent-strong);
}

.sd-masthead .sd-hero-tools .sd-btn:hover,
.sd-masthead .sd-hero-tools .sd-btn:focus-visible {
  background: var(--site-accent);
  border-color: var(--site-accent-strong);
  color: #0b1116;
}

.sd-masthead .sd-hero-tools .sd-btn:hover .bi,
.sd-masthead .sd-hero-tools .sd-btn:focus-visible .bi {
  color: #0b1116;
}

/* Discord keeps its own brand colour; it only needs lifting off the photo. */
.sd-masthead .sd-hero-tools .sd-discord-btn {
  background: #5865f2;
  border-color: rgba(140, 152, 255, 0.75);
  color: #fff;
}

.sd-masthead .sd-hero-tools .sd-discord-btn .bi {
  color: #fff;
}

.sd-masthead .sd-hero-tools .sd-discord-btn:hover,
.sd-masthead .sd-hero-tools .sd-discord-btn:focus-visible {
  background: #6f7afa;
  border-color: #8c98ff;
  color: #fff;
}

.sd-masthead .sd-hero-tools .sd-discord-btn:hover .bi {
  color: #fff;
}

/* Tracked since is context, not a headline figure: it should sit quietly
   beside the live numbers rather than competing with them. */
.sd-vital-tracked .sd-vital-value {
  color: var(--site-text-muted);
  font-size: 0.88rem;
  font-weight: 500;
}

.sd-mods-depnote {
  color: var(--site-text-faint);
  font-size: 0.78rem;
  line-height: 1.5;
  margin: 0 0 0.7rem;
}

.sd-mods-depnote[hidden] {
  display: none;
}

/* -- Vitals: one shape for every figure tile ------------------------------
   Label, then a figure with its unit, then a quiet sub-line. Runtime, ping,
   mods, and tracked-since all follow it, so the rail reads as one component
   at four scales rather than four differently-built widgets. */
.sd-vital .sd-vital-unit {
  color: var(--site-text-faint);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

/* Runtime's figures were chips, which made them the only boxed values in the
   rail. They now use the same type as every other figure. */
.sd-vital-runtime .sd-runtime-stat {
  align-items: baseline;
  background: none;
  border: 0;
  border-radius: 0;
  color: inherit;
  font-size: inherit;
  font-weight: inherit;
  gap: 0.3rem;
  letter-spacing: normal;
  min-height: 0;
  padding: 0;
  text-transform: none;
}

.sd-vital-runtime .sd-vital-value .sd-runtime-stat strong {
  color: var(--site-text-strong);
  font-size: 1.25rem;
  font-weight: 700;
}

.sd-vital-runtime .sd-vital-sub .sd-runtime-stat {
  color: var(--site-text-faint);
  font-size: 0.72rem;
}

.sd-vital-runtime .sd-vital-sub .sd-runtime-stat strong {
  color: var(--site-text-muted);
  font-weight: 600;
}

.sd-vital-tracked .sd-vital-value {
  font-size: 0.95rem;
}

/* Every sub-line is the same quiet grey at the same size, whatever it holds. */
.sd-vital-sub {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 0.1rem 0.4rem;
  min-height: 1rem;
}

/* -- Discord on the masthead ----------------------------------------------
   Brand blue at full strength was the loudest thing on the page, louder than
   the server name. Muted and desaturated it still reads as Discord without
   outshouting the identity it sits beside. */
.sd-masthead .sd-hero-tools .sd-discord-btn {
  background: rgba(88, 101, 242, 0.32);
  border-color: rgba(126, 138, 245, 0.5);
  color: #dfe3fb;
}

.sd-masthead .sd-hero-tools .sd-discord-btn .bi {
  color: #aab2f7;
}

.sd-masthead .sd-hero-tools .sd-discord-btn:hover,
.sd-masthead .sd-hero-tools .sd-discord-btn:focus-visible {
  background: rgba(88, 101, 242, 0.55);
  border-color: rgba(150, 160, 250, 0.7);
  color: #fff;
}

.sd-masthead .sd-hero-tools .sd-discord-btn:hover .bi,
.sd-masthead .sd-hero-tools .sd-discord-btn:focus-visible .bi {
  color: #fff;
}
