/* ============================================================
   ShadowBot — Ops Console
   Forked from ASA Server Manager (ShadowServerManagerHost/public/styles.css)

   Dark only. All colour authored in OKLCH. Every neutral shares
   hue 60 / chroma 0.008 — a warm grey, not a blue grey.

   Two densities, one system:
     body            -> admin, 13.5px, table-led
     body.app-portal -> player portal, 15px, card-led
   ============================================================ */

:root {
  /* Surfaces — warm-tinted neutrals */
  --bg-0:        oklch(0.155 0.008 60);   /* page */
  --bg-1:        oklch(0.195 0.008 60);   /* sidebar / topbar */
  --bg-2:        oklch(0.225 0.008 60);   /* card */
  --bg-3:        oklch(0.265 0.008 60);   /* hover / elevated */
  --bg-input:    oklch(0.175 0.008 60);
  --border:      oklch(0.305 0.008 60);
  --border-soft: oklch(0.255 0.008 60);

  /* Text — note: --fg-mute is lifted from the manager's 0.60 to 0.66
     so micro-labels clear 4.5:1 on --bg-2. Do not lower it. */
  --fg:      oklch(0.96 0.005 60);
  --fg-mid:  oklch(0.78 0.006 60);
  --fg-mute: oklch(0.66 0.008 60);
  --fg-dim:  oklch(0.52 0.008 60);

  /* Brand accent — ember */
  --ember:      oklch(0.74 0.16 55);
  --ember-deep: oklch(0.62 0.18 45);
  --ember-soft: oklch(0.74 0.16 55 / 0.12);
  /* Text drawn ON the accent. The player theme recomputes it from its own
     accent so a pale accent gets dark text and a dark one gets light. */
  --ember-ink:  oklch(0.18 0.03 55);

  /* Status */
  --ok:        oklch(0.78 0.14 150);
  --ok-soft:   oklch(0.78 0.14 150 / 0.14);
  --warn:      oklch(0.82 0.14 85);
  --warn-soft: oklch(0.82 0.14 85 / 0.14);
  --bad:       oklch(0.70 0.19 25);       /* lifted from 0.67 for contrast */
  --bad-soft:  oklch(0.70 0.19 25 / 0.14);
  --info:      oklch(0.78 0.10 220);
  --info-soft: oklch(0.78 0.10 220 / 0.14);

  /* Brand: Discord */
  --discord:      oklch(0.68 0.16 275);
  --discord-deep: oklch(0.55 0.19 275);

  /* Type */
  --f-display: "Space Grotesk", ui-sans-serif, sans-serif;
  --f-body:    "Geist", "IBM Plex Sans", ui-sans-serif, sans-serif;
  --f-mono:    "JetBrains Mono", ui-monospace, "SF Mono", monospace;

  /* Geometry */
  --r-sm: 4px;
  --r:    7px;
  --r-lg: 11px;
  --r-xl: 16px;

  /* Elevation — one token. Depth comes from --bg-* steps and borders. */
  --shadow-lift: 0 1px 0 oklch(1 0 0 / 0.04) inset,
                 0 14px 30px -18px oklch(0 0 0 / 0.6);
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg-0);
  color: var(--fg);
  font-family: var(--f-body);
  font-size: 13.5px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  font-feature-settings: "ss01", "cv11";
}

body.app-portal { font-size: 15px; line-height: 1.6; }

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

/* Mockup pages only — artboards are fixed size. Delete in production. */
body.mockup { display: flex; justify-content: center; padding: 0; }

/* ============================================================
   LAYOUT
   Admin shell : 244px sidebar / 52px topbar
   Portal shell: 264px sidebar / 60px topbar
   Map shell   : 68px icon rail (sidebar collapses on the map)
   Only .main scrolls.
   ============================================================ */

.shell {
  display: grid;
  grid-template-columns: 244px 1fr;
  grid-template-rows: 52px 1fr;
  height: 100vh;
}
.app-portal .shell { grid-template-columns: 264px 1fr; grid-template-rows: 60px 1fr; }
.shell--rail      { grid-template-columns: 68px 1fr; }

