/* ════════════════════════════════════════════════════════════════════════
   SCUM Server Automation — "TEC1 Field Terminal"
   A tactical surveillance/telemetry console for the prison-island server.
   Display: Chakra Petch · Data/readout: Share Tech Mono
   ════════════════════════════════════════════════════════════════════════ */

:root {
  --bg:        #0d0e0a;
  --bg-2:      #0a0b08;
  --panel:     #161710;
  --panel-2:   #1c1d14;
  --inset:     #0b0c08;

  --amber:     #f0820c;
  --amber-hi:  #ff9a2e;
  --on-amber:  #1a1206;   /* text on amber fills (buttons, filled badges) */
  --online:    #6cbf3f;
  --alarm:     #e5443f;
  --standby:   #e0a92e;

  --text:      #e4e2d6;
  --muted:     #8e8f82;
  --faint:     #63645a;

  --line:      rgba(228, 226, 214, 0.09);
  --line-2:    rgba(228, 226, 214, 0.05);
  --line-amber: rgba(240, 130, 12, 0.30);
  --hover-bg:  rgba(255, 255, 255, 0.05);  /* neutral hover fill on dark surfaces */
  /* Focused inputs already turn their border amber (border-color: var(--amber)); this ring used to
     add a SECOND concentric amber line just outside it — a double frame. Keep the single clean
     border only. (Every :focus rule that references this also sets border-color, so nothing loses
     its focus indicator; keyboard nav still gets the global :focus-visible outline.) */
  --focus-ring: none;

  /* Entity accents (map markers, lists, popovers) — one source of truth. */
  --c-player:  #35d07f;
  --c-base:    #ff9a2e;
  --c-vehicle: #5aa0ff;
  --c-chest:   #c9a24b;
  --c-storage: #bd7be6;
  --c-trap:    #e0503a;
  --c-turret:  #e8c33a;
  --c-poi:     #4fb9c9;   /* points of interest — a place, not a thing, so its own hue */
  --offline:   #7c8594;
  --data-text: #b9c2ad;   /* soft sage for raw data/log/INI readouts */

  /* Integrations */
  --discord:    #5865f2;
  --discord-dn: #4752c4;
  --discord-hi: #c9cdfb;

  --mono: 'Share Tech Mono', 'Consolas', monospace;
  --ui:   'Chakra Petch', 'Segoe UI', Tahoma, sans-serif;

  --radius: 4px;      /* inputs, chips, small controls */
  --radius-md: 6px;   /* cards, rows */
  --radius-lg: 8px;   /* panels, modals, popovers */
  --pill: 999px;      /* fully rounded pills */
}

* { box-sizing: border-box; }
img { max-width: 100%; }
a:focus-visible { outline: 2px solid var(--amber); outline-offset: 1px; }

/* Base scale ≈ 20px (124% of 16px) — sized so the UI reads comfortably at 100%
   browser zoom (matches what previously needed zooming to ~110%). Phones keep
   their own tuned scale; layouts there are tight already. */
html { -webkit-text-size-adjust: 100%; font-size: 124%; }
@media (max-width: 640px) { html { font-size: 106%; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--ui);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.01em;
}

/* faint scanlines — surveillance-monitor atmosphere */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9999;
  background:
    repeating-linear-gradient(
      to bottom,
      rgba(255, 255, 255, 0.018) 0,
      rgba(255, 255, 255, 0.018) 1px,
      transparent 1px,
      transparent 3px
    );
}

.hidden { display: none !important; }

.screen {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

h1, h2, h3 {
  font-family: var(--ui);
  font-weight: 600;
  letter-spacing: 0.02em;
}

a { color: var(--amber); }

code {
  font-family: var(--mono);
  background: var(--inset);
  border: 1px solid var(--line);
  padding: 0.05rem 0.35rem;
  border-radius: 3px;
  color: var(--amber-hi);
  font-size: 0.9em;
}

.error {
  color: var(--alarm);
  font-size: 0.85rem;
  margin: 0;
  min-height: 1em;
}

/* ── Brand lockup ──────────────────────────────────────────────────────── */

.brand {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  min-width: 0;
  cursor: pointer;
}

.brand-logo {
  height: 50px;
  width: auto;
  flex-shrink: 0;
}

.brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
  min-width: 0;
}

.brand-title {
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: 0.04em;
}

.brand-sub {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--amber);
}

/* ── Header / command bar ──────────────────────────────────────────────── */

header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.75rem 1.5rem;
  background: linear-gradient(180deg, var(--panel), var(--bg-2));
  border-bottom: 1px solid var(--line);
}

/* ── Persistent topbar + tab nav (Field-Console-style) ─────────────────────── */
#app-shell { display: flex; flex-direction: column; min-height: 100vh; }
.topbar { position: sticky; top: 0; z-index: 30; transition: transform 0.25s ease; }
.topbar.nav-hidden { transform: translateY(-100%); }
.header-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
/* Manager lifecycle buttons in the header (update / restart / shut down). */
.header-sep { width: 1px; align-self: stretch; background: var(--line); margin: 0.2rem 0.1rem; }
.hdr-icon-btn { padding: 0.42rem 0.7rem; }
.mgr-danger:hover { border-color: var(--alarm) !important; color: #ff6b6b !important; }
/* Field Console is an <a>, so the button{} rules don't reach it — mirror the
   secondary button look so it sits identically next to Logout (no underline). */
.header-actions a.secondary {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem;
  font-family: var(--ui); font-weight: 600; font-size: 0.82rem; letter-spacing: 0.03em;
  padding: 0.55rem 1.05rem; border-radius: var(--radius); text-decoration: none;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--text);
  transition: background 0.14s, border-color 0.14s, color 0.14s;
}
.header-actions a.secondary:hover { border-color: var(--line-amber); color: var(--amber-hi); background: var(--panel-2); text-decoration: none; }
.header-actions a.secondary .ico { width: 16px; height: 16px; }
/* Full-screen overlay while the manager restarts / shuts down. */
.mgr-overlay { position: fixed; inset: 0; z-index: 9999; display: none; align-items: center; justify-content: center; background: rgba(8, 8, 6, 0.92); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
.mgr-overlay-card { text-align: center; color: var(--text); padding: 2rem; max-width: 440px; }
.mgr-overlay-card p { margin-top: 1.1rem; color: var(--muted); line-height: 1.55; }
.mgr-overlay-ic { width: 44px; height: 44px; color: var(--amber); }
.mgr-spinner { width: 42px; height: 42px; margin: 0 auto; border: 3px solid var(--line); border-top-color: var(--amber); border-radius: 50%; animation: mgr-spin 0.9s linear infinite; }
@keyframes mgr-spin { to { transform: rotate(360deg); } }
.admin-nav { display: flex; gap: 0.25rem; padding: 0 1.5rem; background: var(--bg-2); border-bottom: 1px solid var(--line-amber); overflow-x: auto; }
.anav-btn {
  display: inline-flex; align-items: center; gap: 0.45rem; cursor: pointer; white-space: nowrap;
  background: transparent; border: 0; border-bottom: 2px solid transparent;
  color: var(--muted); font-family: var(--ui); font-weight: 600; font-size: 0.9rem;
  padding: 0.8rem 0.9rem; letter-spacing: 0.02em; text-transform: none;
}
/* The nav items are real <a href="#tab"> so the browser's own link behaviour works —
   middle-click / Ctrl-click opens the tab in a new browser tab, and they can be bookmarked.
   These two rules keep them looking exactly like the buttons they replaced. */
.anav-btn { text-decoration: none; }
.anav-btn:hover { color: var(--text); border-color: transparent; background: transparent; text-decoration: none; }
.anav-btn.active { color: var(--amber); border-bottom-color: var(--amber); }
.anav-btn .ico { font-size: 1.05rem; }

/* Plugins nav dropdown (DESKTOP with a real pointer only). The button still works everywhere; on a
   desktop it also gets a hover menu of plugins that ship a UI. The menu is fixed-positioned on <body>
   (JS) so the horizontally-scrolling nav can't clip it. On touch / narrow screens it's disabled. */
.anav-dd { display: inline-flex; }
.anav-dd.has-dd > .anav-btn::after { content: ''; margin-left: 0.3rem; align-self: center; border: 4px solid transparent; border-top-color: currentColor; border-bottom: 0; opacity: 0.55; }
.anav-dd-menu { position: fixed; z-index: 6000; min-width: 210px; max-width: calc(100vw - 16px); background: var(--panel); border: 1px solid var(--line-amber); border-radius: 10px; box-shadow: 0 24px 70px rgba(0, 0, 0, 0.55); padding: 6px; display: none; }
.anav-dd-menu.open { display: block; }
.anav-dd-item { display: flex; align-items: center; justify-content: flex-start; gap: 10px; width: 100%; padding: 0.5rem 0.6rem; background: none; border: 0; color: var(--text); cursor: pointer; border-radius: 7px; font-family: var(--ui); font-weight: 500; font-size: 0.88rem; text-align: left; }
.anav-dd-item:hover { background: var(--panel-2); color: var(--amber-hi, var(--amber)); }
.anav-dd-item img { width: 22px; height: 22px; border-radius: 5px; object-fit: contain; flex: none; }
.anav-dd-item .ico { width: 18px; height: 18px; color: var(--muted); flex: none; font-size: 1rem; }
.anav-dd-item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.anav-dd-sep { height: 1px; background: var(--line); margin: 5px 4px; }
@media (hover: none), (max-width: 760px) { .anav-dd.has-dd > .anav-btn::after { display: none; } .anav-dd-menu { display: none !important; } }

.tab-panel { flex: 1 0 auto; }
.tab-panel.hidden { display: none !important; }

/* Slim per-screen title bar (replaces the old sub-screen headers) */
.screen-bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin: 1.25rem 1.5rem 0; }
.screen-title { display: flex; align-items: center; gap: 0.5rem; font-size: 1rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--amber); }
.screen-title .ico { color: var(--amber); }
.screen-bar-actions { display: flex; gap: 0.5rem; }

header h1 {
  margin: 0;
  font-size: 1.15rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.nav-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* ── Buttons ───────────────────────────────────────────────────────────── */

/* ── Unified button system ─────────────────────────────────────────────────
   One consistent look everywhere. Default = PRIMARY (amber). `.secondary` =
   outline, `.danger` / `.warning` = coloured. All buttons share size + shape so
   Save/actions look the same on every screen. */
button {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem;
  font-family: var(--ui); font-weight: 600; font-size: 0.82rem; letter-spacing: 0.03em;
  padding: 0.55rem 1.05rem; cursor: pointer; border-radius: var(--radius);
  border: 1px solid var(--amber); background: var(--amber); color: var(--on-amber);
  transition: background 0.14s, border-color 0.14s, color 0.14s, transform 0.05s;
}
/* The hover paint is a PAIR, and it is read from tokens so it cannot be split in half.
   For years the rule below hard-coded both values, and every button that wanted a different hover
   fill overrode `background` alone — inheriting dark `--on-amber` text onto a dark fill, which is
   invisible. The stylesheet is full of one-at-a-time patches for exactly that (see the "increment"
   blocks further down), and each one left the next case waiting.
   The way to retune a button's hover is now to set the trio ON THE ELEMENT, not to override the
   hover rule: `.my-btn { --hov-bg: transparent; --hov-fg: var(--text); --hov-line: var(--line); }`.
   Both halves then travel together by construction. `npm run check-contrast` fails the build on any
   that still state one half without the other. */
:root { --hov-bg: var(--amber-hi); --hov-fg: var(--on-amber); --hov-line: var(--amber-hi); }
button:hover { background: var(--hov-bg); border-color: var(--hov-line); color: var(--hov-fg); }
button:active { transform: translateY(1px); }
button .ico { font-size: 1.05em; }

button.secondary {
  background: var(--panel-2); border-color: var(--line); color: var(--text);
}
button.secondary:hover { background: var(--panel-2); border-color: var(--line-amber); color: var(--amber-hi); }

button.danger { background: rgba(229, 68, 63, 0.12); border-color: rgba(229, 68, 63, 0.4); color: var(--alarm); }
button.danger:hover { background: rgba(229, 68, 63, 0.2); border-color: var(--alarm); }

button.warning { background: rgba(224, 169, 46, 0.12); border-color: rgba(224, 169, 46, 0.4); color: var(--standby); }
button.warning:hover { background: rgba(224, 169, 46, 0.2); border-color: var(--standby); }

button.btn-icon { padding: 0.5rem; }
button:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Panels ────────────────────────────────────────────────────────────── */

.status-card,
.leaderboards,
.players,
.players-admin,
.bans,
.cmd-console,
.logs {
  position: relative;
  margin: 1.25rem 1.5rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.15rem 1.4rem;
}

/* Front-style corner ticks on every panel. */
.status-card::before, .players::before, .players-admin::before, .bans::before, .cmd-console::before, .logs::before,
.status-card::after, .players::after, .players-admin::after, .bans::after, .cmd-console::after, .logs::after {
  content: ''; position: absolute; width: 10px; height: 10px;
  border-color: var(--line-amber); border-style: solid; border-width: 0; pointer-events: none;
}
.status-card::before, .players::before, .players-admin::before, .bans::before, .cmd-console::before, .logs::before {
  top: -1px; left: -1px; border-top-width: 2px; border-left-width: 2px;
}
.status-card::after, .players::after, .players-admin::after, .bans::after, .cmd-console::after, .logs::after {
  bottom: -1px; right: -1px; border-bottom-width: 2px; border-right-width: 2px;
}

.status-card { margin-bottom: 0; }
.status-card + .status-card { margin-top: 1.25rem; }
.controls + .status-card { margin-top: 0; }

/* Eyebrow label — names the feed each panel carries */
.panel-eyebrow {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 1rem;
}

.panel-eyebrow .ico {
  width: 13px; height: 13px; flex: none; color: var(--amber);
}
/* The dot is the panel's "live" tick. A panel that leads with an icon has its marker already, and a
   second one beside it is just noise. */
.panel-eyebrow:has(.ico)::before, .panel-eyebrow:has(.live-dot)::before { content: none; }
.panel-eyebrow::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--amber);
  box-shadow: 0 0 7px var(--amber);
}

.panel-eyebrow .feed-id {
  margin-left: auto;
  color: var(--faint);
  letter-spacing: 0.15em;
}

/* when a live dot is present, drop the static amber marker to avoid two dots */
.panel-eyebrow:has(.live-dot)::before { display: none; }

.panel-eyebrow .live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--online);
  box-shadow: 0 0 8px var(--online);
  animation: pulse 1.6s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}

/* ── Vitals viewport (primary status panel) — the signature element ────── */

.viewport {
  position: relative;
  border-color: var(--line-amber);
}

.viewport::before,
.viewport::after {
  content: "";
  position: absolute;
  width: 18px;
  height: 18px;
  border: 2px solid var(--amber);
  pointer-events: none;
}

.viewport::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.viewport::after  { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

/* ── Status grid / telemetry readouts ──────────────────────────────────── */

.status-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1.1rem 1.4rem;
}

.status-item {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding-left: 0.7rem;
  border-left: 1px solid var(--line-2);
  min-width: 0;   /* let long values wrap instead of overflowing the grid cell */
}

.status-item .label {
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.16em;
}

.status-item .value {
  font-family: var(--mono);
  font-size: 1.4rem;
  color: var(--text);
  letter-spacing: 0.01em;
  min-width: 0;
  overflow-wrap: anywhere;   /* break long strings (filenames) that have no spaces */
}
/* Backup filename is long and not a headline metric — smaller so it wraps neatly. */
#last-backup-value { font-size: 0.92rem; line-height: 1.3; }

/* ── Vitals badge ──────────────────────────────────────────────────────── */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  align-self: flex-start;
  padding: 0.2rem 0.7rem 0.2rem 0.6rem;
  border-radius: 3px;
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--muted);
}

.badge::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

.badge.online {
  color: var(--online);
  border-color: rgba(108, 191, 63, 0.4);
  background: rgba(108, 191, 63, 0.08);
}
.badge.online::before { box-shadow: 0 0 8px var(--online); }

.badge.offline {
  color: var(--alarm);
  border-color: rgba(229, 68, 63, 0.4);
  background: rgba(229, 68, 63, 0.08);
}

.badge.transitional {
  color: var(--standby);
  border-color: rgba(224, 169, 46, 0.4);
  background: rgba(224, 169, 46, 0.08);
}
.badge.transitional::before { animation: pulse 1s ease-in-out infinite; }

/* Win over `.status-item .value` font-size for the badge specifically */
.status-item .value.badge { font-size: 0.9rem; }

/* ── Controls ──────────────────────────────────────────────────────────── */

.controls {
  margin: 1.25rem 1.5rem;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem;
}
/* "When" picker inside the confirm dialog — scheduling lives in the confirm an admin already
   has to click, so the controls row stays exactly as it was. */
.ssa-when { display: flex; flex-wrap: wrap; gap: 0.4rem 0.75rem; margin: 0.15rem 0 0.9rem; }
.ssa-when-opt { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.85rem;
  color: var(--text); cursor: pointer; }
.ssa-when-opt input[type="radio"] { accent-color: var(--amber, #ff6a1a); cursor: pointer; }
.ssa-when-min { width: 62px; background: var(--inset); border: 1px solid var(--line-2); border-radius: 6px;
  color: var(--text); font: inherit; font-size: 0.82rem; padding: 0.2rem 0.35rem; margin-left: 0.15rem; }
.ssa-when-note { flex-basis: 100%; margin: 0.15rem 0 0; font-size: 0.76rem; color: var(--muted); }
.sched-pending { display: inline-flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.sched-chip { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.78rem;
  background: color-mix(in srgb, var(--amber, #ff6a1a) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--amber, #ff6a1a) 40%, transparent);
  border-radius: 999px; padding: 0.2rem 0.3rem 0.2rem 0.65rem; }
.sched-x { background: none; border: 0; color: var(--muted); cursor: pointer; font-size: 1rem;
  line-height: 1; padding: 0 0.25rem; }
.sched-x:hover { color: var(--alarm, #e5443f); }

.controls button {
  padding: 0.6rem 1.25rem;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  font-size: 0.82rem;
  text-transform: uppercase;
}

.controls button:hover { border-color: var(--line-amber); color: var(--amber-hi); }

/* Primary action — Start */
#start-btn {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--on-amber);
}
#start-btn:hover { background: var(--amber-hi); border-color: var(--amber-hi); color: var(--on-amber); }

.controls button.danger { color: var(--alarm); border-color: rgba(229, 68, 63, 0.35); }
.controls button.danger:hover { background: rgba(229, 68, 63, 0.12); color: var(--alarm); }

.controls button.warning { color: var(--standby); border-color: rgba(224, 169, 46, 0.35); }
.controls button.warning:hover { background: rgba(224, 169, 46, 0.12); color: var(--standby); }

/* The result of a Start/Stop/Restart, as a chip BESIDE the buttons.
   It began as the last child of the flex row (bare text between the buttons, reflowing them), then
   as a full-width row underneath — which left a permanent empty band above the next panel. A chip
   that hides when empty takes no room, and carries its state in its colour: "update failed" and
   "update succeeded" must not look the same. */
#control-message.ctrl-chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin-left: 0.35rem; padding: 0.34rem 0.75rem;
  border: 1px solid var(--line); border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  font-size: 0.8rem; color: var(--muted);
  max-width: 46ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#control-message[hidden] { display: none; }
#control-message.ctrl-chip::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: currentColor; box-shadow: 0 0 7px currentColor;
}
.ctrl-busy  { color: var(--standby) !important; border-color: rgba(224, 169, 46, 0.35) !important; }
.ctrl-ok    { color: var(--online) !important;  border-color: rgba(108, 191, 63, 0.35) !important; }
.ctrl-bad   { color: var(--alarm) !important;   border-color: rgba(229, 68, 63, 0.40) !important; }
.ctrl-note  { color: var(--text) !important; }

/* ── Skip-restart switch ───────────────────────────────────────────────── */

/* A real switch, not the operating system's checkbox. It sits in a row of large mono readouts, and a
   13px native tickbox next to "04:26" and "20.1 / 31.9 GB" read as an unstyled leftover. Built from
   the checkbox itself so it stays a checkbox: keyboard, labels and form semantics are unchanged. */
.switch {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.95rem;
  user-select: none;
}
.switch input {
  appearance: none; -webkit-appearance: none;
  position: relative; flex: none; margin: 0;
  width: 2.35rem; height: 1.25rem; border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.switch input::after {
  content: ''; position: absolute; top: 50%; left: 0.18rem;
  width: 0.85rem; height: 0.85rem; border-radius: 50%;
  background: var(--muted); transform: translateY(-50%);
  transition: transform 0.15s ease, background 0.15s ease;
}
.switch input:checked {
  background: var(--amber);
  border-color: var(--amber);
}
.switch input:checked::after {
  background: var(--on-amber);
  transform: translate(1.03rem, -50%);
}
.switch input:focus-visible { outline: 2px solid var(--amber-hi); outline-offset: 2px; }
.switch:hover input { border-color: var(--line-amber); }

/* ── Section headers w/ feed tick ──────────────────────────────────────── */

.leaderboards h2,
.players h2,
.players-admin h2,
.bans h2,
.cmd-console h2,
.logs h2 {
  margin: 0 0 0.85rem;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

/* Inline SVG icons (shared with the public site look). */
.svg-defs { position: absolute; }
.ico {
  width: 1.05em; height: 1.05em; flex: none; vertical-align: middle;
  stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.players h2 .ico, .players-admin h2 .ico, .bans h2 .ico, .cmd-console h2 .ico, .logs h2 .ico { color: var(--amber); }
.nav-actions button, .controls button, #player-search-btn { display: inline-flex; align-items: center; gap: 0.4rem; }

/* ── Players list ──────────────────────────────────────────────────────── */

#players-list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--mono);
  font-size: 0.9rem;
  /* Fill the width with as many columns as fit instead of one tall column that wastes the space on
     the right. Items flow left→right, top→bottom in login order. */
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 0 1.4rem;
}

#players-list li {
  padding: 0.4rem 0.2rem;
  border-bottom: 1px solid var(--line-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#players-list li:first-child::before {
  content: none;
}

/* ── Live log feed ─────────────────────────────────────────────────────── */

.logs {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

#log-tail {
  flex: 1;
  overflow-y: auto;
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
  font-family: var(--mono);
  font-size: 0.82rem;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 400px;
  color: var(--data-text);
}

/* ── App footer ────────────────────────────────────────────────────────── */

/* Footer styled to match the Field Console: full-width, amber top accent, a brand
   line and a bottom bar with social links. */
.app-footer {
  margin-top: auto;   /* pin to the bottom of the screen */
  border-top: 1px solid var(--line-amber);
  background: linear-gradient(180deg, var(--bg-2), var(--bg));
}
.af-top {
  display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 0.4rem 2rem;
  padding: 1.3rem 1.5rem 0.9rem;
}
.af-brand { display: flex; align-items: center; gap: 0.55rem; font-weight: 700; letter-spacing: 0.04em; }
.af-logo { width: 24px; height: 24px; }
.af-bottom {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 0.6rem;
  padding: 0.7rem 1.5rem 1.1rem; border-top: 1px solid var(--line);
}
.af-social { display: flex; gap: 1.4rem; }
.af-social a { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--muted); }
.af-social a:hover { color: var(--amber-hi); }

.app-footer-links {
  display: flex;
  gap: 1.5rem;
}

.app-footer-links a {
  color: var(--muted);
  text-decoration: none;
  transition: color 0.14s;
}

.app-footer-links a:hover {
  color: var(--amber-hi);
}

/* ── Login screen ──────────────────────────────────────────────────────── */

#login-screen {
  align-items: center;
  justify-content: center;
  padding: 2rem 1rem;
}

.login-box {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line-amber);
  padding: 2.25rem 2rem 2rem;
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  width: 400px;
  max-width: calc(100vw - 2rem);
  text-align: center;
}

.login-box::before,
.login-box::after {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
  border: 2px solid var(--amber);
  pointer-events: none;
}
.login-box::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.login-box::after  { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

.login-logo {
  width: 64px;
  height: 64px;
  border-radius: var(--radius-lg);
  margin: 0 auto 0.25rem;
  border: 1px solid var(--line);
}

.login-box h1 {
  font-size: 1rem;
  margin: 0 0 0.4rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.login-box .login-tagline {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--amber);
  margin: 0 0 0.5rem;
}

.login-box input {
  padding: 0.6rem 0.7rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--inset);
  color: var(--text);
  font-family: var(--mono);
  text-align: center;
  letter-spacing: 0.1em;
}

.login-box input:focus { outline: none; border-color: var(--amber); }

.login-box button {
  padding: 0.6rem;
  border-radius: var(--radius);
  border: 1px solid var(--amber);
  background: var(--amber);
  color: var(--on-amber);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.login-box button:hover { background: var(--amber-hi); border-color: var(--amber-hi); }

/* ── Settings / Game Settings / Discord notes ──────────────────────────── */

.settings-note, .settings-message {
  margin: 0 1.5rem;
  font-size: 0.85rem;
  color: var(--muted);
}

.settings-note { margin-top: 1rem; line-height: 1.6; }

.settings-message {
  font-family: var(--mono);
  letter-spacing: 0.03em;
}
.settings-message.success { color: var(--online); }
.settings-message.error { color: var(--alarm); }

.settings-form {
  margin: 1rem 1.5rem 2rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* ── Settings search toolbar + collapsible categories ──────────────────── */

.settings-toolbar {
  margin: 1rem 1.5rem 0;
  display: flex;
  gap: 0.75rem;
  align-items: center;
  flex-wrap: wrap;
}

/* Inside the Game Settings tab the container already has horizontal margin */
.gs-tab-content .settings-toolbar {
  margin: 0 0 1rem;
}

.settings-search {
  flex: 1;
  min-width: 200px;
  padding: 0.6rem 0.8rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--inset);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.85rem;
  letter-spacing: 0.02em;
}

.settings-search:focus {
  outline: none;
  border-color: var(--amber);
  box-shadow: var(--focus-ring);
}

.settings-toolbar-actions {
  display: flex;
  gap: 0.5rem;
}

/* Expand/collapse buttons use the shared .secondary button style. */
.settings-toolbar-actions button { font-size: 0.76rem; padding: 0.45rem 0.85rem; }

.settings-section { margin-bottom: 1.8rem; }
.settings-section-head { margin: 0 0 0.75rem; padding-bottom: 0.55rem; border-bottom: 1px solid var(--line-amber); }
.settings-section-title { display: flex; align-items: center; gap: 0.5rem; font-size: 1.15rem; font-weight: 700; color: var(--amber-hi); }
.settings-section-title .ico { width: 1.15em; height: 1.15em; }
.settings-section-desc { margin: 0.35rem 0 0; font-size: 0.85rem; color: var(--muted); line-height: 1.45; max-width: 70ch; }
.settings-section .settings-category { margin-bottom: 0.6rem; }

/* Sub-section divider heading (e.g. Discord → Actions) */
.subhead { display: flex; align-items: center; gap: 0.5rem; margin: 2rem 1.5rem 0.9rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--line-amber); font-size: 0.9rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--amber); }
.subhead .ico { width: 1.05em; height: 1.05em; color: var(--amber); }
.group-note { margin: 0 0 0.9rem; font-size: 0.85rem; color: var(--muted); line-height: 1.5; }
.group-actions { display: flex; align-items: center; gap: 0.8rem; margin-top: 0.9rem; flex-wrap: wrap; }
.group-result { font-size: 0.82rem; color: var(--muted); }

/* Data tables (linked accounts, rosters, bans, user lists) */
.table-wrap { overflow-x: auto; margin-top: 0.4rem; }
.data-table { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
.data-table th { text-align: left; padding: 0.5rem 0.7rem; border-bottom: 1px solid var(--line-amber); color: var(--muted); font-weight: 600; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap; }
.data-table td { padding: 0.55rem 0.7rem; border-bottom: 1px solid var(--line-2); }
.data-table tbody tr:hover { background: rgba(240, 130, 12, 0.05); }
.data-table tr.clickable { cursor: pointer; }
.data-table td.mono { font-family: var(--mono); font-size: 0.8rem; color: var(--muted); }
.data-table td.dim { color: var(--muted); font-size: 0.8rem; }

/* Click-to-edit row editors (Raid Times, Notifications…) */
.editor-rows { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 0.6rem; }
.editor-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.7rem; padding: 0.85rem 0.9rem; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); }
.editor-row .er-f { display: flex; flex-direction: column; gap: 0.28rem; font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted); }
.editor-row .er-day { flex: 1 1 150px; }
.editor-row input { padding: 0.45rem 0.55rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); color: var(--text); font-family: var(--mono); font-size: 0.85rem; }
.editor-row input:focus { outline: none; border-color: var(--amber); box-shadow: var(--focus-ring); }
.editor-row input[type="number"] { width: 6rem; }
.editor-row .rr-del { margin-left: auto; align-self: center; }
/* Make the (black by default) time/date picker icon visible on the dark theme */
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator { filter: invert(0.85); cursor: pointer; }
.editor-empty { color: var(--muted); font-size: 0.88rem; padding: 1.1rem 0; }
.editor-row.notif-row .er-msg { flex: 1 1 100%; }
.editor-row.notif-row .er-day { flex: 1 1 130px; }
.editor-row.notif-row .er-times { flex: 2 1 220px; }
.editor-row.notif-row .er-num { flex: 0 0 auto; }
.editor-row.notif-row .er-num input { width: 5rem; }
.editor-row.notif-row .er-col { flex: 0 0 auto; }
.editor-row.notif-row .nn-col-wrap { display: inline-flex; align-items: center; gap: 0.4rem; }
.editor-row.notif-row .nn-color-on { width: 1rem; height: 1rem; accent-color: var(--amber); cursor: pointer; }
.editor-row.notif-row .nn-del { align-self: flex-end; margin-bottom: 0.1rem; }
.editor-row input[type="color"] { width: 3rem; height: 2.25rem; padding: 2px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); cursor: pointer; }

/* ── Economy Override editor ─────────────────────────────────────────────── */
.eco-subtabs { display: flex; gap: 0.4rem; margin: 0.6rem 0 1rem; }
.eco-subtab { background: var(--panel-2); border: 1px solid var(--line); color: var(--muted); font-size: 0.82rem; }
.eco-subtab.active { background: var(--amber); border-color: var(--amber); color: var(--on-amber); }
.eco-subtab:not(.active):hover { background: var(--panel); border-color: var(--line-amber); color: var(--amber-hi); }
.eco-subtab.active:hover { background: var(--amber-hi); border-color: var(--amber-hi); color: var(--on-amber); }
.eco-pane.hidden { display: none; }
.eco-global-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: 0.85rem; }
.eco-gfield { display: flex; flex-direction: column; gap: 0.22rem; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.7rem 0.8rem; }
.eco-glabel { font-size: 0.82rem; font-weight: 600; color: var(--text); }
.eco-ghelp { font-size: 0.73rem; color: var(--muted); line-height: 1.35; }
.eco-gin { margin-top: 0.3rem; padding: 0.45rem 0.55rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); color: var(--text); font-family: var(--mono); font-size: 0.85rem; }
.eco-gin:focus, .eco-search:focus { outline: none; border-color: var(--amber); box-shadow: var(--focus-ring); }
/* Toggle fields (0/1 settings) */
.eco-gtoggle-f { flex-direction: row; align-items: center; justify-content: space-between; gap: 0.7rem; }
.eco-gtxt { display: flex; flex-direction: column; gap: 0.22rem; min-width: 0; }
.eco-switch { position: relative; flex: none; width: 40px; height: 22px; cursor: pointer; }
.eco-switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.eco-slider { position: absolute; inset: 0; background: var(--inset); border: 1px solid var(--line); border-radius: var(--pill); transition: background 0.15s, border-color 0.15s; }
.eco-slider::before { content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--muted); transition: transform 0.15s, background 0.15s; }
.eco-switch input:checked + .eco-slider { background: rgba(240,130,12,0.25); border-color: var(--amber); }
.eco-switch input:checked + .eco-slider::before { transform: translateX(18px); background: var(--amber-hi); }
/* Vehicle limits editor */
.eco-veh { margin-top: 1.3rem; padding-top: 1rem; border-top: 1px solid var(--line-amber); }
.eco-veh-head { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; flex-wrap: wrap; }
.eco-veh-title { margin: 0; font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.12em; color: var(--text); }
.eco-veh-sub { font-family: var(--mono); font-size: 0.72rem; color: var(--muted); text-transform: none; letter-spacing: 0; margin-left: 0.35rem; }
.eco-veh-rows { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.6rem; }
.eco-veh-row { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.eco-veh-row .evr-group { flex: 1 1 240px; min-width: 0; }
.eco-veh-row .evr-max { flex: 0 0 150px; }
.eco-veh-row input { padding: 0.45rem 0.55rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); color: var(--text); font-family: var(--mono); font-size: 0.85rem; }
.eco-veh-row input:focus { outline: none; border-color: var(--amber); }
.eco-trader { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); margin-bottom: 0.55rem; }
.eco-trader[open] { border-color: var(--line-amber); }
.eco-trader-h { display: flex; justify-content: space-between; align-items: center; gap: 0.6rem; padding: 0.7rem 0.9rem; cursor: pointer; list-style: none; }
.eco-trader-h::-webkit-details-marker { display: none; }
.eco-trader-name { font-weight: 600; color: var(--amber); }
.eco-trader-n { font-family: var(--mono); font-size: 0.76rem; color: var(--muted); }
.eco-trader-body { padding: 0 0.9rem 0.9rem; }
.eco-items { display: flex; flex-direction: column; gap: 0.5rem; }
.eco-item { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.6rem; padding: 0.6rem 0.65rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); }
.eco-item-id { display: flex; align-items: center; gap: 0.5rem; flex: 1 1 180px; min-width: 140px; }
.eco-item-img { width: 34px; height: 34px; object-fit: contain; flex: none; }
/* item image placeholders (shown when an item has no image — free tier / no active subscription) */
.eco-item-imgslot { width: 34px; height: 34px; flex: none; display: inline-flex; align-items: center; justify-content: center; }
.eco-item-imgslot img { width: 34px; height: 34px; object-fit: contain; }
.eco-item-ph { width: 22px; height: 22px; color: var(--muted); }
.mp-c-ph { width: 14px; height: 14px; color: var(--muted); }
.cmd-sg-ph { display: inline-flex; align-items: center; justify-content: center; color: var(--muted); }
.cmd-sg-ph .ico { width: 16px; height: 16px; }
.gl-preview-ph { width: 56px; height: 56px; display: flex; align-items: center; justify-content: center; background: var(--inset); border-radius: 6px; flex: none; color: var(--muted); }
.gl-preview-ph .ico { width: 30px; height: 30px; }
.eco-item-txt { display: flex; flex-direction: column; min-width: 0; }
.eco-item-name { font-weight: 600; font-size: 0.87rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.eco-item-code { font-family: var(--mono); font-size: 0.68rem; color: var(--faint); }
.eco-item .er-f { display: flex; flex-direction: column; gap: 0.22rem; font-size: 0.62rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.eco-item .er-f input, .eco-item .er-f select { width: 5.4rem; padding: 0.4rem 0.45rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); color: var(--text); font-family: var(--mono); font-size: 0.82rem; }
.eco-item .ei-del { align-self: center; margin-left: auto; }
.eco-add { position: relative; margin-top: 0.75rem; }
.eco-add-search { display: flex; flex-direction: column; gap: 0.22rem; font-size: 0.62rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); max-width: 340px; }
.eco-search { padding: 0.45rem 0.55rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); color: var(--text); font-size: 0.85rem; }
.eco-results { position: absolute; z-index: 20; left: 0; max-width: 360px; width: 100%; background: var(--panel-2); border: 1px solid var(--line-amber); border-radius: var(--radius); margin-top: 0.2rem; max-height: 280px; overflow-y: auto; }
.eco-res { display: flex; align-items: center; gap: 0.5rem; width: 100%; text-align: left; background: none; border: none; border-bottom: 1px solid var(--line-2); color: var(--text); border-radius: 0; padding: 0.45rem 0.6rem; }
.eco-res:hover { background: rgba(240,130,12,0.08); color: var(--text); }
.eco-res img { width: 26px; height: 26px; object-fit: contain; flex: none; }
.eco-res-ph { width: 26px; height: 26px; flex: none; display: flex; align-items: center; justify-content: center; color: var(--muted); }
.eco-res-ph .ico { width: 18px; height: 18px; }
.eco-res-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.eco-res-code { font-family: var(--mono); font-size: 0.68rem; color: var(--faint); }
.eco-res-empty { padding: 0.5rem 0.6rem; color: var(--muted); font-size: 0.82rem; }

