/* Component styles. All colors come from tokens.css. */

* { box-sizing: border-box; }

html, body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.5;
  margin: 0;
}

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

h1, h2, h3, h4, h5 { color: var(--text); margin: 0 0 12px; line-height: 1.2; }
h1 { font-size: 32px; font-weight: 700; }
h2 { font-size: 24px; font-weight: 700; }
h3 { font-size: 20px; font-weight: 600; }
h4 { font-size: 16px; font-weight: 600; }

.text-muted { color: var(--text-muted); }
.text-dim { color: var(--text-dim); }
.text-mono { font-family: var(--font-mono); }
.text-accent { color: var(--accent); }
.text-success { color: var(--success); }
.text-danger { color: var(--danger); }

/* Inline error line under a form/field. Colour is NOT baked in here — it composes with
   .text-danger the same way every other colour utility in this file (.text-muted,
   .text-accent, .text-success) is applied alongside a structural/layout class rather than
   folded into it (e.g. #players-empty carries class="text-muted" plus its own layout styles). */
.r-form-error { margin: 12px 0 0; font-size: 12px; }

/* A step up from .text-danger: for status messages where the routine "something went wrong,
   try again" reading would be actively misleading — e.g. a failure that already wrote to a
   customer's game directory and must not be retried by reflex. Same red family, but a filled
   banner instead of plain colored text, so it doesn't blend in with every other error line on
   the page. */
.status-severe { background: var(--danger-soft); border: 1px solid var(--danger); border-radius: var(--r-sm); font-weight: 600; }

/* Top-level page container with max-width for marketing pages. */
.r-container { max-width: 1200px; margin: 0 auto; padding: 0 24px; }
.r-container.narrow { max-width: 960px; }

/* Navbar */
.r-navbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  height: 64px;
  display: flex; align-items: center;
  padding: 0 24px;
  gap: 16px;
}
.r-navbar .logo img { height: 36px; }
.r-navbar nav { display: flex; gap: 18px; margin-left: 16px; }
.r-navbar nav a { color: var(--text-muted); font-size: 14px; }
.r-navbar nav a:hover { color: var(--text); }
.r-navbar .spacer { flex: 1; }

/* View as (#414): the read-only banner above the header. Warning tokens, not danger: nothing is
   wrong, but the operator must not mistake this for their own panel. */
.r-view-as {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 16px;
  font-size: 14px;
  background: var(--warning-soft);
  border-bottom: 2px solid var(--warning);
}

/* Buttons */
.r-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px;
  padding: 8px 14px;
  font-size: 13px; font-weight: 500;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}
.r-btn:hover { background: var(--surface-2); }
.r-btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
[data-bs-theme="dark"] .r-btn.primary { color: var(--bg); }
.r-btn.primary:hover { filter: brightness(.95); }
.r-btn.danger { color: var(--danger); border-color: var(--danger); background: transparent; }
.r-btn.danger:hover { background: var(--danger-soft); }
.r-btn.ghost { background: transparent; }

/* Inputs */
.r-input {
  display: block; width: 100%;
  padding: 8px 12px;
  font-size: 13px;
  font-family: inherit;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.r-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}
.r-label {
  display: block; font-size: 12px; font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .04em;
  margin-bottom: 6px;
}

/* Pills */
.r-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 11px; font-weight: 600;
}
.r-pill.success { background: var(--success-soft); color: var(--success); }
.r-pill.danger { background: var(--danger-soft); color: var(--danger); }
.r-pill.warning { background: var(--warning-soft); color: var(--warning); }
.r-pill.muted { background: var(--surface-2); color: var(--text-muted); }

/* Cards */
.r-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
.r-card-body { padding: 18px 20px; }

/* Modal chrome: shared by every dialog (wipe, file-browser, ban, credential). The backdrop
   sets display:none as its base state; the JS that opens/closes these modals always writes
   an explicit inline display value ('flex' or 'none') rather than reading the current one, so
   the inline style wins over this class's display:none on every state change — see the
   task-1 report for the precedence note. No overlay token exists in tokens.css, so the rgba
   stays literal here (matches the audit's brief). */
.r-modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  z-index: 200;
  align-items: center;
  justify-content: center;
}
.r-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
}
.r-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 14px 20px;
  border-top: 1px solid var(--border);
}