.sidebar { grid-row: 1 / 3; background: var(--bg-1); border-right: 1px solid var(--border-soft); display: flex; flex-direction: column; }
.topbar  { background: var(--bg-1); border-bottom: 1px solid var(--border-soft); display: flex; align-items: center; gap: 14px; padding: 0 20px; }
.main    { overflow-y: auto; padding: 22px 28px 64px; }
.app-portal .main { padding: 26px 30px 64px; }

.page-h  { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 18px; }
.page-h h1 { margin: 0; font-family: var(--f-display); font-size: 24px; font-weight: 500; letter-spacing: -0.2px; }
.app-portal .page-h h1 { font-size: 26px; font-weight: 600; letter-spacing: -0.3px; }
.page-h .sub { margin: 4px 0 0; color: var(--fg-mid); font-size: 12.5px; }
.app-portal .page-h .sub { font-size: 14px; }

.grid { display: grid; gap: 14px; }
.grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid.cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.grid.cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }

/* ============================================================
   NAV
   ============================================================ */

.brand { height: 52px; display: flex; align-items: center; gap: 10px; padding: 0 16px; border-bottom: 1px solid var(--border-soft); flex-shrink: 0; }
.app-portal .brand { height: 60px; gap: 11px; padding: 0 18px; }
.brand .mark { width: 26px; height: 26px; border-radius: var(--r-sm); background: linear-gradient(160deg, oklch(0.32 0.06 55), oklch(0.21 0.02 55)); border: 1px solid oklch(0.74 0.16 55 / 0.4); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.app-portal .brand .mark { width: 30px; height: 30px; border-radius: var(--r); }
.brand .name { font-family: var(--f-display); font-size: 14px; font-weight: 600; line-height: 1.1; }
.app-portal .brand .name { font-size: 15px; line-height: 1.2; }
.brand .tag  { font-family: var(--f-mono); font-size: 9px; letter-spacing: 1.4px; text-transform: uppercase; color: var(--ember); }

.nav { flex-grow: 1; padding: 14px 10px; display: flex; flex-direction: column; gap: 16px; overflow-y: auto; }
.app-portal .nav { padding: 16px 12px; gap: 18px; }
.nav-label { font-family: var(--f-mono); font-size: 9.5px; letter-spacing: 1.6px; text-transform: uppercase; color: var(--fg-dim); padding: 0 10px 7px; }
.app-portal .nav-label { padding: 0 11px 8px; }

.nav-item {
  position: relative; display: flex; align-items: center; gap: 10px;
  height: 32px; padding: 0 10px; border-radius: var(--r);
  color: var(--fg-mid); font-size: 12.5px;
  background: none; border: 0; width: 100%; text-align: left; cursor: pointer;
  font-family: inherit;
}
.app-portal .nav-item { height: 38px; gap: 12px; padding: 0 11px; font-size: 14px; }
.nav-item:hover  { background: var(--bg-2); color: var(--fg); }
.nav-item.active { background: var(--bg-2); color: var(--fg); font-weight: 600; }
.nav-item.active::before { content: ""; position: absolute; left: -10px; top: 7px; bottom: 7px; width: 2px; background: var(--ember); border-radius: 999px; }
.app-portal .nav-item.active::before { left: -12px; top: 9px; bottom: 9px; }
.nav-item.active svg { color: var(--ember); }
.nav-item .count { margin-left: auto; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--bg-3); color: var(--fg-mid); font-family: var(--f-mono); font-size: 9.5px; display: flex; align-items: center; justify-content: center; }
.nav-item .count.warn { background: var(--warn-soft); color: var(--warn); }

.sidebar-foot { flex-shrink: 0; border-top: 1px solid var(--border-soft); padding: 12px 16px; }
.app-portal .sidebar-foot { padding: 14px 18px; }