.settings-category {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.settings-category[open] { border-color: var(--line-amber); }

.settings-cat-summary {
  cursor: pointer;
  list-style: none;
  padding: 0.95rem 1.3rem;
  user-select: none;
}

.settings-cat-summary::-webkit-details-marker { display: none; }
.settings-cat-summary:hover .settings-cat-title { color: var(--amber-hi); }

.settings-cat-title {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.82rem;
  color: var(--text);
}

.settings-cat-title::before {
  content: "▸";
  color: var(--amber);
  margin-right: 0.6rem;
  display: inline-block;
  transition: transform 0.15s;
}

.settings-category[open] .settings-cat-title::before { transform: rotate(90deg); }

.settings-cat-desc {
  display: block;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--muted);
  letter-spacing: 0.02em;
  margin-top: 0.3rem;
  padding-left: 1.2rem;
}

.settings-cat-body {
  padding: 0.4rem 1.3rem 1.2rem;
  border-top: 1px solid var(--line);
}

.settings-cat-body > .settings-field:first-child { margin-top: 0.8rem; }

.settings-cat-body .settings-group {
  background: var(--bg-2);
  margin-top: 0.85rem;
}

.filtered-hidden { display: none !important; }

/* INI setting metadata: unit/type badge + description help text */
.ini-type {
  margin-left: 0.5rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--line-amber);
  border-radius: 3px;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--amber);
  vertical-align: middle;
}

.ini-desc {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.74rem;
  line-height: 1.45;
  color: var(--muted);
}

/* checkbox rows need to wrap so the description sits on its own line */
.settings-field.checkbox { flex-wrap: wrap; }
.settings-field.checkbox .ini-desc { flex-basis: 100%; margin-top: 0.35rem; }

.sync-notify-btn {
  align-self: flex-start;
  margin-top: 0.55rem;
  padding: 0.4rem 0.85rem;
  font-size: 0.78rem;
  background: var(--panel-2);
  color: var(--amber);
  border: 1px solid var(--line-amber);
}
/* Keep the quiet background on hover — without it the global amber button
   hover shines through and amber-on-amber text becomes unreadable. */
.sync-notify-btn:hover { background: var(--panel-2); border-color: var(--amber-hi); color: var(--amber-hi); }
.sync-notify-btn:disabled { opacity: 0.5; cursor: default; }

.settings-group {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem 1.4rem 1.2rem;
}

.settings-group > legend {
  font-weight: 600;
  padding: 0 0.5rem;
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--amber);
}

.settings-group .settings-group {
  background: var(--bg-2);
  margin-top: 0.75rem;
}

/* Premium-locked group (Field Console without an active subscription). */
.settings-group.settings-locked { border-color: var(--line-amber); position: relative; }
.settings-group.settings-locked > legend { color: var(--muted); }
.settings-group.settings-locked .settings-field,
.settings-group.settings-locked .settings-group { opacity: 0.55; pointer-events: none; }
.settings-lock-note {
  opacity: 1; margin: 0.2rem 0 1rem; padding: 0.6rem 0.85rem;
  background: rgba(240, 130, 12, 0.1); border: 1px solid var(--line-amber);
  border-radius: var(--radius); color: var(--amber-hi); font-size: 0.85rem; line-height: 1.5;
}
.settings-lock-note strong { color: var(--amber-hi); }
.settings-lock-note .link-btn { font-size: 0.85rem; vertical-align: baseline; }

/* Two-column row: name + description on the left, the control on the right —
   scannable and clear, instead of stacked mystery inputs. */
.settings-field {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(220px, 360px);
  grid-template-areas: "label control" "desc control";
  align-items: center;
  column-gap: 1.5rem;
  row-gap: 0.25rem;
  margin: 0;
  padding: 0.8rem 0;
  border-top: 1px solid var(--line-2);
}
.settings-field:first-child { border-top: none; }

.settings-field.checkbox {
  grid-template-columns: minmax(0, 1fr) auto;
}
.settings-field.checkbox input[type="checkbox"] { grid-area: control; justify-self: end; }

.settings-field label {
  grid-area: label;
  font-family: var(--ui);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text);
  text-transform: none;
  letter-spacing: normal;
}

.settings-field .ini-desc {
  grid-area: desc;
  font-family: var(--ui);
  font-size: 0.8rem;
  color: var(--muted);
  line-height: 1.45;
  margin: 0;
  letter-spacing: normal;
  text-transform: none;
}
/* A field can carry a second hint (e.g. restart times + the sync note) and an
   action button. Only the FIRST description owns the "desc" grid area — the rest
   would overlap it in the same cell. Flow them into fresh rows in column 1. */
.settings-field .ini-desc ~ .ini-desc { grid-area: auto; grid-column: 1; margin-top: 0.15rem; }
.settings-field .sync-notify-btn { grid-area: auto; grid-column: 1; justify-self: start; }
/* Image (logo / wallpaper) upload control sits under the URL input. */
.settings-field .img-upload { grid-area: auto; grid-column: 2; display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem 0.7rem; margin-top: 0.45rem; }

/* Leaderboard/stat visibility checklists: span the FULL field width and flow into columns, with
   group headers — so a long category list stays scannable instead of a cramped single column. */
.settings-field.lb-vis-field { grid-template-columns: 1fr; grid-template-areas: "label" "desc" "control"; align-items: start; }
.settings-field.lb-vis-field > label { grid-area: label; }
.lb-vis {
  grid-area: control; margin-top: 0.6rem;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 0.1rem 1.4rem; align-content: start;
  background: var(--inset, rgba(0,0,0,0.15)); border: 1px solid var(--line-2); border-radius: var(--radius-md, 6px);
  padding: 0.7rem 0.9rem;
}
.lb-vis-cats { max-height: 460px; overflow-y: auto; }
.lb-vis-groups { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
.lb-vis .lb-vis-grp {
  grid-column: 1 / -1; font-family: var(--ui); font-weight: 700; font-size: 0.72rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--amber, #f0820c); margin: 0.85rem 0 0.3rem; padding-bottom: 0.25rem;
  border-bottom: 1px solid var(--line-2);
}
.lb-vis .lb-vis-grp:first-child { margin-top: 0; }
/* IMPORTANT: `.settings-field label { grid-area: label }` (the 2-col field grid) also matches these
   inner labels and would pile them all into the top-left cell. Reset placement so they flow normally. */
.lb-vis .lang-check, .lb-vis .ini-desc { grid-area: auto; grid-column: auto; grid-row: auto; }
.lb-vis .lang-check { display: flex; align-items: center; gap: 0.5rem; font-family: var(--ui); font-weight: 400;
  font-size: 0.86rem; color: var(--text); padding: 0.2rem 0; cursor: pointer; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lb-vis .lang-check input[type="checkbox"] { flex: none; margin: 0; width: 1rem; height: 1rem; }
@media (max-width: 640px) { .settings-field .img-upload { grid-column: 1; } }
.img-upload-btn { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.7rem; background: var(--panel-2); border: 1px solid var(--line-amber); color: var(--amber-hi); border-radius: var(--radius); cursor: pointer; font-family: var(--ui); font-size: 0.82rem; }
.img-upload-btn:hover { background: var(--amber); color: var(--on-amber); }
.img-upload-btn .ico { width: 0.9em; height: 0.9em; }
.img-upload-status { font-size: 0.8rem; color: var(--muted); }
.img-upload-prev { display: none; max-height: 40px; max-width: 170px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-2); object-fit: contain; }
/* Theme colour picker swatch next to the hex input. */
.settings-field .color-field { grid-area: auto; grid-column: 2; margin-top: 0.45rem; }
@media (max-width: 640px) { .settings-field .color-field { grid-column: 1; } }
.color-pick { width: 48px; height: 30px; padding: 0; border: 1px solid var(--line-amber); border-radius: var(--radius); background: var(--inset); cursor: pointer; }

.settings-field input[type="text"],
.settings-field input[type="number"],
.settings-field input[type="password"],
.settings-field textarea,
.settings-field select { grid-area: control; }

@media (max-width: 640px) {
  .settings-field {
    grid-template-columns: 1fr;
    grid-template-areas: "label" "desc" "control";
    row-gap: 0.4rem;
  }
  .settings-field.checkbox {
    grid-template-columns: 1fr auto;
    grid-template-areas: "label control" "desc desc";
  }
}

.settings-field input[type="text"],
.settings-field input[type="number"],
.settings-field input[type="password"],
.settings-field textarea,
.setup-box .settings-field input {
  padding: 0.55rem 0.65rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--inset);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.9rem;
  width: 100%;
}

.settings-field input:focus,
.settings-field textarea:focus,
.setup-box .settings-field input:focus,
.login-box input:focus {
  outline: none;
  border-color: var(--amber);
  box-shadow: var(--focus-ring);
}

.settings-field textarea {
  min-height: 4.5rem;
  resize: vertical;
  white-space: pre;
}

.settings-field input[type="checkbox"] {
  width: 1.1rem;
  height: 1.1rem;
  accent-color: var(--amber);
}

/* ── Game Settings tabs ────────────────────────────────────────────────── */

.gs-tabs {
  display: flex;
  gap: 0.35rem;
  flex-wrap: wrap;
  margin: 1.2rem 1.5rem 0;
  border-bottom: 1px solid var(--line);
}

.gs-tab {
  padding: 0.55rem 1.1rem;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: var(--radius) var(--radius) 0 0;
  background: transparent;
  color: var(--muted);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.gs-tab:hover { color: var(--text); background: var(--panel-2); border-color: var(--line); }

.gs-tab.active {
  background: var(--panel);
  border-color: var(--line);
  color: var(--amber);
  margin-bottom: -1px;
}

.gs-tab-content {
  margin: 0 1.5rem 2rem;
  padding-top: 1.25rem;
}
/* Notes/messages inside a tab already sit in the padded tab body — drop the extra
   horizontal margin so they line up with the header and form fields. */
.gs-tab-content > .settings-note,
.gs-tab-content > .settings-message { margin-left: 0; margin-right: 0; }

/* Editor ⇄ JSON mode toggle */
.gs-mode { display: inline-flex; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.gs-mode-btn { background: var(--panel-2); color: var(--muted); border: 0; padding: 0.42rem 0.85rem; font-family: var(--ui); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; cursor: pointer; }
.gs-mode-btn + .gs-mode-btn { border-left: 1px solid var(--line); }
.gs-mode-btn.active { background: var(--amber); color: var(--bg); font-weight: 700; }
.gs-mode-btn.active:hover { background: var(--amber-hi); color: var(--bg); }
.gs-mode-btn:not(.active):hover { background: var(--panel); color: var(--amber-hi); }
.gs-tab-content.json-mode .gs-editor-body { display: none; }
.gs-tab-content:not(.json-mode) .gs-json-pane { display: none; }
.gs-tab-content.json-mode .gs-add-btn { display: none; }
.gs-json-ta { width: 100%; min-height: 320px; box-sizing: border-box; }
.gs-json-err { margin: 0.55rem 0 0; }
.gs-json-err.hidden { display: none; }

.gs-tab-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.85rem;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.gs-tab-header h2, .gs-tab-header h3 {
  margin: 0;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text);
}

.gs-btn-row { display: flex; gap: 0.5rem; }

/* .gs-save-btn / #settings-save-btn / #discord-config-save inherit the unified
   primary button — all Save buttons now look identical. */

.gs-tab-content .secondary {
  padding: 0.45rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel-2);
  color: var(--text);
  font-size: 0.78rem;
  text-transform: uppercase;
}
.gs-tab-content .secondary:hover { border-color: var(--line-amber); color: var(--amber-hi); }

.gs-ini-form { margin: 0; }

.gs-user-lists {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.4rem;
}

@media (max-width: 700px) {
  .gs-user-lists { grid-template-columns: 1fr; }
}

.gs-list-section {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.gs-list-section textarea,
.gs-code-textarea {
  width: 100%;
  padding: 0.75rem 0.85rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--inset);
  color: var(--data-text);
  font-family: var(--mono);
  font-size: 0.85rem;
  line-height: 1.6;
  resize: vertical;
}

.gs-code-textarea:focus,
.gs-list-section textarea:focus {
  outline: none;
  border-color: var(--amber);
}

/* ── Interactive user lists (admins / bans / whitelist / exclusive) ────────── */
.ul-section { background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.9rem 1rem; gap: 0.6rem; }
.ul-wide { grid-column: 1 / -1; }
.ul-count { color: var(--muted); font-family: var(--mono); font-weight: 600; letter-spacing: 0; text-transform: none; }
.ul-note { margin: 0; }
.ul-add { display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: flex-start; }
.ul-ac-wrap { position: relative; flex: 1 1 200px; min-width: 0; }
.ul-add-input { width: 100%; box-sizing: border-box; }
.ul-add-note { flex: 1 1 140px; min-width: 0; }
.ul-add input { background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); color: var(--text); font-family: var(--ui); font-size: 0.85rem; padding: 0.45rem 0.6rem; }
.ul-add input:focus { outline: none; border-color: var(--amber); }
/* Styled autocomplete dropdown */
.ul-ac { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20; max-height: 260px; overflow-y: auto; background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius); box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45); padding: 0.25rem; }
.ul-ac.hidden { display: none; }
.ul-ac-item { display: flex; align-items: center; gap: 0.55rem; width: 100%; text-align: left; background: transparent; border: 0; border-radius: var(--radius); color: var(--text); padding: 0.4rem 0.5rem; cursor: pointer; }
.ul-ac-item:hover, .ul-ac-item:focus { background: var(--hover-bg); color: var(--amber-hi); }
.ul-ac-name { font-weight: 600; font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ul-ac-id { margin-left: auto; font-size: 0.72rem; color: var(--muted); white-space: nowrap; }
.ul-avatar.sm { width: 24px; height: 24px; }
.ul-avatar.sm .ico { font-size: 0.85rem; }
.ul-rows { display: flex; flex-direction: column; gap: 0.35rem; }
.ul-empty { color: var(--muted); font-size: 0.85rem; margin: 0.2rem 0; }
.ul-row { display: flex; align-items: center; gap: 0.6rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.45rem 0.6rem; }
.ul-avatar { display: inline-flex; width: 30px; height: 30px; flex: none; align-items: center; justify-content: center; border-radius: 50%; background: var(--panel); border: 1px solid var(--line); }
.ul-avatar .ico { font-size: 1rem; color: var(--amber); }
.ul-idcol { display: flex; flex-direction: column; gap: 0.05rem; min-width: 0; flex: 1 1 auto; }
.ul-name { font-weight: 600; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ul-sid { font-size: 0.74rem; color: var(--muted); }
.ul-flags { display: flex; align-items: center; gap: 0.7rem; flex: none; }
.ul-flag { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.76rem; color: var(--muted); white-space: nowrap; cursor: pointer; }
.ul-flag input { accent-color: var(--amber); }
.ul-extra { font-family: var(--mono); font-size: 0.7rem; color: var(--standby); border: 1px dashed var(--line); border-radius: var(--radius); padding: 0.05rem 0.35rem; }
.ul-bancol { display: flex; flex-direction: column; gap: 0.1rem; flex: 1 1 140px; min-width: 0; text-align: right; }
.ul-bannote { font-size: 0.8rem; color: var(--text); overflow: hidden; text-overflow: ellipsis; }
.ul-when { font-size: 0.7rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.ul-del { flex: none; width: 28px; height: 28px; cursor: pointer; background: transparent; border: 1px solid var(--line); border-radius: var(--radius); color: var(--muted); font-size: 0.85rem; line-height: 1; }
.ul-del:hover { color: var(--alarm); border-color: var(--alarm); background: transparent; }

/* ── Setup wizard ──────────────────────────────────────────────────────── */

.setup-center {
  align-items: center;
  justify-content: center;
  padding: 2.5rem 1rem;
}

.setup-box {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg, 14px);
  padding: 2rem 2.4rem 2.2rem;
  width: 100%;
  max-width: 720px;
  box-shadow: 0 24px 60px -28px rgba(0, 0, 0, 0.7);
}
@media (max-width: 640px) { .setup-box { padding: 1.5rem 1.3rem 1.7rem; } }

.setup-brand {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1.4rem;
  padding-bottom: 1.2rem;
  border-bottom: 1px solid var(--line);
}

.setup-brand img {
  width: 52px;
  height: 52px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  flex-shrink: 0;
}
.setup-brand-text { min-width: 0; }

.setup-box h1 {
  font-size: 1.15rem;
  margin: 0;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.setup-tagline {
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  margin: 0.25rem 0 0;
}

/* ── Wizard progress bar ───────────────────────────────────────────────── */
.su-progress {
  height: 6px;
  border-radius: 99px;
  background: var(--inset);
  overflow: hidden;
  margin-bottom: 1rem;
}
.su-progress-lg { height: 9px; margin: 0.4rem 0 1.2rem; }
.su-progress-fill {
  height: 100%;
  width: 0;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--amber), var(--amber-hi));
  transition: width 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Indeterminate shimmer for the long install phases. */
.su-progress-anim {
  background-image: linear-gradient(90deg, var(--amber), var(--amber-hi)),
    linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.35) 50%, transparent 100%);
  background-size: 100% 100%, 40% 100%;
  background-repeat: no-repeat, no-repeat;
  animation: su-shimmer 1.4s linear infinite;
}
@keyframes su-shimmer { from { background-position: 0 0, -40% 0; } to { background-position: 0 0, 140% 0; } }

/* Indeterminate "working" bar — moving diagonal stripes filling the track. Used
   during the game download, where SteamCMD gives no reliable fine-grained
   progress. Clearly animating (never a frozen fill), and the stripes read as
   "in progress, unknown %" rather than a solid "done" bar. */
.su-progress-fill.su-progress-indet {
  width: 100% !important;
  transition: none;
  background-image: repeating-linear-gradient(
    -45deg,
    var(--amber) 0, var(--amber) 12px,
    var(--amber-hi) 12px, var(--amber-hi) 24px
  );
  background-size: 34px 100%;
  animation: su-indet 0.7s linear infinite;
}
@keyframes su-indet { from { background-position: 0 0; } to { background-position: 34px 0; } }

/* ── Stepper dots ──────────────────────────────────────────────────────── */
.su-stepper {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 1rem;
  flex-wrap: wrap;
}
.su-dot {
  flex: 1 1 0;
  min-width: 12px;
  height: 4px;
  border-radius: 99px;
  background: var(--line-2, #2a2c22);
  border: 0;
  padding: 0;
  transition: background 0.3s;
}
.su-dot.done { background: var(--amber); }
.su-dot.active { background: var(--amber-hi); box-shadow: 0 0 0 3px rgba(240, 130, 12, 0.18); }
.su-dot.done { cursor: pointer; }

.su-step-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1.2rem;
}
.su-step-title {
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--text);
}
.su-step-count {
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--faint);
  white-space: nowrap;
}

/* ── Wizard steps + stacked fields ─────────────────────────────────────── */
.su-step { animation: su-fade 0.28s ease; }
.su-step[hidden] { display: none; }
@keyframes su-fade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

.su-field { margin-bottom: 1.35rem; }
.su-field:last-child { margin-bottom: 0; }
.su-field > label {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 0.15rem;
}
.su-field .su-help {
  margin: 0 0 0.5rem;
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--muted);
}
.su-field input[type="text"],
.su-field input[type="number"],
.su-field input[type="password"] {
  width: 100%;
  padding: 0.6rem 0.7rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--inset);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.9rem;
}
.su-field input:focus {
  outline: none;
  border-color: var(--amber);
  box-shadow: var(--focus-ring);
}

/* Checkbox field: box + label on one row, help underneath. */
.su-field.su-check {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  column-gap: 0.6rem;
  row-gap: 0.35rem;
}
.su-field.su-check input[type="checkbox"] {
  width: 1.2rem;
  height: 1.2rem;
  accent-color: var(--amber);
  margin: 0;
}
.su-field.su-check > label { margin: 0; grid-column: 2; }
.su-field.su-check .su-help { grid-column: 1 / -1; margin: 0; }

.su-req { color: #f87171; font-weight: 700; }
.su-opt, .su-hint { font-weight: normal; font-size: 0.82em; color: var(--faint); }

/* ── Review summary ────────────────────────────────────────────────────── */
.su-review {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-2);
  overflow: hidden;
}
.su-review-row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem 0.9rem;
  border-top: 1px solid var(--line-2, #2a2c22);
  font-size: 0.86rem;
}
.su-review-row:first-child { border-top: 0; }
.su-review-row .k { color: var(--muted); }
.su-review-row .v { font-family: var(--mono); color: var(--text); text-align: right; word-break: break-word; }
.su-review-row .v.muted { color: var(--faint); }

/* ── Wizard nav ────────────────────────────────────────────────────────── */
.su-nav {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-top: 1.6rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line);
}
.su-btn-primary, .su-btn-ghost {
  padding: 0.65rem 1.3rem;
  border-radius: var(--radius);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}
.su-btn-primary {
  margin-left: auto;
  border: 1px solid var(--amber);
  background: var(--amber);
  color: var(--on-amber);
}
.su-btn-primary:hover { background: var(--amber-hi); border-color: var(--amber-hi); }
.su-btn-primary:disabled { opacity: 0.55; cursor: not-allowed; }
.su-btn-ghost {
  border: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
}
.su-btn-ghost:hover { color: var(--text); border-color: var(--faint); }
.su-btn-ghost[hidden] { display: none; }

/* ── Install progress log + done / error ───────────────────────────────── */
.su-log {
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
  font-family: var(--mono);
  font-size: 0.82rem;
  line-height: 1.7;
  min-height: 180px;
  max-height: 420px;
  overflow-y: auto;
  color: var(--data-text);
}
.su-done-title { color: #4ade80; margin: 1.2rem 0 0.4rem; font-weight: 700; }
.su-done-text { color: var(--muted); font-size: 0.9em; margin: 0; line-height: 1.6; }
.su-install-error { color: #f87171; margin: 1rem 0 0; }

/* First-run wizard language picker (top-right of the setup box header) — compact
   so it doesn't dominate the header. */
.su-lang { margin-left: auto; flex: none; }
.su-lang[hidden] { display: none; }
.su-lang .admin-lang-select { padding: 0.32rem 0.55rem; font-size: 0.76rem; gap: 0.4rem; letter-spacing: 0.02em; }
.su-lang .lang-flag { width: 16px; height: 12px; }
.su-lang .lang-dd-chev { font-size: 0.62rem; }
.su-lang .lang-dd-list { min-width: 150px; }
.su-lang .lang-dd-item { font-size: 0.8rem; padding: 0.4rem 0.55rem; }
.su-lang .lang-dd-item .lang-flag { width: 18px; height: 13.5px; }

/* ── Field Console premium promo (Frontend tab without a subscription) ────── */
.fc-promo {
  max-width: 580px;
  margin: 2.5rem auto;
  text-align: center;
  padding: 2.2rem 1.8rem;
  background: linear-gradient(135deg, rgba(240, 130, 12, 0.14), rgba(240, 130, 12, 0.02));
  border: 1px solid var(--line-amber);
  border-radius: var(--radius-lg, 14px);
}
.fc-promo-icon {
  width: 54px; height: 54px; margin: 0 auto 1rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: rgba(240, 130, 12, 0.16);
  border: 1px solid var(--line-amber);
  color: var(--amber-hi);
}
.fc-promo-icon .ico { width: 1.6em; height: 1.6em; }
.fc-promo h3 { margin: 0 0 0.6rem; font-size: 1.05rem; letter-spacing: 0.02em; color: var(--text); }
.fc-promo p { margin: 0 auto 1.4rem; max-width: 46ch; color: var(--muted); font-size: 0.9rem; line-height: 1.6; }
.fc-promo-btn {
  padding: 0.65rem 1.4rem;
  border: 1px solid var(--amber);
  border-radius: var(--radius);
  background: var(--amber);
  color: var(--on-amber);
  font-size: 0.9rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  cursor: pointer;
}
.fc-promo-btn:hover { background: var(--amber-hi); border-color: var(--amber-hi); }

/* ── Premium lock overlay (frosted glass over visible-but-locked settings) ── */
.prem-lock-wrap { position: relative; min-height: 210px; border-radius: var(--radius-lg); }
.prem-lock-overlay {
  position: absolute; inset: 0; z-index: 6;
  display: flex; align-items: flex-start; justify-content: center;
  padding: 1.6rem 1.2rem;
  background: rgba(10, 11, 8, 0.5);
  -webkit-backdrop-filter: blur(2.5px);
  backdrop-filter: blur(2.5px);
  border-radius: var(--radius-lg);
}
.prem-lock-card {
  position: sticky; top: 1.2rem;
  max-width: 480px; text-align: center;
  padding: 1.5rem 1.6rem;
  background: linear-gradient(135deg, rgba(240, 130, 12, 0.18), rgba(18, 18, 14, 0.94));
  border: 1px solid var(--line-amber);
  border-radius: var(--radius-lg);
  box-shadow: 0 18px 44px -14px rgba(0, 0, 0, 0.7);
}
.prem-lock-card h3 { margin: 0 0 0.5rem; font-size: 1rem; letter-spacing: 0.02em; color: var(--text); }
.prem-lock-card p { margin: 0 auto 1.2rem; max-width: 44ch; color: var(--muted); font-size: 0.86rem; line-height: 1.55; }

/* ── Focus visibility (a11y) ───────────────────────────────────────────── */

button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
.gs-tab:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 1px;
}

/* ── Responsive ────────────────────────────────────────────────────────── */

@media (max-width: 640px) {
  header { padding: 0.85rem 1rem; }
  .brand-title { font-size: 1.1rem; }
  /* Tighten the 1.5rem desktop gutters to 1rem so content isn't squeezed. */
  .status-card, .leaderboards, .players, .logs, .controls,
  .screen-bar, .subhead, .settings-form, .settings-note, .settings-message,
  .prem-body, .adm-card, .adm-form, .gs-tabs, .gs-tab-content,
  .players-admin, .bans, .cmd-console, #pl-lockwrap, .pl-workspace, .mu-card {
    margin-left: 1rem; margin-right: 1rem;
  }
  .admin-nav { padding: 0 0.75rem; }
  .anav-btn { padding: 0.75rem 0.7rem; font-size: 0.85rem; }
  /* Modals fill the phone width with slimmer padding. */
  .amodal { padding: 0.6rem; }
  .amodal-box { padding: 1.1rem 1rem; }
  /* Roster table scrolls sideways instead of crushing its columns. */
  .roster-table { min-width: 640px; }
}
/* The roster wrapper always allows horizontal scrolling (kicks in on phones). */
#player-roster { overflow-x: auto; }

/* Phone: enlarge icon/close buttons to a comfortable ~40px touch target. */
@media (max-width: 640px) {
  .amodal-close, .pd-close, .ul-del, .rr-admin-btn {
    width: 40px; height: 40px;
  }
  .mp-close { width: 34px; height: 34px; }
}
/* Body-positioned dropdowns never grow wider than the viewport on mobile. */
.cdd-menu, .roster-country-menu, .lang-dd-list { max-width: calc(100vw - 16px); }

/* ── Reduced motion ────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ── Players & bans (admin) ───────────────────────────────────────────────── */
.roster-bar { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.9rem; }
.player-search { flex: 1; display: flex; align-items: center; gap: 0.5rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.5rem 0.7rem; }
.player-search .ico { color: var(--muted); flex: none; }
.player-search input { flex: 1; background: transparent; border: 0; outline: none; color: var(--text); font-family: var(--ui); font-size: 0.95rem; }
.roster-count { font-family: var(--mono); font-size: 0.82rem; color: var(--muted); white-space: nowrap; }
/* Filter / sort / page-size dropdowns in the roster + chat toolbars. */
.roster-select {
  padding: 0.5rem 0.6rem; font-family: var(--ui); font-size: 0.85rem;
  background: var(--inset); color: var(--text); border: 1px solid var(--line); border-radius: var(--radius);
  cursor: pointer; outline: none;
}
.roster-select:hover, .roster-select:focus { border-color: var(--line-amber); }
/* Custom country filter dropdown (flags in a native <select> aren't possible) */
.roster-country { position: relative; }
.roster-country-btn { display: inline-flex; align-items: center; gap: 0.4rem; }
.roster-country-btn .rr-flag, .roster-country-btn .rr-flag-unknown { margin-right: 0.1rem; }
.dd-caret { color: var(--muted); font-size: 0.7rem; }
.roster-country-menu { position: absolute; z-index: 40; top: calc(100% + 4px); left: 0; min-width: 12rem; max-height: 320px; overflow-y: auto; background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius); padding: 0.3rem; box-shadow: 0 8px 24px rgba(0,0,0,0.45); }
.rc-item { display: flex; align-items: center; gap: 0.5rem; width: 100%; padding: 0.4rem 0.55rem; background: none; border: none; border-radius: var(--radius); color: var(--text); cursor: pointer; font: inherit; font-size: 0.85rem; text-align: left; }
.rc-item:hover { background: var(--hover-bg); color: var(--amber-hi); }
.rc-item.on { background: rgba(240, 130, 12, 0.18); color: var(--amber-hi); }
.rc-name { flex: 1; }
.rc-n { font-family: var(--mono); font-size: 0.78rem; color: var(--muted); }
.rc-empty { display: block; padding: 0.5rem; color: var(--muted); font-size: 0.82rem; }
/* Enhanced native <select> → styled dropdown identical to the country/language pickers */
.cdd { position: relative; display: inline-flex; }
.cdd-native { position: absolute; width: 1px; height: 1px; padding: 0; margin: 0; opacity: 0; pointer-events: none; }
.cdd-btn { display: inline-flex; align-items: center; gap: 0.5rem; white-space: nowrap; }
/* higher specificity than the global `button { justify-content:center }` + chevron rule → left-align + drop bg arrow */
.cdd .cdd-btn { justify-content: flex-start; text-align: left; background-image: none; padding-right: 0.7rem; }
.cdd-lbl { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: left; }
.cdd .cdd-btn .dd-caret { margin-left: auto; padding-left: 0.4rem; }
/* Menu is moved to <body> and absolutely positioned in page coords by JS (escapes clip/transform). */
.cdd-menu { position: absolute; z-index: 3000; min-width: 8rem; overflow-y: auto; background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius); padding: 0.3rem; box-shadow: 0 10px 28px rgba(0,0,0,0.5); }
.cdd-menu[hidden] { display: none; }
.cdd-item { display: flex; align-items: center; justify-content: flex-start; gap: 0.5rem; width: 100%; white-space: nowrap; padding: 0.4rem 0.55rem; background: none; border: none; border-radius: var(--radius); color: var(--text); cursor: pointer; font: inherit; font-size: 0.85rem; text-align: left; }
/* The hover PAIR travels on the element, per the rule at the end of this file. Two hover rules used
   to fight here and a third beat them both: `.cdd-item.cdd-check.on` is three classes, so on a TICKED
   row in a multi-select it won the background (none) while the hover rule still supplied the colour
   (near-black on amber) — near-black text on the dark panel, invisible, and only while hovering a row
   you had already selected. Retuned as a pair instead, so neither half can be overridden alone. */
.cdd-item { --hov-bg: var(--amber); --hov-fg: var(--on-amber); }
.cdd-item.on { background: rgba(240, 130, 12, 0.18); color: var(--amber-hi); }
/* Checkable items — a multi-select rendered as the same dropdown, so "vehicles AND chests"
   is one familiar control instead of a native multi-list. */
.cdd-item.cdd-check { gap: 0.55rem; }
.cdd-item.cdd-check .cdd-box {
  flex: none; width: 14px; height: 14px; border-radius: 3px;
  border: 1px solid var(--line-amber, var(--line)); background: var(--inset);
}
/* A ticked row keeps its quiet ground — the tick is the state, not a fill — so it retunes the pair
   rather than cancelling one half of it. */