/* Server tile (used on /servers list) */
.r-server-tile {
  display: block;
  width: 380px; height: 180px;
  border-radius: var(--r-md);
  /* Bundled default banner; a server's own image replaces it only once it has
     actually loaded (see servers.html), so a missing or dead URL never leaves a
     blank tile. */
  background-image: url('/server-default.jpg');
  background-size: cover; background-position: center;
  position: relative;
  border: 1px solid var(--border);
  overflow: hidden;
  text-decoration: none;
  margin: 10px;
}
.r-server-tile .overlay {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0.0) 30%, rgba(0,0,0,0.7) 100%);
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 14px;
  color: #fff;
}
.r-server-tile h4 { color: #fff; margin: 0; font-size: 16px; }
.r-server-tile .meta { font-size: 12px; opacity: .85; margin-top: 4px; font-family: var(--font-mono); }
/* Status row, pinned above the gradient. Solid backgrounds throughout: the tile's background is an
   operator-supplied banner image, so contrast underneath cannot be assumed. */
.r-server-tile .r-tile-top {
  position: absolute; top: 10px; left: 10px; right: 10px; z-index: 1;
  display: flex; align-items: center; gap: 8px;
}
.r-tile-pill {
  display: inline-flex; align-items: center; gap: 6px;
  background: rgba(0,0,0,0.72);
  color: #fff;
  font-size: 11px; font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
}
.r-tile-pill::before {
  content: '●';
  font-size: 9px;
  /* Colour carries the same meaning as the words, never instead of them — the label is always
     rendered, so this stays legible to anyone who can't distinguish the dot. */
  color: var(--text-muted);
}
.r-tile-pill[data-state="CONNECTED"]::before { color: #3fb950; }
.r-tile-pill[data-state="CONNECTING"]::before { color: #d29922; }
.r-tile-pill[data-state="NOT_CONNECTED"]::before { color: #f85149; }
.r-tile-pill[data-state="NEVER_CONNECTED"]::before { color: #8b949e; }
.r-tile-players {
  margin-left: auto;
  background: rgba(0,0,0,0.72);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 3px 8px;
  border-radius: 999px;
}
.r-server-tile .r-tile-wipes { opacity: .8; font-family: inherit; display: flex; gap: 5px; flex-wrap: wrap; }

.r-server-tile.add {
  background: var(--surface-2);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-muted);
}
.r-server-tile.add:hover { color: var(--accent); border-color: var(--accent); }

/* Footer */
.r-footer {
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 32px 24px;
  margin-top: 48px;
  color: var(--text-muted);
  font-size: 13px;
  display: flex; justify-content: space-between; align-items: center;
}
.r-footer a { color: var(--text-muted); margin: 0 12px; }
.r-footer a:hover { color: var(--text); }

/* Theme toggle button */
.r-theme-toggle {
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text-muted);
  cursor: pointer;
}
.r-theme-toggle:hover { color: var(--text); background: var(--surface-2); }

@keyframes r-spin {
    to { transform: rotate(360deg); }
}

/* Server sidebar: "Servers" at top, then the current server's own pages. */
.r-sidenav { background: var(--surface); border-right: 1px solid var(--border); padding: 24px 0; }
.r-sidenav ul { list-style: none; margin: 0; padding: 0; }
.r-sidenav a,
.r-sidenav .disabled {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 20px;
  color: var(--text-muted);
  border-left: 2px solid transparent;
}
.r-sidenav a:hover { color: var(--text); background: var(--surface-2); }
.r-sidenav a.active {
  color: var(--accent);
  background: var(--accent-soft);
  border-left-color: var(--accent);
  font-weight: 600;
}
/* A nested list is a sub-page of the row above it (Permissions under Plugins). Indented past the
   parent's icon so the hierarchy reads from the text column alone, and a step smaller so a child
   never competes with its parent for the eye. */
.r-sidenav ul ul a {
  padding-left: 44px;
  font-size: 13px;
}
/* Not built yet — rendered as plain text, so it reads as unavailable rather than broken. */
.r-sidenav .disabled { opacity: .55; }
.r-sidenav .r-sidenav-soon {
  margin-left: auto;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .04em;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 1px 5px;
}
.r-sidenav .r-sidenav-group {
  padding: 0 20px;
  margin: 20px 0 8px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Server switcher — lives in the page header, where a full server name fits. It has to READ as a
   control: styled as the plain heading it replaced, with only a native disclosure triangle to go
   on, people did not find it at all. Hence the explicit "Switch" chip and the hover state. */
.r-server-switch { position: relative; }
.r-server-switch > summary { cursor: pointer; list-style: none; }
.r-server-switch > summary::-webkit-details-marker { display: none; }
.r-server-switch-summary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  margin: -4px -8px;
  padding: 4px 8px;
}
.r-server-switch-summary:hover { background: var(--surface-2); border-color: var(--border); }
.r-server-switch[open] > .r-server-switch-summary { background: var(--surface-2); border-color: var(--border); }
.r-server-switch-name { margin: 0; }
.r-server-switch-hint {
  flex: none;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 1px 5px;
}
.r-server-switch-hint::after { content: ' ▾'; }
.r-server-switch[open] .r-server-switch-hint::after { content: ' ▴'; }
.r-server-switch-summary:hover .r-server-switch-hint { color: var(--text); border-color: var(--text-muted); }
/* Absolutely positioned so the list can be wider than the heading it hangs off — the whole point
   of moving out of the 220px sidebar was that names were being truncated — and so opening it
   never reflows the header row it sits in. */
.r-server-switch-list {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  left: 0;
  min-width: 280px;
  max-width: 460px;
  list-style: none;
  margin: 0;
  padding: 4px 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
}
.r-server-switch-list a {
  display: block;
  padding: 8px 14px;
  font-size: 13px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.r-server-switch-list a:hover { background: var(--surface-2); }

/* Tables. .r-table supplies the shared width/border-collapse baseline every table already had;
   each page family keeps its own distinct th/td metrics exactly as before — including the data
   family's deliberate th 10px / td 8px vertical padding difference (preserved, not "fixed"). */
.r-table { width: 100%; border-collapse: collapse; }

/* admin: users.html, userAccess.html, sessions.html */
.r-table.admin th { padding: 10px 18px; font-size: 12px; color: var(--text-muted); text-transform: uppercase; }
.r-table.admin td { padding: 10px 18px; }

/* data: serverPlayers.html, serverPlugins.html (JS-built tbody rows restyle for free once the
   <table> carries this class — see server-players.js / server-plugins.js). */
.r-table.data { font-size: 13px; }
.r-table.data th { text-align: left; padding: 10px 12px; color: var(--text-muted); font-weight: 600; border-bottom: 1px solid var(--border); }
.r-table.data td { padding: 8px 12px; border-bottom: 1px solid var(--border); }

/* pricing: home.html's compare-plans table */
.r-table.pricing { font-size: 13px; }
.r-table.pricing th { padding: 14px 16px; color: var(--text); font-weight: 600; border-bottom: 1px solid var(--border); }
.r-table.pricing td { text-align: center; padding: 12px 16px; border-bottom: 1px solid var(--border); }
/* Row-label header cells: a distinct pattern from the plan-name th's above (smaller padding,
   lighter weight, left-aligned, no explicit colour — inherits the page default, already
   var(--text), so leaving colour unset here reproduces the original computed value). */
.r-table.pricing th[scope="row"] { text-align: left; padding: 12px 16px; font-weight: 500; border-bottom: 1px solid var(--border); }

/* Variant cells built by server-players.js / server-plugins.js on top of .r-table.data td.
   r-cell-actions is the manually-built actions cell (text-align + no-wrap, so the Kick/Ban or
   Reload/Unload buttons never stack); r-cell-right is the narrower alignRight-only case used by
   td()'s alignRight toggle (no white-space:nowrap — that was never part of its original inline
   string, so it isn't added now). */
.r-cell-actions { text-align: right; white-space: nowrap; }
.r-cell-right { text-align: right; }
/* .r-table.data td (0,2,1) sets padding and border-bottom, so a bare .r-cell-divider (0,1,0)
   loses the padding fight and silently falls back to the family's 8px instead of this row's
   6px. Qualifying the selector to .r-table.data td.r-cell-divider (0,3,1) wins unambiguously. */
.r-table.data td.r-cell-divider { padding: 6px 12px; border-bottom: 1px solid var(--border); background: var(--surface-2); color: var(--text-muted); font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }

/* Top-level page layout. A class rather than an inline style so the sidebar column can collapse
   when the nav has no entries at all — for a non-admin the section list is empty (Credentials and
   Users are admin-only), and a blank 220px stripe on /servers, the page they land on at sign-in,
   reads as a rendering bug. */
.r-page-grid { display: grid; grid-template-columns: 220px 1fr; min-height: calc(100vh - 64px - 96px); }
/* These two rules fire together (a nav with no links, e.g. any moderator on /servers) and must
   agree: display:none removes the sidenav from grid PLACEMENT, so the content div becomes the
   grid's only item and lands in the FIRST column. A `0 1fr` template here put that content into
   the zero-width column — the #380 one-word-per-line collapse. Single `1fr` column instead. */
.r-page-grid:not(:has(.r-sidenav a)) { grid-template-columns: 1fr; }
.r-sidenav:not(:has(a)) { display: none; }

/* Stats charts (#502): the SVG is 100% wide; the wrapper keeps a minimum height while loading. */
.stats-chart { min-height: 60px; }
/* Legend under a multi-series chart: one entry per server, swatch coloured like its line. */
.chart-legend { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 12px; color: var(--text-muted); }
.chart-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; vertical-align: -1px; }