/* ============================================================
   CARDS
   ============================================================ */

.card { background: var(--bg-2); border: 1px solid var(--border-soft); border-radius: var(--r-lg); box-shadow: var(--shadow-lift); overflow: hidden; }
.app-portal .card { border-radius: var(--r-xl); }
.card > .head { padding: 11px 16px; border-bottom: 1px solid var(--border-soft); display: flex; align-items: center; justify-content: space-between; font-family: var(--f-mono); font-size: 10.5px; letter-spacing: 1.4px; text-transform: uppercase; color: var(--fg-mid); }
.app-portal .card > .head { padding: 14px 20px; font-family: var(--f-display); font-size: 16px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--fg); }
.card > .body { padding: 16px; }
.app-portal .card > .body { padding: 18px 20px; }

.card.ok   { border-color: var(--ok); }
.card.warn { border-color: var(--warn); }
.card.bad  { border-color: var(--bad); }
.card.info { border-color: var(--info); }

/* Icon chip — the square behind an addon or section glyph */
.chip { width: 32px; height: 32px; border-radius: var(--r); background: var(--bg-3); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.app-portal .chip { width: 40px; height: 40px; border-radius: var(--r-lg); }
.chip.ember { background: var(--ember-soft); }
.chip.ok    { background: var(--ok-soft); }
.chip.warn  { background: var(--warn-soft); }
.chip.info  { background: var(--info-soft); }

/* ============================================================
   BUTTONS — fixed height ladder 26 / 30 / 38 (portal: 34 / 44)
   ============================================================ */

.btn {
  height: 30px; padding: 0 14px;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border: 1px solid transparent; border-radius: var(--r);
  background: transparent; color: var(--fg-mid);
  font-family: var(--f-body); font-size: 12.5px; line-height: 1;
  cursor: pointer; text-decoration: none; white-space: nowrap;
}
.app-portal .btn { height: 34px; font-size: 13.5px; }
.btn.sm { height: 26px; padding: 0 11px; font-size: 12px; }
.btn.lg { height: 38px; padding: 0 18px; font-size: 13.5px; }
.app-portal .btn.lg { height: 44px; font-size: 15px; }

.btn.primary {
  background: linear-gradient(180deg, var(--ember), var(--ember-deep));
  color: var(--ember-ink); font-weight: 600; border-color: transparent;
  box-shadow: 0 1px 0 oklch(1 0 0 / 0.25) inset;
}
.btn.primary:hover { filter: brightness(1.06); }
.btn.ghost   { border-color: var(--border); }
.btn.ghost:hover { background: var(--bg-3); color: var(--fg); }
.btn.danger  { border-color: var(--bad); background: var(--bad-soft); color: var(--bad); }
.btn.discord { background: var(--discord); color: oklch(0.99 0 0); font-weight: 600; }
.btn.accent  { border-color: var(--ember); background: var(--ember-soft); color: var(--ember); font-weight: 600; }
.btn:disabled, .btn.is-disabled { background: var(--bg-3); color: var(--fg-dim); border-color: transparent; cursor: not-allowed; box-shadow: none; }

.icon-btn { width: 30px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--border); border-radius: var(--r); background: transparent; color: var(--fg-mid); cursor: pointer; }
.qty-btn  { width: 28px; height: 28px; padding: 0; border: 1px solid var(--border); border-radius: var(--r-sm); background: transparent; color: var(--fg-mid); cursor: pointer; }

/* ============================================================
   FORM CONTROLS
   ============================================================ */

input[type="text"], input[type="number"], input[type="search"], input[type="password"], input[type="url"],
input[type="date"], input[type="datetime-local"], input[type="time"], select, textarea {
  height: 30px; padding: 0 11px;
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--bg-input); color: var(--fg);
  font-family: var(--f-body); font-size: 12.5px;
}
.app-portal input[type="text"], .app-portal select { height: 34px; font-size: 13.5px; }
textarea { height: auto; padding: 9px 11px; line-height: 1.5; }