.cdd-item.cdd-check.on { background: none; --hov-bg: var(--hover-bg); --hov-fg: var(--amber-hi); }
.cdd-item.cdd-check.on .cdd-box {
  background: var(--amber); border-color: var(--amber);
  /* tick drawn in the box, so no sprite icon is needed inside a generated menu */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230d0e0a' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12.5l4.5 4.5L19 7.5'/></svg>");
  background-size: 11px 11px; background-repeat: no-repeat; background-position: center;
}
.cdd-item.cdd-check .cdd-t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* full-width contexts: settings forms (grid control cell), row editors, game settings */
.settings-field .cdd, .er-f .cdd, .gs-field .cdd { grid-area: control; display: flex; width: 100%; }
.settings-field .cdd-btn, .er-f .cdd-btn, .gs-field .cdd-btn { flex: 1; width: 100%; }
/* Live-map layer filters: compact + full-width, matching the original .sf-select look */
.map-layers .cdd { display: flex; width: 100%; }
.map-layers .cdd-btn { width: 100%; padding: 0.4rem 0.55rem; font-size: 0.78rem; font-weight: 600; background: var(--panel-2); }
.map-layers .cdd-menu { font-size: 0.8rem; }
@media (max-width: 900px) { .roster-bar { flex-wrap: wrap; } .player-search { flex-basis: 100%; } }
/* Chat history: channel badge + wrapping message cell. */
.chat-ts { white-space: nowrap; }
.chat-ch { display: inline-block; padding: 0.1rem 0.5rem; border-radius: var(--pill); font-size: 0.75rem; font-weight: 600; background: var(--inset); border: 1px solid var(--line); color: var(--muted); }
.chat-ch-global { color: var(--amber-hi); border-color: var(--line-amber); }
.chat-ch-squad { color: #57f287; }
.chat-ch-local { color: #6cb2f7; }
.chat-ch-admin { color: var(--alarm); }
.chat-msg { word-break: break-word; }
/* Chat table: shrink time/channel/player to their content so the MESSAGE column
   gets all remaining width — everything packs left instead of spreading out. */
.chat-table th, .chat-table td { text-align: left; }
.chat-table th:nth-child(1), .chat-table td:nth-child(1),
.chat-table th:nth-child(2), .chat-table td:nth-child(2),
.chat-table th:nth-child(3), .chat-table td:nth-child(3) { width: 1%; white-space: nowrap; }
/* Log Viewer table (time · category · entry): undo the chat-table shrink on the
   LAST column so the log entry takes all remaining width and wraps normally. */
.gamelog-table th:last-child, .gamelog-table td:last-child { width: auto; white-space: normal; }
.gamelog-line { font-family: var(--mono); font-size: 0.85rem; }
/* Player names inside a log entry are clickable; 📍 opens the position on scum-map. */
/* Raw log line reads as plain text; players + locations get a subtle underline to show they're
   clickable, without recolouring the sentence (see increment 19). */
.gl-player { color: inherit; cursor: pointer; text-decoration: underline; text-decoration-color: rgba(240, 130, 12, 0.4); text-decoration-thickness: 1px; text-underline-offset: 2px; }
.gl-player:hover { color: var(--amber-hi); text-decoration-color: var(--amber); }
.gl-loc { color: inherit; cursor: pointer; text-decoration: underline dotted; text-decoration-color: rgba(240, 130, 12, 0.45); text-underline-offset: 2px; }
.gl-loc:hover { color: var(--amber); text-decoration-color: var(--amber); }
.gl-map {
  display: inline-flex; align-items: center; gap: 0.15rem; margin-left: 0.3rem;
  padding: 0 0.45rem; border: 1px solid var(--line); border-radius: var(--pill);
  color: var(--muted); font-size: 0.78rem; text-decoration: none; white-space: nowrap;
}
.gl-map:hover { color: var(--amber-hi); border-color: var(--line-amber); }
.gl-dim { color: var(--muted); }
.gl-sid { font-size: 0.8em; }
/* Item / vehicle / weapon class codes → compact mono chips. */
.gl-item { white-space: nowrap; }   /* raw look — styling is in the increment-21 block (no chip box) */
/* Category badge palettes: stable colour per tag (hash-picked), plus semantic
   green/red for login/activated vs logout/finished. */
.gl-tag-ok  { color: #57f287; }
.gl-tag-bad { color: #eb6a6a; }
.gl-tag-0 { color: #6cb2f7; }
.gl-tag-1 { color: #c792ea; }
.gl-tag-2 { color: var(--amber-hi); border-color: var(--line-amber); }
.gl-tag-3 { color: #4dd0c4; }
.gl-tag-4 { color: #e8a87c; }
.gl-tag-5 { color: #9fb2f7; }

/* Player roster table (admin: status · name · steam · squad · IP · Discord · logins) */
.roster-table td.rr-status { width: 1.8rem; text-align: center; }
.roster-table td.rr-name { font-weight: 600; color: var(--text); }
.roster-table td.dim { white-space: nowrap; }
.rr-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--faint); }
.rr-dot.on { background: var(--online); box-shadow: 0 0 7px var(--online); }
.rr-squad, .rr-discord { font-size: 0.85rem; }
.rr-online { color: var(--online); font-weight: 600; }
.roster-table td.rr-act { width: 2.2rem; text-align: center; }
.rr-admin-btn { border: 1px solid var(--line); background: var(--inset); color: var(--amber); cursor: pointer;
  width: 26px; height: 26px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center; }
.rr-admin-btn:hover { background: var(--amber); color: var(--on-amber); border-color: var(--amber); }
.rr-admin-btn .ico { width: 15px; height: 15px; }
#pd-admin-actions { color: var(--amber); }

/* Chat: send-message box */
.chat-send { display: flex; flex-direction: column; gap: 0.7rem; }
.cs-head { display: flex; align-items: center; gap: 0.5rem; font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.18em; color: var(--text); }
.cs-head .ico { color: var(--amber); }
.cs-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.cs-inline { display: inline-flex; align-items: center; gap: 0.45rem; }

/* locked config fields (premium/bridge-gated, e.g. Discord→game relay) */
.cfg-field-locked { opacity: 0.5; }
.cfg-lock-note { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: var(--amber);
  background: rgba(230,185,106,0.08); border: 1px solid var(--line-amber); border-radius: 8px; padding: 0.5rem 0.7rem; margin: 0.5rem 0; }
.cfg-lock-note .ico { width: 14px; height: 14px; }
/* Roster column widths. Everything except Name and Squad is fixed-shape content (an id, a figure,
   a timestamp), so those columns are told to take only what they need — `width: 1%` on a table cell
   collapses it to its content — and Name absorbs the slack. Without this the browser shared the
   width evenly and a 3-digit ID claimed as much room as the player name. */
/* Column WIDTHS live with the fixed-layout block further down (the table is `table-layout: fixed`,
   so a width set here on a <td> would do nothing anyway). This only keeps their content on one line. */
.roster-table td.c-uid, .roster-table td.c-steam, .roster-table td.num,
.roster-table td.c-ip, .roster-table td.c-login, .roster-table td.c-logout { white-space: nowrap; }
/* Hidden by CLASS, never :nth-child — column positions move whenever a column is added. */
@media (max-width: 1400px) {
  .roster-table th.c-logout, .roster-table td.c-logout { display: none; }
}
@media (max-width: 1250px) {
  .roster-table th.c-login, .roster-table td.c-login { display: none; }
}
@media (max-width: 1100px) {
  .roster-table th.c-fame, .roster-table td.c-fame,
  .roster-table th.c-playtime, .roster-table td.c-playtime { display: none; }
}
@media (max-width: 900px) {
  .roster-table th.c-ip, .roster-table td.c-ip,
  .roster-table th.c-money, .roster-table td.c-money,
  .roster-table th.c-gold, .roster-table td.c-gold { display: none; }
}
/* Squad block in the player detail */
.pd-squad-head { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.55rem; }
.pd-squad-name { font-weight: 700; font-size: 1.05rem; }
.pd-squad-meta { font-family: var(--mono); font-size: 0.8rem; color: var(--muted); }
.pd-members { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.4rem; margin-bottom: 0.9rem; }
.pd-member {
  display: flex; align-items: center; gap: 0.5rem; cursor: pointer; text-align: left;
  padding: 0.45rem 0.6rem; background: var(--inset); border: 1px solid var(--line);
  border-radius: var(--radius); color: var(--text); font-family: var(--ui);
}
.pd-member:hover { border-color: var(--amber); background: rgba(240, 130, 12, 0.14); }
.pd-m-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--faint); flex: none; }
.pd-m-dot.on { background: var(--online); box-shadow: 0 0 7px var(--online); }
.pd-m-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pd-m-rank { margin-left: auto; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--amber); white-space: nowrap; }

.player-detail { background: var(--inset); border: 1px solid var(--line-amber); border-radius: var(--radius); padding: 0.9rem 1rem; }
.player-detail.hidden { display: none; }
/* Player detail hero: avatar + name/flag/status + Steam ID subline */
.pd-hero { display: flex; align-items: center; gap: 0.9rem; margin-bottom: 0.9rem; padding-right: 2.2rem; }
.pd-avatar { position: relative; width: 54px; height: 54px; border-radius: 50%; flex: none; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.pd-avatar-ph { background: var(--inset); border: 1px solid var(--line-amber); color: var(--amber); font-weight: 700; font-size: 1.5rem; }
.pd-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.pd-hero-main { min-width: 0; display: flex; flex-direction: column; gap: 0.28rem; }
.pd-hero-top { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; }
.pd-hero-sub { display: flex; align-items: center; gap: 0.4rem; font-family: var(--mono); font-size: 0.8rem; color: var(--muted); }
.pd-hero-sub .ico { width: 1em; height: 1em; color: var(--amber); }
.pd-hero-top .rr-flag { width: 1.5em; height: 1.12em; margin: 0; }
.pd-name { font-size: 1.25rem; font-weight: 700; }
.pd-online { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.08em; padding: 0.15rem 0.5rem; border-radius: var(--pill); border: 1px solid var(--line); color: var(--muted); }
.pd-online.on { color: var(--online); border-color: var(--online); }
.pd-banned { font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--alarm); border: 1px solid var(--alarm); padding: 0.15rem 0.5rem; border-radius: var(--pill); }
.pd-id { display: flex; flex-wrap: wrap; gap: 0.35rem 1.4rem; margin-bottom: 0.8rem; padding-bottom: 0.7rem; border-bottom: 1px solid var(--line); }
/* CFTools-style player summary card */
.pd-card { background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius); padding: 1rem 1.1rem; margin: 0.5rem 0 1.1rem; }
.pd-id-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 0.8rem 1.5rem; }
.pd-idc { display: flex; flex-direction: column; gap: 0.22rem; min-width: 0; }
.pd-il { display: flex; align-items: center; gap: 0.35rem; font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); font-weight: 600; }
.pd-il .ico { width: 1.5em; height: 1.5em; color: var(--amber); flex: none; }
.pd-iv { font-size: 0.9rem; color: var(--text); overflow-wrap: anywhere; display: flex; align-items: center; gap: 0.3rem; }
.pd-iv.mono { font-family: var(--mono); font-size: 0.85rem; }
/* Position → compact map link (mono, fits its cell; full value in the tooltip). */
.pd-maplink {
  font-family: var(--mono); font-size: 0.82rem; font-variant-numeric: tabular-nums;
  color: var(--text); text-decoration: none; border-bottom: 1px dashed var(--line-amber); white-space: nowrap;
}
.pd-maplink:hover { color: var(--amber-hi); }
.pd-quick { display: flex; flex-wrap: wrap; gap: 0.55rem; margin: 0 0 0.9rem; }
.pd-q { flex: 1 1 88px; display: flex; flex-direction: column; align-items: center; gap: 0.18rem; padding: 0.55rem 0.4rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); }
.pd-qv { font-family: var(--mono); font-size: 1.05rem; font-weight: 700; color: var(--amber-hi); }
.pd-qk { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.pd-card .pd-ban { margin-top: 1rem; padding-top: 0.9rem; border-top: 1px solid var(--line); }
.pd-id span { font-size: 0.86rem; color: var(--text); font-family: var(--mono); }
.pd-id i { font-style: normal; color: var(--muted); margin-right: 0.3rem; text-transform: uppercase; font-size: 0.66rem; letter-spacing: 0.04em; }
/* Stat cards (icon + value/label), uniform like the public My Stats. */
.pd-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 0.5rem; margin-bottom: 1.1rem; }
.pd-stat { display: flex; align-items: center; gap: 0.55rem; padding: 0.55rem 0.7rem; background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius); }
.pd-stat .ico { color: var(--amber); font-size: 1.3rem; flex: none; }
.pd-stat-t { display: flex; flex-direction: column; line-height: 1.1; min-width: 0; }
.pd-stat-v { font-family: var(--mono); font-size: 1.05rem; color: var(--text); overflow-wrap: anywhere; min-width: 0; }
.pd-stat-k { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
/* Player-detail tabs + close */
.pd-close { margin-left: auto; cursor: pointer; background: transparent; border: 1px solid var(--line); border-radius: var(--radius); color: var(--muted); width: 28px; height: 28px; font-size: 0.9rem; line-height: 1; }
.pd-close:hover { background: transparent; color: var(--alarm); border-color: var(--alarm); }
/* Detail tabs: pill buttons (Stats · Skills · Squad · Bank) — clearer active
   state than the old underline-only look. */
.pd-tabnav { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 1rem 0 0.9rem; }
.pd-tab {
  display: inline-flex; align-items: center; gap: 0.45rem; cursor: pointer;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--pill);
  color: var(--muted); font-family: var(--ui); font-weight: 600; font-size: 0.85rem;
  padding: 0.45rem 0.95rem;
}
.pd-tab .ico { font-size: 1rem; }
.pd-tab:hover { color: var(--text); background: var(--hover-bg); border-color: var(--line-amber); }
.pd-tab.active { color: var(--amber-hi); border-color: var(--line-amber); background: rgba(255, 154, 46, 0.09); }
.pd-pane[hidden] { display: none; }
/* Skills & attributes (grouped by attribute, with the attribute value) */
.pd-sec-title { display: flex; align-items: center; gap: 0.4rem; margin: 0.2rem 0 0.7rem; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--amber); font-weight: 700; }
.pd-sec-title .ico { color: var(--amber); }
.pd-skills { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 0.8rem; margin-bottom: 0.9rem; }
.pd-sg { display: flex; flex-direction: column; gap: 0.4rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.8rem 0.85rem; }
.pd-sg-head { display: flex; align-items: center; gap: 0.4rem; padding-bottom: 0.3rem; border-bottom: 1px solid var(--line-amber); }
.pd-sg-head .ico { color: var(--amber); }
.pd-sg-head b { color: var(--amber); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.06em; }
.pd-attr { margin-left: auto; font-family: var(--mono); color: var(--amber-hi); font-size: 0.95rem; }
.pd-sk { display: grid; grid-template-columns: 1fr auto; gap: 0.1rem 0.5rem; font-size: 0.82rem; }
.pd-sk-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pd-sk-tier { font-size: 0.68rem; text-transform: uppercase; color: var(--muted); letter-spacing: 0.04em; }
.pd-sk-bar { grid-column: 1 / -1; height: 4px; background: var(--inset); border: 1px solid var(--line); border-radius: 3px; overflow: hidden; }
.pd-sk-bar > span { display: block; height: 100%; background: var(--amber); }
.pd-sk.sk-l0 .pd-sk-bar > span { background: var(--faint); }
.pd-sk.sk-l1 .pd-sk-bar > span { background: var(--standby); }
.pd-sk.sk-l2 .pd-sk-bar > span { background: #b8bcc2; }
.pd-sk.sk-l3 .pd-sk-bar > span { background: var(--amber); }
.pd-sk.sk-l4 .pd-sk-bar > span { background: var(--amber-hi); }
.pd-ban-note { font-size: 0.82rem; color: var(--standby); }
/* Admin action bar on the player card */
.pd-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 1rem; padding-top: 0.9rem; border-top: 1px solid var(--line); }
.pd-actions .ico { font-size: 0.95rem; }
.pd-note-in { display: block; width: 100%; margin-top: 0.6rem; min-height: 68px; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius); color: var(--text); font-family: var(--ui); font-size: 0.88rem; line-height: 1.4; padding: 0.55rem 0.7rem; box-sizing: border-box; resize: vertical; }
.pd-note-in:focus { outline: none; border-color: var(--amber); }
.pd-action-msg { display: inline-block; margin-top: 0.5rem; font-size: 0.85rem; color: var(--standby); }
#ban-msg { font-size: 0.85rem; color: var(--standby); }

/* Player Intelligence / risk */
.bad { color: #e39a9a; }
.pd-stat-v.bad { color: #e39a9a; }
.pd-risk { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; padding: 0.15rem 0.5rem; border-radius: 999px; border: 1px solid var(--line); }
.pd-risk.high  { color: #f0b2b2; border-color: rgba(224,80,80,0.6);  background: rgba(224,80,80,0.12); }
.pd-risk.watch { color: #e6c789; border-color: rgba(230,166,26,0.55); background: rgba(230,166,26,0.12); }
.pd-risk.clean { color: #a4d6ad; border-color: rgba(59,165,93,0.5);  background: rgba(59,165,93,0.12); }
.pd-risk-head { display: flex; align-items: center; gap: 0.8rem; padding: 0.7rem 0.9rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); }
.pd-risk-head.high  { border-color: rgba(224,80,80,0.5); }
.pd-risk-head.watch { border-color: rgba(230,166,26,0.45); }
.pd-risk-badge { font-weight: 700; font-size: 0.9rem; white-space: nowrap; }
.pd-risk-bar { flex: 1; height: 8px; background: var(--bg); border: 1px solid var(--line); border-radius: 999px; overflow: hidden; }
.pd-risk-fill { height: 100%; background: linear-gradient(90deg, var(--standby), var(--amber-hi)); }
.pd-risk-head.high .pd-risk-fill  { background: linear-gradient(90deg, #d98b8b, #e05050); }
.pd-risk-head.watch .pd-risk-fill { background: linear-gradient(90deg, #d9bd7a, #e6a61a); }
.pd-risk-head.clean .pd-risk-fill { background: linear-gradient(90deg, #7ec48a, #3ba55d); }
.pd-risk-score { font-family: var(--mono); font-size: 1.1rem; font-weight: 700; white-space: nowrap; }
.pd-risk-score span { font-size: 0.72rem; color: var(--muted); }
.pd-flags { margin: 0.3rem 0 0.6rem; padding-left: 1.1rem; font-size: 0.85rem; color: var(--standby); }
.pd-flags li { margin: 0.15rem 0; }
/* Intel tab — readable label→value rows (values wrap fully, no truncation) */
.pi-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 1rem; margin-bottom: 1.1rem; }
.pi-rows { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--panel-2); }
.pi-row { display: grid; grid-template-columns: minmax(104px, 36%) 1fr; gap: 0.3rem 1rem; padding: 0.5rem 0.8rem; border-top: 1px solid var(--line); align-items: baseline; }
.pi-row:first-child { border-top: none; }
.pi-k { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); font-weight: 600; }
.pi-v { color: var(--text); font-size: 0.88rem; overflow-wrap: anywhere; display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }
.pi-row.bad .pi-v { color: #e39a9a; }
.pi-v .rr-flag { margin: 0; }
/* Intel-tab section headings get a bit more breathing room */
.pd-pane[data-ppane="intel"] .pd-sec-title { margin-top: 1.5rem; margin-bottom: 1rem; }
.pd-intel-checked { align-self: center; font-size: 0.76rem; color: var(--muted); }
/* Risk marker shown after the player name — icon coloured by level */
.rr-risk { display: inline-flex; vertical-align: -0.15em; }
.rr-risk .ico { width: 0.95em; height: 0.95em; }
.rr-risk.high { color: #e05050; }
.rr-risk.watch { color: #e6a61a; }
.rr-risk.clean { color: #57b87f; opacity: 0.85; }
.rr-risk.watch .ico, .rr-risk.clean .ico { stroke-width: 1.9; }
/* Country flag before a player name (dashboard + roster) — bundled SVG, real flag on every OS */
img.rr-flag { width: 1.15em; height: 0.86em; margin-right: 0.35rem; vertical-align: -0.08em; border-radius: 1px; box-shadow: 0 0 0 1px rgba(0,0,0,0.25); object-fit: cover; }
/* "unknown country" placeholder — identical footprint to a flag so rows stay aligned */
.rr-flag-unknown { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: 1.15em; height: 0.86em; margin-right: 0.35rem; vertical-align: -0.08em; border-radius: 1px; background: var(--inset); box-shadow: 0 0 0 1px rgba(0,0,0,0.25); color: var(--muted); overflow: hidden; }
.rr-flag-unknown i { font-size: 0.58rem; font-style: normal; font-weight: 700; line-height: 1; }
.pd-hero-top .rr-flag-unknown { width: 1.5em; height: 1.12em; font-size: 0.8em; }

/* Player Intelligence dashboard panel (above the online-players box) */
/* Dashboard panel — clickable stat tiles (each filters the Players table) */
.intel-tiles { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; }
.intel-tile { display: inline-flex; align-items: center; gap: 0.45rem; padding: 0.55rem 0.85rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); color: var(--text); cursor: pointer; font: inherit; transition: border-color 0.12s, background 0.12s; }
.intel-tile:hover { border-color: var(--amber); background: rgba(240, 130, 12, 0.14); }
.intel-tile .ico { width: 1.15em; height: 1.15em; color: var(--muted); }
.intel-tile .v { font-size: 1.2rem; font-weight: 700; font-family: var(--mono); color: var(--text); }
.intel-tile .k { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); }
.intel-tile.high .ico, .intel-tile.high .v { color: #e05050; }
.intel-tile.watch .ico, .intel-tile.watch .v { color: #e6a61a; }
.intel-tile.wl .ico { color: var(--amber); }
.intel-tile.bad .ico { color: #e05050; }
/* panel sub-sections: top countries + flagged */
.intel-sub-block { margin-top: 0.9rem; }
.intel-sub-h { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted); font-weight: 700; margin-bottom: 0.45rem; }
.intel-countries { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.intel-cc { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.24rem 0.55rem 0.24rem 0.4rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--pill); color: var(--text); cursor: pointer; font: inherit; }
.intel-cc:hover { border-color: var(--amber); background: rgba(240, 130, 12, 0.14); }
.intel-cc .rr-flag { margin: 0; }
.intel-cc-n { font-family: var(--mono); font-size: 0.82rem; font-weight: 700; color: var(--text); }
/* flagged players — name button + a "mark reviewed" ✓ */
.intel-flagged { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.intel-fp { display: inline-flex; align-items: stretch; background: var(--inset); border: 1px solid var(--line); border-radius: var(--pill); overflow: hidden; max-width: 17rem; }
.intel-fp:hover { border-color: var(--line-amber); }
.intel-fp-open { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.3rem 0.55rem; background: none; border: none; color: var(--text); cursor: pointer; font: inherit; font-size: 0.84rem; min-width: 0; }
.intel-fp-open:hover { background: rgba(240, 130, 12, 0.14); }
.intel-fp .rr-risk .ico { width: 0.9em; height: 0.9em; }
.intel-fp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.intel-fp-on { width: 7px; height: 7px; border-radius: 50%; background: var(--online); flex: none; margin-left: 0.1rem; }
.intel-fp-ok { display: inline-flex; align-items: center; justify-content: center; padding: 0 0.45rem; background: none; border: none; border-left: 1px solid var(--line); color: var(--muted); cursor: pointer; }
.intel-fp-ok:hover { background: rgba(87,184,127,0.16); color: #57b87f; }
.intel-fp-ok .ico { width: 0.95em; height: 0.95em; }
/* active dashboard filter chip in the roster bar */
.roster-intel-chip:empty { display: none; }
.roster-fchip { display: inline-flex; align-items: center; gap: 0.25rem; padding: 0.2rem 0.25rem 0.2rem 0.5rem; background: rgba(240,130,12,0.12); border: 1px solid var(--line-amber); border-radius: var(--pill); font-size: 0.8rem; color: var(--amber-hi); }
.roster-fchip .rr-flag { margin: 0 0.15rem 0 0; }
.roster-fchip-x { background: none; border: none; color: inherit; cursor: pointer; font-size: 1.05rem; line-height: 1; padding: 0 0.15rem; }
.roster-fchip-x:hover { color: var(--text); }

.bans code { font-family: var(--mono); color: var(--amber); }
#bans-table { width: 100%; border-collapse: collapse; }
#bans-table th, #bans-table td { text-align: left; padding: 0.45rem 0.55rem; border-bottom: 1px solid var(--line); font-size: 0.9rem; }
#bans-table th { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
#bans-table .mono { font-family: var(--mono); color: var(--muted); }
#bans-table .muted { color: var(--muted); }

/* ── Roster pagination ────────────────────────────────────────────────────── */
.roster-pager { display: flex; align-items: center; justify-content: center; gap: 0.9rem; margin-top: 0.9rem; }
.roster-pager.hidden { display: none; }
.roster-pager button[disabled] { opacity: 0.4; cursor: default; }
.rr-uid, .pd-uid { margin-left: .45rem; color: var(--muted); font-size: .78rem; opacity: .85; }
.roster-pager-info { flex-basis: 100%; text-align: center; font-family: var(--mono); font-size: 0.8rem; color: var(--muted); }

/* ── Clickable player rows / online list ──────────────────────────────────── */
.clickable { cursor: pointer; transition: color 0.12s, background 0.12s; }
#players-list li.clickable:hover { color: var(--amber-hi); }
#dc-linked-tbody tr.clickable:hover { background: rgba(240, 130, 12, 0.06); }
.players-hint { color: var(--muted); font-size: 0.82rem; margin: 0 0 0.6rem; }

/* ── Field Console link (back to public site) ─────────────────────────────── */
.nav-actions .navlink, .header-actions .navlink {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.5rem 1rem; border: 1px solid var(--line-amber); border-radius: var(--radius);
  background: var(--panel-2); color: var(--amber-hi);
  font-family: var(--ui); font-weight: 600; font-size: 0.82rem; letter-spacing: 0.05em; text-transform: uppercase;
}
.nav-actions .navlink:hover, .header-actions .navlink:hover { background: var(--panel); border-color: var(--amber); }

/* ── Player detail: bank cards ────────────────────────────────────────────── */
.pd-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: 0.7rem; margin-bottom: 0.9rem; }
.pd-cards .pd-card { display: flex; gap: 0.8rem; padding: 0.7rem 0.8rem; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); margin: 0; }
.pd-card-img { width: 80px; height: 52px; flex: none; object-fit: contain; align-self: center; }
.pd-card-body { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.pd-card-type { font-weight: 700; color: var(--amber); text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.82rem; }
.pd-card-rows { display: flex; flex-wrap: wrap; gap: 0.1rem 1rem; font-size: 0.8rem; color: var(--muted); }
.pd-card-rows b { color: var(--text); font-family: var(--mono); margin-left: 0.2rem; }

/* ── Dashboard top menu: lighter, Field-Console-like nav (ghost buttons) ───── */
#dashboard-screen .nav-actions .secondary,
#dashboard-screen .nav-actions .navlink {
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted);
}
#dashboard-screen .nav-actions .secondary:hover,
#dashboard-screen .nav-actions .navlink:hover {
  color: var(--amber-hi);
  border-color: var(--line-amber);
  background: var(--panel-2);
}
#dashboard-screen .nav-actions #logout-btn:hover { color: var(--alarm); border-color: rgba(229,68,63,0.4); background: transparent; }

/* ── App footer + App status modal ─────────────────────────────────────────── */
.foot-link { background: none; border: 0; padding: 0; color: var(--amber); cursor: pointer; font: inherit; }
.foot-link:hover { background: none; border: 0; color: var(--amber-hi); text-decoration: underline; }
.amodal { position: fixed; inset: 0; z-index: 100; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,0.6); padding: 1rem; }
.amodal.hidden { display: none; }
.amodal-box { position: relative; background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius); padding: 1.4rem 1.6rem; max-width: 440px; width: 100%; max-height: 85vh; overflow: auto; }
.amodal.wide .amodal-box { max-width: 1040px; }
.amodal-close { position: absolute; top: 0.7rem; right: 0.7rem; cursor: pointer; background: transparent; border: 1px solid var(--line); border-radius: var(--radius); color: var(--muted); width: 28px; height: 28px; }
.amodal-close:hover { background: transparent; color: var(--alarm); border-color: var(--alarm); }
.amodal-box h2 { margin: 0 0 0.4rem; font-size: 1.1rem; }
.appstat { display: flex; flex-direction: column; margin-top: 0.6rem; }
.appstat-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.55rem 0.1rem; border-bottom: 1px solid var(--line); }
.appstat-row:last-child { border-bottom: 0; }
.appstat-k { color: var(--muted); font-size: 0.85rem; }
.appstat-v { color: var(--text); font-family: var(--mono); margin-left: auto; text-align: right; }

/* ── Premium ──────────────────────────────────────────────────────────────── */
.prem-body { margin: 1rem 1.5rem 2rem; }
/* Notes/messages inside the premium column already sit in the padded body — drop
   the global horizontal margin (was double-indenting them) and give real spacing. */
.prem-body .settings-note, .prem-body .settings-message { margin-left: 0; margin-right: 0; }
.prem-form .settings-note { margin: 0.4rem 0 1.1rem; }
.prem-form .settings-message { margin: 0 0 0.6rem; }
.prem-hero { display: flex; align-items: center; gap: 1.4rem; flex-wrap: wrap; background: linear-gradient(135deg, rgba(240,130,12,0.14), rgba(240,130,12,0.02)); border: 1px solid var(--line-amber); border-radius: var(--radius); padding: 1.3rem 1.5rem; margin: 0 0 1.2rem; }
.prem-hero-txt { flex: 1 1 320px; min-width: 0; }
.prem-h { margin: 0 0 0.4rem; font-size: 1.35rem; color: var(--amber-hi); letter-spacing: 0.01em; }
.prem-lead { margin: 0; color: var(--text); line-height: 1.55; font-size: 0.92rem; }
.prem-lead code { font-family: var(--mono); color: var(--amber); }
.prem-price { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 0.1rem; padding: 0.6rem 1.1rem; border: 1px solid var(--line-amber); border-radius: var(--radius); background: var(--panel); }
.prem-price-row { display: flex; align-items: baseline; gap: 0.3rem; }
.prem-price-amt { font-size: 2.1rem; font-weight: 800; color: var(--amber-hi); font-family: var(--mono); }
.prem-price-per { font-size: 0.85rem; color: var(--muted); }
.prem-price-extra { font-size: 0.72rem; color: var(--muted); white-space: nowrap; }
.prem-features { background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.1rem 1.3rem; margin-bottom: 1.2rem; }
.prem-list { list-style: none; margin: 0.3rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.prem-list li { position: relative; padding-left: 1.5rem; font-size: 0.9rem; color: var(--text); line-height: 1.45; }
.prem-list li::before { content: '✓'; position: absolute; left: 0; color: var(--online); font-weight: 700; }
.prem-list li.prem-soon { color: var(--muted); }
.prem-list li.prem-soon::before { content: '+'; color: var(--amber); }
.prem-note-min { margin: 0.9rem 0 0; font-size: 0.82rem; color: var(--muted); line-height: 1.5; }
.prem-status { display: flex; align-items: center; gap: 0.85rem; background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.85rem 1.1rem; margin-bottom: 1.4rem; }
.prem-status-dot { width: 12px; height: 12px; border-radius: 50%; flex: none; background: var(--faint); }
.prem-status-dot[data-state="active"] { background: var(--online); box-shadow: 0 0 8px var(--online); }
.prem-status-dot[data-state="inactive"] { background: var(--standby); }
.prem-status-dot[data-state="error"] { background: var(--alarm); }
.prem-status-dot[data-state="unknown"] { background: var(--faint); }
.prem-status-txt { display: flex; flex-direction: column; gap: 0.12rem; min-width: 0; flex: 1 1 auto; }
.prem-status-line { font-weight: 700; font-size: 0.95rem; color: var(--text); }
.prem-status-sub { font-size: 0.8rem; color: var(--muted); }
.prem-manage { flex: none; display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.9rem; border: 1px solid var(--line-amber); border-radius: var(--radius); background: var(--panel-2); color: var(--amber-hi); font-weight: 600; font-size: 0.82rem; }
.prem-manage:hover { background: var(--panel); border-color: var(--amber); text-decoration: none; }
.prem-manage .ico { width: 1em; height: 1em; }
.prem-form { background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.1rem 1.3rem 1.3rem; }
.prem-field { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.9rem; }
.prem-field label { font-size: 0.8rem; font-weight: 600; color: var(--text); }
.prem-field input { width: 100%; max-width: 620px; box-sizing: border-box; padding: 0.55rem 0.7rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); color: var(--text); font-family: var(--mono); font-size: 0.88rem; }
.prem-field input:focus { outline: none; border-color: var(--amber); box-shadow: var(--focus-ring); }
/* The shared .subhead carries a 1.5rem side margin; inside the premium cards that
   double-indents it — reset so it lines up with the card content. */
.prem-features .subhead, .prem-form .subhead { margin: 0 0 0.9rem; }
/* Kill the browser autofill's light background/text on the dark theme. */
.prem-field input:-webkit-autofill,
.prem-field input:-webkit-autofill:hover,
.prem-field input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--text);
  -webkit-box-shadow: 0 0 0 1000px var(--inset) inset;
  box-shadow: 0 0 0 1000px var(--inset) inset;
  caret-color: var(--text);
}
.prem-form-actions { display: flex; gap: 0.6rem; margin-top: 1.1rem; flex-wrap: wrap; }

/* ── Live Map ────────────────────────────────────────────────────────────── */
.map-updated { color: var(--muted); font-size: 0.82rem; align-self: center; }
.map-wrap {
  position: relative; display: flex; margin: 1.2rem;
  border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden;
}

/* Side panel — layer toggles, options + searchable entity lists (CFTools-style). */
.map-side {
  width: 280px; flex: 0 0 280px; display: flex; flex-direction: column;
  height: 72vh; min-height: 380px; background: var(--panel); border-right: 1px solid var(--line);
  overflow-y: auto;
}
/* The lists, on the other side of the map from the controls. */
.map-side-r { border-right: 0; border-left: 1px solid var(--line); }

/* Either side slides away. Width and flex-basis both go to zero — flex-basis alone leaves the
   panel's min-content width behind, which is how a "collapsed" panel keeps a 60px stub. */
.map-side.collapsed { width: 0; flex-basis: 0; min-width: 0; border-width: 0; overflow: hidden; }
.map-side.collapsed > * { display: none; }

/* The rail that brings it back: a full-height sliver, so the target is easy to hit without taking
   width from the map. */
.map-rail {
  flex: 0 0 14px; width: 14px; align-self: stretch; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--panel-2); border: 0; border-right: 1px solid var(--line);
  color: var(--muted); font-size: 0.8rem; line-height: 1; cursor: pointer;
  --hov-bg: var(--inset); --hov-fg: var(--amber-hi); --hov-line: transparent;
}
.map-rail-r { border-right: 0; border-left: 1px solid var(--line); }
.map-rail span { display: block; transition: transform .15s ease; }
.map-rail.closed span { transform: rotate(180deg); }
/* The two find fields — "filter by player" on the left, "search name, sector" on the right — are
   ONE style. They were written apart and drifted into two looks for the same job: a boxed input on
   one side, a bare one on the other, at different sizes. Two treatments for one control read as two
   different kinds of control. Anything specific to either field belongs below this block, not in a
   second copy of it. The player one is a type-to-search combobox rather than a <select>: a full
   server has hundreds of names. */
.map-find { position: relative; display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--line); }
.map-find > .ico { width: 15px; height: 15px; color: var(--muted); flex: none; }
.map-find input {
  flex: 1; min-width: 0; padding: 0.3rem 0.4rem; font-family: var(--ui); font-size: 0.8rem;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); color: var(--text);
}
.map-find input:focus { outline: none; border-color: var(--amber); }
.map-find input::placeholder { color: var(--muted); }
/* Both fields clear the same way. The search one had no clear button at all, which is half the
   reason the pair looked unrelated — one row was visibly a field with controls, the other was not. */
.map-find > button[hidden] { display: none; }
.map-owner-sug {
  position: absolute; z-index: 20; left: 0.75rem; right: 0.75rem; top: 100%;
  max-height: 260px; overflow-y: auto; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line-amber, var(--line)); border-radius: var(--radius);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
}
.map-owner-sug[hidden] { display: none; }
/* Beats the global button skin, which would paint each suggestion solid amber. */
.map-owner-sug .mos-item,
.map-owner-sug .mos-item:hover {
  display: block; width: 100%; text-align: left; padding: 0.35rem 0.6rem; border: 0; border-radius: 0;
  background: none; color: var(--text); font: inherit; font-size: 0.8rem; letter-spacing: 0; transform: none;
}
.map-owner-sug .mos-item:hover, .map-owner-sug .mos-item.hi { background: var(--panel-2); color: var(--amber-hi); }

/* Each side says what it is. With one panel there was nothing to confuse; with two, an unlabelled
   column of controls beside an unlabelled column of rows makes the reader work out which is which. */
.map-side-h {
  display: flex; align-items: center; gap: 0.45rem;
  padding: 0.55rem 0.75rem; border-bottom: 1px solid var(--line);
  background: var(--panel-2); color: var(--muted);
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  position: sticky; top: 0; z-index: 2;
}
.map-side-h .ico { width: 14px; height: 14px; color: var(--amber); flex: none; }
.map-side-reset { margin-left: auto; }
/* .map-search-row keeps its name for the scripts that query it; its LOOK comes from .map-find. */

/* Collapsible "Layers & filters" dropdown — keeps the tall toggle list from crowding
   out the entity lists below. */
.map-filters { border-bottom: 1px solid var(--line); }
.map-filters-head {
  display: flex; align-items: center; gap: 0.5rem; width: 100%;
  padding: 0.6rem 0.75rem; background: none; border: none; cursor: pointer;
  color: var(--muted); font-family: var(--ui); font-weight: 700; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.08em;
}
.map-filters-head:hover { color: var(--text); background: var(--hover-bg); border-color: transparent; }
.map-filters-head .ico { width: 15px; height: 15px; flex: none; color: var(--amber); }
.mf-chev { margin-left: auto; font-size: 0.85rem; transition: transform 0.15s; }
.map-filters.collapsed .mf-chev { transform: rotate(-90deg); }
.map-filters.collapsed .map-layers { display: none; }

/* Layer cards: a header (badge + name + count, toggles visibility) + segmented sub-filter. */
.map-layers { display: flex; flex-direction: column; gap: 0.4rem; padding: 0.6rem; }
.mlyr { border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden; background: var(--panel-2); }
.map-layer {
  display: flex; align-items: center; gap: 0.55rem; width: 100%; padding: 0.5rem 0.6rem;
  background: none; border: none; color: var(--text);
  font-family: var(--ui); font-weight: 600; font-size: 0.88rem; cursor: pointer; text-align: left;
}
/* ── one place where a kind's badge colour is written ────────────────────────
   Three lists used to say this: the layer picker (.ml-badge), the card icon (.mp-ico) and the card's
   fallback glyph behind an image (.mp-ico-fb). They drifted, as three copies of one fact do — the
   seven live kinds were added to the picker and never to the card, so a puppet or an NPC with no
   artwork opened a card with no colour behind its glyph at all while the same thing in the filter
   list was properly coloured. Now the kind carries its colour as a token and every badge reads it. */
.mk-player   { --badge-bg: var(--c-player); }
.mk-base     { --badge-bg: var(--c-base); }
.mk-vehicle  { --badge-bg: var(--c-vehicle); }
.mk-chest    { --badge-bg: var(--c-chest); }
.mk-storage  { --badge-bg: var(--c-storage); }
.mk-trap     { --badge-bg: var(--c-trap); }
.mk-turret   { --badge-bg: var(--c-turret); }
.mk-poi      { --badge-bg: var(--c-poi); }
/* Each POI category its own colour and glyph — see POI_STYLE. A field of identical stars is the one
   thing a single "places" layer must not become. */