:where(input, select, textarea, button, a, [tabindex]):focus-visible {
  outline: none;
  border-color: var(--ember);
  box-shadow: 0 0 0 3px var(--ember-soft);
}

.field-label { display: block; font-family: var(--f-mono); font-size: 10px; letter-spacing: 1.3px; text-transform: uppercase; color: var(--fg-mute); margin-bottom: 7px; }

/* Toggle — 32x18 ember switch */
.toggle { width: 32px; height: 18px; border-radius: 999px; background: var(--bg-3); border: 1px solid var(--border); position: relative; flex-shrink: 0; cursor: pointer; padding: 0; }
.toggle::after { content: ""; position: absolute; top: 1px; left: 1px; width: 14px; height: 14px; border-radius: 999px; background: var(--fg-dim); transition: left .12s ease; }
.toggle[aria-checked="true"] { background: var(--ember); border-color: transparent; }
.toggle[aria-checked="true"]::after { left: 16px; background: oklch(0.18 0.02 55); }

/* Segmented control */
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; }
.seg > * { height: 30px; padding: 0 13px; display: flex; align-items: center; justify-content: center; border: 0; border-left: 1px solid var(--border); background: transparent; color: var(--fg-mute); font-family: var(--f-mono); font-size: 10.5px; letter-spacing: 1.2px; text-transform: uppercase; cursor: pointer; }
.seg > *:first-child { border-left: 0; }
.seg > .on { background: var(--bg-3); color: var(--fg); }
.seg > .on.accent { background: var(--ember-soft); color: var(--ember); font-weight: 600; }

/* Filter pills — the portal's version of the segmented control */
.pill { height: 32px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--border); color: var(--fg-mid); font-size: 13px; display: inline-flex; align-items: center; gap: 7px; background: transparent; cursor: pointer; font-family: inherit; }
.pill.on { background: var(--ember); border-color: transparent; color: var(--ember-ink); font-weight: 600; }

/* ============================================================
   STATUS
   ============================================================ */

.badge { display: inline-flex; align-items: center; gap: 6px; height: 22px; padding: 0 10px; border-radius: 999px; background: var(--bg-3); color: var(--fg-mute); font-family: var(--f-mono); font-size: 10px; letter-spacing: 1.2px; text-transform: uppercase; border: 1px solid transparent; }
.badge .dot { width: 6px; height: 6px; border-radius: 999px; background: currentColor; }
.badge.ok    { background: var(--ok-soft);    color: var(--ok); }
.badge.warn  { background: var(--warn-soft);  color: var(--warn); }
.badge.bad   { background: var(--bad-soft);   color: var(--bad); }
.badge.info  { background: var(--info-soft);  color: var(--info); }
.badge.ember { background: var(--ember-soft); color: var(--ember); }

.tag { display: inline-flex; align-items: center; height: 20px; padding: 0 8px; border-radius: var(--r-sm); background: var(--bg-3); color: var(--fg-mid); font-family: var(--f-mono); font-size: 9.5px; letter-spacing: 1.1px; text-transform: uppercase; }
.tag.ember { background: var(--ember-soft); color: var(--ember); }
.tag.warn  { background: var(--warn-soft);  color: var(--warn); }
.tag.ok    { background: var(--ok-soft);    color: var(--ok); }

.pulse { width: 7px; height: 7px; border-radius: 999px; background: var(--ok); box-shadow: 0 0 0 3px var(--ok-soft); flex-shrink: 0; }

/* Stat tile */
.stat { background: var(--bg-2); border: 1px solid var(--border-soft); border-radius: var(--r-lg); padding: 13px 14px; box-shadow: var(--shadow-lift); }
.stat .label { font-family: var(--f-mono); font-size: 9.5px; letter-spacing: 1.3px; text-transform: uppercase; color: var(--fg-mute); }
.stat .value { font-family: var(--f-display); font-size: 26px; font-weight: 500; font-variant-numeric: tabular-nums; margin-top: 3px; }
.app-portal .stat .value { font-size: 28px; font-weight: 600; }
.stat .value .of { font-size: 15px; color: var(--fg-dim); }
.stat .delta { font-size: 11px; color: var(--fg-mute); }
.stat .delta.up   { color: var(--ok); }
.stat .delta.down { color: var(--bad); }