.poi-sentry  { --badge-bg: var(--lk-sentry); }
.poi-trader  { --badge-bg: var(--c-chest); }
.poi-bank    { --badge-bg: var(--c-turret); }
.poi-bunker  { --badge-bg: var(--lk-boss); }
.poi-fuel    { --badge-bg: var(--c-poi); }
.poi-sentry .mk-dot { background: var(--lk-sentry); }
.poi-trader .mk-dot { background: var(--c-chest); }
.poi-bank   .mk-dot { background: var(--c-turret); }
.poi-bunker .mk-dot { background: var(--lk-boss); }
.poi-fuel   .mk-dot { background: var(--c-poi); }
/* Hollow centre on every one of them: a POI is a place, not a thing standing there. */
.poi-sentry .mk-dot, .poi-trader .mk-dot, .poi-bank .mk-dot,
.poi-bunker .mk-dot, .poi-fuel .mk-dot { box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.45); }
.ml-dot.poi-sentry { background: var(--lk-sentry); }
.ml-dot.poi-trader { background: var(--c-chest); }
.ml-dot.poi-bank   { background: var(--c-turret); }
.ml-dot.poi-bunker { background: var(--lk-boss); }
.ml-dot.poi-fuel   { background: var(--c-poi); }
/* An item's condition sits between its name and its count, and must not squeeze either. */
/* `.mp-c-hp` held the bar at the END of a contents row. The condition is a dot and a figure beside
   the picture now, so the wrapper is gone rather than left behind styling an element nothing emits. */
.mk-puppet   { --badge-bg: var(--lk-puppet); }
.mk-animal   { --badge-bg: var(--lk-animal); }
.mk-sentry   { --badge-bg: var(--lk-sentry); }
.mk-npc      { --badge-bg: var(--lk-npc); }
.mk-drone    { --badge-bg: var(--lk-drone); }
.mk-dropship { --badge-bg: var(--lk-dropship); }
.mk-boss     { --badge-bg: var(--lk-boss); }
/* The per-KIND classes need the token too, and this is where the same bug was hiding one level
   further in. A live thing's card is styled by its KIND (`lk-puppet`), not by its layer (`mk-puppet`)
   — LIVE_KIND_STYLE against MAP_LAYERS — and every `lk-*` rule in this stylesheet is a DESCENDANT
   rule painting `.mk-dot`. A card icon has no dot inside it, so a puppet, an NPC or a sentry with no
   artwork opened a card with a transparent square while the same kind was properly coloured in the
   filter list. Same colours, from the same tokens, so the two can no longer disagree. */
.lk-player   { --badge-bg: var(--c-player); }
.lk-puppet   { --badge-bg: var(--lk-puppet); }
.lk-animal   { --badge-bg: var(--lk-animal); }
.lk-shark    { --badge-bg: var(--lk-shark); }
.lk-sentry   { --badge-bg: var(--lk-sentry); }
.lk-sentryd  { --badge-bg: var(--lk-sentry); }
.lk-npc      { --badge-bg: var(--lk-npc); }
.lk-drone    { --badge-bg: var(--lk-drone); }
.lk-dropship { --badge-bg: var(--lk-dropship); }
.lk-boss     { --badge-bg: var(--lk-boss); }
.lk-vehicle  { --badge-bg: var(--c-vehicle); }
.lk-base     { --badge-bg: var(--c-base); }
/* No colour on the bare wrapper: .mp-ico.mp-img carries no kind class, so it stays transparent and
   the artwork sits on the panel rather than on a coloured square. */
.ml-badge, .mp-ico, .mp-ico-fb { background: var(--badge-bg, transparent); }

.ml-badge {
  display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
  border-radius: var(--radius-md); flex: none; border: 1px solid rgba(0, 0, 0, 0.35);
}
.ml-badge .ico { width: 14px; height: 14px; color: var(--bg); }







/* The live layers. Their marker colour is written as `.mk-puppet .mk-dot` — a DESCENDANT rule, and
   a badge has no dot inside it, so these seven had no background at all and showed the bare panel
   through a coloured-looking border. The picker is the one place a layer is nothing BUT its icon
   and its colour, so this is not cosmetic. Same values as the markers, from the same tokens. */







.map-layer .ml-name { flex: 1; }
.map-layer:hover { background: rgba(255, 255, 255, 0.06); color: var(--text); }
.map-count {
  min-width: 1.6em; padding: 0.05rem 0.45em; text-align: center; border-radius: var(--pill);
  background: rgba(0, 0, 0, 0.3); color: var(--muted); font-size: 0.75rem; font-variant-numeric: tabular-nums;
}
.map-layer.active .map-count { color: var(--text); }
/* Hidden layer: dim the group, grey the badge, strike the name. */
.mlyr:has(.map-layer:not(.active)) { opacity: 0.55; }
.map-layer:not(.active) .ml-badge { filter: grayscale(1); }
.map-layer:not(.active) .ml-name { text-decoration: line-through; text-decoration-color: var(--muted); }

/* `nowrap` was right when this row held three toggles and two links in one panel. Split across two
   panels the contents differ per side, and a fixed row squeezed the exports into unreadable slivers
   beside a checkbox — so it wraps, and the export links take a line of their own where they read as
   the actions they are. */
.map-opts { display: flex; align-items: center; flex-wrap: wrap;
  gap: 0.5rem; padding: 0.4rem 0.7rem 0.6rem; border-bottom: 1px solid var(--line); }

/* The three view toggles, as three equal cells rather than three labels crammed edge to edge on one
   line — where the last one ran into the panel border and the row read as an afterthought under the
   search field rather than as a control. Equal columns also mean a longer word in another language
   cannot squeeze the other two. `:has(:checked)` lights the cell, so on/off is legible at a glance
   instead of resting on a 14 px native tick. */
/* Cells sized by their own word, not three equal thirds. Equal thirds fitted English and clipped
   German the moment "Sectors" became "Sektoren" — a layout that only works in the language it was
   designed in. They still fill the row: each takes what its label needs and the slack is shared. */
.map-opts-top {
  display: flex; align-items: stretch;
  gap: 0.4rem; padding: 0.5rem 0.75rem;
}
.map-opts-top .map-toggle {
  flex: 1 0 auto;   /* grow into the slack, never shrink below the word */
  justify-content: center; gap: 0; padding: 0.34rem 0.3rem; min-width: 0;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
  --hov-bg: var(--panel-2); --hov-fg: var(--text);
}
/* The native tick is gone from the cell but not from the document: it still takes the click, the
   keyboard focus and the checked state, and screen readers still read a checkbox. Only its 14 px of
   box is reclaimed — with it, three cells in a 280 px panel left about 55 px of text each and every
   label rendered as "Na…", "Sec…", "In vi…". The cell itself is now the tick. */
.map-opts-top .map-toggle input {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.map-opts-top .map-toggle > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-opts-top .map-toggle:has(:checked) {
  border-color: var(--amber); background: rgba(255, 154, 46, 0.12); color: var(--amber-hi);
}
.map-opts-top .map-toggle:hover { background: var(--hov-bg); color: var(--hov-fg); }
.map-opts-top .map-toggle:has(:checked):hover { border-color: var(--amber-hi); }
/* Keyboard users get the focus ring the hidden input would have shown. */
.map-opts-top .map-toggle:has(:focus-visible) { outline: 2px solid var(--amber); outline-offset: 1px; }

/* An empty layer keeps its row and loses its dropdowns: "All (0)" and "All types (0)" are two
   controls that cannot do anything, and with the bridge off there were five such layers spending
   three rows each. The row itself stays — "Sentries: 0" is information. */
.mlyr.empty .map-subf { display: none; }
.mlyr.empty .map-layer { opacity: 0.55; }
.map-toggle { display: inline-flex; align-items: center; gap: 0.3rem; color: var(--muted);
  font-size: 0.78rem; line-height: 1; white-space: nowrap; flex: 0 0 auto; cursor: pointer; }
.map-toggle input { accent-color: var(--amber); width: 14px; height: 14px; margin: 0; flex: none; }
.map-zoom-btn {
  display: inline-flex; margin-left: auto; padding: 0.3rem; background: var(--panel-2); border: 1px solid var(--line);
  color: var(--muted); border-radius: var(--radius-md); cursor: pointer;
}
.map-zoom-btn:hover { color: var(--amber-hi); border-color: var(--line-amber); background: var(--panel-2); }
.map-zoom-btn .ico { width: 15px; height: 15px; }

.map-lists { flex: 0 0 auto; padding: 0 0 0.6rem; }
.ml-sec { margin-bottom: 0.3rem; }
/* Prominent, flush sticky header — full width, bottom rule, no top gap. */
.ml-sec-h {
  display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.6rem; position: sticky; top: 0; z-index: 2;
  background: var(--panel); border-bottom: 1px solid var(--line);
  color: var(--text); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
}
.ml-sec-h .ico { width: 13px; height: 13px; color: var(--amber-hi); }
/* The caveat beside a section title ("loaded areas"), which had no style of its own at all — so it
   inherited the header's uppercase, 700 weight and letter-spacing, came out as wide as the title it
   was qualifying, and wrapped onto a second line that pushed the count pill out of the row. It is an
   aside: lower case, lighter, quieter, and it never wraps. */
.ml-sec-h .ml-sec-note {
  flex: 0 0 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-transform: none; letter-spacing: 0; font-weight: 400; font-size: 0.64rem;
  color: var(--muted); cursor: help;
}
/* Section count — same pill as .map-count so all counters in the panel match. */
.ml-sec-n {
  margin-left: auto; min-width: 1.6em; padding: 0.05rem 0.45em; text-align: center;
  border-radius: var(--pill); background: rgba(0, 0, 0, 0.3); color: var(--muted);
  font-size: 0.75rem; font-variant-numeric: tabular-nums;
}
.ml-row {
  display: flex; align-items: center; gap: 0.5rem; width: 100%; padding: 0.4rem 0.6rem;
  background: none; border: none; cursor: pointer; text-align: left; color: var(--text);
}
.ml-row:hover { background: rgba(255, 255, 255, 0.06); color: var(--text); }
.ml-row .ml-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; border: 1px solid rgba(0, 0, 0, 0.5); }
.mlr-main { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.mlr-name { font-size: 0.82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mlr-meta { font-size: 0.7rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mlr-sec { font-size: 0.72rem; color: var(--muted); font-variant-numeric: tabular-nums; padding: 0 0.35em; border: 1px solid var(--line); border-radius: var(--radius); align-self: center; }
.ml-empty { padding: 1rem; text-align: center; color: var(--muted); font-size: 0.85rem; }
.ml-dot.mk-player  { background: var(--c-player); }
.ml-dot.mk-base    { background: var(--c-base); }
.ml-dot.mk-vehicle { background: var(--c-vehicle); }
.ml-dot.mk-chest   { background: var(--c-chest); }
.ml-dot.mk-storage { background: var(--c-storage); }
.ml-dot.mk-trap    { background: var(--c-trap); }
.ml-dot.mk-turret  { background: var(--c-turret); }

/* Sub-filter = a compact dropdown at the bottom of each layer card (scales to any
   number of options, stays readable in the narrow panel — unlike a row of chips). */
.map-subf { padding: 0.4rem 0.5rem 0.5rem; border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 0.35rem; }
.sf-select, .sf-type-select {
  width: 100%; padding: 0.4rem 1.6rem 0.4rem 0.55rem; background: var(--panel-2); border: 1px solid var(--line);
  color: var(--text); font-family: var(--ui); font-weight: 600; font-size: 0.78rem; border-radius: var(--radius-md); cursor: pointer;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23ff9a2e' stroke-width='2.5'><path d='M6 9l6 6 6-6'/></svg>");
  background-repeat: no-repeat; background-position: right 0.55rem center;
}
.sf-select:hover, .sf-select:focus, .sf-type-select:hover, .sf-type-select:focus { border-color: var(--line-amber); outline: none; }
.sf-select option, .sf-type-select option { background: var(--panel); color: var(--text); }
.map-mk.mk-player:not(.off) .mk-dot { box-shadow: 0 0 0 2px rgba(53, 208, 127, 0.4), 0 1px 3px rgba(0, 0, 0, 0.6); }
.map-mk.mk-player.off .mk-dot { background: var(--offline); }
/* Online but dead (awaiting respawn): red ring so the state reads at a glance. */
.map-mk.mk-player.dead:not(.off) .mk-dot { background: var(--alarm); box-shadow: 0 0 0 2px rgba(237, 66, 69, 0.45), 0 1px 3px rgba(0, 0, 0, 0.6); }
.map-mk.mk-player.off { opacity: 0.72; }
.ml-row.off .ml-dot { background: var(--offline); }
.ml-row.off .mlr-name { color: var(--muted); }

/* Sector grid — inside the canvas so it pans/zooms with the map. */
.map-sectors { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
.map-sectors.hidden { display: none; }
.sec-line { position: absolute; background: rgba(255, 200, 120, 0.20); }
.sec-v { top: 0; bottom: 0; width: 1px; }
.sec-h { left: 0; right: 0; height: 1px; }
.sec-lab {
  position: absolute; transform: scale(var(--mz, 1)); transform-origin: top left;
  padding: 3px 6px; color: #fff; font-family: var(--ui); font-weight: 800; font-size: 22px;
  letter-spacing: 0.04em; white-space: nowrap;
  text-shadow: 0 0 3px #000, 1px 1px 2px #000, -1px 1px 2px #000;
}

.map-viewport {
  position: relative; flex: 1; min-width: 0; height: 72vh; min-height: 380px; overflow: hidden; cursor: grab;
  background: #070a10; touch-action: none; user-select: none;
}
@media (max-width: 760px) {
  .map-wrap { flex-direction: column; margin: 0.75rem; }
  .map-side { width: auto; flex: none; height: auto; max-height: 46vh; border-right: none; border-bottom: 1px solid var(--line); }
  .map-side-r { border-left: 0; border-top: 1px solid var(--line); border-bottom: 0; }
  /* Stacked, a collapsed panel has to lose its HEIGHT — zero width means nothing in a column. */
  .map-side.collapsed { max-height: 0; height: 0; width: auto; flex-basis: auto; }
  /* …and a vertical sliver between stacked panels would be unhittable, so the rail lies flat. */
  .map-rail { flex: 0 0 16px; width: auto; height: 16px; align-self: auto; border: 0; border-bottom: 1px solid var(--line); }
  .map-rail-r { border-bottom: 0; border-top: 1px solid var(--line); }
  .map-rail span { transform: rotate(90deg); }
  .map-rail.closed span { transform: rotate(-90deg); }
  .map-viewport { height: 58vh; }
}
.map-viewport.dragging { cursor: grabbing; }
/* On-screen zoom buttons (touch has no wheel). */
.map-zoom { position: absolute; right: 10px; bottom: 10px; z-index: 15; display: flex; flex-direction: column; gap: 5px; }
.map-zoom-b {
  width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
  font-size: 1.4rem; line-height: 1; background: var(--panel); border: 1px solid var(--line-amber);
  color: var(--amber-hi); border-radius: var(--radius-md); cursor: pointer; user-select: none; padding: 0;
}
.map-zoom-b:hover { background: var(--panel-2); }
.map-zoom-b:active { background: var(--amber); color: var(--on-amber); }
/* No will-change/GPU layer here: it would cache one rasterisation and upscale it,
   blurring the markers/labels (and the map) on zoom. Without it the browser
   re-rasterises at the current zoom, keeping icons + text crisp. */
.map-canvas { position: absolute; top: 0; left: 0; width: 100%; transform-origin: 0 0; }
.map-img { display: block; width: 100%; height: auto; user-select: none; -webkit-user-drag: none; }
.map-img.map-img-missing {
  min-height: 640px;
  background:
    repeating-linear-gradient(0deg, rgba(240,130,12,0.05) 0 1px, transparent 1px 64px),
    repeating-linear-gradient(90deg, rgba(240,130,12,0.05) 0 1px, transparent 1px 64px), #0a1018;
}
.map-markers { position: absolute; inset: 0; }

.map-area {
  position: absolute; border: 1px solid; border-radius: 3px;
}
.map-area.area-player { border-color: rgba(255, 154, 46, 0.55); background: rgba(240, 130, 12, 0.10); }
.map-area.area-npc { border-color: rgba(120, 170, 255, 0.45); background: rgba(90, 140, 240, 0.08); }

/* The dot is anchored exactly on the point; the label floats below (out of flow) so
   it never shifts the dot, and ::before widens the click target. */
/* Zero-size anchor placed EXACTLY at the point (its top-left = X%,Y%); the dot and
   label position + counter-scale themselves against that anchor. No box-model math. */
.map-mk {
  position: absolute; width: 0; height: 0; padding: 0; background: none; border: none; cursor: pointer; z-index: 2;
}
.map-mk .mk-dot {
  position: absolute; left: 0; top: 0; transform: translate(-50%, -50%) scale(var(--mz, 1));
  box-sizing: border-box; display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%;
  border: 1.5px solid rgba(0, 0, 0, 0.55); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.map-mk .mk-dot::before { content: ''; position: absolute; inset: -8px; border-radius: 50%; }
.map-mk .mk-dot .ico {
  width: 12px; height: 12px; color: var(--bg);
  /* The sprite is drawn for a 1.05em icon; at 12px a 1.6 stroke is under a pixel and detail turns
     to mush. Heavier here only, so a puppet or a sentry reads as clearly as a truck does. */
  stroke-width: 2.4;
}
.map-mk .mk-label {
  /* The vertical offset is counter-scaled too (calc · --mz), so the gap between the
     dot and the name stays constant on screen at every zoom (was growing with zoom). */
  display: none; position: absolute; left: 0; top: 0;
  transform: translate(-50%, calc(13px * var(--mz, 1))) scale(var(--mz, 1)); transform-origin: top center;
  max-width: 130px; padding: 1px 5px; border-radius: var(--radius);
  background: rgba(8, 10, 14, 0.85); color: var(--text); font-size: 11px; font-weight: 600;
  line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none;
}
.map-markers.labels-on .mk-label, .map-mk:hover .mk-label { display: block; }
.map-mk:hover { z-index: 5; }
.map-mk:hover .mk-dot { outline: 2px solid var(--amber-hi); }

.mk-player .mk-dot { background: var(--c-player); }
.mk-base   .mk-dot { background: var(--c-base); }
.mk-vehicle .mk-dot { background: var(--c-vehicle); }
.mk-chest  .mk-dot { background: var(--c-chest); }
.mk-storage .mk-dot { background: var(--c-storage); }
.mk-trap   .mk-dot { background: var(--c-trap); }
.mk-turret .mk-dot { background: var(--c-turret); }
/* A POI is a place, not a thing — a hollow marker, so it reads as scenery beside the entities rather
   than competing with them. It is never "live" and never fades: it is there whether the game is
   running or not, which is the whole point of moving it out of the live layers. */
.mk-poi .mk-dot { background: var(--c-poi); box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.45); }
/* Popover header icon fallback tints for the new layers */



/* Info card — anchored next to the clicked marker (position set in JS). */
.map-popover {
  /* Wider than it was. Every card carries artwork, a title, a status row and up to a dozen detail
     rows, and at 330px the names of things were clipped mid-word. */
  position: absolute; z-index: 20; width: 380px; max-width: calc(100% - 24px); padding: 0.9rem 1.05rem;
  background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius-lg);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
  /* Never taller than the map, with the body scrolling inside it. A card with an open parts list and
     an open contents list runs well past the bottom of the viewport, and what fell off the end was
     the buttons — the things you opened the card to press. */
  display: flex; flex-direction: column; max-height: calc(100% - 16px);
}
/* Header fixed, everything below it scrolls. The header is also the drag handle, which is the other
   reason it sits outside the scrolling part. */
.map-popover .mp-body {
  /* `min-height: 0` is the whole reason this scrolls. A flex item defaults to `min-height: auto`,
     which refuses to shrink below its content — so the card grew past its own max-height and the
     overflow rule never came into play. The card looked capped and simply was not. */
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain; margin: 0 -0.35rem; padding: 0 0.35rem;
  /* The body owns the spacing between the things in it. Each button used to carry its own
     margin-top — 0.5, 0.6 and 0.65rem — so three buttons in a row sat at three different distances
     and the gaps added up into a gulf. One gap, applied once, is also one number to change. */
  display: flex; flex-direction: column; gap: 0.45rem;
}
/* …except a content box, which belongs TO the button above it and should read as attached to it. */
.map-popover .mp-body > .mp-contents { margin-top: -0.3rem; }
/* The rows are the card's own detail and sit tighter to the header than the actions do. */
.map-popover .mp-body > .mp-rows { margin-bottom: 0.15rem; }
.map-popover .mp-head { flex: none; cursor: grab; user-select: none; }
.map-popover.dragging { cursor: grabbing; }
.map-popover.dragging .mp-head { cursor: grabbing; }
/* Dragged away from its marker, it is no longer anchored to anything — say so quietly. */
.map-popover.moved { border-color: var(--line); }
.map-popover, .map-popover .mp-body { scrollbar-width: thin; scrollbar-color: rgba(240, 130, 12, 0.4) transparent; }

/* Right-click context menu — "Copy teleport" for the clicked spot. */
.map-ctx {
  position: absolute; z-index: 25; padding: 0.25rem;
  background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius-md);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
}
.map-ctx-item {
  display: flex; align-items: center; gap: 0.45rem; width: 100%; white-space: nowrap;
  background: none; border: none; color: var(--text); font-size: 0.82rem; font-weight: 600;
  padding: 0.45rem 0.6rem; border-radius: var(--radius); letter-spacing: 0;
}
.map-ctx-item:hover { background: var(--hover-bg); border: none; color: var(--amber-hi); }
.map-ctx-item .ico { color: var(--amber); }
.map-ctx-item.copied, .map-ctx-item.copied:hover { color: var(--online); background: none; }
.map-ctx-xy { color: var(--muted); font-family: var(--mono); font-size: 0.75rem; font-weight: 400; }
.mp-close {
  position: absolute; top: 5px; right: 7px; width: 22px; height: 22px; padding: 0; line-height: 1;
  background: none; border: none; color: var(--muted); font-size: 20px; cursor: pointer;
}
.mp-close:hover { color: var(--amber-hi); background: none; border: none; }
/* The artwork is the thing being looked at, so it gets room: a 26px chip could not show what a
   vehicle or a crate actually was. Aligned to the TOP, because the text beside it is now two or
   three lines and centring made the plate drift down the card. */
.mp-head { display: flex; align-items: flex-start; gap: 0.7rem; margin: 0 1.4rem 0.7rem 0; }
/* One plate size for every card. A photographed thing and an unphotographed one opened at two
   different header heights, so the card visibly changed shape depending on what you clicked and the
   title and rows moved with it. The artwork fills the square when there is artwork; when there is
   not, the layer's own colour and glyph fill the same square. */
.mp-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 58px; height: 58px; border-radius: var(--radius-md); flex: none;
  border: 1px solid rgba(0, 0, 0, 0.4);
}
.mp-ico .ico { width: 28px; height: 28px; color: var(--bg); }





.mp-headtxt { min-width: 0; display: flex; flex-direction: column; gap: 0.15rem; padding-top: 0.1rem; }
/* Two lines rather than an ellipsis: "Kinglet Duster (Squad Bravo)" is a name worth reading, and
   clipping it to one line to keep a fixed height told the reader nothing. */
.mp-title {
  color: var(--text); font-weight: 700; font-size: 1rem; line-height: 1.25; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.mp-sub { font-size: 0.76rem; color: var(--muted); line-height: 1.35; }
/* Status markers get a row of their own instead of being concatenated onto the end of the subtitle —
   see the comment where they are built. Same shape on every kind of thing, and they wrap. */
.mp-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.12rem; }
.mp-chip {
  display: inline-flex; align-items: center; gap: 0.22rem;
  padding: 0.08rem 0.42rem; border-radius: var(--pill);
  background: var(--inset); border: 1px solid var(--line-2);
  font-size: 0.7rem; font-weight: 600; line-height: 1.5; white-space: nowrap;
}
.mp-on { color: var(--c-player); font-weight: 600; }
.mp-off { color: var(--offline); font-weight: 600; }
.map-popover .mp-row { display: flex; justify-content: space-between; gap: 0.75rem; font-size: 0.82rem; padding: 0.2rem 0; border-top: 1px solid var(--line-2); }
.map-popover .mp-rows .mp-row:first-child { border-top: none; }
.map-popover .mp-row span { color: var(--muted); }
.map-popover .mp-row b { color: var(--text); font-variant-numeric: tabular-nums; text-align: right; }
/* A sentence about the card rather than a value in it — full width, left aligned, quiet. */
/* A row whose value needs the whole width: label on its own line, content under it. Used where a
   value is a group of chips or a bar set that cannot share a line with its label at 380px. */
.map-popover .mp-row.mp-stack { display: block; }
.map-popover .mp-row.mp-stack > span:first-child { display: block; margin-bottom: 0.28rem; }
.map-popover .mp-row.mp-stack .mp-bands { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.map-popover .mp-note {
  border-top: 1px solid var(--line-2); padding: 0.4rem 0 0.2rem;
  color: var(--muted); font-size: 0.78rem; line-height: 1.45;
}
.map-popover .dim { color: var(--muted); }
.mp-id { font-family: var(--mono); font-size: 0.78rem; user-select: all; cursor: text; }
/* Player card → Property: collapsible groups, one row per owned thing, the whole row opens it
   on the live map.

   Everything below fights ONE thing: the global `button:hover` at the top of this file paints
   every button solid amber. These are buttons for keyboard/AT reasons, not visually — so each
   state is stated explicitly and at a specificity that beats it, or the amber block comes back
   and the grey sub-text on it becomes unreadable. */
.pd-stuff-head {
  display: flex; align-items: center; gap: .45rem; width: 100%; margin: .8rem 0 .1rem;
  padding: .35rem .1rem; text-align: left; cursor: pointer; font: inherit; letter-spacing: .04em;
  background: none; border: 0; border-bottom: 1px solid var(--line-2); color: var(--text); border-radius: 0;
}
.pd-stuff-head:hover, .pd-stuff-head:focus-visible { background: none; border-color: var(--line-2); color: var(--amber-hi); }
.pd-stuff-head:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
/* Drawn, not from the sprite — there is no chevron in it, and inventing one would mean an
   icon that renders as an empty box everywhere it is reused. */
.pd-stuff-head .pd-caret {
  margin-left: auto; width: 0; height: 0; transition: transform .15s, border-top-color .14s;
  border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid var(--muted);
}
.pd-stuff-head[aria-expanded="false"] .pd-caret { transform: rotate(-90deg); }
.pd-stuff-head:hover .pd-caret { border-top-color: var(--amber-hi); }
.pd-stuff-n { padding: 0 .4rem; border-radius: 999px; background: var(--inset); color: var(--muted); font-size: .72rem; font-weight: 400; }
.pd-stuff-list { display: flex; flex-direction: column; gap: .3rem; margin: .45rem 0 .3rem; }
.pd-stuff-list[hidden] { display: none; }
.pd-stuff {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: .15rem .6rem;
  width: 100%; padding: .5rem .65rem; text-align: left; cursor: pointer; font: inherit; letter-spacing: 0;
  background: var(--panel-2); border: 1px solid var(--line-2); border-radius: var(--radius); color: var(--text);
}
/* Tinted, not filled: the row keeps a dark ground so both lines stay legible on hover. */
.pd-stuff:hover:not([disabled]), .pd-stuff:focus-visible:not([disabled]) {
  background: rgba(240, 130, 12, 0.10); border-color: var(--amber); color: var(--text);
}
.pd-stuff:focus-visible { outline: none; }
.pd-stuff[disabled] { cursor: default; opacity: .55; background: var(--panel-2); border-color: var(--line-2); color: var(--text); }
.pd-stuff-img {
  grid-column: 1; grid-row: 1 / span 2; display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: var(--radius); background: var(--inset); overflow: hidden;
}
.pd-stuff-img img { max-width: 100%; max-height: 100%; object-fit: contain; }
.pd-stuff-img-fb { color: var(--muted); }
.pd-stuff-main { grid-column: 2; font-weight: 600; }
.pd-stuff-sub { grid-column: 2; font-size: .76rem; color: var(--muted); font-weight: 400; }
.pd-stuff:hover:not([disabled]) .pd-stuff-sub { color: var(--text); opacity: .75; }
.pd-stuff-go { grid-column: 3; grid-row: 1 / span 2; color: var(--muted); }
.pd-stuff:hover:not([disabled]) .pd-stuff-go { color: var(--amber-hi); }

/* Owner name inside a popup row. It sits in running text, so it must NOT look or behave like a
   button — the global button skin is cancelled in every state and only the colour reacts. */
.map-popover .mp-owner,
.map-popover .mp-owner:hover,
.map-popover .mp-owner:active {
  display: inline; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none;
  transform: none; font: inherit; letter-spacing: 0;
}
.map-popover .mp-owner { color: var(--text); cursor: pointer; border-bottom: 1px dotted var(--muted); }
.map-popover .mp-owner:hover { color: var(--amber-hi); border-bottom: 1px solid var(--amber-hi); }
.map-popover .mp-owner:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.mp-card-btn {
  display: flex; align-items: center; justify-content: center; gap: 0.4rem; width: 100%;
  padding: 0.4rem; background: var(--panel-2); border: 1px solid var(--line-amber); color: var(--amber-hi);
  font-family: var(--ui); font-weight: 600; font-size: 0.82rem; border-radius: var(--radius-md); cursor: pointer;
}
.mp-card-btn:hover { background: var(--amber-hi); color: var(--on-amber); }
.mp-card-btn .ico { width: 14px; height: 14px; }
.mp-tp {
  display: flex; align-items: center; justify-content: center; gap: 0.4rem; width: 100%;
  padding: 0.4rem; background: var(--panel-2); border: 1px solid var(--line); color: var(--text);
  font-family: var(--ui); font-weight: 600; font-size: 0.8rem; border-radius: var(--radius-md); cursor: pointer;
}
.mp-tp:hover { border-color: var(--line-amber); color: var(--amber-hi); background: var(--panel-2); }
.mp-tp.copied { border-color: var(--c-player); color: var(--c-player); }
.mp-tp .ico { width: 14px; height: 14px; }
/* Container contents (chest/vehicle) */
.mp-contents-btn, .mp-parts-btn {
  display: flex; align-items: center; justify-content: center; gap: 0.4rem; width: 100%;
  padding: 0.4rem; background: var(--panel-2); border: 1px solid var(--line); color: var(--text);
  font-family: var(--ui); font-weight: 600; font-size: 0.8rem; border-radius: var(--radius-md); cursor: pointer;
}
.mp-contents-btn:hover, .mp-parts-btn:hover { border-color: var(--line-amber); color: var(--amber-hi); background: var(--panel-2); }
.mp-contents-btn.open, .mp-parts-btn.open { color: var(--amber-hi); border-color: var(--line-amber); }
.mp-contents-btn .ico, .mp-parts-btn .ico { width: 14px; height: 14px; }
/* No height cap and no scrollbar of its own. It used to stop at 240px and scroll internally, which
   meant the CARD almost never reached its own limit — so the only scrollbar anyone ever saw was the
   inner one, and the card itself felt stuck. Opening two panels gave you two of them. Now these grow
   to their content and the card's body is the single thing that scrolls. */
.mp-contents { margin-top: 0.5rem; border-top: 1px solid var(--line); }
.mp-contents[hidden] { display: none; }
.mp-c-head {
  /* Same horizontal inset as the rows below it (0.1rem), or the heading sits visibly off from the
     column it heads. Opaque and above them, so nothing shows through while scrolling. */
  display: flex; align-items: center; gap: 0.4rem; padding: 0.45rem 0.1rem; position: sticky; top: 0; z-index: 2;
  background: var(--panel); border-bottom: 1px solid var(--line);
  color: var(--text); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
}
.mp-c-head .ico { width: 13px; height: 13px; color: var(--amber-hi); }
.mp-c-count { margin-left: auto; color: var(--muted); font-weight: 600; text-transform: none; letter-spacing: 0; }
.mp-c-msg { padding: 0.7rem; text-align: center; color: var(--muted); font-size: 0.82rem; }
.mp-c-row { display: flex; align-items: center; gap: 0.5rem; padding: 0.18rem 0.1rem; }
.mp-c-img { width: 22px; height: 22px; flex: none; display: inline-flex; align-items: center; justify-content: center; background: var(--panel-2); border-radius: var(--radius); overflow: hidden; }
.mp-c-img img { width: 100%; height: 100%; object-fit: contain; }
.mp-c-ph { width: 13px; height: 13px; color: var(--muted); opacity: 0.7; }
.mp-c-name { flex: 1; min-width: 0; font-size: 0.8rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mp-c-n { font-size: 0.78rem; color: var(--muted); font-variant-numeric: tabular-nums; }
/* Vehicle-parts group header (kind: body / wheel / armor / …). */
.mp-c-grp { display: flex; align-items: center; gap: 0.35rem; margin: 0.35rem 0 0.1rem; padding: 0.1rem 0.1rem;
  color: var(--amber-hi); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; }
.mp-c-grp:first-of-type { margin-top: 0.1rem; }
.mp-c-gn { color: var(--muted); font-weight: 600; font-variant-numeric: tabular-nums; }

/* Slim, themed scrollbars for the map's scroll areas. */
.map-side, .mp-contents { scrollbar-width: thin; scrollbar-color: rgba(240, 130, 12, 0.4) transparent; }
.map-side::-webkit-scrollbar, .mp-contents::-webkit-scrollbar { width: 9px; }
.map-side::-webkit-scrollbar-track, .mp-contents::-webkit-scrollbar-track { background: transparent; }
.map-side::-webkit-scrollbar-thumb, .mp-contents::-webkit-scrollbar-thumb {
  background: rgba(240, 130, 12, 0.35); border-radius: var(--radius-md); border: 2px solid transparent; background-clip: content-box;
}
.map-side::-webkit-scrollbar-thumb:hover, .mp-contents::-webkit-scrollbar-thumb:hover {
  background: rgba(240, 130, 12, 0.6); background-clip: content-box;
}
/* Header thumbnail (item-DB image) with icon fallback */
/* A photographed thing gets a proper plate rather than a coloured chip — bigger, padded so the art
   is not flush to the edge, and on an inset ground so a cut-out icon reads against the panel. */
/* Same square as every other plate; what changes is the ground — an inset, so a cut-out icon reads
   against it — and the padding, so the art is not flush to the edge. */
.mp-ico.mp-img { position: relative; overflow: hidden; padding: 4px; background: var(--inset); border-color: var(--line-2); }
.mp-ico.mp-img img { width: 100%; height: 100%; object-fit: contain; display: block; }
.mp-ico.mp-img .mp-ico-fb { display: none; position: absolute; inset: 0; align-items: center; justify-content: center; }
.mp-ico.mp-img .mp-ico-fb .ico { width: 22px; height: 22px; color: var(--amber-hi); }
.mp-ico.mp-img.mp-imgfail img { display: none; }
.mp-ico.mp-img.mp-imgfail .mp-ico-fb { display: flex; }
/* List-row thumbnail */
.ml-thumb { width: 22px; height: 22px; flex: none; border-radius: var(--radius); overflow: hidden; background: var(--panel-2); display: inline-flex; align-items: center; justify-content: center; }
.ml-thumb img { width: 100%; height: 100%; object-fit: contain; }

.map-empty {
  position: absolute; top: 1rem; left: 50%; transform: translateX(-50%);
  padding: 0.4rem 0.9rem; background: rgba(8, 10, 14, 0.85); border: 1px solid var(--line);
  border-radius: var(--pill); color: var(--muted); font-size: 0.85rem;
}

.map-locked {
  position: absolute; inset: 0; z-index: 30; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 0.5rem; text-align: center;
  background: rgba(8, 10, 8, 0.9); backdrop-filter: blur(3px); border: 1px solid var(--line); border-radius: var(--radius-md);
}
.map-locked .map-lock-ico { width: 40px; height: 40px; color: var(--amber); margin-bottom: 0.3rem; }
.map-locked h3 { margin: 0; color: var(--text); }
.map-locked p { margin: 0; color: var(--muted); max-width: 340px; }
.map-locked button { margin-top: 0.6rem; }
.map-locked.hidden { display: none; }

/* ── Login (Discord + username) + Admins section ─────────────────────────── */
.login-discord {
  display: flex; align-items: center; justify-content: center; gap: 0.5rem; width: 100%; margin-top: 0.5rem;
  padding: 0.6rem; background: var(--discord); border: 1px solid var(--discord); color: #fff;
  font-family: var(--ui); font-weight: 600; border-radius: var(--radius-md); cursor: pointer; text-decoration: none;
}
.login-discord:hover { background: var(--discord-dn); border-color: var(--discord-dn); }
.login-discord .ico { width: 18px; height: 18px; }
.anav-btn[hidden] { display: none; }

.amodal.amodal-md .amodal-box { max-width: 620px; }
#admins-content { margin-top: 1.25rem; }
.adm-card {
  display: flex; align-items: center; gap: 1rem; padding: 0.85rem 1rem; margin: 0 1.5rem 0.6rem;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.adm-card.adm-default { border-color: var(--line-amber); }
.adm-card.adm-off { opacity: 0.6; }
.adm-main { flex: 1; min-width: 0; }
.adm-avatar { width: 44px; height: 44px; border-radius: 50%; flex: none; object-fit: cover; border: 1px solid var(--line); }
.adm-avatar-fb {
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 1.1rem; color: var(--muted); background: var(--panel-2);
}
.adm-avatar-fb.discord { color: #fff; background: var(--discord); border-color: var(--discord); }
.adm-name { font-weight: 700; color: var(--text); }
.adm-sub { display: flex; align-items: center; font-size: 0.8rem; color: var(--muted); margin: 0.1rem 0 0.4rem; }
.adm-did { font-variant-numeric: tabular-nums; opacity: 0.75; }
.adm-dot { margin: 0 0.4rem; opacity: 0.5; }
.adm-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; }
.adm-methods { display: inline-flex; gap: 0.3rem; margin-right: 0.15rem; }
.adm-method, .adm-badge {
  display: inline-flex; align-items: center; height: 20px; box-sizing: border-box;
  padding: 0 0.5em; border-radius: var(--radius); background: var(--panel-2); border: 1px solid var(--line);
  color: var(--muted); font-size: 0.7rem; line-height: 1;
}
.adm-method { font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
.adm-method.discord { color: var(--discord-hi); background: rgba(88, 101, 242, 0.18); border-color: rgba(88, 101, 242, 0.5); }
.adm-method.off { color: var(--alarm); border-color: rgba(229, 68, 63, 0.4); }
.adm-perms { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.adm-badge.full { font-weight: 700; color: var(--on-amber); background: var(--amber); border-color: var(--amber); letter-spacing: 0.01em; }
.adm-badge.dim { opacity: 0.7; }
.adm-actions { display: flex; gap: 0.4rem; flex: none; }
.adm-actions .secondary { padding: 0.35rem 0.6rem; font-size: 0.82rem; }
.adm-toggle { display: inline-flex; align-items: center; gap: 0.45rem; color: var(--text); font-weight: 600; font-size: 0.85rem; cursor: pointer; flex: none; }
.adm-toggle input { accent-color: var(--amber); }
.adm-form { margin: 1rem 1.5rem 0; padding: 1rem; background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius-lg); }
.adm-form-title { font-weight: 700; color: var(--amber-hi); margin-bottom: 0.6rem; }
.adm-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-bottom: 0.75rem; }
.adm-field { display: flex; flex-direction: column; gap: 0.3rem; }
.adm-field label { font-size: 0.8rem; color: var(--muted); font-weight: 600; }
.adm-field input { padding: 0.5rem 0.6rem; background: var(--panel-2); border: 1px solid var(--line); color: var(--text); border-radius: var(--radius-md); font-family: var(--ui); }
.adm-field input:disabled { opacity: 0.6; }
.adm-perm-actions { display: flex; align-items: center; gap: 0.5rem; margin: 0.5rem 0; }
.adm-perm-heading { font-size: 0.82rem; color: var(--muted); font-weight: 600; margin-right: auto; }
.adm-perm-group { margin-bottom: 0.7rem; }
.adm-perm-gtitle { font-size: 0.68rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; margin: 0 0 0.35rem; }
.adm-perm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(155px, 1fr)); gap: 0.45rem; }
.adm-perm {
  display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.6rem;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-md); cursor: pointer;
  font-size: 0.82rem; font-weight: 600; color: var(--text); user-select: none;
}
.adm-perm:hover { border-color: var(--line-amber); background: var(--hover-bg); }
.adm-perm:has(input:checked) { border-color: var(--amber); background: rgba(240, 130, 12, 0.14); color: var(--amber-hi); }
.adm-perm input { accent-color: var(--amber); width: 15px; height: 15px; flex: none; }
.adm-form-actions { display: flex; gap: 0.6rem; margin-top: 1rem; }
.link-btn { background: none; border: none; color: var(--amber-hi); cursor: pointer; font-family: var(--ui); font-weight: 600; font-size: 0.8rem; padding: 0.15rem 0.25rem; }
.link-btn:hover { background: none; color: #fff; text-decoration: underline; }
@media (max-width: 640px) { .adm-grid { grid-template-columns: 1fr; } .adm-card { flex-direction: column; align-items: stretch; } }

/* Language settings: default-language dropdown + enabled-languages checkbox list */
.settings-field select { font-family: var(--ui); font-size: 0.9rem; padding: 0.5rem 0.6rem; background: var(--panel-2, #14150f); color: var(--fg, #e8e6df); border: 1px solid var(--line, #2a2c22); border-radius: 6px; }
.lang-checks { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; margin-top: 0.4rem; }
.lang-check { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.9rem; cursor: pointer; }
.lang-check input { width: auto; margin: 0; }
.lang-check input:disabled { opacity: 0.6; cursor: default; }
/* Header per-admin language switcher (premium) — same metrics as the nav links
   next to it (padding/font/border), so all header controls share one height. */
.admin-lang-select {
  padding: 0.5rem 0.8rem; font-size: 0.82rem; font-family: var(--ui); font-weight: 600; letter-spacing: 0.05em;
  background: var(--panel-2); color: var(--text); border: 1px solid var(--line); border-radius: var(--radius);
  cursor: pointer; outline: none;
}
.admin-lang-select:hover { background: var(--panel-2); border-color: var(--line-amber); color: var(--amber-hi); }
/* Custom language dropdown with flags (native selects can't show images). */
.lang-dd { position: relative; display: inline-flex; }
/* line-height 1.5 = what the neighbouring <a class="navlink"> controls inherit
   from body; buttons default to ~1.2 and end up a few px shorter without it. */
.lang-dd-btn { display: inline-flex; align-items: center; gap: 0.5rem; line-height: 1.5; }
.lang-flag { width: 1.333em; height: 1em; border-radius: 2px; object-fit: cover; flex: none; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45); } /* 4:3, matches flag-icons SVGs (640×480) */
.lang-dd-chev { color: var(--muted); font-size: 0.7rem; }
.lang-dd-list {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 90; min-width: 175px;
  display: flex; flex-direction: column; gap: 2px; padding: 0.35rem;
  background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
}
.lang-dd-list[hidden] { display: none; }
.lang-dd-item {
  display: flex; align-items: center; gap: 0.55rem; padding: 0.45rem 0.6rem;
  background: none; border: 0; border-radius: var(--radius); color: var(--text);
  font-family: var(--ui); font-size: 0.85rem; cursor: pointer; text-align: left;
}
.lang-dd-item:hover { background: var(--hover-bg); color: var(--amber-hi); }
.lang-dd-item.active { color: var(--amber-hi); }

/* Unified MOTD editor: full-width field — label + language buttons on the top row,
   one large textarea spanning underneath (the default label/control grid is too
   narrow for a text banner). */
.settings-field.motd-field { display: flex; flex-direction: column; align-items: stretch; gap: 0.45rem; }
.motd-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.motd-langs { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.motd-lang-btn {
  padding: 0.22rem 0.65rem; font-size: 0.8rem; cursor: pointer;
  background: var(--inset); color: var(--muted); border: 1px solid var(--line); border-radius: var(--pill);
}
.motd-lang-btn:hover { background: var(--inset); color: var(--text); border-color: var(--line-amber); }
.motd-lang-btn.active { background: var(--panel-2); color: var(--amber-hi); border-color: var(--line-amber); }
.motd-editor { display: block; }
.motd-editor-text { width: 100%; min-height: 5.5rem; resize: vertical; line-height: 1.4; }

/* Custom dropdown chevron with breathing room — the native arrow sits glued to the
   right border and ignores padding, so replace it (appearance:none) with an inline
   SVG placed a comfortable 0.7rem from the edge. Kept at the END of the sheet so no
   later `background:` shorthand can wipe the image. Covers the header language
   switcher, roster/chat toolbars, config pickers and row editors. */
.roster-select, .settings-field select, .er-f select {
  appearance: none; -webkit-appearance: none;
  padding-right: 2rem;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23949aa8' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
}

/* Floating "back to top" button (bottom-right). Overrides the global amber button
   base — this one is a quiet circular outline control that fades in on scroll. */
.back-top {
  position: fixed; right: 1.1rem; bottom: 1.1rem; z-index: 80;
  width: 46px; height: 46px; padding: 0; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--panel-2); border: 1px solid var(--line-amber); color: var(--amber-hi);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
  opacity: 0; visibility: hidden; transform: translateY(10px);
  transition: opacity 0.18s, transform 0.18s, visibility 0.18s, border-color 0.14s, color 0.14s;
}
.back-top.show { opacity: 1; visibility: visible; transform: none; }
.back-top:hover { background: var(--panel-2); border-color: var(--amber); color: var(--amber); }
.back-top svg { width: 20px; height: 20px; }

/* ══ Motion design (mirrors the public site) ════════════════════════════════
   Tab transitions, modal reveal and microinteractions. CSS-only; animations
   restart naturally when an element is re-shown (.hidden toggles display).
   NOTE: nothing here sets a `background:` shorthand on selects — the custom
   dropdown-chevron rule above must stay effective. */

/* Tab switch: the shown panel fades in and rises slightly. */
@keyframes viewIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.tab-panel { animation: viewIn 0.26s ease both; }

/* Intro: the nav buttons settle in once on load. */
@keyframes topIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.anav-btn { animation: topIn 0.3s ease both; }

/* Modal reveal (player detail / app status). */
@keyframes modalIn { from { opacity: 0; transform: translateY(14px) scale(0.985); } to { opacity: 1; transform: none; } }
.amodal-box { animation: modalIn 0.22s ease; }

/* Dropdown reveal (language picker). */
@keyframes ddIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.lang-dd-list { animation: ddIn 0.16s ease; }

/* Microinteractions: smooth state changes on nav/tab/quiet controls (the main
   button system already transitions via the global `button` rule). */
.anav-btn, .pd-tab, .lang-dd-btn, .lang-dd-item, .navlink, .roster-select,
.motd-lang-btn, .foot-link, .gl-map, .gl-player, .pd-maplink {
  transition: color 0.15s, border-color 0.15s, background-color 0.15s, transform 0.15s;
}

/* Summary/stat cards lift gently on hover. */
.pd-stat, .pd-card { transition: border-color 0.18s, transform 0.18s; }
.pd-stat:hover { border-color: var(--line-amber); transform: translateY(-2px); }

/* Themed scrollbars for the whole panel (the map lists already had them). */
html { scrollbar-width: thin; scrollbar-color: rgba(255, 154, 46, 0.35) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255, 154, 46, 0.28); border-radius: 5px; }
::-webkit-scrollbar-thumb:hover { background: rgba(255, 154, 46, 0.45); }

/* Accessibility: honour the OS "reduce motion" preference. */
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; scroll-behavior: auto; } }