/* Progress bar */
.bar { height: 6px; border-radius: 999px; background: var(--bg-input); overflow: hidden; }
.bar > span { display: block; height: 100%; background: var(--ember); }
.bar.warn > span { background: var(--warn); }
.bar.bad  > span { background: var(--bad); }

/* ============================================================
   TABLE
   ============================================================ */

.tbl { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.tbl th {
  text-align: left; padding: 8px 16px; background: var(--bg-1);
  font-family: var(--f-mono); font-size: 9.5px; font-weight: 500;
  letter-spacing: 1.2px; text-transform: uppercase; color: var(--fg-mute);
}
.tbl td { padding: 10px 16px; border-bottom: 1px solid var(--border-soft); }
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover { background: var(--bg-3); }
.tbl .num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl .mono { font-family: var(--f-mono); font-size: 11.5px; color: var(--fg-mid); }

/* ============================================================
   FEEDBACK
   ============================================================ */

.banner { border: 1px solid var(--border); border-radius: var(--r); padding: 11px 13px; font-size: 12.5px; color: var(--fg-mid); }
.banner.bad   { border-color: var(--bad);   background: var(--bad-soft);   color: var(--bad); }
.banner.warn  { border-color: var(--warn);  background: var(--warn-soft);  color: var(--warn); }
.banner.info  { border-color: var(--info);  color: var(--fg-mid); }
.banner.ember { border-color: var(--ember); background: var(--ember-soft); }

.empty { border: 1px dashed var(--border); border-radius: var(--r); padding: 20px 14px; text-align: center; color: var(--fg-mid); font-size: 12.5px; }
.empty .hint { color: var(--fg-dim); font-size: 11.5px; margin-top: 3px; }

.modal-shell { position: fixed; inset: 0; background: oklch(0 0 0 / 0.5); display: grid; place-items: center; z-index: 100; }
/* The height limit belongs here rather than on each dialog: the shell centres
   its card, so one taller than the window loses its heading and its buttons at
   the same time, and .modal-shell is position:fixed with nothing to scroll.
   dvh rather than vh so a phone's address bar does not take the buttons with
   it; the vh line above is the fallback for anything that lacks dvh. */
.modal-card  { background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 22px; box-shadow: var(--shadow-lift);
               max-height: calc(100vh - 48px); max-height: calc(100dvh - 48px); overflow-y: auto; }

/* ============================================================
   MAP — see mockups/11-portal-live-map.html
   The plane is a square div; markers are positioned with
   left: lon%, top: lat% (GPS space is 0-100 on both axes).
   Pan/zoom is transform: translate(x,y) scale(z) on the plane.
   Coordinate maths comes from ShadowLiveMaps/app/shared/projection.ts —
   port that file and its tests unchanged.
   ============================================================ */

.map-plane { position: relative; border-radius: var(--r); overflow: hidden; }
.map-grid  { position: absolute; inset: 0; background-image: linear-gradient(oklch(1 0 0 / 0.045) 1px, transparent 1px), linear-gradient(90deg, oklch(1 0 0 / 0.045) 1px, transparent 1px); }
.marker    { position: absolute; transform: translate(-50%, -50%); }
.marker.drop   { width: 9px;  height: 9px;  border-radius: 2px; }
.marker.player { width: 11px; height: 11px; border-radius: 999px; border: 2px solid oklch(0.18 0.02 55); background: var(--ok); }
.marker.player.me { width: 14px; height: 14px; background: var(--ember); box-shadow: 0 0 0 6px var(--ember-soft); }
.marker.node   { width: 5px;  height: 5px;  border-radius: 999px; background: oklch(0.66 0.008 60 / 0.65); transform: none; }
.marker.recovered { border-style: dashed; }

/* Beacon tier colours — deliberately NOT themeable, they mirror the game. */
.drop.white  { background: oklch(0.92 0.01 60); }
.drop.green  { background: oklch(0.76 0.15 150); }
.drop.blue   { background: oklch(0.70 0.14 245); }
.drop.purple { background: oklch(0.62 0.18 300); }
.drop.yellow { background: oklch(0.82 0.14 85); }
.drop.red    { background: oklch(0.72 0.19 25); }
.vein  { background: oklch(0.80 0.14 195); box-shadow: 0 0 10px 2px oklch(0.80 0.14 195 / 0.5); }
.osd   { background: oklch(0.72 0.17 45);  box-shadow: 0 0 12px 3px oklch(0.72 0.17 45 / 0.45); }
.boss  { background: oklch(0.66 0.20 330); }

/* ============================================================
   ATMOSPHERE — optional, matches the manager's body::before
   ============================================================ */

.atmosphere::before {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background:
    radial-gradient(900px 600px at 94% 6%,  oklch(0.74 0.16 55 / 0.06), transparent 70%),
    radial-gradient(800px 600px at 4% 94%,  oklch(0.62 0.18 275 / 0.04), transparent 70%);
}

@keyframes pulseDot { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
@keyframes fadeIn   { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.fade-in { animation: fadeIn 240ms ease both; }

/* ============================================================
   NOTES
   - Touch targets are >= 44px on the portal, >= 30px on admin
     (mouse-only console, same as the manager).
   - Text contrast: --fg-mid and up on --bg-2 clears 4.5:1.
     --fg-dim is for >= 24px or decorative use only.
   - No responsive breakpoints. Admin is desktop-only by design;
     the portal is public, so it WILL need them — see README.
   ============================================================ */

/* ------------------------------------------------------------------ *
 * Player pages layout (home, tickets, suggestions, calendar, purchases)
 * ps-* classes are used by the player side only.
 * ------------------------------------------------------------------ */

/* One content width for every player page. */
.ps-page { max-width: 1280px; margin: 0 auto; display: grid; gap: 18px; }

/* Cards side by side; they wrap to one column when there isn't room. */
.ps-cols { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); align-items: stretch; }
.ps-cols.wide { grid-template-columns: repeat(auto-fit, minmax(460px, 1fr)); }

/* Main column + side column (detail pages, calendar). */
.ps-split { display: grid; gap: 16px; grid-template-columns: minmax(0, 1.65fr) minmax(300px, 1fr); align-items: start; }
.ps-split > .ps-side { display: grid; gap: 16px; position: sticky; top: 0; }

/* Navigation in the top bar. */
.ps-topnav { flex: 1; min-width: 0; display: flex; align-items: center; gap: 4px; overflow-x: auto; scrollbar-width: none; }
.ps-topnav::-webkit-scrollbar { display: none; }
.ps-navlink { display: inline-flex; align-items: center; gap: 7px; height: 34px; padding: 0 12px; border-radius: var(--r); text-decoration: none; font-size: 13.5px; font-weight: 500; white-space: nowrap; color: var(--fg-mid); border: 1px solid transparent; }
.ps-navlink:hover { color: var(--fg); background: var(--bg-2); }
.ps-navlink.active { color: var(--fg); background: var(--bg-3); border-color: var(--border); }
.ps-navlink.active svg { color: var(--ember); }

@media (max-width: 960px) {
	.ps-split { grid-template-columns: minmax(0, 1fr); }
	.ps-split > .ps-side { position: static; }
	/* A side column marked .lead goes above the main column when stacked. */
	.ps-split > .ps-side.lead { order: -1; }
	.ps-cols.wide { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
	.app-portal .main { padding: 18px 14px 48px; }
	.ps-cols { grid-template-columns: minmax(0, 1fr); }
	.ps-topnav .ps-navlabel { display: none; }
}

/* ============================================================
   LIVE MAPS
   Ported from the standalone app's client/app.css. Every selector is
   lm- prefixed: that stylesheet owned its whole document, and this one
   shares one with the dashboard shell.
   ============================================================ */

.lm-live {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 12px;
  height: calc(100vh - 210px);
  min-height: 520px;
}

.lm-stage { position: relative; min-width: 0; }

/* The viewport the plane is dragged around inside. overflow:hidden is what
   makes a pan look like a pan rather than a page that grew. */
.lm-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--bg-0);
  touch-action: none;
}

.lm-plane { position: absolute; left: 0; top: 0; will-change: transform; }

.lm-base {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  pointer-events: none;
  user-select: none;
}

/* Contour lines multiply over the terrain so the relief reads through. */
.lm-contour { opacity: 0.5; mix-blend-mode: multiply; }

.lm-canvas { position: absolute; left: 0; top: 0; pointer-events: none; }

.lm-grid { pointer-events: none; }

/* --- pins ------------------------------------------------------------- */

.lm-pin {
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  text-shadow: 0 0 3px rgba(0, 0, 0, 0.9);
  user-select: none;
}
.lm-pin:hover { filter: brightness(1.3); }
.lm-explorer { font-size: 9px; }
.lm-note { color: var(--fg); font-size: 13px; }

.lm-marker { cursor: pointer; }
.lm-marker-dot {
  display: block;
  width: 11px;
  height: 11px;
  border-radius: 999px;
  border: 1px solid rgba(0, 0, 0, 0.75);
  box-shadow: 0 0 4px rgba(0, 0, 0, 0.7);
}
.lm-marker.is-selected .lm-marker-dot {
  outline: 2px solid var(--ember);
  outline-offset: 2px;
}

.lm-cluster {
  min-width: 22px;
  height: 22px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--bg-3);
  border: 1px solid var(--border);
  color: var(--fg);
  font-size: 11px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.lm-player { display: flex; flex-direction: column; align-items: center; cursor: pointer; }
.lm-player-dot {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  border: 1px solid rgba(0, 0, 0, 0.8);
}
.lm-player-name {
  font-size: 9px;
  color: var(--fg);
  text-shadow: 0 0 3px rgba(0, 0, 0, 0.95);
  white-space: nowrap;
}

/* A census ring is a whole base, so it can be large — but it is held to a
   minimum on-screen size in JS so a small outpost stays clickable. */
.lm-census-area {
  border-style: solid;
  border-radius: var(--r);
  background: rgba(255, 255, 255, 0.04);
  cursor: pointer;
}
.lm-census-area:hover { background: rgba(255, 255, 255, 0.1); }

/* A rare sighting highlights the grid square it was seen in, not a point —
   the plugin reports the square on purpose, so the map should not imply more
   precision than it has. */
.lm-sighting-cell {
  position: absolute;
  border-style: dashed;
  border-radius: 4px;
  padding: 0;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: filter 120ms ease;
}
.lm-sighting-cell:hover { filter: brightness(1.35); }

.lm-sighting-count {
  font-size: 11px;
  font-weight: 800;
  color: #fff;
  text-shadow: 0 1px 2px rgb(0 0 0 / 0.9);
  /* The fill is translucent; the badge stays readable regardless. */
  opacity: 1;
}

/* --- controls --------------------------------------------------------- */

.lm-zoom {
  position: absolute;
  right: 12px;
  bottom: 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.lm-zoom button {
  width: 30px;
  height: 30px;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--bg-2);
  color: var(--fg-mid);
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
}
.lm-zoom button:hover { background: var(--bg-3); color: var(--fg); }

.lm-cursor-gps {
  position: absolute;
  left: 12px;
  bottom: 12px;
  padding: 3px 7px;
  border-radius: var(--r);
  background: var(--bg-2);
  border: 1px solid var(--border-soft);
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--fg-mid);
  pointer-events: none;
}