/* ── Manager self-update: badge, footer link, card, modal, progress ───────── */
#mgr-update-btn.has-update { border-color: var(--amber); color: var(--amber-hi); }
#mgr-update-btn.has-update::after { content: ''; position: absolute; top: -3px; right: -3px; width: 9px; height: 9px; border-radius: 50%; background: var(--amber); box-shadow: 0 0 0 2px var(--panel), 0 0 6px var(--amber); animation: mu-pulse 1.8s ease-in-out infinite; }
#mgr-update-btn { position: relative; }
@keyframes mu-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

.foot-update { margin-left: .5rem; background: none; border: 0; padding: 0; font: inherit; color: var(--amber); cursor: pointer; text-decoration: none; }
.foot-update:hover { color: var(--amber-hi); text-decoration: underline; }
.foot-update.hidden { display: none; }

.mu-card { border: 1px solid var(--line); background: var(--panel); border-radius: var(--radius); padding: 1.1rem 1.3rem; margin: 0.5rem 1.5rem 2rem; }
.mu-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.mu-head-l { min-width: 0; }
.mu-title { display: flex; align-items: center; gap: .5rem; font-family: var(--display, inherit); font-size: .82rem; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--text); }
.mu-title .ico { width: 16px; height: 16px; color: var(--amber); }
.mu-ver { margin-top: .35rem; color: var(--muted); font-size: .84rem; }
.mu-ver b { color: var(--text); font-weight: 600; }
.mu-head #mu-check-btn { flex: none; }
.mu-status { margin-top: .7rem; font-size: .9rem; min-height: 1.2em; }
.mu-avail { color: var(--amber); font-weight: 600; }
.mu-ok { color: var(--online); }
.mu-muted { color: var(--muted); }
.mu-link { background: none; border: 0; padding: 0 0 0 .3rem; font: inherit; color: var(--amber); cursor: pointer; text-decoration: underline; }
.mu-auto { display: flex; align-items: center; gap: .5rem; margin-top: .9rem; padding-top: .8rem; border-top: 1px dashed var(--line); color: var(--muted); font-size: .86rem; cursor: pointer; }
.mu-auto.hidden { display: none; }

.mu-modal-ver { font-size: 1.15rem; margin: .3rem 0 .8rem; }
.mu-vfrom { color: var(--muted); }
.mu-arrow { color: var(--amber); margin: 0 .3rem; }
.mu-vto { color: var(--amber-hi); font-weight: 700; }
.mu-notes { max-height: 40vh; overflow: auto; background: var(--bg-2); border: 1px solid var(--line); border-radius: 6px; padding: .7rem .9rem; font-size: .86rem; line-height: 1.55; color: var(--data-text); white-space: pre-wrap; }
.mu-links { margin: .8rem 0; font-size: .88rem; }
.mu-links a { color: var(--amber); }
.mu-modal-actions { margin: 1rem 0 .5rem; }
.mu-install-btn { background: var(--amber); color: var(--on-amber); border: 0; padding: .6rem 1.4rem; border-radius: 6px; font-weight: 700; cursor: pointer; letter-spacing: .02em; }
.mu-install-btn:hover { background: var(--amber-hi); }
.mu-prem-note { margin: 1rem 0 .5rem; font-size: .86rem; line-height: 1.5; }
.mu-dismiss { display: flex; align-items: center; gap: .5rem; margin-top: 1rem; padding-top: .8rem; border-top: 1px solid var(--line); color: var(--muted); font-size: .84rem; cursor: pointer; }

.mu-progbar { width: 100%; max-width: 320px; height: 8px; margin: 1rem auto 0; background: var(--bg-2); border: 1px solid var(--line); border-radius: 5px; overflow: hidden; }
.mu-progfill { height: 100%; width: 0; background: var(--amber); transition: width .3s ease; }
.mu-progfill.mu-progerr { background: #d9534f; }

.mu-feed { margin-top: .9rem; padding-top: .8rem; border-top: 1px dashed var(--line); }
.mu-feed-label { display: block; font-size: .8rem; letter-spacing: .03em; text-transform: uppercase; color: var(--muted); margin-bottom: .4rem; }
.mu-feed-row { display: flex; gap: .5rem; flex-wrap: wrap; }
.mu-feed-input { flex: 1 1 340px; min-width: 0; background: var(--bg-2); border: 1px solid var(--line); border-radius: 6px; padding: .5rem .7rem; color: var(--text); font-family: inherit; font-size: .86rem; }
.mu-feed-input:focus { outline: none; border-color: var(--amber); }
.mu-feed-hint { margin-top: .4rem; font-size: 0.75rem; line-height: 1.45; color: var(--muted); }
.mu-feed-msg { margin-top: .4rem; font-size: .84rem; min-height: 1em; }
.mu-feed-msg.mu-ok { color: var(--online); }
.mu-feed-msg.mu-err, .mu-err { color: #d9534f; }

/* ── Plugins section ─────────────────────────────────────────────────────── */
.pl-viewtabs { display: inline-flex; gap: 2px; background: rgba(255,255,255,0.04); border: 1px solid var(--line); border-radius: 999px; padding: 2px; }
.pl-vtab { border: 0; background: transparent; color: var(--muted); font: inherit; font-size: 0.8rem; padding: 0.32rem 0.95rem; border-radius: 999px; cursor: pointer; }
.pl-vtab.active { background: var(--amber); color: var(--on-amber); font-weight: 600; }
.pl-installed { font-size: 0.76rem; color: #8fe0a8; display: inline-flex; align-items: center; gap: 0.3rem; }
.pl-filter { display: inline-flex; gap: 2px; background: rgba(255,255,255,0.04); border: 1px solid var(--line); border-radius: 999px; padding: 2px; }
.pl-fbtn { border: 0; background: transparent; color: var(--muted); font: inherit; font-size: 0.8rem; padding: 0.3rem 0.8rem; border-radius: 999px; cursor: pointer; }
.pl-fbtn.active { background: var(--amber); color: var(--on-amber); font-weight: 600; }
#pl-lockwrap { margin: 1rem 1.5rem 2rem; }
/* drag-and-drop install overlay */
#plugins-screen { position: relative; }
.pl-drop { position: absolute; inset: 0; z-index: 60; display: none; align-items: center; justify-content: center;
  background: rgba(10,11,8,0.82); backdrop-filter: blur(2px); border: 2px dashed var(--amber); border-radius: var(--radius-lg); pointer-events: none; }
#plugins-screen.pl-dragging .pl-drop { display: flex; }
.pl-drop-in { display: flex; align-items: center; gap: 0.6rem; font-family: var(--ui, inherit); font-weight: 600; font-size: 1.1rem; color: var(--amber); }
.pl-drop-in .ico { width: 22px; height: 22px; }
.pl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr)); gap: 1rem; padding: 0.2rem; }

/* Reusable section card for plugin workspaces (the SDK documents `.card`). A plain boxed panel
   with its own padding — plugins stack these inside their workspace. */
.card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-md); padding: 1.1rem 1.2rem; }
.card > h2:first-child, .card > h3:first-child { margin-top: 0; }

/* card: fixed-shape row (thumb + body); body is a column with the footer pinned to the bottom
   so every card lines up regardless of description length. */
.pl-card { display: flex; gap: 0.9rem; padding: 1rem; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius-lg); transition: border-color 0.15s, box-shadow 0.15s; }
.pl-card.on { border-color: var(--line-amber); }
.pl-thumb { flex: 0 0 56px; width: 56px; height: 56px; border-radius: var(--radius); overflow: hidden;
  background: rgba(255,255,255,0.05); display: flex; align-items: center; justify-content: center; }
.pl-thumb img { width: 100%; height: 100%; object-fit: cover; }
.pl-ph { width: 26px; height: 26px; opacity: 0.4; }
.pl-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 0.35rem; }

/* header row: name grows & truncates, badge + toggle stay put on one line */
.pl-head { display: flex; align-items: center; gap: 0.5rem; }
.pl-name { font-weight: 600; color: var(--text); font-size: 0.98rem; min-width: 0; flex: 1 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pl-badge { font-size: 0.64rem; text-transform: uppercase; letter-spacing: 0.04em; padding: 0.14rem 0.44rem;
  border-radius: 999px; border: 1px solid var(--line); color: var(--muted); flex: 0 0 auto; white-space: nowrap; }
.pl-b-ue { border-color: rgba(240,130,12,0.5); color: var(--amber); }
.pl-b-rt { border-color: rgba(120,160,255,0.5); color: #9db8ff; }
.pl-b-mgr { border-color: rgba(120,220,150,0.5); color: #8fe0a8; }
.pl-actions { display: flex; align-items: center; gap: 0.4rem; flex: 0 0 auto; }
.pl-switch { position: relative; display: inline-block; width: 40px; height: 22px; flex: 0 0 auto; }
.pl-switch input { opacity: 0; width: 0; height: 0; }
.pl-slider { position: absolute; inset: 0; background: rgba(255,255,255,0.14); border-radius: 999px; cursor: pointer; transition: 0.18s; }
.pl-slider::before { content: ""; position: absolute; height: 16px; width: 16px; left: 3px; top: 3px; background: #ddd; border-radius: 50%; transition: 0.18s; }
.pl-switch input:checked + .pl-slider { background: var(--amber); }
.pl-switch input:checked + .pl-slider::before { transform: translateX(18px); background: var(--on-amber); }
.pl-switch input:disabled + .pl-slider { opacity: 0.4; cursor: not-allowed; }

/* meta / description / deps — all clamp so cards keep an even height */
.pl-meta { color: var(--muted); font-size: 0.76rem; display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; min-width: 0; }
.pl-meta a { color: var(--amber); text-decoration: none; }
.pl-rt { font-size: 0.66rem; color: #e6b96a; border: 1px dashed rgba(230,185,106,0.5); border-radius: 4px; padding: 0.02rem 0.34rem; }
.pl-desc { margin: 0; color: var(--muted); font-size: 0.82rem; line-height: 1.5; min-width: 0; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.pl-deps { font-size: 0.74rem; color: var(--muted); display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; min-width: 0; }
.pl-deps-l { text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.66rem; }
.pl-dep { padding: 0.06rem 0.4rem; border-radius: 999px; border: 1px solid var(--line); }
.pl-dep.ok { border-color: rgba(120,220,150,0.5); color: #8fe0a8; }
.pl-dep.bad { border-color: rgba(220,120,120,0.5); color: #e39a9a; }
.pl-incompat { display: flex; align-items: center; gap: 0.4rem; font-size: 0.74rem; color: #e39a9a;
  background: rgba(220,120,120,0.1); border: 1px solid rgba(220,120,120,0.4); border-radius: 6px; padding: 0.35rem 0.5rem; overflow-wrap: anywhere; }
.pl-incompat .ico { width: 14px; height: 14px; flex: 0 0 auto; }
.pl-empty { grid-column: 1 / -1; color: var(--muted); font-size: 0.85rem; padding: 1.4rem; text-align: center; }

/* footer: buttons on one aligned row, pinned to the card bottom; uninstall floats right */
.pl-foot { margin-top: auto; padding-top: 0.5rem; display: flex; flex-wrap: wrap; gap: 0.45rem; align-items: center; }
.pl-btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.35rem; font: inherit;
  font-size: 0.76rem; line-height: 1; height: 30px; box-sizing: border-box; white-space: nowrap;
  border: 1px solid var(--line); background: rgba(255,255,255,0.03); color: var(--text);
  padding: 0 0.7rem; border-radius: 7px; cursor: pointer; transition: background .12s, border-color .12s, color .12s; }
.pl-btn:hover { background: rgba(230,185,106,0.14); border-color: rgba(230,185,106,0.5); color: var(--amber); }
.pl-btn .ico { width: 15px; height: 15px; flex: 0 0 auto; }
.pl-btn.primary { background: var(--amber); border-color: var(--amber); color: var(--on-amber); font-weight: 600; }
.pl-btn.primary:hover { filter: brightness(1.08); color: var(--on-amber); }
.pl-btn.pl-update { border-color: rgba(120,160,255,0.55); color: #9db8ff; }
.pl-btn.pl-update:hover { background: rgba(120,160,255,0.14); border-color: rgba(120,160,255,0.7); color: #b9cbff; }
.pl-btn.pl-danger { color: #e39a9a; }
.pl-btn.pl-danger:hover { background: rgba(220,120,120,0.14); border-color: rgba(220,120,120,0.5); color: #e39a9a; }

/* plugin workspace (a plugin's own tabs, opened in-place from its card) */
#plugins-screen.pl-in-workspace > .screen-bar,
#plugins-screen.pl-in-workspace > .settings-note,
#plugins-screen.pl-in-workspace > .prem-lock-wrap { display: none; }
.pl-workspace { display: flex; flex-direction: column; margin: 0.2rem 1.5rem 2rem; }
.pl-workspace.hidden { display: none; }
/* header bar: prominent back button + plugin title, clearly separated from the content */
.pl-ws-bar { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: 0.2rem 0 1rem; margin-bottom: 1.2rem; border-bottom: 1px solid var(--line); }
.pl-ws-back { display: inline-flex; align-items: center; gap: 0.45rem; font: inherit; font-size: 0.85rem;
  border: 1px solid var(--line); background: rgba(255,255,255,0.04); color: var(--text);
  padding: 0.5rem 0.9rem; border-radius: 8px; cursor: pointer; transition: background .12s, border-color .12s, color .12s; }
.pl-ws-back:hover { background: rgba(230,185,106,0.14); border-color: rgba(230,185,106,0.5); color: var(--amber); }
.pl-ws-arrow { font-size: 1.1em; line-height: 1; }
.pl-ws-title { font-family: var(--ui, inherit); font-weight: 600; font-size: 1.1rem; color: var(--text);
  display: inline-flex; align-items: center; gap: 0.5rem; }
.pl-ws-title::before { content: ""; width: 4px; height: 1.1em; border-radius: 2px; background: var(--amber); }
/* sub-nav of the plugin's own tabs */
.pl-ws-nav { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 1.2rem; }
.pl-ws-tab { display: inline-flex; align-items: center; gap: 0.4rem; font: inherit; font-size: 0.85rem;
  border: 1px solid var(--line); background: rgba(255,255,255,0.03); color: var(--muted); padding: 0.45rem 0.9rem;
  border-radius: 8px; cursor: pointer; transition: background .12s, color .12s, border-color .12s; }
.pl-ws-tab:hover { color: var(--text); background: rgba(255,255,255,0.06); }
.pl-ws-tab.active { color: var(--on-amber); background: var(--amber); border-color: var(--amber); font-weight: 600; }
.pl-ws-tab .fic, .pl-ws-tab .ssa-ic { width: 16px; height: 16px; display: inline-flex; }
/* content surface: a plain container — the plugin supplies its own `.card` sections as the boxes,
   so we never double-frame. */
.pl-ws-body { min-width: 0; overflow: visible; }
.pl-ws-empty { color: var(--muted); padding: 1.5rem; }

/* config editor modal */
.pl-modal-ov { position: fixed; inset: 0; background: rgba(0,0,0,0.6); backdrop-filter: blur(3px);
  display: flex; align-items: center; justify-content: center; z-index: 200; padding: 1.5rem; }
.pl-modal { background: var(--panel, #1b1b1e); border: 1px solid var(--line); border-radius: 12px;
  width: min(720px, 100%); max-height: 88vh; display: flex; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
.pl-modal-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.9rem 1.1rem; border-bottom: 1px solid var(--line); font-size: 0.9rem; }
.pl-modal-head code { color: var(--amber); font-size: 0.82rem; }
.pl-modal-x { border: 0; background: transparent; color: var(--muted); font-size: 1.4rem; line-height: 1; cursor: pointer; padding: 0 0.2rem; }
.pl-modal-x:hover { color: var(--text); }
.pl-modal-ta { margin: 0; border: 0; border-radius: 0; resize: none; flex: 1; min-height: 320px;
  background: #101012; color: #d7d7db; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.8rem;
  line-height: 1.5; padding: 0.9rem 1.1rem; outline: none; white-space: pre; overflow: auto; }
.pl-modal-err { color: #e39a9a; font-size: 0.76rem; min-height: 1rem; padding: 0 1.1rem; }
.pl-modal-err:not(:empty) { padding: 0.5rem 1.1rem; }
.pl-modal-foot { display: flex; justify-content: flex-end; gap: 0.6rem; padding: 0.8rem 1.1rem; border-top: 1px solid var(--line); }

/* trader-map editor (reuses .pl-modal shell) */
.pl-modal.trader-map { width: min(760px, 100%); }
.trmap-intro { margin: 0.8rem 1.1rem 0.4rem; }
.trmap-body { flex: 1; overflow: auto; padding: 0.4rem 1.1rem 0.8rem; }
.trmap-rows { display: flex; flex-direction: column; gap: 0.5rem; }
.trmap-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.55rem 0.7rem; border: 1px solid var(--line); border-radius: 8px; background: rgba(255,255,255,0.02); }
.trmap-meta { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; flex: 1; }
.trmap-idline { display: flex; align-items: baseline; gap: 0.7rem; flex-wrap: wrap; }
.trmap-id { color: var(--amber); font-weight: 600; font-size: 0.85rem; }
.trmap-guess { color: var(--muted); font-size: 0.74rem; }
.trmap-funds { color: var(--text); font-size: 0.74rem; font-family: var(--mono); }
.trmap-cur { color: var(--muted); }
/* Sample items wrap over a few lines (an identification aid) instead of truncating to one. */
.trmap-sells { color: var(--muted); font-size: 0.72rem; line-height: 1.4; max-height: 3.6em; overflow-y: auto; }
.trmap-nosell { color: var(--alarm); font-style: italic; }
.trmap-gactions { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.trmap-applytypes { padding: 0.32rem 0.6rem; font-size: 0.76rem; border-radius: var(--radius); cursor: pointer; }
.trmap-inputs { display: flex; gap: 0.4rem; flex-shrink: 0; }
/* Match the admin settings-field controls (those styles are scoped to .settings-field, so the modal
   inputs need their own copy or they render as unstyled browser defaults). */
.trmap-type, .trmap-outpost { padding: 0.5rem 0.6rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); color: var(--text); font-family: var(--mono); font-size: 0.9rem; }
.trmap-type { min-width: 150px; font-family: var(--ui); }
.trmap-outpost { min-width: 110px; font-family: var(--ui); }
.trmap-type:focus, .trmap-outpost:focus { outline: none; border-color: var(--amber); box-shadow: var(--focus-ring); }
/* Outpost groups: one card per auto-grouped outpost with a bulk "set all to" sector picker. */
.trmap-group { border: 1px solid var(--line); border-radius: 10px; margin-bottom: 0.8rem; overflow: hidden; }
/* Outpost gold block — same rows as the trader groups, with its own explanatory subtitle. */
.trmap-gold .trmap-ghead { align-items: flex-start; }
.trmap-ghint { flex-basis: 100%; font-size: 0.76rem; color: var(--muted); font-weight: 400; }
.trmap-goldrow .trmap-inputs { justify-content: flex-end; }
.trmap-ghead { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; flex-wrap: wrap;
  padding: 0.55rem 0.75rem; background: rgba(255, 255, 255, 0.03); border-bottom: 1px solid var(--line); }
.trmap-gtitle { color: var(--amber); font-weight: 700; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.04em; }
.trmap-gcount { color: var(--muted); font-weight: 400; text-transform: none; }
.trmap-gset { display: flex; align-items: center; gap: 0.4rem; font-size: 0.78rem; color: var(--muted); }
.trmap-gsector { padding: 0.35rem 0.5rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--inset); color: var(--text); font-family: var(--ui); font-size: 0.85rem; }
.trmap-group .trmap-rows { display: flex; flex-direction: column; gap: 0.4rem; padding: 0.6rem 0.75rem; }
.trmap-foot { display: flex; align-items: center; justify-content: flex-end; gap: 0.6rem;
  padding: 0.8rem 1.1rem; border-top: 1px solid var(--line); }
.trmap-msg { margin-right: auto; font-size: 0.8rem; }
.trmap-msg.success { color: var(--online); }
.trmap-msg.error { color: var(--alarm); }
@media (max-width: 640px) {
  .trmap-row { flex-direction: column; align-items: stretch; }
  .trmap-inputs { justify-content: stretch; }
  .trmap-type { flex: 1; }
}

/* readme viewer (rendered HTML) */
.pl-modal.readme { width: min(820px, 100%); }
.pl-readme-body { flex: 1; overflow: auto; padding: 1.1rem 1.3rem; line-height: 1.6; font-size: 0.88rem; color: var(--text); }
.pl-readme-body h1 { font-size: 1.3rem; margin: 0.2rem 0 0.8rem; }
.pl-readme-body h2 { font-size: 1.08rem; margin: 1.4rem 0 0.5rem; padding-bottom: 0.3rem; border-bottom: 1px solid var(--line); }
.pl-readme-body h3 { font-size: 0.96rem; margin: 1.1rem 0 0.4rem; color: var(--amber); }
.pl-readme-body p { margin: 0.5rem 0; }
.pl-readme-body ul, .pl-readme-body ol { margin: 0.5rem 0; padding-left: 1.4rem; }
.pl-readme-body li { margin: 0.25rem 0; }
.pl-readme-body a { color: var(--amber); }
.pl-readme-body code { background: rgba(255,255,255,0.08); padding: 0.08rem 0.34rem; border-radius: 4px; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.84em; }
.pl-readme-body pre { background: #101012; border: 1px solid var(--line); border-radius: 8px; padding: 0.8rem 1rem; overflow: auto; margin: 0.6rem 0; }
.pl-readme-body pre code { background: none; padding: 0; }
.pl-readme-body table { border-collapse: collapse; width: 100%; margin: 0.6rem 0; font-size: 0.84rem; }
.pl-readme-body th, .pl-readme-body td { border: 1px solid var(--line); padding: 0.4rem 0.6rem; text-align: left; vertical-align: top; }
.pl-readme-body th { background: rgba(255,255,255,0.04); }
.pl-readme-body hr { border: 0; border-top: 1px solid var(--line); margin: 1.1rem 0; }
.pl-readme-body blockquote { margin: 0.6rem 0; padding: 0.4rem 0.9rem; border-left: 3px solid var(--amber); color: var(--muted); }

/* ── Overview admin console (SSA Bridge) ──────────────────────────────────── */
.cmd-console h2 { display: flex; align-items: center; gap: 0.5rem; }
/* the console is short, so keep the premium lock card near the top and compact */
.cmd-console .prem-lock-wrap { min-height: 0; }
.cmd-console .prem-lock-overlay { align-items: flex-start; padding: 0.5rem; }
.cmd-console .prem-lock-card { top: 0.4rem; max-width: 420px; padding: 1rem 1.2rem; }
.cmd-console .prem-lock-card .map-lock-ico { width: 26px; height: 26px; margin-bottom: 0.15rem; }
.cmd-console .prem-lock-card h3 { font-size: 0.92rem; margin-bottom: 0.35rem; }
.cmd-console .prem-lock-card p { font-size: 0.8rem; margin-bottom: 0.8rem; }
.cmd-state { margin-left: auto; font-size: 0.72rem; font-weight: 400; color: var(--muted);
  text-transform: none; letter-spacing: 0; border: 1px solid var(--line); border-radius: var(--pill); padding: 0.1rem 0.6rem; }
.cmd-state:empty { display: none; }
.cmd-state.ok { color: #8fe0a8; border-color: rgba(120,220,150,0.45); }
.cmd-state.warn { color: #e6b96a; border-color: rgba(230,185,106,0.45); }
.cmd-state.off { color: #e39a9a; border-color: rgba(220,120,120,0.45); }
.cmd-inner { display: flex; flex-direction: column; gap: 0.9rem; }

/* toolbar (chat-style) */
.cmd-bar { margin-bottom: 0; flex-wrap: wrap; }
.cmd-mode.seg { display: inline-flex; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.cmd-mode-btn { border: 0; background: var(--inset); color: var(--muted); font-family: var(--ui); font-size: 0.85rem;
  padding: 0.5rem 0.95rem; cursor: pointer; }
.cmd-mode-btn + .cmd-mode-btn { border-left: 1px solid var(--line); }
.cmd-mode-btn.active { background: var(--amber); color: var(--on-amber); font-weight: 600; }
.cmd-inline { display: inline-flex; align-items: center; gap: 0.45rem; }
.cmd-exec-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
.cmd-check { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--muted); font-size: 0.85rem; cursor: pointer; }
.cmd-check input { accent-color: var(--amber); }

.cmd-pane { display: flex; flex-direction: column; gap: 0.7rem; }
.cmd-row { display: flex; gap: 0.6rem; align-items: stretch; flex-wrap: wrap; }
.cmd-search { position: relative; flex: 1; min-width: 12rem; }
.cmd-search input { font-family: var(--mono); }
.cmd-send { display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem; white-space: nowrap;
  background: var(--amber); border: 1px solid var(--amber); color: var(--on-amber); font-weight: 600;
  padding: 0.6rem 1.15rem; border-radius: var(--radius); cursor: pointer; }
.cmd-send:hover { filter: brightness(1.08); }
.cmd-send:disabled { opacity: 0.5; cursor: not-allowed; filter: none; }
.cmd-send .ico { width: 15px; height: 15px; }
/* Mobile: never let Send run off the box — the search takes the row, Send wraps to a full-width line below. */
@media (max-width: 640px) {
  .cmd-search { flex: 1 0 100%; min-width: 0; }
  .cmd-send { width: 100%; }
  .cmd-build-foot .cmd-preview { flex: 1 0 100%; }
}
/* The lock / bridge-offline overlay covers the WHOLE console box (header included), not just the inner
   content — so the section is the positioning context and keeps a fixed height while gated, so the
   overlay never spills over the live server log below it. */
.cmd-console { position: relative; }
.cmd-console.locked { min-height: 220px; }
.cmd-console.locked > h2 { opacity: 0; }               /* the overlay's card carries the title instead */
.cmd-console.locked .prem-lock-wrap .cmd-inner { min-height: 160px; }

/* autocomplete dropdown */
.cmd-suggest { position: absolute; z-index: 20; top: calc(100% + 6px); left: 0; right: 0;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  box-shadow: 0 14px 34px rgba(0,0,0,0.45); max-height: 320px; overflow-y: auto; }
.cmd-sg-item { display: flex; align-items: baseline; gap: 0.5rem; padding: 0.5rem 0.75rem; cursor: pointer; }
.cmd-sg-item:hover, .cmd-sg-item.active { background: var(--hover-bg); color: var(--amber-hi); }
.cmd-sg-name { color: var(--amber); font-family: var(--mono); font-size: 0.82rem; }
.cmd-sg-args { color: var(--muted); font-size: 0.74rem; font-family: var(--mono); }
.cmd-sg-desc { color: var(--muted); font-size: 0.74rem; margin-left: auto; text-align: right; max-width: 45%; }
.cmd-sg-arg { gap: 0.55rem; }
.cmd-sg-img { width: 30px; height: 30px; flex: none; object-fit: contain; background: rgba(255,255,255,0.05); border-radius: 4px; }
.cmd-sg-arg .cmd-sg-name { font-family: var(--ui); color: var(--text); }
.cmd-sg-arg .cmd-sg-args { margin-left: auto; }

/* interactive builder */
/* searchable command picker — type-to-filter dropdown, alphabetical */
.cmd-cmdpick { position: relative; flex: 1; display: flex; align-items: center; }
.cmd-cmdpick-ico { position: absolute; left: 0.6rem; width: 15px; height: 15px; color: var(--muted); pointer-events: none; }
.cmd-cmdpick-q { width: 100%; padding: 0.5rem 0.75rem 0.5rem 2rem; background: var(--panel-2); border: 1px solid var(--line);
  color: var(--text); font-family: var(--ui); font-size: 0.85rem; border-radius: var(--radius-md); }
.cmd-cmdpick-q:hover, .cmd-cmdpick-q:focus { border-color: var(--line-amber); outline: none; }
.cmd-cmdpick-list { position: absolute; z-index: 20; top: calc(100% + 6px); left: 0; right: 0;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  box-shadow: 0 14px 34px rgba(0,0,0,0.45); max-height: 340px; overflow-y: auto; }
.cmd-cmdpick-item { display: flex; align-items: baseline; gap: 0.6rem; padding: 0.45rem 0.75rem; cursor: pointer; }
.cmd-cmdpick-item:hover, .cmd-cmdpick-item.active { background: var(--hover-bg); }
.cmd-cmdpick-n { color: var(--amber); font-family: var(--mono); font-size: 0.82rem; }
.cmd-cmdpick-cat { color: var(--muted); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; margin-left: auto; }
.cmd-cmdpick-empty { padding: 0.6rem 0.75rem; color: var(--muted); font-size: 0.8rem; }
.cmd-params { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0.7rem; }
.cmd-params:empty { display: none; }
.cmd-field { display: flex; flex-direction: column; gap: 0.3rem; }
.cmd-field label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.cmd-field .req { color: var(--amber); }
.cmd-field input, .cmd-field select { padding: 0.5rem 0.6rem; font-family: var(--ui); font-size: 0.86rem;
  background: var(--inset); color: var(--text); border: 1px solid var(--line); border-radius: var(--radius); outline: none; }
.cmd-field input:focus, .cmd-field select:focus { border-color: var(--amber); }
.cmd-coords { display: flex; gap: 0.4rem; }
.cmd-coords input { width: 100%; }
.cmd-pick { display: flex; gap: 0.4rem; align-items: center; }
.cmd-pick input { flex: 1; }
.cmd-pick-btn { display: inline-flex; align-items: center; gap: 0.3rem; white-space: nowrap; border: 1px solid var(--line);
  background: var(--inset); color: var(--text); font-size: 0.8rem; padding: 0.45rem 0.7rem; border-radius: var(--radius); cursor: pointer; }
.cmd-pick-btn:hover { border-color: var(--line-amber); }
.cmd-pick-thumb { width: 26px; height: 26px; object-fit: contain; border-radius: 4px; background: rgba(255,255,255,0.05); flex: none; }
.cmd-build-foot { align-items: center; }
.cmd-preview { flex: 1; font-family: var(--mono); font-size: 0.82rem; color: var(--amber); background: var(--inset);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 0.55rem 0.75rem; overflow-x: auto; white-space: nowrap; }
.cmd-preview:empty::before { content: '—'; color: var(--muted); }

/* output log */
.cmd-output { margin: 0; background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 0.7rem 0.9rem; min-height: 2.4rem; max-height: 220px; overflow-y: auto;
  font-family: var(--mono); font-size: 0.78rem; line-height: 1.55; color: #cfcfd4;
  white-space: pre-wrap; word-break: break-word; }
.cmd-output:empty { display: none; }
.cmd-out-ok { color: #8fe0a8; }
.cmd-out-err { color: #e39a9a; }
.cmd-out-cmd { color: #e6b96a; }
.cmd-out-res { color: #cfd6e4; padding-left: 1.1rem; }
.cmd-out-note { color: var(--muted); padding-left: 1.1rem; font-style: italic; }
.cmd-out-t { color: var(--muted); }

/* item picker modal (interactive spawn) — reuses .pl-modal-ov shell */
.cmd-ip { width: min(680px, 100%); }
.cmd-ip-search { margin: 0.9rem 1.1rem 0; }
.cmd-ip-filters { display: flex; gap: 0.5rem; margin: 0.6rem 1.1rem 0; }
.cmd-ip-filters select { flex: 1; min-width: 0; padding: 0.4rem 0.5rem; border: 1px solid var(--line);
  border-radius: 7px; background: var(--inset); color: var(--text); font-size: 0.8rem; }
.cmd-ip-filters select:focus { border-color: var(--amber); outline: none; }
.cmd-ip-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 0.6rem;
  padding: 0.9rem 1.1rem; overflow-y: auto; }
/* Pager sits below the scrolling grid and stays put — the grid scrolls, the controls don't. */
.cmd-ip-pager { flex: none; margin: 0; padding: 0.6rem 1.1rem 0.9rem; border-top: 1px solid var(--line); }
.cmd-ip-card { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; text-align: center;
  border: 1px solid var(--line); border-radius: var(--radius); padding: 0.6rem; cursor: pointer; background: var(--inset); }
.cmd-ip-card:hover { border-color: var(--amber); }
.cmd-ip-card img { width: 56px; height: 56px; object-fit: contain; }
.cmd-ip-ph { width: 56px; height: 56px; display: flex; align-items: center; justify-content: center; color: var(--muted); }
.cmd-ip-ph .ico { width: 30px; height: 30px; }
.cmd-ip-card .n { font-size: 0.74rem; color: var(--text); line-height: 1.25; }
.cmd-ip-card .id { font-size: 0.66rem; color: var(--muted); font-family: var(--mono); word-break: break-all; }

/* count / amount picker (reuses .pl-modal shell) */
.cmd-count { width: min(360px, 100%); }
.cmd-count-body { padding: 1rem 1.1rem 1.2rem; display: flex; flex-direction: column; gap: 0.9rem; }
.cmd-count-chips { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; }
.cmd-count-chip { border: 1px solid var(--line); background: var(--inset); color: var(--text); font-size: 0.9rem;
  padding: 0.5rem 0; border-radius: 8px; cursor: pointer; font-family: var(--mono); }
.cmd-count-chip:hover { border-color: var(--amber); background: var(--amber); color: var(--on-amber); }
.cmd-count-row { display: flex; align-items: center; gap: 0.6rem; }
.cmd-count-row span { font-size: 0.78rem; color: var(--muted); flex: none; }
.cmd-count-in { flex: 1; padding: 0.45rem 0.6rem; border: 1px solid var(--line); border-radius: 7px;
  background: var(--inset); color: var(--text); font-size: 0.95rem; }
.cmd-count-go { width: 100%; justify-content: center; }

/* ── SSA Bridge admin actions (map info-card + player tools) ───────────────── */
.mp-acts { margin-top: 0.7rem; padding-top: 0.7rem; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 0.4rem; }
.mp-acts-h { flex-basis: 100%; font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); margin-bottom: 0.1rem; }
.mp-acts-lock .ico { width: 12px; height: 12px; color: var(--amber); vertical-align: middle; }
.mp-act { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.76rem; cursor: pointer;
  border: 1px solid var(--line); background: var(--inset); color: var(--text); padding: 0.35rem 0.6rem; border-radius: 7px; }
.mp-act:hover:not(:disabled) { border-color: var(--amber); background: var(--amber); color: var(--on-amber); }
.mp-act .ico { width: 14px; height: 14px; }
.mp-act.danger { color: #e39a9a; }
.mp-act.danger:hover:not(:disabled) { border-color: #d98a8a; background: #d98a8a; color: #1a0808; }
.mp-act:disabled { opacity: 0.45; cursor: not-allowed; }
.mp-actions-btn { width: 100%; justify-content: center; margin-top: 0.7rem; padding: 0.5rem; font-weight: 600; }
.mp-actions-btn .mp-acts-lock { width: 12px; height: 12px; }
.map-ctx-item:disabled, .map-ctx-item.locked { opacity: 0.45; cursor: not-allowed; }

/* online-player picker (teleport here, …) */
.pp-list { display: flex; flex-direction: column; gap: 0.3rem; padding: 0.8rem 1rem; max-height: 60vh; overflow-y: auto; }
.pp-row { display: flex; align-items: center; gap: 0.5rem; text-align: left; font-size: 0.86rem; cursor: pointer;
  border: 1px solid var(--line); background: var(--inset); color: var(--text); padding: 0.55rem 0.7rem; border-radius: 8px; }
.pp-row:hover { border-color: var(--amber); background: var(--amber); color: var(--on-amber); }
.pp-row .ico { width: 15px; height: 15px; }

/* hierarchical action menu (modal or cursor popover) */
.am-modal { width: min(460px, 100%); }
.am-anchor-ov { position: fixed; inset: 0; z-index: 210; }
.am-pop { position: fixed; width: 264px; max-width: calc(100vw - 16px); background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 16px 44px rgba(0,0,0,0.5); overflow: hidden; }
.am-head { display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.55rem; border-bottom: 1px solid var(--line); }
.am-title { flex: 1; font-size: 0.8rem; color: var(--muted); font-family: var(--mono); }
.am-back, .am-x { border: 0; background: transparent; color: var(--muted); cursor: pointer; font-size: 1.15rem; line-height: 1; padding: 0 0.35rem; }
.am-back:hover, .am-x:hover { color: var(--text); }
.am-search { margin: 0.5rem 0.5rem 0; }
.am-search input { font-size: 0.84rem; }
.am-list { max-height: min(62vh, 430px); overflow-y: auto; padding: 0.35rem; }
.am-modal .am-list { max-height: min(70vh, 560px); }
.am-item { display: flex; align-items: center; gap: 0.55rem; width: 100%; text-align: left; cursor: pointer;
  border: 0; background: transparent; color: var(--text); padding: 0.55rem 0.6rem; border-radius: 7px; font-size: 0.86rem; }
.am-item:hover:not(:disabled) { background: var(--amber); color: var(--on-amber); }
.am-item:disabled { opacity: 0.45; cursor: not-allowed; }
.am-item .ico { width: 15px; height: 15px; flex: none; }
.am-l { flex: 1; }
.am-arrow { color: var(--muted); font-size: 1rem; }
.am-item:hover:not(:disabled) .am-arrow { color: #4a3a12; }
.am-item.danger { color: #e39a9a; }
.am-item.danger:hover:not(:disabled) { background: #d98a8a; color: #1a0808; }
#map-admin:disabled { opacity: 0.5; cursor: not-allowed; }
.map-updated-badge { position: absolute; right: 8px; top: 8px; z-index: 4; font-size: 0.72rem; font-family: var(--mono);
  color: var(--muted); background: rgba(10, 11, 8, 0.72); border: 1px solid var(--line); border-radius: 6px; padding: 0.15rem 0.5rem; pointer-events: none; }

/* unified action-feedback toast */
.bt-title { font-weight: 600; }
.bt-detail { margin-top: 0.25rem; font-family: var(--mono); font-size: 0.78rem; color: var(--muted); white-space: pre-wrap; max-height: 40vh; overflow-y: auto; }
.bridge-toast.ok .bt-title { color: #8fe0a8; }
.bridge-toast.err .bt-title { color: #f0a8a8; }
.bridge-toast.warn .bt-title { color: #f0cf8a; }

/* transient action toast */
.bridge-toast { position: fixed; left: 50%; bottom: 24px; transform: translate(-50%, 20px); z-index: 300;
  max-width: min(560px, 92vw); padding: 0.7rem 1.1rem; border-radius: 10px; font-size: 0.85rem;
  background: #1b1b1f; color: var(--text); border: 1px solid var(--line); box-shadow: 0 14px 40px rgba(0,0,0,0.5);
  opacity: 0; pointer-events: none; transition: opacity .18s, transform .18s; white-space: pre-wrap; }
.bridge-toast.show { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; cursor: pointer; }
.bridge-toast.ok { border-color: rgba(120,220,150,0.5); }
.bridge-toast.err { border-color: rgba(220,120,120,0.55); color: #f0c0c0; }
.bridge-toast.warn { border-color: rgba(230,200,120,0.6); color: #efdcae; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 1 (additive polish)
   Appended last so it LAYERS over the base without touching existing rules.
   Every selector targets ONLY classes/elements that already exist. Nothing is
   renamed, removed or restructured — this is pure visual polish + a11y.
   ════════════════════════════════════════════════════════════════════════ */
:root {
  /* Layered elevation for floating surfaces (subtle — preserves the flat tactical look). */
  --elev-1: 0 1px 2px rgba(0,0,0,0.4);
  --elev-2: 0 8px 24px -8px rgba(0,0,0,0.6), 0 2px 6px rgba(0,0,0,0.45);
  --elev-3: 0 22px 55px -14px rgba(0,0,0,0.72), 0 6px 16px rgba(0,0,0,0.5);
  /* Focus halo — was a 3px amber ring layered on top of a focused field's amber border, which read
     as a DOUBLE border on every text box. Neutralised: focused inputs now show only their single
     amber border, and buttons/links/tabs keep the 2px :focus-visible outline, so nothing loses its
     focus indicator. */
  --ring:   none;
}

/* Modern, consistent focus RING everywhere — keeps the high-contrast outline for
   a11y and adds a soft amber halo so focus reads as deliberate, not a default. */
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
a:focus-visible,
[tabindex]:focus-visible,
.gs-tab:focus-visible,
.anav-btn:focus-visible,
.tab:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  box-shadow: var(--ring);
}

/* Button busy/loading affordance — OPT-IN via aria-busy / .loading, so it can
   never affect an existing button unless code sets that state. */
button[aria-busy="true"], button.loading {
  color: transparent !important;
  pointer-events: none;
  position: relative;
}
button[aria-busy="true"]::after, button.loading::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 1em; height: 1em; border-radius: 50%;
  border: 2px solid var(--on-amber); border-top-color: transparent;
  animation: ssa-spin 0.6s linear infinite;
}
button.secondary[aria-busy="true"]::after, button.secondary.loading::after,
button.danger[aria-busy="true"]::after, button.danger.loading::after { border-color: currentColor; border-top-color: transparent; }
@keyframes ssa-spin { to { transform: rotate(360deg); } }

/* Real elevation on floating surfaces — the Grafana/Proxmox "layer that lifts". */
.amodal-box { box-shadow: var(--elev-3); }
.cdd-menu, .roster-country-menu, .lang-dd-list { box-shadow: var(--elev-2); }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 2 — navigation interactivity
   Additive only; targets confirmed classes (.admin-nav / .anav-btn). The tab
   nav previously changed only text colour on hover — add a subtle hover fill +
   a faint underline hint + smooth transitions so tabs read as interactive
   (GitHub/Grafana style). Active tab stays amber, unchanged.
   ════════════════════════════════════════════════════════════════════════ */
.admin-nav { scrollbar-width: thin; scrollbar-color: var(--line-amber) transparent; }
.anav-btn {
  transition: color .14s ease, background .14s ease, border-color .14s ease;
  border-top-left-radius: var(--radius); border-top-right-radius: var(--radius);
}
.anav-btn:hover:not(.active) {
  color: var(--text);
  background: var(--hover-bg);
  border-bottom-color: var(--line-amber);
}

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 3 — data tables (roster & co.)
   Additive; targets the existing .roster-table. Adds a readable header, a clear
   row-hover surface (rows are clickable → open a player), and a whisper-subtle
   zebra for scannability. No columns/markup/behaviour touched.
   ════════════════════════════════════════════════════════════════════════ */
.roster-table thead th {
  background: var(--bg-2);
  color: var(--muted);
  border-bottom: 1px solid var(--line-amber);
  position: sticky; top: 0; z-index: 1;
}
.roster-table tbody tr { transition: background .12s ease; }
.roster-table tbody tr:nth-child(even) { background: rgba(255,255,255,0.014); }
.roster-table tbody tr:hover { background: var(--hover-bg); }
/* keep the action button readable on a hovered row */
.roster-table tbody tr:hover .rr-admin-btn { border-color: var(--line-amber); }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 4 — form-field clarity
   The settings inputs sat on --inset (≈ page black) with a 9% border, so they
   barely read as editable. Lift them with a whisper of fill + a clearer border
   + a hover cue + the modern focus halo. Same selectors as the base rule, so it
   overrides by source order; behaviour/markup untouched. Helps every settings
   screen AND the setup wizard read as "these are fields you can edit".
   ════════════════════════════════════════════════════════════════════════ */
:root { --input-bg: rgba(255,255,255,0.025); --input-line: rgba(228,226,214,0.18); }
.settings-field input[type="text"],
.settings-field input[type="number"],
.settings-field input[type="password"],
.settings-field textarea,
.settings-field select,
.setup-box .settings-field input {
  background: var(--input-bg);
  border-color: var(--input-line);
  transition: border-color .14s ease, box-shadow .14s ease, background .14s ease;
}
.settings-field input:hover:not(:focus),
.settings-field textarea:hover:not(:focus),
.settings-field select:hover:not(:focus),
.setup-box .settings-field input:hover:not(:focus) { border-color: var(--line-amber); }
.settings-field input:focus,
.settings-field textarea:focus,
.settings-field select:focus,
.setup-box .settings-field input:focus,
.login-box input:focus { border-color: var(--amber); box-shadow: var(--ring); outline: none; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 5 — modals & empty/loading/error states
   Additive; targets confirmed classes only. (a) Modal: dim+blur the backdrop so
   the dialog is the clear focus, and give the header (.amodal-box h2) a hairline
   divider so title/body read as distinct zones. (b) Empty/loading/error
   placeholders (.pl-empty / .rc-empty / .cmd-cmdpick-empty) get one consistent,
   calm, centred treatment instead of three slightly different looks. No markup,
   copy, sizing or behaviour touched.
   ════════════════════════════════════════════════════════════════════════ */
.amodal { background: rgba(0,0,0,0.62); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); }
.amodal-box h2 {
  padding-bottom: 0.7rem;
  margin-bottom: 0.9rem;
  border-bottom: 1px solid var(--line);
  letter-spacing: 0.01em;
}
/* consistent, quiet placeholder for the three empty/loading/error slots */
.pl-empty, .rc-empty, .cmd-cmdpick-empty {
  color: var(--muted);
  text-align: center;
  letter-spacing: 0.01em;
}
.pl-empty { min-height: 4.5rem; display: flex; align-items: center; justify-content: center; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 6 — interactive card affordance
   The plugin cards already DECLARE a border/shadow transition but never had a
   :hover target, so they sat inert. Give them (and the clickable squad-member
   chips in the player detail) a real hover: amber-hinted border + a whisper of
   lift — the "cards that respond" feel. Purely visual; .pl-card.on already owns
   the amber border for active plugins, so this just adds the shadow on hover.
   ════════════════════════════════════════════════════════════════════════ */
.pl-card:hover { border-color: var(--line-amber); box-shadow: var(--elev-1); }
.pd-member { transition: border-color .14s ease, background .14s ease; }
.pd-member:hover { border-color: var(--line-amber); background: var(--hover-bg); }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 7 — cohesive themed scrollbars
   The one un-themed element left across the whole panel was the default chunky
   grey OS scrollbar (visible on the page, modals, textareas, the roster wrapper,
   dropdowns). Give every scroll area a thin, dark, amber-on-hover scrollbar so
   scrolling reads as part of the same tactical surface — the Grafana/Proxmox
   "custom scrollbars everywhere" cohesion. Firefox via scrollbar-*, Chrome (the
   primary target) via ::-webkit-scrollbar. Purely cosmetic; .admin-nav keeps its
   own amber track (more specific rule wins).
   ════════════════════════════════════════════════════════════════════════ */
* { scrollbar-width: thin; scrollbar-color: rgba(228,226,214,0.22) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: rgba(228,226,214,0.16);
  border-radius: 6px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
  background: rgba(240,130,12,0.5);
  background-clip: padding-box;
}
::-webkit-scrollbar-corner { background: transparent; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 8 — mobile header menu (hamburger)
   On phones/small tablets the header's manager buttons (Update/Restart/Shutdown
   + language + Field Console + Logout) wrapped onto multiple rows and ate a lot
   of vertical space. Collapse them behind a hamburger that opens a proper drawer
   under the header. Desktop is 100% unchanged (the hamburger is display:none and
   .header-actions keeps its normal inline flex row). Wired by a tiny isolated
   inline script (toggles header.menu-open); markup added: #mobile-menu-btn + an
   id on the existing .header-actions. No app.js/API/behaviour touched — the same
   buttons, just relocated into a drawer on small screens.
   ════════════════════════════════════════════════════════════════════════ */
.mobile-menu-btn { display: none; }              /* desktop: hidden */
.mobile-menu-btn .ico { width: 22px; height: 22px; }
.mobile-menu-btn .mm-close { display: none; }    /* show X only when open */

@media (max-width: 760px) {
  /* brand + hamburger share one row; the actions drop into an absolute drawer */
  header { flex-wrap: nowrap; position: relative; }
  .mobile-menu-btn {
    display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto; padding: 0.5rem; width: 42px; height: 42px;
  }
  header.menu-open .mobile-menu-btn { border-color: var(--line-amber); color: var(--amber-hi); }
  header.menu-open .mobile-menu-btn .mm-open { display: none; }
  header.menu-open .mobile-menu-btn .mm-close { display: inline-flex; }

  .header-actions {
    display: none;                 /* closed by default */
    position: absolute;
    top: calc(100% + 6px);
    right: 0.75rem; left: 0.75rem;
    flex-direction: column;
    align-items: stretch;
    gap: 0.45rem;
    padding: 0.7rem;
    background: var(--panel);
    border: 1px solid var(--line-amber);
    border-radius: var(--radius);
    box-shadow: var(--elev-3);
    z-index: 200;
  }
  header.menu-open .header-actions { display: flex; animation: modalIn 0.16s ease; }
  .header-actions > button,
  .header-actions > a.secondary {
    width: 100%; justify-content: flex-start;
    padding-top: 0.6rem; padding-bottom: 0.6rem;
  }
  /* the thin vertical divider becomes a horizontal rule when stacked */
  .header-actions .header-sep { width: 100%; height: 1px; align-self: auto; margin: 0.2rem 0; }
  /* language picker + its trigger fill the drawer width */
  #admin-lang-wrap, #admin-lang-wrap .cdd, #admin-lang-wrap .cdd-btn,
  #admin-lang-wrap .lang-dd, #admin-lang-wrap .lang-dd-btn { width: 100%; }
}

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 9 — styled toast notifications
   Replaces the ugly native browser alert() (the "Windows/Google" bar at the top
   of the page) with an on-brand toast that stacks top-right. Wired by overriding
   window.alert in the isolated inline script — every existing alert() call site
   is unchanged, they just render as a proper toast now. Error-ish messages get a
   red rail, notices get the amber rail. z-index above modals/dropdowns.
   ════════════════════════════════════════════════════════════════════════ */
.ssa-toast-wrap {
  position: fixed; top: 1rem; right: 1rem; z-index: 4000;
  display: flex; flex-direction: column; gap: 0.5rem;
  width: min(380px, calc(100vw - 2rem));
  pointer-events: none;
}
.ssa-toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: 0.6rem;
  background: var(--panel);
  border: 1px solid var(--line-amber);
  border-left: 3px solid var(--amber);
  border-radius: var(--radius);
  padding: 0.7rem 0.8rem;
  box-shadow: var(--elev-3);
  color: var(--text); font-size: 0.88rem; line-height: 1.45;
  animation: ssaToastIn 0.2s ease;
}
.ssa-toast.err { border-left-color: var(--alarm); }
.ssa-toast.err .ssa-toast-ic { color: var(--alarm); }
.ssa-toast.ok { border-left-color: var(--online); }
.ssa-toast.ok .ssa-toast-ic { color: var(--online); }
.ssa-toast-ic { flex: 0 0 auto; width: 18px; height: 18px; color: var(--amber); margin-top: 1px; }
.ssa-toast-msg { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; white-space: pre-wrap; }
.ssa-toast-x {
  flex: 0 0 auto; background: none; border: 0; color: var(--muted);
  cursor: pointer; font-size: 1.1rem; line-height: 1; padding: 0 0.1rem;
}
.ssa-toast-x:hover { color: var(--text); }
.ssa-toast.hide { animation: ssaToastOut 0.2s ease forwards; }
@keyframes ssaToastIn { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: none; } }
@keyframes ssaToastOut { to { opacity: 0; transform: translateX(18px); } }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 10 — Live Map marker hover
   The marker hover was a single hard 2px outline sitting right on the dot edge,
   with no transition — barely legible against the busy map and abrupt. Give it a
   floating ring (outline-offset) + a soft amber glow halo + a smooth transition,
   and a slightly larger hover hit-area, so a hovered marker clearly "lifts" off
   the map. Colours/positions/behaviour otherwise unchanged.
   ════════════════════════════════════════════════════════════════════════ */
.map-mk .mk-dot { transition: box-shadow .12s ease; }
.map-mk .mk-dot::before { inset: -10px; }            /* a touch more forgiving hover target */
.map-mk:hover, .map-mk.mk-selected { z-index: 6; }
/* Tight amber ring HUGGING the icon (no gap) + soft glow. !important so it wins over
   the per-status green/red rings (which have higher specificity). Applies on hover AND
   while selected — the clicked marker stays lit so you can see what the open popover is for. */
.map-mk:hover .mk-dot,
.map-mk.mk-selected .mk-dot {
  outline: none !important;
  box-shadow: 0 0 0 2px var(--amber-hi), 0 0 9px 2px rgba(240,130,12,0.6), 0 1px 4px rgba(0,0,0,0.7) !important;
}
/* keep the selected marker's name label visible while its popover is open */
.map-mk.mk-selected .mk-label { display: block; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 11 — Live Map LEFT SIDEBAR polish
   The layer cards + entity-list rows hovered to a flat, washed grey
   (rgba(255,255,255,.06)) with no accent — off-identity and bland against the
   amber tactical panel. Give every interactive sidebar row a crisp on-brand
   hover: an inset amber left-rail + the name going amber-hi + a smooth
   transition, so hovering reads clearly and matches the rest of the panel.
   Colours/counts/behaviour otherwise unchanged.
   ════════════════════════════════════════════════════════════════════════ */
.map-layer, .ml-row, .map-filters-head, .ml-sec-h {
  transition: background .12s ease, box-shadow .12s ease, color .12s ease;
}
.map-layer:hover, .ml-row:hover {
  background: var(--hover-bg);
  box-shadow: inset 2px 0 0 var(--amber);
}
.ml-row:hover .mlr-name { color: var(--amber-hi); }
.map-layer:hover .ml-name { color: var(--amber-hi); }
.map-filters-head:hover { color: var(--amber-hi); }
.map-filters-head:hover .ico { color: var(--amber-hi); }
/* offline rows still get the amber name on hover (override the muted default) */
.ml-row.off:hover .mlr-name { color: var(--amber); }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 12 — dropdown hover + collapsible map lists
   (a) .cdd-item hover was amber TEXT on a barely-lighter grey → low contrast,
   reads as muddy/"dark". Make it a clear solid-amber highlight bar with dark
   text, like a proper dropdown selection. Applies to every custom dropdown
   (map layer sub-filters, language, executor, country…) for consistency.
   (b) Live Map entity lists (Players/Vehicles/Chests/Storage/Bases) become
   collapsible by clicking their section header — same affordance as the
   "Layers & filters" panel. Chevron + hidden rows when collapsed; wired by the
   isolated inline script (state survives the map's periodic re-render).
   ════════════════════════════════════════════════════════════════════════ */
/* Both halves from the element's own pair, so a more specific rule cannot take one and leave the
   other — which is exactly what `.cdd-item.cdd-check.on` did, giving near-black text on the dark
   panel for any row you had ticked. The defaults live on `.cdd-item` itself. */
.cdd-item:hover, .rc-item:hover {
  background: var(--hov-bg, var(--amber));
  color: var(--hov-fg, var(--on-amber));
}

/* collapsible entity-list sections */
.ml-sec-h { cursor: pointer; user-select: none; }
.ml-sec-h::after {
  content: '▾'; display: inline-block; flex: none;
  margin-left: 0.5rem; color: var(--muted); font-size: 0.7rem;
  transition: transform .15s ease, color .12s ease;
}
.ml-sec-h:hover { color: var(--amber-hi); }
.ml-sec-h:hover::after, .ml-sec-h:hover .ico { color: var(--amber-hi); }
.ml-sec-h .ml-sec-n { margin-left: auto; }        /* keep count pinned right, chevron after it */
.ml-sec.collapsed .ml-sec-h::after { transform: rotate(-90deg); }
.ml-sec.collapsed .ml-row,
.ml-sec.collapsed .ml-empty { display: none; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 13 — custom-dropdown TRIGGER hover fix
   The global `button:hover` (amber bg + dark on-amber text) was hijacking the
   .cdd-btn dropdown triggers (e.g. the map layer "All (85)"). Context rules like
   `.map-layers .cdd-btn{background:panel-2}` kept the bg DARK but the dark text
   from button:hover still applied → dark-on-dark, unreadable on mere hover.
   Give .cdd triggers a proper dropdown hover instead: keep the dark surface,
   light label, amber border + amber caret as the cue. Fixes every custom
   dropdown trigger (map filters, language, executor, country…).
   ════════════════════════════════════════════════════════════════════════ */
.cdd-btn:hover {
  background: var(--panel-2);
  border-color: var(--line-amber);
  color: var(--text);
}
.cdd-btn:hover .dd-caret,
.cdd-btn:hover .cdd-lbl { color: var(--amber-hi); }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 14 — styled confirm / prompt dialogs
   Replaces the native browser confirm()/prompt() (the "ugly window" on header
   Restart/Shutdown and the player admin actions) with an on-brand modal. Driven
   by window.ssaConfirm()/ssaPrompt() (Promise-based) in the inline script; the
   app.js call sites are converted to `await` these. Reuses the existing button
   system (primary / .danger / .secondary) so it matches everything else.
   ════════════════════════════════════════════════════════════════════════ */
.ssa-dialog-back {
  position: fixed; inset: 0; z-index: 5000;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.62); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  padding: 1rem;
}
.ssa-dialog {
  width: min(440px, 100%);
  background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius);
  box-shadow: var(--elev-3); padding: 1.3rem 1.4rem; animation: modalIn 0.18s ease;
}
.ssa-dialog h3 {
  margin: 0 0 0.8rem; font-size: 1.05rem; color: var(--text); letter-spacing: 0.01em;
  padding-bottom: 0.7rem; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: 0.5rem;
}
.ssa-dialog h3 .ico { width: 18px; height: 18px; color: var(--amber); flex: none; }
.ssa-dialog.danger h3 .ico { color: var(--alarm); }
.ssa-dialog-msg { margin: 0 0 1rem; color: var(--text); font-size: 0.9rem; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
.ssa-dialog-input {
  width: 100%; margin: 0 0 1rem; padding: 0.55rem 0.7rem; box-sizing: border-box;
  background: var(--input-bg, rgba(255,255,255,0.025)); border: 1px solid var(--input-line, rgba(228,226,214,0.18));
  border-radius: var(--radius); color: var(--text); font-family: var(--ui); font-size: 0.9rem;
}
.ssa-dialog-input:focus { border-color: var(--amber); box-shadow: var(--ring); outline: none; }
.ssa-dialog-btns { display: flex; justify-content: flex-end; gap: 0.5rem; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 15 — Plugins: search + restart clarity
   Adds a compact search box to the Plugins toolbar (reuses .player-search) and
   turns the section note into a scannable "what to restart" callout so it's
   obvious that UE4SS mods apply on a SERVER restart while manager plugins apply
   instantly (and why a just-disabled mod can still read as online).
   ════════════════════════════════════════════════════════════════════════ */
/* In the toolbar the search must not devour the row like it does on the roster
   bar — cap it, and let it collapse to full width only when the bar wraps. */
.pl-search { flex: 0 1 220px; min-width: 150px; padding: 0.4rem 0.6rem; transition: border-color 0.12s ease, box-shadow 0.12s ease; }
.pl-search input { font-size: 0.9rem; }
/* Focus the whole search pill — not the inner <input>. The global input:focus-visible rule
   otherwise paints a second amber frame INSIDE the pill (2px outline + a 3px --ring box-shadow
   halo) on top of the pill's own amber border — a double border. Kill BOTH on the inner input so
   only the single pill border shows. Applies to every .player-search box. */
.player-search:focus-within { border-color: var(--amber); box-shadow: var(--focus-ring); }
.player-search input:focus-visible { outline: none; box-shadow: none; }
/* The restart-guidance note reads as a proper callout box, not a bare rule. */
.pl-note {
  background: rgba(240, 130, 12, 0.06);
  border: 1px solid var(--line);
  border-left: 3px solid var(--amber);
  border-radius: var(--radius);
  padding: 0.75rem 0.95rem;
  line-height: 1.6;
}
.pl-note b { color: var(--text); font-weight: 600; }
@media (max-width: 720px) { .pl-search { flex-basis: 100%; } }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 17 — click-to-sort table headers
   Column headers on the Players / Chat / Game-log tables sort by that column
   (replacing the old "Sort" dropdowns). A faint ↕ marks a sortable column; the
   active one shows an amber ▲/▼.
   ════════════════════════════════════════════════════════════════════════ */
.data-table th.th-sort { cursor: pointer; user-select: none; transition: color 0.12s ease, background 0.12s ease; }
.th-sort .th-sort-in { display: inline-flex; align-items: center; gap: 0.3rem; }
.th-sort .th-arrow { font-size: 0.74em; line-height: 1; color: var(--amber); }
.data-table th.th-sort:not(.active) .th-arrow::after { content: '↕'; color: var(--muted); opacity: 0.4; }
.data-table th.th-sort:hover { color: var(--text); background: rgba(240, 130, 12, 0.06); }
.data-table th.th-sort:not(.active):hover .th-arrow::after { opacity: 0.75; }
.data-table th.th-sort.active { color: var(--amber-hi, var(--amber)); }
.data-table th.th-sort:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 18 — stable table columns
   Fixed layout so column widths stay put across sorting and paging (auto layout
   re-measured each page's content, which made columns jump/resize). Long cells
   truncate with an ellipsis; message/log cells wrap. Widths are scoped per table
   (roster / chat / game-log all share the .roster-table class).
   ════════════════════════════════════════════════════════════════════════ */
.data-table.roster-table { table-layout: fixed; width: 100%; }
.roster-table td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.roster-table th { overflow: hidden; text-overflow: ellipsis; }
.roster-table td.chat-msg, .roster-table td.gamelog-line { white-space: normal; overflow: visible; text-overflow: clip; }

/* Players — widths by CLASS, never :nth-child. The old rules were positional, so each column kept
   the width of whatever used to sit in its slot once columns were inserted; that is how a
   three-digit ID ended up claiming 18% of the table.
   The layout is `fixed`, so content does not size anything: EVERY column must state a width, or it
   collapses to nothing while the rest divide the space up between them. The numbers below were
   measured in a browser against this server's real roster (longest name, longest squad, 17-digit
   Steam IDs, six-figure balances) rather than estimated. Squad / Discord / Name are the ones
   allowed to ellipsis — everything else fits its widest real value.
   Percentages, NOT rem: the panel scales its root font size (1rem measures ~19.8px here, not 16),
   so rem widths came out about a quarter too wide and pushed the table into a scrollbar. A
   percentage resolves against the table itself and is immune to that. */
/* Tighter cells than the other tables: at 0.7rem a side, padding alone ate 392px of a 1400px table
   across 14 columns — a quarter of the width before a single value was drawn. */
.roster-table:not(.chat-table) th,
.roster-table:not(.chat-table) td { padding-left: 0.45rem; padding-right: 0.45rem; }

.roster-table:not(.chat-table) th.rr-status-h { width: 1.9%; }   /* just a dot */
.roster-table:not(.chat-table) th.c-name     { width: 12.6%; }
.roster-table:not(.chat-table) th.c-uid      { width: 3.2%; }    /* in-game id: a few digits */
.roster-table:not(.chat-table) th.c-steam    { width: 12.1%; }   /* 17 digits, monospace — must not clip */
.roster-table:not(.chat-table) th.c-playtime { width: 7.1%; }
.roster-table:not(.chat-table) th.c-fame     { width: 4.9%; }
.roster-table:not(.chat-table) th.c-money    { width: 5.8%; }    /* "163,314" */
.roster-table:not(.chat-table) th.c-gold     { width: 4.9%; }
.roster-table:not(.chat-table) th.c-squad    { width: 7.9%; }    /* long names ellipsis */
.roster-table:not(.chat-table) th.c-ip       { width: 10.9%; }
.roster-table:not(.chat-table) th.c-discord  { width: 7.1%; }    /* long names ellipsis */
/* The admin-actions button inherits the generic button padding (~21px a side), which made it 44px
   wide inside a 41px cell — it overflowed and read as shoved off to the right. It is an icon
   button; size it like one. */
.roster-table td.rr-act .rr-admin-btn { padding: 0.25rem 0.35rem; min-width: 0; }
/* That cell holds a button, not text — the shared ellipsis rule was appending dots next to it. */
.roster-table td.rr-act { overflow: visible; text-overflow: clip; }
.roster-table:not(.chat-table) th.c-login    { width: 8.1%; }
.roster-table:not(.chat-table) th.c-logout   { width: 9.2%; }
.roster-table:not(.chat-table) th.rr-act-h   { width: 2.9%; }

/* Chat — 4 columns (Time, Channel, Player, Message) */
.chat-table:not(.gamelog-table) th:nth-child(1) { width: 11.5rem; }
.chat-table:not(.gamelog-table) th:nth-child(2) { width: 6.5rem; }
.chat-table:not(.gamelog-table) th:nth-child(3) { width: 18%; }

/* Game log — 3 columns (Time, Category, Log entry). Fit Time to its content (the full timestamp is
   only ~150px wide) and trim the oversized right padding, so Category sits right next to it instead
   of after a dead gap; the log entry then gets all the remaining room. */
.gamelog-table th:nth-child(1), .gamelog-table td:nth-child(1) { width: 174px; white-space: nowrap; padding-right: 0.35rem; }
.gamelog-table th:nth-child(2) { width: 8rem; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 19 — Log Viewer: raw-line reveal
   A per-entry "raw" toggle reveals the ORIGINAL log line (nothing is lost when
   the enriched view folds coordinates/blobs away), plus styling for the session
   picker. Used in the Log Viewer and the player card's Logs tab.
   ════════════════════════════════════════════════════════════════════════ */
.gl-raw-btn {
  font: inherit; font-size: 0.72em; cursor: pointer; margin-left: 0.4rem; vertical-align: middle;
  padding: 0.05rem 0.4rem; border-radius: 4px;
  border: 1px solid var(--line); background: transparent; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.gl-raw-btn:hover, .gl-raw-btn.on { border-color: var(--line-amber); color: var(--amber); }
.gl-raw {
  margin-top: 0.35rem; padding: 0.45rem 0.6rem;
  background: #101012; border: 1px solid var(--line); border-radius: 6px;
  font-family: var(--mono); font-size: 0.8rem; color: var(--muted);
  white-space: pre-wrap; word-break: break-word;
}
/* Player card → Logs tab: a compact filter bar + a scrollable list, matching the Log Viewer. */
.pd-logs-body { min-height: 4rem; }
.pdl-bar { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.6rem; flex-wrap: wrap; }
.pdl-bar .pdl-search-wrap { flex: 1 1 180px; min-width: 140px; }
.pdl-bar .pdl-count { margin-left: auto; color: var(--muted); font-size: 0.82rem; white-space: nowrap; }
.pdl-list { max-height: 46vh; overflow: auto; }
.pdl-list .table-wrap { margin-top: 0; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 20 — Log Viewer: entity icons + item
   preview. Small SVG markers show WHAT each clickable thing is (user / location
   / weapon / box / vehicle) without recolouring the line, and item/vehicle codes
   open a game-DB preview (icon + name). Also fixes the player-card Logs table so
   its time column isn't collapsed.
   ════════════════════════════════════════════════════════════════════════ */
.gl-ic { width: 0.95em; height: 0.95em; vertical-align: -0.14em; margin-right: 0.22rem; color: var(--muted); opacity: 0.7; flex: none; }
.gl-player:hover .gl-ic, .gl-loc:hover .gl-ic, .gl-item:hover .gl-ic { color: var(--amber); opacity: 1; }
/* clickable item/vehicle/weapon code → dashed underline (distinct from players' solid) */
.gl-item { color: inherit; cursor: pointer; text-decoration: underline dashed; text-decoration-color: rgba(240, 130, 12, 0.4); text-underline-offset: 2px; }
.gl-item:hover { color: var(--amber-hi); text-decoration-color: var(--amber); }
/* item preview popover */
.gl-preview { position: absolute; z-index: 5000; max-width: 250px; padding: 0.6rem 0.7rem;
  background: var(--panel); border: 1px solid var(--line-amber); border-radius: 8px; box-shadow: var(--elev-3); }
.gl-preview-body { display: flex; gap: 0.65rem; align-items: center; }
.gl-preview img { width: 56px; height: 56px; object-fit: contain; background: var(--inset); border-radius: 6px; flex: none; }
.gl-preview-t { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.gl-preview-t b { color: var(--text); font-size: 0.9rem; }
.gl-preview-t .mono { font-size: 0.72rem; word-break: break-all; }
/* player-card Logs tab: content-sized columns so the time isn't crushed to 10px */
.pdl-list .data-table { table-layout: auto; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 21 — Log Viewer + player modal polish
   Fixes: icons were too dark to see; Steam IDs rendered a size smaller than the
   rest of the line; the item preview was cramped/dark; the player modal was
   tight for reading the Logs tab.
   ════════════════════════════════════════════════════════════════════════ */
/* Entity icons — bright enough to read, on-brand amber; shape signals the type. */
.gl-ic { width: 0.92em; height: 0.92em; vertical-align: -0.13em; margin-right: 0.26rem; color: var(--amber); opacity: 0.95; flex: none; }
/* a touch more breathing room between log rows so the icons + text don't feel cramped */
.gamelog-line { padding-top: 0.5rem; padding-bottom: 0.5rem; line-height: 1.5; }
.gl-player:hover .gl-ic, .gl-loc:hover .gl-ic, .gl-item:hover .gl-ic { color: var(--amber-hi); opacity: 1; }
/* One consistent type size across the whole log line — no shrunk Steam IDs. */
.gamelog-line, .gamelog-line a, .gamelog-line .gl-sid { font-size: 0.85rem; }
.gl-sid { font-size: inherit; }
/* Item preview — larger image on a light plate, clearer text. */
.gl-preview { max-width: 290px; padding: 0.85rem 0.9rem; }
.gl-preview-body { gap: 0.85rem; }
.gl-preview img { width: 84px; height: 84px; padding: 5px; background: rgba(255, 255, 255, 0.07); border: 1px solid var(--line); }
.gl-preview-t b { font-size: 1rem; line-height: 1.2; }
.gl-preview-t .mono { font-size: 0.74rem; }
/* Player modal — bigger, and the Logs list gets real room to read. */
.amodal.wide .amodal-box { max-width: 1120px; max-height: 92vh; }
.pdl-list { max-height: 58vh; }
.pdl-bar { margin-bottom: 0.7rem; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 22 — Log Viewer: per-type category chip
   Every row's category chip carries its log-type icon (skull=kills, coins=economy,
   shield=raid, car=vehicles, lock=bunkers…) + a friendly label / sub-category, so
   each category reads at a glance in the viewer and the player card's Logs tab.
   ════════════════════════════════════════════════════════════════════════ */
.gl-cat { display: inline-flex; align-items: center; gap: 0.3rem; max-width: 100%; vertical-align: middle; }
.gl-type-ic { width: 0.95em; height: 0.95em; flex: none; color: currentColor; opacity: 0.9; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 23 — Log Viewer: category never overflows
   The category column is a fixed 9rem; long tags (VehicleInactiveTimerReached,
   OutOfInteractionRange…) must ellipsize inside the pill, not spill into the entry
   column. Full text stays available via the cell's title tooltip.
   ════════════════════════════════════════════════════════════════════════ */
.gamelog-table td:nth-child(2), .pdl-list .data-table td:nth-child(1) { overflow: hidden; }
.gl-cat-t { display: inline-block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
/* Unify category casing — every tag reads with a capital first letter (login → Login,
   out of range → Out of range) while leaving command / UE-category camel case intact. */
.gl-cat-t::first-letter { text-transform: uppercase; }
/* The player-card Logs table is auto-layout, so cap its category so a long label
   ("Vehicle Destruction") can't crowd out the message. */
.pdl-list .data-table td:nth-child(1) { max-width: 12rem; }

/* ════════════════════════════════════════════════════════════════════════
   DESIGN-SYSTEM REFINEMENTS · increment 24 — Live Map: location ping
   Clicking a log location opens the map centred on the spot AND drops a pulsing
   pin there, so the admin sees exactly where the event happened. Lives inside the
   transformed #map-canvas and counter-scales (--mz) so it stays a constant size.
   ════════════════════════════════════════════════════════════════════════ */
.map-ping { position: absolute; z-index: 30; pointer-events: none; }
.map-ping.hidden { display: none; }
/* Bold target marker centred ON the spot: a bright ring + an X, with a dark halo so it reads on any
   terrain, plus an expanding pulse to catch the eye. All three counter-scale (--mz) to a constant
   on-screen size regardless of map zoom. */
.map-ping-ring, .map-ping-x, .map-ping-pulse {
  position: absolute; left: 0; top: 0; transform-origin: center;
}
.map-ping-ring {
  width: 38px; height: 38px; border-radius: 50%;
  border: 3px solid var(--amber-hi); background: rgba(240, 130, 12, 0.14);
  transform: translate(-50%, -50%) scale(var(--mz, 1));
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.75), 0 0 10px 2px rgba(240, 130, 12, 0.55), inset 0 0 6px rgba(0, 0, 0, 0.5);
}
.map-ping-x { width: 20px; height: 20px; transform: translate(-50%, -50%) scale(var(--mz, 1)); }
.map-ping-x::before, .map-ping-x::after {
  content: ''; position: absolute; left: 50%; top: 0; width: 3px; height: 100%;
  background: var(--amber-hi); border-radius: 2px;
  box-shadow: 0 0 0 1.5px rgba(0, 0, 0, 0.7);
}
.map-ping-x::before { transform: translateX(-50%) rotate(45deg); }
.map-ping-x::after { transform: translateX(-50%) rotate(-45deg); }
.map-ping-pulse {
  width: 38px; height: 38px; border-radius: 50%;
  transform: translate(-50%, -50%) scale(var(--mz, 1));
}
.map-ping.ping-anim .map-ping-pulse { animation: mapPingPulse 1.5s ease-out infinite; }
.map-ping.ping-anim .map-ping-ring { animation: mapPingPop 0.4s cubic-bezier(0.2, 0.8, 0.3, 1.5); }
@keyframes mapPingPulse {
  0% { box-shadow: 0 0 0 0 rgba(240, 130, 12, 0.55); }
  70% { box-shadow: 0 0 0 calc(26px * var(--mz, 1)) rgba(240, 130, 12, 0); }
  100% { box-shadow: 0 0 0 0 rgba(240, 130, 12, 0); }
}
@keyframes mapPingPop {
  0% { transform: translate(-50%, -50%) scale(calc(var(--mz, 1) * 2.2)); opacity: 0; }
  100% { transform: translate(-50%, -50%) scale(var(--mz, 1)); opacity: 1; }
}

/* Numbered pages in the native table pager (same shape as the Field Console). */
.roster-pager-nums { display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.roster-pager .pg-n { min-width: 2rem; padding: .3rem .5rem; font-variant-numeric: tabular-nums; }
.roster-pager .pg-n.cur { border-color: var(--accent, var(--amber, #ff6a1a)); color: var(--accent, var(--amber, #ff6a1a)); cursor: default; }
.roster-pager-gap { color: var(--muted, #8b9099); padding: 0 2px; user-select: none; }

/* Table pagination on a phone: drop the outer page numbers, keep Prev/Next, first, last and the
   pages next to the current one. The row also wraps instead of pushing the table sideways. */
.roster-pager { flex-wrap: wrap; }
@media (max-width: 560px) {
  .roster-pager .pg-far { display: none; }
  .roster-pager .pg-n { min-width: 1.8rem; padding: .3rem .4rem; }
}

/* Fullscreen: the whole widget, side panel included. `position: fixed` covers the browser
   window when the Fullscreen API is unavailable; under the real API the element already fills
   the screen and these values are harmless.

   The panel and the viewport are sized `height: 72vh` for the normal in-page layout — which in
   fullscreen left the bottom ~28% of the screen empty black. Both have to be released to 100%
   of the (now full-screen) wrapper, and `height`, not `max-height`, is what actually pins them. */
.map-wrap.map-fs {
  position: fixed; inset: 0; z-index: 4000; width: 100vw; height: 100vh; max-height: none;
  border-radius: 0; margin: 0;
}
.map-wrap:fullscreen { width: 100vw; height: 100vh; max-height: none; border-radius: 0; margin: 0; }
.map-wrap.map-fs .map-side, .map-wrap:fullscreen .map-side,
.map-wrap.map-fs .map-viewport, .map-wrap:fullscreen .map-viewport {
  height: 100%; max-height: none; min-height: 0;
}
.map-fs-b .ico { width: 14px; height: 14px; }

/* ── roster: numeric columns + in-game id ────────────────────────────────────
   Playtime / Fame / Money / Gold read as a column of figures, so they are right-aligned with
   tabular numerals — proportional digits make columns of numbers impossible to compare. */
.roster-table td.num, .roster-table th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.roster-table th.num .th-sort-in { justify-content: flex-end; }
/* The in-game profile id, now its own column instead of a suffix on the Steam ID. */
.roster-table td.rr-uid-col { color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }

/* Country filter items are multi-select, so they carry the same tick box as every other
   checkable menu (.cdd-check) rather than looking like single-choice rows. */
.rc-item.rc-check .cdd-box {
  flex: none; width: 14px; height: 14px; border-radius: 3px;
  border: 1px solid var(--line-amber, var(--line)); background: var(--inset);
}
.rc-item.rc-check.on { background: none; color: var(--amber-hi); }
.rc-item.rc-check.on .cdd-box {
  background: var(--amber); border-color: var(--amber);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230d0e0a' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12.5l4.5 4.5L19 7.5'/></svg>");
  background-size: 12px 12px; background-position: center; background-repeat: no-repeat;
}

/* ── admin audit trail ───────────────────────────────────────────────────────
   Built to match the Players roster, because it is the same kind of object — a long list of rows
   scanned top to bottom — and two tables in one panel that behave differently read as a bug.
   It used to be a bare .data-table: auto layout, so the columns were sized by whatever happened to
   be in them and the table ran to the edges of the page; no zebra, no sticky header, no hover.

   Same rules as .roster-table: layout is FIXED, so every column must state a width or it collapses
   while the others divide the space. Percentages, not rem — the panel scales its root font size,
   and a rem width comes out about a quarter too wide against it. */
.data-table.audit-table { table-layout: fixed; width: 100%; }
.audit-table th, .audit-table td { padding-left: 0.5rem; padding-right: 0.5rem; }
.audit-table td { vertical-align: top; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.audit-table th { overflow: hidden; text-overflow: ellipsis; }

.audit-table th.c-when   { width: 12%; }
.audit-table th.c-who    { width: 13%; }
.audit-table th.c-action { width: 16%; }   /* "players.ban", "control.schedule" */
.audit-table th.c-target { width: 15%; }
.audit-table th.c-status { width: 6%; }
.audit-table th.c-ip     { width: 11%; }
.audit-table th.c-details{ width: 24%; }   /* a preview — the full body is in the expanded row */
.audit-table th.c-exp    { width: 2.4%; }  /* just the chevron */

/* Readable header + scannable rows, the same treatment increment 3 gave the roster. */
.audit-table thead th {
  background: var(--bg-2);
  border-bottom: 1px solid var(--line-amber);
  position: sticky; top: 0; z-index: 1;
}
.audit-table tbody tr.audit-row { transition: background .12s ease; cursor: pointer; }
.audit-table tbody tr.audit-row:nth-of-type(4n+3) { background: rgba(255, 255, 255, 0.014); }
.audit-table tbody tr.audit-row:hover { background: var(--hover-bg); }
.audit-table tbody tr.audit-row:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }
/* A failed action is the one worth spotting, so it is marked rather than just listed. It has to
   win over the zebra and the hover, hence the later position and the explicit hover rule. */
.audit-table tbody tr.audit-row.audit-bad,
.audit-table tbody tr.audit-row.audit-bad:nth-of-type(4n+3) { background: rgba(229, 68, 63, 0.07); }
.audit-table tbody tr.audit-row.audit-bad:hover { background: rgba(229, 68, 63, 0.12); }
.audit-status-bad { color: #ff8b80; }
.audit-table code { font-family: var(--mono); font-size: 0.78rem; color: var(--amber-hi, var(--amber)); }
.audit-details { font-family: var(--mono); font-size: 0.75rem; color: var(--muted); }
.audit-table td.c-exp { position: relative; overflow: visible; }

/* The chevron is drawn, not fetched: the panel's sprite has no chevron symbol, and the alternative
   is adding one to the sheet for a single 6px mark. */
.audit-chev {
  position: absolute; right: 0.35rem; top: 50%;
  width: 0.42em; height: 0.42em; margin-top: -0.3em;
  border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
  transform: rotate(45deg); transform-origin: center;
  transition: transform .15s ease, border-color .15s ease;
}
.audit-row:hover .audit-chev { border-color: var(--amber); }
.audit-row.audit-open .audit-chev { transform: rotate(-135deg); margin-top: -0.12em; }
.audit-table tbody tr.audit-row.audit-open { background: var(--hover-bg); }

/* Shown only when the recorded IP cannot be the admin's own — a permanent hint is a hint nobody
   reads. Amber, not red: nothing is broken, the value just isn't what it looks like. */
.audit-ip-hint {
  border-left: 2px solid var(--amber);
  padding-left: 0.7rem;
  color: var(--text);
}

/* ── the expanded row ──────────────────────────────────────────────────────
   The details column used to be the whole recorded body truncated to 70 characters, with the rest
   only in a `title` tooltip — unreadable for JSON and impossible to copy. */
.audit-detail-row > td {
  white-space: normal;
  background: var(--inset, rgba(0, 0, 0, 0.18));
  border-bottom: 1px solid var(--line-amber);
  padding: 0.85rem 1rem 1rem;
}
.audit-dd-grid {
  display: grid; gap: 0.5rem 1.4rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  margin-bottom: 0.75rem;
}
.audit-dd-f { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.audit-dd-k {
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); font-weight: 600;
}
.audit-dd-v { font-size: 0.84rem; color: var(--text); word-break: break-word; }
.audit-dd-v.mono { font-family: var(--mono); font-size: 0.78rem; }
.audit-dd-body { margin-top: 0.2rem; }
.audit-json {
  margin: 0.3rem 0 0; padding: 0.6rem 0.75rem; max-height: 18rem; overflow: auto;
  background: var(--bg-2); border: 1px solid var(--line); border-radius: calc(var(--radius) / 2);
  font-family: var(--mono); font-size: 0.76rem; line-height: 1.5; color: var(--text);
  white-space: pre-wrap; word-break: break-word;
}

/* Narrow screens drop columns rather than squeezing them — the same order Players uses, least
   useful first. Nothing is lost: every dropped value is in the expanded row. */
@media (max-width: 1100px) {
  .audit-table th.c-ip, .audit-table td.c-ip { display: none; }
  .audit-table th.c-details { width: 35%; }
}
@media (max-width: 820px) {
  .audit-table th.c-target, .audit-table td.c-target { display: none; }
  .audit-table th.c-details, .audit-table td.c-details { display: none; }
  .audit-table th.c-when { width: 24%; }
  .audit-table th.c-who { width: 28%; }
  .audit-table th.c-action { width: 30%; }
  .audit-table th.c-status { width: 12%; }
  .audit-table th.c-exp { width: 6%; }
}

/* ── In-game mods (SSA Bridge modules) ─────────────────────────────────────
   One card per module: kill-switch and live state in the header, what it did in the middle, its
   settings below. The forms are drawn from a description the module ships, so nothing here is
   specific to any one feature. */

.mod-list { display: flex; flex-direction: column; gap: 1rem; }
.mod-note { margin: 0; }

.mod-offline {
  display: flex; gap: 0.9rem; align-items: flex-start;
  padding: 1.1rem 1.25rem;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-md);
}
.mod-offline .ico { width: 20px; height: 20px; flex: none; color: var(--standby); }
.mod-offline b { display: block; margin-bottom: 0.25rem; }
.mod-offline p { margin: 0 0 0.5rem; color: var(--muted); font-size: 0.88rem; line-height: 1.5; }
.mod-offline code { color: var(--faint); font-size: 0.78rem; }

.mod-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-md);
  overflow: hidden;
}
.mod-card.on { border-color: var(--line-amber); }

.mod-head {
  display: flex; align-items: center; gap: 0.9rem;
  padding: 0.85rem 1.1rem;
  background: var(--panel-2); border-bottom: 1px solid var(--line);
}
.mod-title { display: flex; align-items: center; gap: 0.6rem; flex: 1; min-width: 0; flex-wrap: wrap; }
.mod-name { font-weight: 600; }
.mod-head-actions { display: flex; gap: 0.5rem; align-items: center; }

.mod-state {
  padding: 0.1rem 0.45rem; border-radius: 999px;
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em;
  border: 1px solid var(--line); color: var(--muted); background: var(--inset);
}
.mod-s-ready { color: var(--online); border-color: rgba(108, 191, 63, 0.35); }
.mod-s-loading { color: var(--standby); border-color: rgba(224, 169, 46, 0.35); }
.mod-s-dry { color: var(--amber-hi); border-color: var(--line-amber); }

/* what it did */
.mod-status { padding: 0.9rem 1.1rem; border-bottom: 1px solid var(--line-2); }
.mod-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr)); gap: 0.7rem; }
.mod-stat { text-align: center; }
.mod-stat-v { display: block; font-size: 1.35rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.mod-stat-k { display: block; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.mod-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.8rem; }
.mod-chip {
  padding: 0.1rem 0.45rem; border-radius: 999px; font-size: 0.73rem;
  background: var(--inset); border: 1px solid var(--line); color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.mod-chip b { color: var(--text); }

/* settings */
.mod-groups { display: grid; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); gap: 0 1.6rem; padding: 1rem 1.1rem 1.2rem; }
.mod-group { min-width: 0; }
.mod-group-t {
  margin: 0.8rem 0 0.5rem; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--muted); font-weight: 600;
}

.mf { display: block; margin-bottom: 0.85rem; min-width: 0; }
/* A field whose condition isn't met stays visible but inert — hiding it makes a setting look lost. */
.mf-off { opacity: 0.4; pointer-events: none; }
.mf-label { display: block; font-size: 0.86rem; font-weight: 500; }
.mf-hint { margin: 0.15rem 0 0.35rem; color: var(--muted); font-size: 0.78rem; line-height: 1.45; }

.mf-check { display: flex; gap: 0.6rem; align-items: flex-start; cursor: pointer; }
.mf-check input { margin-top: 0.2rem; flex: none; }
.mf-check .mf-body { min-width: 0; }
.mf-check .mf-hint { margin-bottom: 0; }

.mf-opts { display: grid; gap: 0.45rem; margin-top: 0.35rem; }
.mf-opt {
  display: block; width: 100%; text-align: left; cursor: pointer; font: inherit; color: var(--text);
  padding: 0.6rem 0.75rem; background: var(--inset);
  border: 1px solid var(--line); border-radius: var(--radius);
  transition: border-color .12s ease, background .12s ease;
}
.mf-opt:hover { background: var(--hover-bg); }
.mf-opt.on { border-color: var(--amber); background: rgba(240, 130, 12, 0.07); }
.mf-opt-t { display: block; font-weight: 600; font-size: 0.88rem; }
.mf-opt-h { display: block; margin-top: 0.15rem; color: var(--muted); font-size: 0.78rem; line-height: 1.45; }

.mf-rangerow { display: flex; align-items: center; gap: 0.8rem; }
.mf-rangerow input { flex: 1; min-width: 0; }
.mf-out { font-variant-numeric: tabular-nums; font-weight: 700; min-width: 3rem; text-align: right; }

.mf-int input, .mf-text input {
  width: 100%; padding: 0.4rem 0.6rem;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text); font: inherit;
}
.mf-int input:focus, .mf-text input:focus { border-color: var(--amber); outline: none; }

.mf-places {
  margin-top: 0.35rem; padding: 0; background: none; border: 0;
  color: var(--amber-hi); font: inherit; font-size: 0.78rem; cursor: pointer; text-decoration: underline;
}
.mf-places-list {
  /* The list is complete rather than capped, so the window has to be worth scrolling: 14rem showed
     about six of 848 rows at a time. The search header is sticky, so it stays reachable however far
     down you are. */
  margin-top: 0.4rem; max-height: min(26rem, 55vh); overflow: auto; overscroll-behavior: contain;
  border: 1px solid var(--line); border-radius: var(--radius);
}
.mf-place {
  display: flex; width: 100%; gap: 0.6rem; align-items: center;
  padding: 0.3rem 0.55rem; background: none; border: 0; border-bottom: 1px solid var(--line-2);
  color: var(--text); font: inherit; font-size: 0.8rem; cursor: pointer; text-align: left;
}
.mf-place:last-child { border-bottom: 0; }
.mf-place:hover { background: var(--hover-bg); }
.mf-place.picked { color: var(--amber-hi); }
.mf-place-n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ui-monospace, monospace; }
.mf-place-c { color: var(--muted); font-variant-numeric: tabular-nums; }

/* module state chips beyond the module's own reported state */
.mod-s-offline { color: var(--muted); }
.mod-s-restart { color: var(--standby); border-color: rgba(224, 169, 46, 0.35); }

/* places picker — 848 rows, so it needs a filter and the chosen ones on top */
.mf-place-search {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.4rem 0.55rem; border-bottom: 1px solid var(--line);
  background: var(--panel-2); position: sticky; top: 0; z-index: 1;
}
.mf-place-q {
  flex: 1; min-width: 0; padding: 0.25rem 0.5rem;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text); font: inherit; font-size: 0.8rem;
}
.mf-place-q:focus { border-color: var(--amber); outline: none; }
.mf-place-n-of { color: var(--muted); font-size: 0.72rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mf-place.picked { color: var(--amber-hi); background: rgba(240, 130, 12, 0.07); }
.mf-place.picked .mf-place-n::before { content: "✓ "; }

/* a stat that actually has a value — a zero stays plain, so what matters stands out */
.mod-stat-v.mod-hi { color: var(--amber-hi, var(--amber)); }

/* the bridge core card: no kill-switch, so an icon holds the switch's place and the row stays aligned */
.mod-card.core { border-color: var(--line-amber); }
.mod-core-ico { width: 20px; height: 20px; color: var(--amber); flex: none; }

/* "nothing was found" — a real outcome, not a wait */
.mod-s-none { color: var(--alarm); border-color: rgba(229, 68, 63, 0.35); }
.mod-clash { border-color: rgba(229, 68, 63, 0.45); }
.mod-clash .ico { color: var(--alarm); }

/* multi-select: a compact grid, because a dozen entity kinds as a stacked list is a scroll */
.mf-multi-opts {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: 0.15rem 0.6rem; margin-top: 0.35rem;
}
.mf-multi-opt { display: flex; align-items: center; gap: 0.4rem; cursor: pointer; font-size: 0.85rem; }
.mf-multi-opt input { flex: none; }

/* ── live map layers ───────────────────────────────────────────────────────
   Read out of the running game rather than the last save. */
.mk-live { background: var(--c-player, #35d07f); }
.mk-livebase { background: var(--amber); }

/* condition bar — banded, not a gradient: "wrecked / damaged / fine" is the decision */
.hpb {
  display: inline-block; vertical-align: middle;
  width: 5.5rem; height: 0.5rem; margin-right: 0.4rem;
  background: var(--inset); border: 1px solid var(--line); border-radius: 999px; overflow: hidden;
}
.hpb.sm { width: 3.5rem; height: 0.4rem; }
.hpb-t { display: block; height: 100%; }
.hpb-ok { background: var(--online); }
.hpb-warn { background: var(--standby); }
.hpb-bad { background: var(--alarm); }
.hpb-n { font-variant-numeric: tabular-nums; font-size: 0.8rem; }

/* Wrapper for the parts box in a card — the database-backed one and the live one alike. */
/* The parts box is ALSO the scroll container, so it must carry no padding at the top: a sticky
   header sticks to the container's PADDING edge, which leaves that strip above it for rows to scroll
   through — the heading reads as sitting slightly low with a gap above it that items slide past.
   The header supplies its own spacing instead. */
.mp-parts { margin-top: 0.5rem; border-top: 1px solid var(--line); padding-top: 0; }
/* A live part row is a content row with a condition bar on the end. The bar must not be squeezed by
   a long name, so it keeps its width and the NAME gives way — a half-drawn bar reads as a wrong
   number, while a clipped name is still obviously a clipped name. */
.mp-parts .mp-c-row .hpb { flex: none; margin-right: 0.25rem; }
.mp-parts .mp-c-row .hpb-n { flex: none; min-width: 2.6em; text-align: right; font-size: 0.72rem; }
/* Why a column of bars is missing — stated once at the top of the panel rather than left blank for
   the reader to interpret as "everything is fine". */
.mp-c-note { padding: 0.35rem 0.1rem 0.15rem; color: var(--standby); font-size: 0.74rem; line-height: 1.4; }

/* how many markers a dense layer left out — sits under the map, never in the way of it */
.map-trunc {
  position: absolute; left: 50%; bottom: 0.6rem; transform: translateX(-50%);
  padding: 0.25rem 0.7rem; border-radius: 999px;
  background: rgba(13, 14, 10, 0.85); border: 1px solid var(--line-amber);
  color: var(--amber-hi, var(--amber)); font-size: 0.78rem; white-space: nowrap;
  pointer-events: none; z-index: 5;
}
.ml-more { padding: 0.35rem 0.6rem; color: var(--muted); font-size: 0.78rem; font-style: italic; }

/* ── live-layer kinds ──────────────────────────────────────────────────────
   Sixteen different things share one layer, so the colour has to carry the meaning: hostile things
   read red, wildlife green, machines blue, structures amber. Drawn in one colour they are a sea of
   identical dots and the layer answers nothing. */
:root {
  --lk-puppet:  #e5443f;   /* hostile */
  --lk-boss:    #ff2d55;
  --lk-sentry:  #ff7a45;   /* dangerous, but static */
  --lk-npc:     #c9a24b;   /* people */
  --lk-animal:  #6cbf3f;   /* wildlife */
  --lk-shark:   #29a3c9;
  --lk-drone:   #a06cff;   /* machines */
  /* A dropship is not a big drone — it delivers a cargo drop, which is an event players converge on,
     while a patrol drone is ambient. They get their own layers, so they get their own colour. */
  --lk-dropship: #00d3c2;
  --lk-vehicle: #5aa0ff;
  --lk-base:    #f0820c;
}
.mk-live .mk-dot, .mk-livebase .mk-dot { border-color: rgba(0, 0, 0, 0.35); }
.lk-player  .mk-dot, .ml-dot.lk-player  { background: var(--c-player); }
.lk-puppet  .mk-dot, .ml-dot.lk-puppet  { background: var(--lk-puppet); }
.lk-animal  .mk-dot, .ml-dot.lk-animal  { background: var(--lk-animal); }
.lk-shark   .mk-dot, .ml-dot.lk-shark   { background: var(--lk-shark); }
.lk-sentry  .mk-dot, .ml-dot.lk-sentry  { background: var(--lk-sentry); }
.lk-sentryd .mk-dot, .ml-dot.lk-sentryd { background: var(--lk-sentry); }
.lk-npc     .mk-dot, .ml-dot.lk-npc     { background: var(--lk-npc); }
.lk-drone   .mk-dot, .ml-dot.lk-drone   { background: var(--lk-drone); }
.lk-dropship .mk-dot, .ml-dot.lk-dropship { background: var(--lk-dropship); }
.lk-boss    .mk-dot, .ml-dot.lk-boss    { background: var(--lk-boss); }
.lk-vehicle .mk-dot, .ml-dot.lk-vehicle { background: var(--lk-vehicle); }
.lk-base    .mk-dot, .ml-dot.lk-base    { background: var(--lk-base); }
/* A deployed sentry is somebody's, so it gets a ring the world ones don't have. */
.lk-sentryd .mk-dot { box-shadow: 0 0 0 2px rgba(240, 130, 12, 0.55); }
/* Dead things stay on the map — where a body fell is information — but must not read as alive. */
.map-mk.dead .mk-dot { opacity: 0.35; filter: grayscale(1); }
/* a note inside a module card — why what you are looking at is not the whole story */
.mod-note-inline {
  margin: 0; padding: 0.6rem 1.1rem;
  border-bottom: 1px solid var(--line-2, var(--line));
  color: var(--standby); font-size: 0.83rem; line-height: 1.5;
}



/* a marker whose position came from the running game, not the last save */
.mp-live { color: var(--online); }
/* A missing frame is a finding, not a blank — it reads in the same register as the trap colour,
   which is the palette's existing "this one is a problem". */
.mp-bad { color: var(--c-trap); font-weight: 600; }
.map-mk.livepos .mk-dot { box-shadow: 0 0 0 2px rgba(108, 191, 63, 0.55); }

/* live layers — one colour per layer, mirroring the per-kind marker colours */
.mk-puppet .mk-dot, .ml-dot.mk-puppet     { background: var(--lk-puppet); }
.mk-animal .mk-dot, .ml-dot.mk-animal     { background: var(--lk-animal); }
.mk-sentry .mk-dot, .ml-dot.mk-sentry     { background: var(--lk-sentry); }
.mk-npc .mk-dot, .ml-dot.mk-npc           { background: var(--lk-npc); }
.mk-drone .mk-dot, .ml-dot.mk-drone       { background: var(--lk-drone); }
.mk-dropship .mk-dot, .ml-dot.mk-dropship { background: var(--lk-dropship); }
.mk-boss .mk-dot, .ml-dot.mk-boss         { background: var(--lk-boss); }

/* ════════════════════════════════════════════════════════════════════════
   HOVER PAIRING · the one place a button opts out of the amber hover

   `button:hover` paints the amber fill with dark text. A button that wants a
   different hover has to change BOTH halves, and the way to do that is here —
   set the trio on the element, never override `background` or `color` alone in
   a `:hover` rule. Splitting the pair is what produced dark-on-dark and
   light-on-light labels all over the panel, one patch at a time.

   Two shapes, and everything falls into one of them:
     ghost   — an icon or text button that should never take a fill at all
     tinted  — a row or tile that takes its own soft fill and keeps normal text

   `npm run check-contrast` folds the cascade and fails on any button whose
   hover states one half without the other, so this cannot drift back.
   ════════════════════════════════════════════════════════════════════════ */

/* ghost: no fill, no border flash — the colour change alone is the affordance */
.sched-x, .su-btn-ghost, .pl-modal-x, .ssa-toast-x, .am-back, .am-x, .foot-update,
.amodal-close, .mp-close, .ul-del, .link-btn, .map-filters-head, .pd-stuff-head,
.foot-link, .mos-item {
  --hov-bg: transparent;
  --hov-line: currentColor;
  --hov-fg: var(--text);
}
/* …except these carry a real border of their own, which must not turn amber */
.su-btn-ghost { --hov-line: var(--faint); }
.link-btn, .foot-link, .foot-update { --hov-line: transparent; }

/* tinted: a soft fill of its own, with the panel's normal text on top */
.pd-member, .intel-tile, .intel-cc, .map-zoom-b, .mf-opt, .mf-place,
.map-layer, .ml-row, .anav-dd-item, .adm-perm {
  --hov-fg: var(--text);
}

/* Base damage, in the same three bands the game's own base screen uses. Colour carries the severity
   so the row reads at a glance; the numbers are cumulative, as the game counts them. */
/* Three equal cells, not three pills sharing a line. As pills they wrapped — two on one row and the
   third dropped below — which read as a broken row rather than as three bands of one measurement.
   A grid cannot wrap, and the counts line up, which is the only reason to show the three together. */
.mp-bands { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.35rem; }
.mp-band {
  display: flex; flex-direction: column; align-items: center; gap: 0;
  padding: 0.2rem 0.25rem; border-radius: var(--radius);
  background: var(--inset); border: 1px solid var(--line-2);
  line-height: 1.25; white-space: nowrap; color: var(--muted);
}
.mp-band b { font-size: 0.95rem; font-weight: 700; }
.mp-band i { font-style: normal; font-size: 0.62rem; letter-spacing: 0.02em; }
.mp-band b { font-variant-numeric: tabular-nums; }
.mp-band.b75 b { color: var(--standby); }
.mp-band.b50 b { color: var(--amber-hi); }
.mp-band.b25 b { color: var(--alarm); }

/* A container found inside another one: the same row shape as an item, but it opens. */
.mp-c-open {
  width: 100%; text-align: left; font: inherit; letter-spacing: 0; cursor: pointer;
  background: none; border: 0; border-radius: var(--radius); padding: 0.18rem 0.2rem;
  color: var(--text);
  /* Tinted, not amber-filled: it sits among plain rows and must still read as one of them. */
  --hov-bg: var(--hover-bg); --hov-fg: var(--text); --hov-line: transparent;
}
.mp-c-open .mp-c-chev { width: 11px; height: 11px; color: var(--muted); flex: none; margin-left: 0.15rem; }
.mp-c-open.open .mp-c-chev { color: var(--amber-hi); }
/* Opens straight underneath the row that was clicked. The first version indented it by 1.4rem, which
   read as the panel jumping sideways rather than as something unfolding — the rule was full width and
   suddenly its contents were not. A rule down the left says "these belong to that" without moving
   anything. */
.mp-c-sub {
  margin: 0.15rem 0 0.35rem; padding: 0.2rem 0 0.2rem 0.55rem;
  /* No cap here either — the card is the one thing that scrolls. */
  border-left: 2px solid var(--line-amber);
  background: rgba(0, 0, 0, 0.14); border-radius: 0 var(--radius) var(--radius) 0;
}
/* Boxes and loose items are different answers to "what is in here", so they do not look alike: a
   container is a row you can open, and it sits above the plain contents under its own heading. */
.mp-c-open { border: 1px solid var(--line-2); background: var(--inset); margin-bottom: 0.15rem; }
.mp-c-open:last-of-type { margin-bottom: 0.3rem; }

/* "Everything on this card is what the game says right now" — a mark on the artwork, not a chip.
   It qualifies the whole card, so it belongs on the thing itself rather than in a row of properties
   where it competed with Online and Dead and was the longest of the three. */
.mp-ico.mp-live-at { position: relative; overflow: visible; }
.mp-ico.mp-live-at::after {
  content: ''; position: absolute; right: -3px; bottom: -3px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--online); border: 2px solid var(--panel);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
}
/* A slow pulse says "still current" without being a moving object on a card people read. */
@media (prefers-reduced-motion: no-preference) {
  .mp-ico.mp-live-at::after { animation: mp-live-pulse 2.4s ease-in-out infinite; }
}
@keyframes mp-live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
/* The image plate clips its art, so the mark has to live on a wrapper that does not clip. */
.mp-ico.mp-img.mp-live-at { overflow: visible; }
.mp-ico.mp-img.mp-live-at img { border-radius: calc(var(--radius-md) - 3px); }

/* Options that belong to the panel they head, sitting directly under its title rather than stranded
   below a long list. Quiet ground so they read as settings, not as content. */
.map-opts-top { background: rgba(0, 0, 0, 0.12); }
/* Exports are actions, not filters, so they get their own strip at the foot of the panel. Hidden
   entirely when neither has anything to write. */
/* The export used to be a sticky strip at the foot of the list panel. It is one menu in the
   screen's action bar now — a menu had to open over the lists it belongs to, and as a child of that
   scrollable column it widened the column and pushed the page into a sideways scrollbar. */

/* Average and worst, at the head of the parts they summarise — shown whether the numbers came from
   the running game or from the save. */
.mp-c-sum {
  display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
  padding: 0.35rem 0.1rem 0.4rem; border-bottom: 1px solid var(--line-2);
  font-size: 0.78rem; color: var(--muted);
}
.mp-c-sum-w { margin-left: 0.35rem; }

/* A spawn point with nothing standing on it: where a sentry WILL be, not where one is. Hollow rather
   than merely faded — "not there yet" is a different statement from "dead", and a reader must be able
   to tell them apart without hovering. */
/* The layer's own colour is kept — that is what says WHAT it is — but faded, with a dashed edge.
   A fully hollow dot was the first attempt and it hid the icon: the glyph is coloured for a solid
   fill, so on transparent it disappears. Faded keeps both readable. */
.map-mk.ghost .mk-dot { opacity: 0.45; border-style: dashed; border-width: 2px; }
.map-mk.ghost .mk-label { font-style: italic; opacity: 0.75; }
/* Never on top of a real one: if both are drawn, the thing that is actually there wins the click. */
.map-mk.ghost { z-index: 0; }

/* ── Translations editor ─────────────────────────────────────────────────────
   Laid out like the settings screens, because it is the same shape of problem: something to read on
   the left, the box that changes it on the right. The first version led with the key in a monospace
   column as wide as the text, which turned three thousand strings into a list of developer
   identifiers with the meaning tucked in beside them. */
/* The screen's own margins — 1.5rem each side, the same as every other screen's title bar — rather
   than a capped slab pinned to the left edge. Capping it left two thirds of a wide monitor empty
   while the rows themselves sat hard against the window; the content should use the page and simply
   not touch its edges. */
.tr-bar, .tr-stats, .tr-rows { margin-left: 1.5rem; margin-right: 1.5rem; }

/* One row, one job: what to edit (surface + language), what to show (filter + search). Grouped, not
   spread across the whole width with a search box swallowing the middle. */
.tr-bar { display: flex; flex-wrap: wrap; gap: 0.5rem 0.8rem; align-items: center; }
.tr-pick { display: flex; align-items: center; gap: 0.4rem; font-size: 0.72rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap; }
.tr-pick select { min-width: 11rem; text-transform: none; letter-spacing: 0; }
/* Pushed to the end of the row rather than filling it: it is a filter, not the subject. */
.tr-bar .settings-search { flex: 1 1 12rem; min-width: 9rem; max-width: 20rem; }
.tr-only { --hov-bg: var(--panel-2); --hov-fg: var(--text); white-space: nowrap; }
.tr-bar .settings-toolbar-actions { margin-left: auto; }

/* How far along this language is, as a bar — "2814 of 3241" is a number to work out, a bar is a
   glance, and that is the first question this screen exists to answer. */
.tr-stats { display: flex; gap: 0.9rem; flex-wrap: wrap; align-items: center;
  padding: 0.5rem 0.1rem 0.7rem; color: var(--muted); font-size: 0.8rem; }
.tr-prog { display: flex; align-items: center; gap: 0.45rem; }
.tr-prog-bar { width: 9rem; height: 6px; border-radius: var(--pill); background: var(--inset);
  border: 1px solid var(--line-2); overflow: hidden; }
.tr-prog-bar span { display: block; height: 100%; background: var(--online); }
.tr-prog-n { font-variant-numeric: tabular-nums; color: var(--text); font-weight: 700; }

/* Grouped by key prefix and collapsible — the file's own structure, not one invented for the screen. */
.tr-grp { border: 1px solid var(--line); border-radius: var(--radius); margin-bottom: 0.5rem;
  background: var(--panel); overflow: hidden; }
.tr-grp-h { --hov-bg: var(--panel-2); --hov-fg: var(--amber-hi);
  display: flex; align-items: center; gap: 0.5rem; width: 100%; padding: 0.5rem 0.7rem;
  background: var(--panel-2); border: none; border-bottom: 1px solid var(--line);
  color: var(--text); font: inherit; font-size: 0.76rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; text-align: left; cursor: pointer; }
.tr-grp-h:hover { background: var(--hov-bg); color: var(--hov-fg); }
.tr-grp-n { flex: 1; }
.tr-grp-c { min-width: 1.6em; padding: 0.05rem 0.45em; text-align: center; border-radius: var(--pill);
  background: rgba(0, 0, 0, 0.3); color: var(--muted); font-size: 0.72rem; font-weight: 400;
  font-variant-numeric: tabular-nums; }
/* Untranslated in this group — the number that decides where to start. */
.tr-grp-m { min-width: 1.6em; padding: 0.05rem 0.45em; text-align: center; border-radius: var(--pill);
  background: var(--standby); color: var(--bg); font-size: 0.72rem; font-weight: 700;
  font-variant-numeric: tabular-nums; }
.tr-grp-chev { color: var(--muted); transition: transform 0.12s ease; }
.tr-grp.collapsed .tr-grp-chev { transform: rotate(-90deg); }
.tr-grp.collapsed .tr-grp-b { display: none; }
.tr-grp.collapsed .tr-grp-h { border-bottom: none; }

.tr-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1.2rem; align-items: start; padding: 0.5rem 0.7rem; border-top: 1px solid var(--line-2); }
.tr-row:first-child { border-top: none; }
/* The English is the label: it is what the row is about and what a translator reads. */
.tr-row .tr-en { color: var(--text); font-size: 0.88rem; line-height: 1.4; overflow-wrap: anywhere; }
/* The key is secondary — it matters when hunting one down, not while translating. */
/* The panel gives every <code> a chip: border, fill, padding. On three thousand of them that made
   the key look like a second input box under the text, which is the opposite of secondary. */
.tr-row .tr-k {
  display: block; margin-top: 0.15rem; padding: 0;
  background: none; border: none; border-radius: 0;
  font-family: var(--mono); font-size: 0.68rem; color: var(--muted); overflow-wrap: anywhere;
}
.tr-row textarea { width: 100%; resize: vertical; min-height: 1.9rem; padding: 0.3rem 0.4rem;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text); font: inherit; font-size: 0.85rem; line-height: 1.4; }
.tr-row textarea:focus { outline: none; border-color: var(--amber); }
/* Three states worth telling apart at a glance: never translated, changed by this owner, and changed
   in this session but not yet saved. A left edge, so it reads down the column without shouting. */
.tr-row.tr-missing { box-shadow: inset 3px 0 0 var(--standby); }
.tr-row.tr-mine    { box-shadow: inset 3px 0 0 var(--c-vehicle); }
.tr-row.tr-dirty   { box-shadow: inset 3px 0 0 var(--amber); background: rgba(255, 154, 46, 0.06); }
/* Editing English itself has no second column of meaning — the label would be the box's own text. */
/* Editing English itself: the left column is only the key, and a key does not need half the width —
   the text being edited does. */
.tr-rows.tr-base .tr-row { grid-template-columns: minmax(9rem, 22rem) minmax(0, 1fr); }
.tr-rows.tr-base .tr-row .tr-k { margin-top: 0; font-size: 0.76rem; }
.tr-empty { padding: 1.2rem 0.6rem; color: var(--muted); font-size: 0.86rem; }
/* Unsaved work is announced on the button that clears it. */
#tr-save.tr-has { box-shadow: 0 0 0 1px var(--amber-hi) inset; }
/* One column on a narrow screen — two columns of 12rem each is unreadable well before it wraps. */
@media (max-width: 1100px) {
  .tr-row { grid-template-columns: 1fr; gap: 0.35rem; }
}

/* ── Discord channel / role picker ───────────────────────────────────────────
   The SAME control the rest of the panel uses for a dropdown, so a setting that holds "which
   channel" looks like a choice rather than a box for an 18-digit number with a button bolted on. The
   id box is not gone, just behind the ID toggle — pasting is still the fastest route when somebody
   has one on the clipboard, and the only route when the bot is offline. */
.dp-box { grid-area: control; display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
/* The panel drops the background arrow with `.cdd .cdd-btn`, which needs a .cdd ancestor this
   control does not have — so it kept the painted arrow AND got the caret, and every field showed
   two. Same treatment, applied directly. */
.dp-pick {
  flex: 1 1 auto; min-width: 0;
  justify-content: flex-start; text-align: left;
  background-image: none; padding-right: 0.7rem;
}
.dp-pick .dd-caret { margin-left: auto; padding-left: 0.4rem; }
.dp-pick .cdd-lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* An id that names nothing is usually a deleted channel, and the feed on it has been silently dead
   ever since — the control says so rather than looking like any other setting. */
.dp-pick.dp-bad { border-color: var(--c-trap); }
.dp-pick.dp-bad .cdd-lbl { color: var(--c-trap); }
.dp-raw {
  flex: none; padding: 0.24rem 0.5rem; font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.04em;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius); color: var(--muted);
  --hov-bg: var(--panel-2); --hov-fg: var(--text);
}
.dp-raw:hover { background: var(--hov-bg); color: var(--hov-fg); }
.dp-raw.on { border-color: var(--amber); color: var(--amber-hi); }
.dp-id { flex-basis: 100%; }
.dp-id[hidden] { display: none; }

.dp-menu {
  position: fixed; z-index: 4000; width: 22rem; max-width: calc(100vw - 16px);
  display: flex; flex-direction: column; padding: 0.35rem;
  background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
}
.dp-search {
  width: 100%; padding: 0.32rem 0.45rem; margin-bottom: 0.3rem;
  background: var(--inset); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text); font: inherit; font-size: 0.85rem;
}
.dp-search:focus { outline: none; border-color: var(--amber); }
.dp-list { max-height: 18rem; overflow-y: auto; display: flex; flex-direction: column; }
.dp-item {
  --hov-bg: var(--amber); --hov-fg: var(--on-amber);
  display: flex; align-items: center; gap: 0.4rem; width: 100%;
  padding: 0.32rem 0.45rem; border: none; border-radius: var(--radius);
  background: none; color: var(--text); font: inherit; font-size: 0.85rem; text-align: left; cursor: pointer;
}
.dp-item:hover { background: var(--hov-bg); color: var(--hov-fg); }
/* A ticked row keeps its own quiet ground and retunes the PAIR, so a hover cannot take one half and
   leave the other — the mistake that made a selected dropdown row unreadable elsewhere. */
.dp-item.on { background: rgba(240, 130, 12, 0.18); color: var(--amber-hi);
  --hov-bg: var(--hover-bg); --hov-fg: var(--amber-hi); }
.dp-hash { color: var(--muted); }
.dp-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dp-parent { color: var(--muted); font-size: 0.72rem; }
.dp-dot { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.dp-warn {
  flex: none; width: 15px; height: 15px; line-height: 15px; text-align: center; border-radius: 50%;
  background: var(--c-trap); color: var(--on-amber); font-size: 0.68rem; font-weight: 700; cursor: help;
}
.dp-empty, .dp-loading { padding: 0.6rem 0.45rem; color: var(--muted); font-size: 0.82rem; line-height: 1.4; }
.dp-foot { display: flex; justify-content: flex-end; padding-top: 0.3rem; }

/* The Settings nav entry uses the same dropdown Plugins does — including its arrow, which
   `.anav-dd.has-dd > .anav-btn::after` already draws. Adding a chevron of my own gave every one of
   them two. */
#settings-nav[hidden] { display: none; }

/* Menu entries are links, so they keep middle-click and "open in new tab" — they must still look and
   behave like the buttons beside them. */
.anav-dd-menu a.anav-dd-item { text-decoration: none; }

/* ── Header language menu ──────────────────────────────────────────────────
   A bare list of flags did not say WHAT it switches — the panel, the game server and Discord all
   have a language and all three are a few clicks apart. The menu names itself, marks the current
   choice with a tick rather than colour alone, and states coverage where a language is partial. */
.lang-dd-list { min-width: 15rem; }
.lang-dd-head {
  padding: 0.4rem 0.6rem 0.45rem; margin-bottom: 0.15rem;
  border-bottom: 1px solid var(--line);
  color: var(--muted); font-family: var(--ui); font-size: 0.68rem;
  letter-spacing: 0.08em; text-transform: uppercase;
}
.lang-dd-item .lang-dd-name { flex: 1 1 auto; min-width: 0; }
/* Not a warning — a language at 60% simply falls back to English for the rest. */
.lang-dd-pct {
  flex: none; color: var(--muted); font-family: var(--mono); font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
}
.lang-dd-tick { flex: none; width: 0.95rem; height: 0.95rem; opacity: 0; }
.lang-dd-item.active .lang-dd-tick { opacity: 1; }
.lang-dd-item.active { color: var(--amber-hi); font-weight: 600; }
/* Both halves of the hover pair travel together — set on the element, never one side in :hover. */
.lang-dd-item { --hov-bg: var(--hover-bg); --hov-fg: var(--amber-hi); }
.lang-dd-item:hover .lang-dd-pct { color: inherit; }
.lang-dd-foot {
  margin-top: 0.25rem; padding: 0.45rem 0.6rem 0.15rem;
  border-top: 1px solid var(--line);
}
.lang-dd-foot a {
  color: var(--muted); font-family: var(--ui); font-size: 0.76rem; text-decoration: none;
}
.lang-dd-foot a:hover { color: var(--amber-hi); text-decoration: underline; }

/* ── Which languages a surface offers ──────────────────────────────────────
   Under each language dropdown. The list is not fixed — a language appears once it has strings for
   that surface — and without saying so, a missing one reads as a bug. */
/* Column 1, under the help text — this is explanation, and the control column is a narrow strip that
   wrapped the chips into a ragged block while two thirds of the row sat empty. */
.lang-offer { grid-column: 1; display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.45rem; }
.lang-offer-lead { color: var(--muted); font-family: var(--ui); font-size: 0.76rem; line-height: 1.45; }
.lang-offer-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.lang-offer-chip {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.16rem 0.42rem; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel-2); color: var(--text);
  font-family: var(--ui); font-size: 0.72rem; white-space: nowrap;
}
.lang-offer-chip .lang-flag { width: 1.1em; height: 0.825em; }
.lang-offer-chip.partial { border-style: dashed; }
.lang-offer-pct { color: var(--muted); font-family: var(--mono); font-variant-numeric: tabular-nums; }
.lang-offer-link { align-self: start; color: var(--muted); font-family: var(--ui); font-size: 0.76rem; text-decoration: none; }
.lang-offer-link:hover { color: var(--amber-hi); text-decoration: underline; }

/* ── Add / edit a language ─────────────────────────────────────────────────
   This was three native prompt() boxes in a row. The flag was the field that most needed showing and
   was the one you could not see — a two-letter country code typed blind. */
.ssa-dialog.tr-dlg { max-width: 32rem; text-align: left; }
.ssa-dialog.tr-dlg.wide { max-width: 46rem; }
.tr-dlg-bd { margin: 0.35rem 0 1rem; }
.tr-dlg-grid { display: grid; grid-template-columns: minmax(4rem, auto) minmax(0, 1fr); gap: 0.55rem 0.9rem; align-items: start; }
.tr-dlg-grid > label { padding-top: 0.45rem; color: var(--muted); font-family: var(--ui); font-size: 0.8rem; }
.tr-dlg-grid input[type="text"] { width: 100%; }
.tr-dlg-help { margin: 0.3rem 0 0; color: var(--muted); font-family: var(--ui); font-size: 0.74rem; line-height: 1.45; }
.tr-dlg-err { margin: 0.6rem 0 0; color: var(--bad); font-family: var(--ui); font-size: 0.8rem; }
.tr-dlg-err[hidden] { display: none; }

.tr-f-flagrow { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.tr-f-flagrow .tr-dlg-help { flex: 1 0 100%; }
/* A fixed box so the row does not jump as flags of different widths come and go. */
.tr-f-flagprev {
  display: inline-flex; align-items: center; gap: 0.45rem; min-width: 8rem;
  padding: 0.34rem 0.55rem; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel-2); font-family: var(--ui); font-size: 0.8rem;
}
.tr-f-flagprev .lang-flag { width: 1.5em; height: 1.125em; }
.tr-f-flagname { color: var(--text); }
.tr-f-none, .tr-f-flagprev .tr-f-flagname:only-child { color: var(--muted); font-style: italic; }

.tr-flag-search { width: 100%; margin-bottom: 0.6rem; }
.tr-flag-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-bottom: 0.7rem; }
.tr-flag-hint { flex: 1 1 14rem; color: var(--muted); font-family: var(--ui); font-size: 0.74rem; line-height: 1.4; }
.tr-flag-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr)); gap: 0.35rem;
  max-height: 24rem; overflow-y: auto; padding: 0.2rem;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel-2);
}
/* A section title spans the row rather than sitting in a cell of its own. */
.tr-flag-head {
  grid-column: 1 / -1; padding: 0.5rem 0.4rem 0.25rem;
  color: var(--muted); font-family: var(--ui); font-size: 0.68rem;
  letter-spacing: 0.08em; text-transform: uppercase;
}
.tr-flag-empty { grid-column: 1 / -1; padding: 1.2rem; color: var(--muted); font-family: var(--ui); font-size: 0.82rem; text-align: center; }
.tr-flag-cell {
  display: flex; align-items: center; gap: 0.45rem; min-width: 0;
  padding: 0.34rem 0.45rem; border: 1px solid transparent; border-radius: var(--radius);
  background: none; color: var(--text); font-family: var(--ui); font-size: 0.76rem;
  text-align: left; cursor: pointer;
  --hov-bg: var(--hover-bg); --hov-fg: var(--amber-hi);
}
.tr-flag-cell img { width: 1.5rem; height: 1.125rem; border-radius: 2px; object-fit: cover; flex: none; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45); }
.tr-flag-cell span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tr-flag-cell.on { border-color: var(--amber); background: var(--hover-bg); color: var(--amber-hi); }
.tr-flag-cell.custom img { box-shadow: 0 0 0 1px var(--amber); }

/* The dialog's own fields, painted like every other input in the panel. Left to the browser they
   came out white-on-white in a dark modal. */
.tr-dlg input[type="text"] {
  width: 100%; padding: 0.5rem 0.65rem; box-sizing: border-box;
  background: var(--input-bg, rgba(255, 255, 255, 0.025));
  border: 1px solid var(--input-line, rgba(228, 226, 214, 0.18));
  border-radius: var(--radius); color: var(--text); font-family: var(--ui); font-size: 0.88rem;
}
.tr-dlg input[type="text"]:focus { border-color: var(--amber); box-shadow: var(--ring); outline: none; }
.tr-dlg input[type="text"]:disabled { color: var(--muted); cursor: not-allowed; }
/* Room for the help text to be a sentence rather than a column of two-word lines. */
.ssa-dialog.tr-dlg { max-width: 38rem; }
.tr-dlg-grid { grid-template-columns: 5.5rem minmax(0, 1fr); }
/* `.secondary` sets its own display, which beats the [hidden] attribute's — the Edit button stayed
   on screen for every shipped language, offering to edit something that cannot be edited. */
#tr-edit-lang[hidden] { display: none; }

/* `.ssa-dialog` pins width to 440px, so max-width alone did nothing — the flag grid came out two
   columns wide with a scrollbar. Width follows the max here, and both stay under the viewport. */
.ssa-dialog.tr-dlg { width: min(38rem, calc(100vw - 2rem)); }
.ssa-dialog.tr-dlg.wide { width: min(46rem, calc(100vw - 2rem)); }

/* ── Translation paging ────────────────────────────────────────────────────
   Replaces a hard ceiling of 300 with "narrow the search" under it — which told the reader the rest
   existed and gave them no way to reach it. Search finds a known string; translating a language is
   walking it end to end. */
.tr-pager {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem;
  font-family: var(--ui); font-size: 0.78rem; color: var(--muted);
}
/* The one under the list gets a rule above it and room to breathe. */
.tr-rows > .tr-pager { margin: 0.9rem 1.5rem 0.4rem; padding-top: 0.7rem; border-top: 1px solid var(--line); }
.tr-pager-n { font-variant-numeric: tabular-nums; }
.tr-pager-nav { display: inline-flex; align-items: center; gap: 0.25rem; }
.tr-pager-pos { padding: 0 0.4rem; color: var(--text); font-variant-numeric: tabular-nums; }
.tr-pager .tr-pg {
  min-width: 1.9rem; padding: 0.2rem 0.45rem; font-size: 0.85rem; line-height: 1.2;
  --hov-bg: var(--hover-bg); --hov-fg: var(--amber-hi);
}
.tr-pager .tr-pg:disabled { opacity: 0.35; cursor: default; }
.tr-pager-size { display: inline-flex; align-items: center; gap: 0.35rem; }
.tr-pager-size select { padding: 0.18rem 0.4rem; font-size: 0.78rem; }

/* ── Map export menu ───────────────────────────────────────────────────────
   Two hard-coded buttons became one menu built from what the map actually holds — so it can never
   offer a file that comes back empty, and never miss a layer either. */
.map-exp { position: relative; display: inline-flex; }
.map-exp.hidden { display: none; }
.map-exp-chev { margin-left: 0.3rem; color: var(--muted); font-size: 0.7rem; }
/* FIXED, not absolute, and placed from the button's own rectangle.
   Absolute inside the side column made the menu part of that column's scrollable area: it pushed
   the column wider and opened off to the left. Fixed takes it out of the layout altogether, which
   is what the Discord channel picker already does for the same reason. */
.map-exp-menu {
  position: fixed; z-index: 90;
  width: 16rem; max-width: calc(100vw - 16px); max-height: 22rem; overflow-y: auto;
  display: flex; flex-direction: column; gap: 1px; padding: 0.3rem;
  background: var(--panel); border: 1px solid var(--line-amber); border-radius: var(--radius);
  box-shadow: var(--elev-2);
}
.map-exp-menu[hidden] { display: none; }
.map-exp-sep { height: 1px; margin: 0.25rem 0.2rem; background: var(--line); }
.map-exp-item {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  padding: 0.36rem 0.5rem; border-radius: var(--radius);
  color: var(--text); font-family: var(--ui); font-size: 0.8rem; text-decoration: none;
  --hov-bg: var(--hover-bg); --hov-fg: var(--amber-hi);
}
.map-exp-c { color: var(--muted); font-family: var(--mono); font-size: 0.74rem; font-variant-numeric: tabular-nums; }
.map-exp-item:hover .map-exp-c { color: inherit; }

/* ── Condition as a dot ────────────────────────────────────────────────────
   How the game itself shows it. In a list a bar is the wrong shape: it takes a column of its own and
   pushes the name and the count about, and forty rows of bars answer "how damaged is each one" when
   the question is "which of these is damaged". The exact figure is the tooltip. */
.hpd {
  flex: none; width: 0.5rem; height: 0.5rem; border-radius: 50%;
  /* A ring, so the dot reads on a light row and a dark one alike, and so a pale band still has an
     edge rather than dissolving into the row it sits on. */
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45);
}
/* The SAME three tokens the bar uses. Invented names would have silently fallen back to literals
   and the dot and the bar would have drifted into two different greens. */
.hpd-ok { background: var(--online); }
.hpd-warn { background: var(--standby); }
.hpd-bad { background: var(--alarm); }
/* The row lays out around it: picture, dot, name, count. The dot sits with the picture rather than
   trailing the name, so the column of dots is scannable straight down the list. */
/* Dot AND figure. The dot is what the eye reads down a column of forty rows; the number is what an
   admin acts on — a colour alone means hovering every row to learn whether "warn" is 58% or 27%.
   Together they take about the width the bar took by itself. */
.hpd-w {
  flex: none; display: inline-flex; align-items: center; gap: 0.3rem;
  min-width: 3.1rem;                 /* so the names line up whatever the figure is */
  font-family: var(--mono); font-size: 0.7rem; font-variant-numeric: tabular-nums;
}
.hpd-n { color: var(--muted); }
/* The figure takes the band colour too, but muted — at a glance it is the dot that carries the
   state; the number is there to be read, not to shout. */
.hpd-ok-t { color: var(--online); opacity: 0.85; }
.hpd-warn-t { color: var(--standby); }
.hpd-bad-t { color: var(--alarm); }
.mp-c-row .hpd-w { margin-right: 0.1rem; }

/* Find an item inside a container. Offered once there is enough to need one — looking for one thing
   on somebody ("does this player have a lockpick") is a real question, and forty rows is where the
   eye stops being the right tool. */
.mp-c-find {
  width: calc(100% - 1rem); margin: 0.4rem 0.5rem 0.35rem; padding: 0.34rem 0.55rem; box-sizing: border-box;
  background: var(--input-bg, rgba(255, 255, 255, 0.025));
  border: 1px solid var(--input-line, rgba(228, 226, 214, 0.18));
  border-radius: var(--radius); color: var(--text); font-family: var(--ui); font-size: 0.8rem;
}
.mp-c-find:focus { border-color: var(--amber); box-shadow: var(--ring); outline: none; }
.mp-c-hide { display: none !important; }

/* ── Server history charts ────────────────────────────────────────────────
   Plain SVG, no charting library: the panel ships inside the exe behind a strict CSP, so a CDN
   dependency would have to be vendored and maintained for a line, a fill and a hover readout.

   Styled to sit with the REST of the panel rather than inventing a look: the range picker is the
   same pill filter the Plugins tab uses (it selects a view, it does not act, so full amber blocks
   next to Start/Stop were wrong), and the chart surface is the same soft inset the cards use.
   Every colour is a theme token, so both themes are covered by construction. */
.hist-bar { display: flex; align-items: center; gap: 0.8rem; flex-wrap: wrap; margin: 0.1rem 0 0.9rem; }
.hist-span { font-size: 0.75rem; color: var(--faint); }

.hist-charts { display: grid; gap: 1.1rem; }
.hist-chart { min-width: 0; }
.hist-head {
  display: flex; align-items: baseline; gap: 0.7rem; flex-wrap: wrap;
  margin: 0 0 0.35rem; font-size: 0.78rem;
}
.hist-title { font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.hist-legend { display: inline-flex; gap: 0.7rem; }
/* The legend IS the control — each key is a button that drops or restores its line. A switched-off
   key stays listed and dimmed rather than vanishing: a control you cannot see is one you cannot undo. */
.hist-key {
  display: inline-flex; align-items: center; gap: 0.35rem;
  background: transparent; border: 0; padding: 0.1rem 0.2rem; border-radius: var(--radius);
  color: var(--muted); font: inherit; font-size: 0.75rem; cursor: pointer;
  --hov-bg: transparent; --hov-fg: var(--text); --hov-line: currentColor;
}
.hist-key.off { color: var(--faint); }
.hist-key.off i { opacity: 0.25; }
.hist-key.off { text-decoration: line-through; text-decoration-thickness: 1px; }
.hist-key[disabled] { cursor: default; opacity: 0.45; }
.hist-key i { width: 0.85rem; height: 0.16rem; border-radius: 1px; display: inline-block; }
/* The readout sits at the right of its own line, so numbers appearing on hover never reflow the
   title or the legend — the chart must not twitch while the pointer moves across it. */
.hist-read { margin-left: auto; font-family: var(--mono); font-size: 0.75rem; color: var(--text); white-space: nowrap; }
.hist-read b { font-weight: 600; }
.hist-when { color: var(--faint); margin-right: 0.35rem; }

.hist-svg {
  width: 100%; height: 360px; display: block;
  background: rgba(255, 255, 255, 0.018);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
}
.hist-grid { stroke: var(--line-2); stroke-width: 1; }
.hist-axis { fill: var(--muted); font-family: var(--mono); font-size: 11px; }
.hist-axis-r { text-anchor: start; }
/* The axis NAMES, so "14" on the left and "60" on the right are not two anonymous ladders. */
.hist-unit { fill: var(--faint); font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; }
.hist-time { font-size: 11px; }
/* The clock under each date — the detail, not the row. */
.hist-clock { fill: var(--faint); font-size: 10px; }
.hist-line { fill: none; stroke-width: 1.75; stroke-linejoin: round; stroke-linecap: round; }
.hist-fill { opacity: 0.10; stroke: none; }
.hist-cursor { stroke: var(--amber); stroke-width: 1; opacity: 0.55; }
/* The span being dragged out. Drawn under the pointer, so it must not swallow the pointer events
   that are drawing it. */
.hist-sel { fill: var(--amber); opacity: 0.13; pointer-events: none; }
.hist-svg { cursor: crosshair; }
.hist-reset {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.28rem 0.7rem; border-radius: 999px;
  border: 1px solid var(--line-amber); background: transparent;
  color: var(--amber); font: inherit; font-size: 0.75rem; cursor: pointer;
  --hov-bg: transparent; --hov-fg: var(--amber-hi); --hov-line: currentColor;
}
.hist-reset .ico { width: 11px; height: 11px; }
.hist-reset[hidden] { display: none; }
.hist-empty { color: var(--faint); font-size: 0.8rem; margin: 0.4rem 0 0; }

/* Where a searched-for item was FOUND. The point of searching inside boxes is knowing which box, so
   the location travels with the hit rather than being something you go and look for. */
.mp-c-where {
  margin-left: 0.4rem; padding: 0.05rem 0.4rem;
  border: 1px solid var(--line); border-radius: 999px;
  font-size: 0.68rem; color: var(--muted); white-space: nowrap;
}
.mp-c-hit .mp-c-name { flex: 0 1 auto; }