.lm-tooltip {
  position: fixed;
  z-index: 40;
  padding: 4px 8px;
  border-radius: var(--r);
  background: var(--bg-2);
  border: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--fg);
  pointer-events: none;
}

.lm-selected {
  position: absolute;
  left: 12px;
  top: 12px;
  max-width: 280px;
  padding: 10px 12px;
  border-radius: var(--r-lg);
  background: var(--bg-1);
  border: 1px solid var(--border);
  display: grid;
  gap: 4px;
  font-size: 12.5px;
}
.lm-selected-head { display: flex; align-items: center; gap: 8px; }
.lm-selected-head strong { flex-grow: 1; }

.lm-missing { padding: 24px; }

/* --- the legend ------------------------------------------------------- */

.lm-sidebar {
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-y: auto;
  padding-right: 4px;
}

.lm-sidebar-head { display: grid; gap: 6px; }
.lm-map-name { font-family: var(--f-display); font-size: 14px; font-weight: 600; }
.lm-levels, .lm-tools { display: flex; gap: 4px; flex-wrap: wrap; }

.lm-filter { width: 100%; }

.lm-section { border: 1px solid var(--border-soft); border-radius: var(--r); background: var(--bg-1); }
.lm-section-head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 9px;
  background: transparent;
  border: 0;
  color: var(--fg-mid);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  text-align: left;
}
.lm-section-title { flex-grow: 1; font-weight: 600; }
.lm-caret { display: inline-block; transition: transform 0.12s ease; color: var(--fg-dim); }
.lm-caret.is-open { transform: rotate(90deg); }
.lm-section-body { padding: 0 9px 8px; display: grid; gap: 3px; }

.lm-row { display: flex; align-items: center; gap: 6px; }
.lm-row-main { display: flex; align-items: center; gap: 6px; flex-grow: 1; min-width: 0; cursor: pointer; }
.lm-row-label {
  font-size: 12px;
  color: var(--fg-mid);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lm-row-icon { font-size: 12px; width: 13px; text-align: center; }
.lm-swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; border: 1px solid rgba(0, 0, 0, 0.5); }
.lm-count { font-family: var(--f-mono); font-size: 10.5px; color: var(--fg-dim); }
.lm-muted { color: var(--fg-dim); }
.lm-empty { font-size: 12px; color: var(--fg-dim); padding: 2px 0; }

.lm-group { margin-top: 4px; }
.lm-group-head { display: flex; align-items: center; gap: 6px; }
.lm-group-head button {
  flex-grow: 1;
  text-align: left;
  background: transparent;
  border: 0;
  color: var(--fg-mute);
  font: inherit;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
}

.lm-bulk { display: flex; gap: 6px; margin-bottom: 4px; }
.lm-bulk button {
  background: transparent;
  border: 0;
  color: var(--fg-dim);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}

.lm-res-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 3px 8px; }
.lm-res { display: flex; align-items: center; gap: 5px; cursor: pointer; min-width: 0; }

@media (max-width: 1100px) {
  .lm-live { grid-template-columns: minmax(0, 1fr); height: auto; }
  .lm-stage { height: 60vh; }
  .lm-sidebar { max-height: 50vh; }
}

/* The "click where it belongs" bar, over the map while a place is being moved.
   Above the map's own overlays so a pin cannot hide the way out of the mode. */
.lm-moving {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: calc(100% - 24px);
  padding: 7px 8px 7px 14px;
  font-size: 13px;
  color: var(--fg);
  background: var(--bg-1);
  border: 1px solid var(--ember);
  border-radius: var(--r-lg);
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.45);
}

.lm-moving-bad { border-color: var(--bad); top: 58px; }

/* FilterCombo: the field holds no value and shows the current selection as
   its placeholder, so that placeholder must read as chosen text, not as an
   empty field. Only applies while closed and something is selected. */
.combo-has-value::placeholder {
  color: var(--fg);
  opacity: 1;
}
