/* ═══════════════════════════════════════════════════════════════
   RY TRACKER — design system

   The product has one job: say the instant a tracked player walks
   into the game. Everything here serves that.

   Palette      four colours, no more:
                  #FFF2F2  the page        (light)
                  #FFDADA  raised surfaces, borders, chips
                  #FF788D  the brand, and every primary action
                  #165823  text, and "in the game right now"
                Dark mode keeps the same four roles: rose stays the
                brand, and the forest green is lifted to a mint that
                survives on a dark ground while reading as the same
                colour. Presence and body text share it in both.
   Theming      light is the default and lives on bare :root, so a
                viewer with no JavaScript still gets it. Dark is an
                override on [data-theme="dark"], stamped before first
                paint by theme.js.
   Type         Space Grotesk (display) · Inter (UI) · JetBrains
                Mono (IDs, counts, timestamps — this is a data tool)
   Signature    the glitch action button, and the presence pulse
   Responsive   sidebar becomes a thumb-reachable bottom tab bar,
                tables become stacked cards
   ═══════════════════════════════════════════════════════════════ */

:root {
  /* ── The four ── */
  --cream: #fff2f2;
  --blush: #ffdada;
  --rose: #ff788d;
  --forest: #165823;

  /* ── Ground and surfaces ── */
  --bg-primary: var(--cream);
  --bg-secondary: #fff8f8;
  --bg-card: #ffffff;
  --bg-card-hover: #fff8f8;
  --bg-input: #fffafa;
  --bg-elevated: #ffffff;

  --border: var(--blush);
  --border-hover: #ffb9b9;
  --border-focus: var(--rose);

  /* Text is the deep green, thinned down the hierarchy rather than
     greyed out — a grey would drop the palette on the floor. */
  --text-primary: var(--forest);
  --text-secondary: #4e7256;
  --text-muted: #8fa694;

  /* ── Brand ── */
  --accent: var(--rose);
  --accent-hover: #ff5f79;
  --accent-soft: rgba(255, 120, 141, 0.14);
  --accent-glow: rgba(255, 120, 141, 0.4);

  /* ── Live presence ── */
  --signal: var(--forest);
  --signal-soft: rgba(22, 88, 35, 0.09);
  --signal-line: rgba(22, 88, 35, 0.32);

  --success: var(--forest);
  --success-soft: rgba(22, 88, 35, 0.09);
  --danger: #d62b4e;
  --danger-soft: rgba(214, 43, 78, 0.1);
  --warning: #b26a00;
  --warning-soft: rgba(178, 106, 0, 0.11);
  --info: var(--rose);

  --radius: 16px;
  --radius-sm: 10px;
  --radius-lg: 22px;
  --sidebar-w: 244px;

  --shadow-sm: 0 2px 10px rgba(153, 60, 74, 0.07);
  --shadow: 0 10px 30px rgba(153, 60, 74, 0.11);
  --shadow-lg: 0 26px 60px rgba(153, 60, 74, 0.18);

  --font-display: 'Space Grotesk', 'Inter', system-ui, sans-serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;

  --ease: cubic-bezier(0.22, 0.85, 0.3, 1);

  --glass: rgba(255, 255, 255, 0.86);
  --glass-bar: rgba(255, 242, 242, 0.86);
  --scrim: rgba(88, 30, 40, 0.42);
  --on-rose: #ffffff;

  --danger-ink: #a81f3c;
  --warning-ink: #8c5400;
  --accent-ink: #b03a52;
  --link: #c53a55;
  --rule-bg: #ffffff;

  --bloom-1: rgba(255, 120, 141, 0.2);
  --bloom-2: rgba(255, 218, 218, 0.75);
}

:root[data-theme="dark"] {
  /* ── The same four, carried into the dark ── */
  --cream: #12090c;
  --blush: #2a1820;
  --rose: #ff788d;
  --forest: #4ade80;

  /* ── Ground and surfaces ── */
  --bg-primary: #0e0709;
  --bg-secondary: #170e12;
  --bg-card: #1a1015;
  --bg-card-hover: #23161d;
  --bg-input: #150c10;
  --bg-elevated: #21151b;

  --border: #33202a;
  --border-hover: #4d2f3c;
  --border-focus: var(--rose);

  --text-primary: #f7e9ec;
  --text-secondary: #c2a3ac;
  --text-muted: #8d7079;

  /* ── Brand ── */
  --accent: var(--rose);
  --accent-hover: #ff96a7;
  --accent-soft: rgba(255, 120, 141, 0.16);
  --accent-glow: rgba(255, 120, 141, 0.45);

  /* ── Live presence ──
     #165823 is unreadable on this ground, so the same role is filled
     by a mint that keeps the green meaning. */
  --signal: var(--forest);
  --signal-soft: rgba(74, 222, 128, 0.13);
  --signal-line: rgba(74, 222, 128, 0.4);

  --success: var(--forest);
  --success-soft: rgba(74, 222, 128, 0.13);
  --danger: #ff5f7e;
  --danger-soft: rgba(255, 95, 126, 0.14);
  --warning: #ffb347;
  --warning-soft: rgba(255, 179, 71, 0.14);
  --info: var(--rose);

  --radius: 16px;
  --radius-sm: 10px;
  --radius-lg: 22px;
  --sidebar-w: 244px;

  --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.4);
  --shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 26px 60px rgba(0, 0, 0, 0.66);

  --font-display: 'Space Grotesk', 'Inter', system-ui, sans-serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;

  --ease: cubic-bezier(0.22, 0.85, 0.3, 1);

  /* Surfaces that are translucent in light mode need opaque partners
     here, or the blur turns them to mud. */
  --glass: rgba(23, 14, 18, 0.88);
  --glass-bar: rgba(14, 7, 9, 0.86);
  --scrim: rgba(0, 0, 0, 0.66);
  --on-rose: #2a0d14;

  /* Ink for text sitting on its own tinted background. */
  --danger-ink: #ff9caf;
  --warning-ink: #ffc978;
  --accent-ink: #ffa8b8;
  --link: #ff96a7;
  --rule-bg: #1d1016;

  --bloom-1: rgba(255, 120, 141, 0.14);
  --bloom-2: rgba(74, 222, 128, 0.05);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  font-family: var(--font-body);
  background: var(--bg-primary);
  color: var(--text-primary);
  min-height: 100vh;
  min-height: 100dvh;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* Two soft blooms, fixed so they never travel with the scroll. */
body::before {
  content: '';
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(880px 500px at 8% -8%, var(--bloom-1), transparent 62%),
    radial-gradient(720px 440px at 96% 2%, var(--bloom-2), transparent 60%);
}

a { color: var(--link); text-decoration: none; transition: color 0.16s; }
a:hover { color: var(--accent-hover); text-decoration: underline; }

::selection { background: var(--blush); color: var(--forest); }

:focus-visible {
  outline: 2px solid var(--forest);
  outline-offset: 2px;
  border-radius: 4px;
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--blush); border-radius: 6px; border: 2px solid var(--bg-primary); }
::-webkit-scrollbar-thumb:hover { background: var(--border-hover); }

/* ═══ MOTION ═════════════════════════════════════════════════ */

@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes slideIn { from { opacity: 0; transform: translateX(24px); } to { opacity: 1; transform: none; } }
@keyframes spin { to { transform: rotate(360deg); } }

/* The presence pulse: rings out of a live avatar, so an online player
   is visible without reading a word. */
@keyframes ping {
  0%   { transform: scale(0.9); opacity: 0.5; }
  70%  { transform: scale(1.42); opacity: 0; }
  100% { transform: scale(1.42); opacity: 0; }
}
@keyframes breathe { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }

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

/* ═══ AUTH SCREENS ═══════════════════════════════════════════ */

.auth-shell {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px;
  position: relative; z-index: 1;
}

.auth-card {
  width: 100%;
  max-width: 430px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 42px 34px 36px;
  box-shadow: var(--shadow-lg);
  animation: rise 0.45s var(--ease);
  position: relative;
  overflow: hidden;
}
.auth-card::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--rose), var(--forest));
}

.auth-logo {
  width: 76px; height: 76px;
  border-radius: 24px;
  margin: 0 auto 20px;
  display: grid; place-items: center;
  font-size: 33px;
  background: var(--blush);
  border: 2px solid var(--bg-card);
  box-shadow: 0 8px 24px rgba(255, 120, 141, 0.34);
  overflow: hidden;
}
.auth-logo img { width: 100%; height: 100%; object-fit: cover; }

.auth-card h1 {
  font-family: var(--font-display);
  font-size: 25px; font-weight: 700; text-align: center;
  letter-spacing: -0.6px; margin-bottom: 6px;
}
.auth-card .sub {
  text-align: center; color: var(--text-secondary);
  font-size: 14px; margin-bottom: 28px;
}

.auth-badge {
  display: block; text-align: center; margin-bottom: 20px;
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 600; letter-spacing: 2px;
  text-transform: uppercase; color: var(--accent);
}

/* ═══ FORMS ══════════════════════════════════════════════════ */

.field { margin-bottom: 17px; }

.field label {
  display: block; margin-bottom: 8px;
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 1.1px;
}

input, select, textarea {
  width: 100%;
  padding: 12px 14px;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: inherit;
  font-size: 15px;
  transition: border-color 0.18s, box-shadow 0.18s, background 0.18s;
}
input::placeholder, textarea::placeholder { color: var(--text-muted); }
input:hover, select:hover, textarea:hover { border-color: var(--border-hover); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--border-focus);
  background: var(--bg-card);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
textarea { resize: vertical; min-height: 84px; line-height: 1.6; }
select { cursor: pointer; }

.input-wrap { position: relative; }
.input-wrap .peek {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  background: none; border: none; cursor: pointer;
  font-size: 15px; opacity: 0.5; padding: 5px 7px; line-height: 1;
  border-radius: 6px; transition: opacity 0.16s;
}
.input-wrap .peek:hover { opacity: 1; }
.input-wrap input { padding-right: 42px; }

.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 13px; }

.hint { font-size: 12.5px; color: var(--text-muted); margin-top: 7px; line-height: 1.6; }

/* ═══ BRAINROT SEARCH ════════════════════════════════════════

   Sits under the brainrot dropdown, opened by its last entry. The
   five with artwork are one click away in the dropdown itself; this
   is how the other five hundred names get found. Anything without a
   picture shows the framed question mark instead, so every row keeps
   the same rhythm.
   ═══════════════════════════════════════════════════════════════ */

.brainrot-search {
  margin-top: 10px; padding: 12px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-hover);
  border-radius: var(--radius-sm);
}

.br-bar { display: flex; align-items: center; gap: 9px; }
.br-bar > .icon { color: var(--text-muted); }
.br-bar .br-query { flex: 1; min-width: 0; padding: 9px 11px; font-size: 14px; }
.br-bar .br-cancel { flex: none; }

.br-results {
  max-height: 236px; overflow-y: auto;
  margin-top: 10px;
  display: flex; flex-direction: column; gap: 4px;
}

.br-item {
  display: flex; align-items: center; gap: 11px;
  width: 100%; padding: 7px 9px;
  background: none; border: 1px solid transparent;
  border-radius: 9px;
  color: var(--text-primary);
  font-family: inherit; font-size: 14px; text-align: left;
  cursor: pointer;
  transition: background 0.14s, border-color 0.14s;
}
/* Hover and keyboard land on the same look — the arrow keys move a
   selection, they do not conjure a second kind of highlight. */
.br-item:hover, .br-item.active {
  background: var(--bg-card);
  border-color: var(--border-hover);
}
.br-item .br-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.br-item mark {
  background: var(--accent-soft); color: var(--accent-ink);
  border-radius: 4px; padding: 0 1px; font-weight: 600;
}

.br-art {
  width: 30px; height: 30px; flex: none;
  object-fit: contain; border-radius: 8px;
}
.br-art-none {
  display: grid; place-items: center;
  background: var(--bg-input); border: 1px solid var(--border);
  color: var(--text-muted);
}

.br-empty { padding: 14px 4px; font-size: 13px; color: var(--text-muted); }
.br-note { margin-top: 9px; font-size: 11.5px; color: var(--text-muted); }

/* The same stand-in, at the size the tracker card's artwork uses. */
.art-none {
  width: 32px; height: 32px; flex-shrink: 0;
  display: grid; place-items: center;
  background: var(--bg-input); border: 1px solid var(--border);
  border-radius: 8px; color: var(--text-muted);
}

/* ═══ BUTTONS ════════════════════════════════════════════════

   Everyday buttons stay quiet. The primary action is the one loud
   thing on any screen: a hard-edged block with an offset shadow that
   tears itself apart on hover. Boldness is spent here and nowhere
   else, which is what lets it land.
   ═══════════════════════════════════════════════════════════════ */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 11px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-family: inherit; font-size: 14px; font-weight: 600;
  cursor: pointer;
  transition: transform 0.14s var(--ease), background 0.18s, border-color 0.18s,
              box-shadow 0.18s, opacity 0.18s;
  white-space: nowrap;
  min-height: 40px;   /* never smaller than a comfortable thumb target */
}
.btn:hover { background: var(--blush); border-color: var(--border-hover); transform: translateY(-1px); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }

.btn-danger { background: var(--danger-soft); border-color: rgba(214, 43, 78, 0.32); color: var(--danger); }
.btn-danger:hover { background: rgba(214, 43, 78, 0.18); border-color: var(--danger); }

.btn-success { background: var(--success-soft); border-color: var(--signal-line); color: var(--forest); }
.btn-success:hover { background: rgba(22, 88, 35, 0.16); border-color: var(--forest); }

.btn-ghost { background: transparent; border-color: transparent; color: var(--text-secondary); }
.btn-ghost:hover { background: var(--blush); color: var(--text-primary); }

.btn-block { width: 100%; }
.btn-sm { padding: 7px 12px; font-size: 12.5px; border-radius: 8px; min-height: 32px; }
.btn-icon { padding: 8px 10px; }

a.btn, a.btn:hover { text-decoration: none; color: var(--text-primary); }

/* ── The glitch action ── */

.btn-primary,
.btn-primary::after {
  padding: 12px 20px;
  font-size: 15px;
  font-weight: 700;
  background: linear-gradient(45deg, transparent 5%, var(--rose) 5%);
  border: 0;
  border-radius: 0;
  color: var(--on-rose);
  letter-spacing: 1.6px;
  line-height: 1.2;
  box-shadow: 5px 0 0 var(--forest);
  outline: transparent;
  position: relative;
}
.btn-primary { transform: none; }
.btn-primary:hover {
  background: linear-gradient(45deg, transparent 5%, var(--rose) 5%);
  transform: none;
  box-shadow: 5px 0 0 var(--forest);
}
.btn-primary:active { transform: translateY(1px); }
a.btn-primary, a.btn-primary:hover { color: var(--on-rose); }

/* The overlay carries a copy of the label, set from the button's own
   text by common.js. It is fully clipped until hover. */
.btn-primary::after {
  --slice-0: inset(50% 50% 50% 50%);
  --slice-1: inset(80% -6px 0 0);
  --slice-2: inset(50% -6px 30% 0);
  --slice-3: inset(10% -6px 85% 0);
  --slice-4: inset(40% -6px 43% 0);
  --slice-5: inset(80% -6px 5% 0);
  content: attr(data-text);
  display: grid;
  place-items: center;
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  background: linear-gradient(45deg, transparent 3%, var(--forest) 3%, var(--forest) 5%, var(--rose) 5%);
  text-shadow: -3px -3px 0 var(--blush), 3px 3px 0 var(--forest);
  clip-path: var(--slice-0);
}
.btn-primary:hover::after {
  animation: 1s glitch;
  animation-timing-function: steps(2, end);
}

/* Nothing to copy the label from: no overlay, rather than a blank one. */
.btn-primary:not([data-text])::after { content: none; }

@keyframes glitch {
  0%   { clip-path: var(--slice-1); transform: translate(-20px, -10px); }
  10%  { clip-path: var(--slice-3); transform: translate(10px, 10px); }
  20%  { clip-path: var(--slice-1); transform: translate(-10px, 10px); }
  30%  { clip-path: var(--slice-3); transform: translate(0px, 5px); }
  40%  { clip-path: var(--slice-2); transform: translate(-5px, 0px); }
  50%  { clip-path: var(--slice-3); transform: translate(5px, 0px); }
  60%  { clip-path: var(--slice-4); transform: translate(5px, 10px); }
  70%  { clip-path: var(--slice-2); transform: translate(-10px, 10px); }
  80%  { clip-path: var(--slice-5); transform: translate(20px, -10px); }
  90%  { clip-path: var(--slice-1); transform: translate(-10px, 0px); }
  100% { clip-path: var(--slice-1); transform: translate(0); }
}

@media (prefers-reduced-motion: reduce) {
  .btn-primary:hover::after { animation: none; clip-path: var(--slice-0); }
}

.btn-primary.btn-sm, .btn-primary.btn-sm::after {
  padding: 8px 13px; font-size: 12.5px; letter-spacing: 1px;
  box-shadow: 4px 0 0 var(--forest);
}

/* ═══ MESSAGES ═══════════════════════════════════════════════ */

.msg {
  padding: 12px 15px; border-radius: var(--radius-sm);
  font-size: 13.5px; margin-bottom: 16px; line-height: 1.6;
  border: 1px solid transparent; display: none;
}
.msg.show { display: block; animation: rise 0.28s var(--ease); }
.msg.error { background: var(--danger-soft); border-color: rgba(214, 43, 78, 0.3); color: var(--danger-ink); }
.msg.success { background: var(--success-soft); border-color: var(--signal-line); color: var(--forest); }
.msg.warn { background: var(--warning-soft); border-color: rgba(178, 106, 0, 0.3); color: var(--warning-ink); }
.msg.info { background: var(--accent-soft); border-color: rgba(255, 120, 141, 0.35); color: var(--accent-ink); }

#toasts {
  position: fixed; right: 20px; bottom: 20px; z-index: 900;
  display: flex; flex-direction: column; gap: 10px; max-width: 380px;
}
.toast {
  padding: 13px 17px; border-radius: var(--radius-sm);
  background: var(--bg-card); border: 1px solid var(--border);
  box-shadow: var(--shadow); font-size: 13.5px; font-weight: 500;
  animation: slideIn 0.28s var(--ease);
}
.toast.success { border-color: var(--signal-line); color: var(--forest); }
.toast.error { border-color: rgba(214, 43, 78, 0.4); color: var(--danger-ink); }
.toast.warn { border-color: rgba(178, 106, 0, 0.4); color: var(--warning-ink); }

/* ═══ APP SHELL ══════════════════════════════════════════════ */

.app { display: flex; min-height: 100vh; min-height: 100dvh; position: relative; z-index: 1; }

.sidebar {
  width: var(--sidebar-w);
  background: var(--glass);
  backdrop-filter: blur(18px);
  border-right: 1px solid var(--border);
  position: fixed; inset: 0 auto 0 0; z-index: 100;
  display: flex; flex-direction: column;
}

.sidebar-brand {
  padding: 20px 18px; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 12px;
}
.brand-mark {
  width: 42px; height: 42px; border-radius: 13px; flex-shrink: 0;
  display: grid; place-items: center; font-size: 20px;
  background: var(--blush);
  box-shadow: 0 5px 14px rgba(255, 120, 141, 0.32);
  overflow: hidden;
}
.brand-mark img { width: 100%; height: 100%; object-fit: cover; }
.brand-text { min-width: 0; }
.brand-text strong {
  display: block; font-family: var(--font-display);
  font-size: 14.5px; font-weight: 700;
  letter-spacing: -0.3px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.brand-text span {
  font-family: var(--font-mono); font-size: 10.5px;
  color: var(--text-muted); letter-spacing: 0.3px;
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.sidebar-nav { flex: 1; padding: 14px 12px; display: flex; flex-direction: column; gap: 3px; overflow-y: auto; }

.nav-btn {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 13px; width: 100%;
  border: none; background: transparent;
  color: var(--text-secondary);
  font-family: inherit; font-size: 14px; font-weight: 500;
  border-radius: var(--radius-sm); cursor: pointer;
  transition: background 0.18s, color 0.18s;
  position: relative; text-align: left;
}
.nav-btn:hover { background: var(--blush); color: var(--text-primary); }
.nav-btn.active { background: var(--blush); color: var(--forest); font-weight: 700; }
.nav-btn.active::before {
  content: ''; position: absolute; left: 0; top: 9px; bottom: 9px;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--rose);
}
.nav-icon { font-size: 16px; width: 20px; text-align: center; flex-shrink: 0; }
.nav-count {
  margin-left: auto; font-family: var(--font-mono);
  font-size: 11px; font-weight: 600;
  background: var(--bg-card); border: 1px solid var(--border);
  padding: 1px 8px; border-radius: 20px; color: var(--text-secondary);
}
.nav-count.alert { background: var(--rose); border-color: var(--rose); color: var(--on-rose); }

.sidebar-foot {
  padding: 15px 16px; border-top: 1px solid var(--border);
  font-size: 12px; display: flex; flex-direction: column; gap: 7px;
}

.sys-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-right: 8px; background: var(--text-muted); flex-shrink: 0;
}
.sys-dot.up { background: var(--forest); box-shadow: 0 0 0 3px var(--signal-soft); animation: breathe 2.6s infinite; }
.sys-dot.down { background: var(--danger); box-shadow: 0 0 0 3px var(--danger-soft); }

.main { flex: 1; margin-left: var(--sidebar-w); min-width: 0; display: flex; flex-direction: column; }

.topbar {
  display: flex; align-items: center; gap: 14px;
  padding: 18px 30px; border-bottom: 1px solid var(--border);
  background: var(--glass-bar); backdrop-filter: blur(16px);
  position: sticky; top: 0; z-index: 50;
}
.topbar h1 {
  font-family: var(--font-display);
  font-size: 21px; font-weight: 700; letter-spacing: -0.5px;
}
.topbar .spacer { flex: 1; }

.user-chip {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 14px 5px 5px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 30px; font-size: 13px; font-weight: 600;
}
.user-chip img { width: 27px; height: 27px; border-radius: 50%; object-fit: cover; }

.content { padding: 26px 30px 70px; flex: 1; }

.panel { display: none; }
.panel.active { display: block; animation: rise 0.32s var(--ease); }

/* ═══ STATS ══════════════════════════════════════════════════ */

.stat-grid { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 26px; }
/* Flex, not grid: with six stats over a five-column grid the sixth was
   stranded in a fifth of a row. Wrapping flex items stretch to fill
   whatever row they land on, so any count reads as deliberate. */
.stat-grid > .stat { flex: 1 1 172px; min-width: 0; }

.stat {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius); padding: 18px 20px;
  transition: border-color 0.2s, transform 0.2s var(--ease), box-shadow 0.2s;
  position: relative; overflow: hidden;
}
.stat::after {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--blush);
}
.stat.accent::after { background: var(--rose); }
.stat.money::after { background: var(--forest); }
.stat.warn::after { background: var(--warning); }
.stat.danger::after { background: var(--danger); }

.stat:hover { border-color: var(--border-hover); transform: translateY(-3px); box-shadow: var(--shadow); }
.stat .label {
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 600; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: 9px;
}
.stat .value {
  font-family: var(--font-display);
  font-size: 31px; font-weight: 700;
  letter-spacing: -1.2px; line-height: 1.05;
  font-variant-numeric: tabular-nums;
}
.stat .meta { font-size: 12px; color: var(--text-secondary); margin-top: 6px; }
.stat.accent .value { color: var(--rose); }
.stat.money .value { color: var(--forest); }
.stat.warn .value { color: var(--warning); }
.stat.danger .value { color: var(--danger); }

/* ═══ CARDS & SECTIONS ═══════════════════════════════════════ */

.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; margin-bottom: 22px;
  box-shadow: var(--shadow-sm);
}
.card-head {
  padding: 16px 20px; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--bg-secondary);
}
.card-head h2 {
  font-family: var(--font-display);
  font-size: 16px; font-weight: 700; letter-spacing: -0.3px;
}
.card-head .spacer { flex: 1; }
.card-body { padding: 20px; }
.card-body.flush { padding: 0; }

.section-title {
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 600; letter-spacing: 1.4px;
  text-transform: uppercase; color: var(--text-muted);
  margin: 28px 0 13px;
  display: flex; align-items: center; gap: 11px;
}
.section-title::after { content: ''; flex: 1; height: 1px; background: var(--border); }

/* ═══ TRACKER GRID — the presence signature ══════════════════ */

.tracker-grid {
  display: grid; gap: 15px;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}

.tracker {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius); padding: 17px;
  display: flex; flex-direction: column; gap: 13px;
  transition: border-color 0.2s, transform 0.2s var(--ease), box-shadow 0.2s;
  position: relative; overflow: hidden;
  animation: rise 0.4s var(--ease) backwards;
}
/* Stagger the entrance so a full grid arrives as a sweep, not a slab. */
.tracker:nth-child(1) { animation-delay: 0.02s; }
.tracker:nth-child(2) { animation-delay: 0.06s; }
.tracker:nth-child(3) { animation-delay: 0.1s; }
.tracker:nth-child(4) { animation-delay: 0.14s; }
.tracker:nth-child(5) { animation-delay: 0.18s; }
.tracker:nth-child(n+6) { animation-delay: 0.22s; }

.tracker::before {
  content: ''; position: absolute; inset: 0 auto 0 0; width: 4px;
  background: var(--blush);
}
.tracker.online {
  border-color: var(--signal-line);
  background: linear-gradient(120deg, var(--signal-soft), transparent 42%), var(--bg-card);
}
.tracker.online::before { background: var(--forest); }
.tracker.offline::before { background: var(--danger); }
.tracker:hover { border-color: var(--border-hover); transform: translateY(-3px); box-shadow: var(--shadow); }
.tracker.online:hover { border-color: var(--forest); }

.tracker-top { display: flex; align-items: center; gap: 13px; position: relative; }

.tracker-avatar {
  width: 48px; height: 48px; border-radius: 14px; flex-shrink: 0;
  background: var(--blush); border: 1px solid var(--border);
  object-fit: cover; position: relative; z-index: 1;
}
.tracker.online .tracker-avatar {
  border-color: var(--signal-line);
  box-shadow: 0 0 0 3px var(--signal-soft);
}
.tracker.online .tracker-top::before,
.tracker.online .tracker-top::after {
  content: ''; position: absolute; left: 0; top: 50%;
  width: 48px; height: 48px; margin-top: -24px;
  border-radius: 14px; border: 1.5px solid var(--forest);
  animation: ping 2.6s cubic-bezier(0, 0, 0.2, 1) infinite;
  pointer-events: none;
}
.tracker.online .tracker-top::after { animation-delay: 1.3s; }

.tracker-id { min-width: 0; flex: 1; }
.tracker-name {
  font-family: var(--font-display);
  font-weight: 700; font-size: 16px; letter-spacing: -0.35px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tracker-sub { font-size: 12px; color: var(--text-muted); }
.tracker-sub a { color: var(--text-secondary); }
.tracker-sub a:hover { color: var(--accent); }

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3.5px 10px; border-radius: 20px;
  font-family: var(--font-mono);
  font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.7px;
  border: 1px solid transparent; white-space: nowrap;
}
.pill.online { background: var(--signal-soft); color: var(--forest); border-color: var(--signal-line); }
.pill.online::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--forest); animation: breathe 1.8s infinite;
}
.pill.offline { background: var(--danger-soft); color: var(--danger); border-color: rgba(214, 43, 78, 0.3); }
.pill.unknown { background: var(--blush); color: var(--text-secondary); border-color: var(--border); }
.pill.active { background: var(--signal-soft); color: var(--forest); border-color: var(--signal-line); }
.pill.expired { background: var(--warning-soft); color: var(--warning); border-color: rgba(178, 106, 0, 0.3); }
.pill.suspended { background: var(--danger-soft); color: var(--danger); border-color: rgba(214, 43, 78, 0.3); }
.pill.pending { background: var(--warning-soft); color: var(--warning); border-color: rgba(178, 106, 0, 0.3); }
.pill.allowed { background: var(--signal-soft); color: var(--forest); border-color: var(--signal-line); }
.pill.blocked { background: var(--danger-soft); color: var(--danger); border-color: rgba(214, 43, 78, 0.3); }

.tracker-meta {
  display: flex; flex-wrap: wrap; gap: 6px 15px;
  font-size: 12.5px; color: var(--text-secondary);
  padding-top: 12px; border-top: 1px solid var(--border);
}
.tracker-meta b { color: var(--text-primary); font-weight: 600; }

.tracker-loadout {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 12px; background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 12.5px;
}
.tracker-loadout img { width: 32px; height: 32px; object-fit: contain; flex-shrink: 0; }
.tracker-loadout .lines { min-width: 0; }
.tracker-loadout .lines div { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tracker-loadout .lines div:first-child { font-weight: 700; }

.tracker-actions { display: flex; gap: 8px; }
.tracker-actions .btn { flex: 1; }
/* Destructive actions do not get equal billing with the everyday ones. */
.tracker-actions .btn-danger { flex: 0 0 auto; padding-left: 13px; padding-right: 13px; }

/* ═══ TABLES ═════════════════════════════════════════════════ */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
thead th {
  text-align: left; padding: 12px 18px;
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 600; letter-spacing: 1.1px;
  text-transform: uppercase; color: var(--text-muted);
  border-bottom: 1px solid var(--border); white-space: nowrap;
  background: var(--bg-secondary);
}
tbody td { padding: 13px 18px; border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:last-child td { border-bottom: none; }
tbody tr { transition: background 0.16s; }
tbody tr:hover { background: var(--bg-secondary); }

.cell-user { display: flex; align-items: center; gap: 11px; }
.cell-user img { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; flex-shrink: 0; background: var(--blush); }
.cell-user .who { min-width: 0; }
.cell-user .who b { display: block; font-weight: 700; }
.cell-user .who small { color: var(--text-muted); font-size: 11.5px; font-family: var(--font-mono); }

.row-actions { display: flex; gap: 7px; justify-content: flex-end; flex-wrap: wrap; }

/* ═══ CUSTOMER GROUPS (admin) ════════════════════════════════ */

.group {
  border: 1px solid var(--border); border-radius: var(--radius);
  margin-bottom: 15px; overflow: hidden; background: var(--bg-card);
}
.group-head {
  display: flex; align-items: center; gap: 13px;
  padding: 14px 18px; background: var(--bg-secondary);
  border-bottom: 1px solid var(--border); cursor: pointer;
  user-select: none; transition: background 0.16s;
}
.group-head:hover { background: var(--blush); }
.group-head img { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.group-head .who { flex: 1; min-width: 0; }
.group-head .who b { display: block; font-size: 14.5px; font-weight: 700; }
.group-head .who small { color: var(--text-muted); font-size: 11.5px; font-family: var(--font-mono); }
.group-head .chev { color: var(--text-muted); display: inline-flex; }
.group.collapsed .group-body { display: none; }

/* ═══ OG CLAIMS ══════════════════════════════════════════════

   A cabinet of catches. Everything here is glass — the same glass the
   sidebar and topbar already use, over the same fixed blooms — so the
   feed reads as part of this interface rather than a borrowed style.

   The one idea worth remembering: THE MUTATION IS THE LIGHT SOURCE.
   A card holding a rare mutation is lit from behind in that mutation's
   own colour, and glows harder on hover. A Base catch — three in five
   of them — is plain, unlit glass. So a month of claims is dark water
   with the rare things shining in it, and rarity is legible from
   across the room without reading a word.

   Depth is carried by light and indentation, never by four nested
   boxes: month is a sticky slab, week a hairline, day an eyebrow over
   a rule that fades as it falls.
   ═══════════════════════════════════════════════════════════════ */

:root {
  --claim-glass: rgba(255, 255, 255, 0.58);
  --claim-glass-strong: rgba(255, 255, 255, 0.78);
  --claim-edge: rgba(180, 140, 160, 0.30);
  --claim-inner: rgba(255, 255, 255, 0.6);
  --claim-drop: rgba(153, 60, 74, 0.08);
  --claim-blur: 14px;
}

:root[data-theme="dark"] {
  --claim-glass: rgba(42, 24, 32, 0.46);
  --claim-glass-strong: rgba(42, 24, 32, 0.72);
  --claim-edge: rgba(255, 255, 255, 0.08);
  --claim-inner: rgba(255, 255, 255, 0.06);
  --claim-drop: rgba(0, 0, 0, 0.34);
}

/* A third bloom, low on the page: the body's two sit at the top, and
   glass 900 cards down needs something of its own to refract. */
/* .card clips its content, and a clipping ancestor is a scrollport:
   leave it be and the sticky month heads pin to the top of the card
   instead of to the window. The head keeps the corners the card was
   clipping for it. */
#panel-claims .card { overflow: visible; }
#panel-claims .card-head { border-radius: var(--radius) var(--radius) 0 0; }

#panel-claims::before {
  content: '';
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(700px 480px at 76% 82%, var(--bloom-2), transparent 64%);
}

/* ── Toolbar ── */

.claim-toolbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 18px;
}
.claim-toolbar select { width: auto; min-width: 152px; flex: 0 1 auto; }
.claim-count { margin: 0 0 16px; }

/* ── Month: the sticky slab ── */

.claim-month {
  margin-bottom: 26px;
  border: none; background: none; border-radius: 0;
  /* .group clips its children; sticky needs room to travel. */
  overflow: visible;
}

.claim-month > .group-head {
  position: sticky;
  /* Clear of the topbar, which is sticky at 0 and stands 75px tall:
     18px of padding either side of a 39px user chip. */
  top: 74px; z-index: 12;
  display: flex; align-items: center; gap: 14px;
  padding: 14px 20px;
  background: var(--glass-bar);
  backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid var(--claim-edge);
  border-radius: var(--radius);
  box-shadow: 0 1px 0 var(--claim-inner) inset, 0 10px 28px var(--claim-drop);
  cursor: pointer;
  transition: box-shadow 0.2s var(--ease);
}
.claim-month > .group-head:hover { box-shadow: 0 1px 0 var(--claim-inner) inset, 0 14px 34px var(--claim-drop); }
.claim-month .who b {
  font-family: var(--font-display);
  font-size: 19px; font-weight: 700; letter-spacing: -0.5px;
  display: block;
}
.claim-month .who small {
  font-family: var(--font-mono);
  font-size: 11px; color: var(--text-muted);
}
.claim-month > .group-head .chev { margin-left: auto; color: var(--text-muted); }
.claim-month .group-body { padding: 18px 0 0; }

/* ── Week: a hairline, and the ISO key as the data it is ── */

.claim-week { padding: 0 4px 2px; margin-bottom: 20px; }
.claim-week-head {
  display: flex; align-items: baseline; gap: 10px;
  padding: 0 0 8px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 14px;
}
.claim-week-name {
  font-family: var(--font-display);
  font-weight: 700; font-size: 14px; letter-spacing: -0.2px;
}
.claim-week-key {
  font-family: var(--font-mono);
  font-size: 10px; letter-spacing: 0.6px;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 5px; padding: 1px 6px;
}
.claim-week-head .spacer { flex: 1; }

/* ── Day: an eyebrow over a rule that fades as it falls ── */

.claim-day {
  padding-left: 16px;
  margin-bottom: 20px;
  position: relative;
  /* 300+ cards: let the browser skip the days nobody is looking at. */
  content-visibility: auto;
  contain-intrinsic-size: auto 340px;
}
.claim-day::before {
  content: '';
  position: absolute; left: 0; top: 4px; bottom: 6px; width: 2px;
  border-radius: 2px;
  background: linear-gradient(to bottom, var(--accent-soft), transparent);
}
.claim-day:last-child { margin-bottom: 4px; }
.claim-day-head {
  display: flex; align-items: baseline; gap: 10px;
  margin-bottom: 11px;
}
.claim-day-name {
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 600; letter-spacing: 1.3px;
  text-transform: uppercase; color: var(--text-secondary);
}

/* ── The card ── */

.claim-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(272px, 1fr));
}

.claim {
  position: relative; isolation: isolate;
  display: flex; flex-direction: column; gap: 10px;
  padding: 13px 14px;
  border-radius: 15px;
  background: var(--claim-glass);
  backdrop-filter: blur(var(--claim-blur)) saturate(150%);
  border: 1px solid var(--claim-edge);
  box-shadow:
    0 1px 0 var(--claim-inner) inset,
    0 6px 20px var(--claim-drop);
  transition:
    transform 0.22s var(--ease),
    box-shadow 0.22s var(--ease),
    border-color 0.22s;
}

/* The catch's own colour, behind the glass. Only ever on a card that
   holds something rarer than Base — see .is-rare in dashboard.js. */
.claim.is-rare::before {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background:
    radial-gradient(160px 110px at 12% -10%, var(--mut-soft), transparent 72%),
    radial-gradient(90px 70px at 96% 108%, var(--mut-soft), transparent 76%);
}
.claim.is-rare { border-color: var(--mut-edge); }

.claim:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
  box-shadow:
    0 1px 0 var(--claim-inner) inset,
    0 14px 34px var(--claim-drop),
    0 0 0 1px var(--accent-soft),
    0 0 26px var(--accent-soft);
}
.claim.is-rare:hover {
  border-color: var(--mut);
  box-shadow:
    0 1px 0 var(--claim-inner) inset,
    0 14px 34px var(--claim-drop),
    0 0 0 1px var(--mut-edge),
    0 0 30px var(--mut-soft);
}

/* A claim that landed while you were looking says so once, in its own
   colour, then settles into the feed like every other. */
.claim.is-new { animation: claim-land 2.6s var(--ease); }
@keyframes claim-land {
  0%   { box-shadow: 0 0 0 2px var(--accent), 0 0 34px var(--accent-glow); transform: translateY(-3px); }
  55%  { box-shadow: 0 0 0 1px var(--accent-soft), 0 0 22px var(--accent-soft); transform: translateY(0); }
  100% { box-shadow: 0 1px 0 var(--claim-inner) inset, 0 6px 20px var(--claim-drop); }
}

.claim-top { display: flex; align-items: center; gap: 10px; }
.claim-avatar {
  width: 36px; height: 36px; border-radius: 50%;
  object-fit: cover; flex-shrink: 0;
  background: var(--bg-secondary);
  border: 1px solid var(--claim-edge);
}
.claim-who { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.claim-name {
  font-weight: 700; font-size: 14px; color: var(--text-primary);
  text-decoration: none; letter-spacing: -0.1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
a.claim-name:hover { color: var(--link); text-decoration: underline; }
.claim-time {
  font-family: var(--font-mono);
  font-size: 10.5px; color: var(--text-muted);
}

/* The catch itself, in a second, deeper pane of the same glass. */
.claim-loot {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px;
  background: var(--claim-glass-strong);
  border: 1px solid var(--claim-edge);
  border-radius: 11px;
}
.claim-art { width: 36px; height: 36px; object-fit: contain; flex-shrink: 0; }
.claim-lines { min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.claim-brainrot {
  font-family: var(--font-display);
  font-weight: 700; font-size: 13.5px; letter-spacing: -0.2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.claim-badges { display: flex; flex-wrap: wrap; gap: 5px; }

/* The dot carries the colour, never the label: Divine is nearly white
   and Radioactive nearly black, and neither survives as text in both
   themes. */
.mut-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 9px 2px 7px;
  border-radius: 999px;
  font-size: 11px; font-weight: 600;
  color: var(--text-primary);
  background: var(--mut-soft, var(--bg-card));
  border: 1px solid var(--mut-edge, var(--border));
}
.mut-pill i {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--mut, var(--text-muted));
  box-shadow: 0 0 0 2px var(--mut-soft, transparent), 0 0 7px var(--mut, transparent);
  flex-shrink: 0;
}

.claim-tag {
  display: inline-flex; align-items: center;
  padding: 2px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 600;
  color: var(--text-secondary);
  background: var(--claim-glass-strong);
  border: 1px solid var(--border);
}
.claim-tag.event {
  color: var(--accent-ink);
  background: var(--accent-soft);
  border-color: transparent;
}

.claim-facts {
  font-family: var(--font-mono);
  font-size: 10.5px; color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Reaching the far past is a deliberate act, not an accident of
   scrolling — so the way back is a button, not an endless feed. */
.claim-more {
  display: flex; align-items: center; justify-content: center;
  gap: 12px; flex-wrap: wrap;
  padding: 20px 0 6px;
}

/* The tracker cards' stand-in icon, a size up for a claim card. */
.claim-art.art-none { width: 36px; height: 36px; border-radius: 10px; }

/* Backdrop blur is the whole effect; without it, solid surfaces so the
   text never lands on a half-transparent ground it cannot beat. */
@supports not (backdrop-filter: blur(4px)) {
  .claim, .claim-loot, .claim-month > .group-head { background: var(--bg-card); }
  .claim-tag { background: var(--bg-secondary); }
}

/* ── Track button on claim cards ── */
.claim-track-btn {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  opacity: 0; pointer-events: none;
  transition: opacity 0.18s var(--ease);
  /* Shrink the glitch button to fit a card */
  padding: 6px 13px;
  font-size: 11.5px;
  letter-spacing: 1px;
  min-height: 0;
  box-shadow: 3px 0 0 var(--forest);
}
.claim-track-btn::after {
  padding: 6px 13px;
  font-size: 11.5px;
  letter-spacing: 1px;
}
.claim:hover .claim-track-btn,
.claim:focus-within .claim-track-btn {
  opacity: 1; pointer-events: auto;
}
.claim-track-btn.tracked {
  opacity: 1; pointer-events: none;
  background: linear-gradient(45deg, transparent 5%, var(--forest) 5%);
  color: #fff;
  box-shadow: 3px 0 0 var(--rose);
}
.claim-track-btn.tracked::after { content: none; }

/* On touch / mobile: always show since there is no hover. */
@media (hover: none), (max-width: 640px) {
  .claim-track-btn {
    opacity: 1; pointer-events: auto;
    position: static;
    align-self: flex-end;
    margin-top: -2px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .claim, .claim:hover, .claim.is-rare:hover { transform: none; transition: none; }
  .claim.is-new { animation: none; border-color: var(--accent); }
}

@media (max-width: 640px) {
  .claim-toolbar select, .claim-toolbar .search-box { width: 100%; max-width: none; flex: 1 1 100%; }
  .claim-month > .group-head { top: 0; padding: 12px 15px; }
  .claim-month .who b { font-size: 17px; }
  .claim-grid { grid-template-columns: 1fr; }
  .claim-day { padding-left: 12px; }
}

/* ═══ MODAL ══════════════════════════════════════════════════ */

.modal-backdrop {
  position: fixed; inset: 0; z-index: 800;
  background: var(--scrim); backdrop-filter: blur(6px);
  display: none; align-items: center; justify-content: center; padding: 22px;
}
.modal-backdrop.show { display: flex; }

.modal {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg); width: 100%; max-width: 520px;
  max-height: 88vh; max-height: 88dvh; overflow-y: auto;
  box-shadow: var(--shadow-lg);
  animation: rise 0.3s var(--ease);
  position: relative;
}
.modal::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--rose), var(--forest));
  z-index: 3;
}
.modal-head {
  padding: 20px 24px; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 12px;
  position: sticky; top: 0; z-index: 2;
  background: var(--bg-card);
}
.modal-head h3 {
  font-family: var(--font-display);
  font-size: 17px; font-weight: 700; flex: 1; letter-spacing: -0.3px;
}
.modal-body { padding: 24px; }
.modal-foot {
  padding: 16px 24px; border-top: 1px solid var(--border);
  display: flex; gap: 10px; justify-content: flex-end;
  position: sticky; bottom: 0;
  background: var(--bg-card);
}

.close-x {
  background: none; border: none; color: var(--text-muted);
  font-size: 19px; cursor: pointer; padding: 4px 8px; line-height: 1;
  border-radius: 7px; transition: color 0.16s, background 0.16s;
}
.close-x:hover { color: var(--text-primary); background: var(--blush); }

.secret {
  background: var(--bg-secondary); border: 1px solid var(--border-hover);
  border-radius: var(--radius-sm); padding: 14px 16px;
  font-family: var(--font-mono);
  font-size: 13px; word-break: break-all; margin: 10px 0;
  display: flex; align-items: center; gap: 11px;
}
.secret code { flex: 1; color: var(--forest); font-weight: 600; }

.check {
  display: flex; align-items: flex-start; gap: 11px;
  font-size: 13px; color: var(--text-secondary); cursor: pointer;
  line-height: 1.55; padding: 11px 13px;
  background: var(--bg-secondary); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  transition: border-color 0.16s, background 0.16s;
}
.check:hover { border-color: var(--border-hover); background: var(--blush); }
.check input[type="checkbox"] {
  width: 17px; height: 17px; margin-top: 1px; flex: none;
  accent-color: var(--rose); cursor: pointer;
}

/* ═══ THE RULE ═══════════════════════════════════════════════
   One red line, used only where sharing an account is at stake.
   It looks like nothing else in the interface, on purpose. */

.rule-line {
  display: flex; gap: 12px; align-items: flex-start;
  margin: 18px 0;
  padding: 14px 16px;
  background: var(--rule-bg);
  border: 2px solid var(--danger);
  border-left-width: 6px;
  border-radius: 4px;
  color: var(--danger-ink);
  font-size: 13.5px; font-weight: 600; line-height: 1.55;
}
.rule-line .mark { font-size: 17px; line-height: 1.3; }

/* ═══ NETWORK LOCK ═══════════════════════════════════════════ */

.ip-list { display: flex; flex-direction: column; gap: 8px; }
.ip-row {
  display: flex; align-items: center; gap: 11px; flex-wrap: wrap;
  padding: 11px 13px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.ip-row.pending { border-color: rgba(178, 106, 0, 0.4); background: var(--warning-soft); }
.ip-row.blocked { border-color: rgba(214, 43, 78, 0.35); background: var(--danger-soft); }
.ip-row .addr {
  font-family: var(--font-mono); font-size: 13px; font-weight: 600;
  color: var(--text-primary);
}
.ip-row .meta { font-size: 11.5px; color: var(--text-muted); width: 100%; }
.ip-row .spacer { flex: 1; }

/* ═══ ADMIN PREVIEW BANNER ═══════════════════════════════════ */

.preview-bar {
  display: none;
  align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 20px;
  background: var(--warning-soft);
  border-bottom: 1px solid rgba(178, 106, 0, 0.34);
  color: var(--warning-ink);
  font-size: 13px; font-weight: 600;
}
.preview-bar.show { display: flex; }
.preview-bar .spacer { flex: 1; }
.preview-bar a { color: var(--warning-ink); text-decoration: underline; }

/* ═══ EMPTY / LOADING ════════════════════════════════════════ */

.empty { text-align: center; padding: 58px 22px; color: var(--text-muted); }
.empty .icon {
  width: 40px; height: 40px; margin: 0 auto 14px;
  display: block; opacity: 0.4; stroke-width: 1.5;
}
.empty h3 {
  font-family: var(--font-display);
  font-size: 17px; color: var(--text-secondary); margin-bottom: 7px; font-weight: 700;
}
.empty p { font-size: 13.5px; max-width: 390px; margin: 0 auto; line-height: 1.65; }

.spinner {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--blush); border-top-color: var(--rose);
  animation: spin 0.7s linear infinite; display: inline-block;
}
.btn-primary .spinner { border-color: rgba(255, 255, 255, 0.35); border-top-color: var(--on-rose); }

.loading { display: grid; place-items: center; padding: 58px; }

/* ═══ UTILITIES ══════════════════════════════════════════════ */

.row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.grow { flex: 1; }
.muted { color: var(--text-secondary); }
.dim { color: var(--text-muted); }
.small { font-size: 12.5px; }
.mono { font-family: var(--font-mono); font-size: 12.5px; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.mt { margin-top: 16px; }
.mb { margin-bottom: 16px; }
.ok { color: var(--forest); }
.bad { color: var(--danger); }
.warnc { color: var(--warning); }
.hidden { display: none !important; }

.search-box { position: relative; min-width: 210px; flex: 1; max-width: 300px; }
.search-box input { padding-left: 38px; }

#cursor-fx { position: fixed; inset: 0; z-index: 950; pointer-events: none; }

/* ═══ ICONS ══════════════════════════════════════════════════
   Drawn in currentColor, so an icon inherits whatever colour its
   container already has and needs no second set for dark mode. */

.icon {
  flex: none;
  vertical-align: -0.15em;
  stroke: currentColor;
}
.btn .icon { margin-right: -1px; }
.nav-icon .icon, .nav-btn > .icon { width: 19px; height: 19px; }

.search-box .icon {
  position: absolute; left: 12px; top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted); pointer-events: none;
}
.group-head .chev .icon { transition: transform 0.24s var(--ease); }
.group.collapsed .chev .icon { transform: rotate(-90deg); }

/* ═══ THEME SWITCH ═══════════════════════════════════════════

   A two-up segmented control rather than a single toggle: it shows
   which theme is on AND what the alternative is, in the space one
   ambiguous button would have taken. The thumb slides between them, so
   the change reads as a movement rather than a repaint.
   ═══════════════════════════════════════════════════════════════ */

.theme-switch {
  position: relative;
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 2px; padding: 3px; width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: 999px;
  isolation: isolate;
}

/* The thumb. Sits behind both labels and slides on theme change. */
.theme-switch::before {
  content: '';
  position: absolute; z-index: -1;
  top: 3px; bottom: 3px; left: 3px;
  width: calc(50% - 4px);
  background: var(--bg-card);
  border: 1px solid var(--border-hover);
  border-radius: 999px;
  box-shadow: var(--shadow-sm);
  transition: transform 0.28s var(--ease), background 0.2s, border-color 0.2s;
}
:root[data-theme="dark"] .theme-switch::before {
  transform: translateX(calc(100% + 2px));
  background: var(--accent-soft);
  border-color: var(--signal-line);
}

.theme-opt {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 6px 4px; min-width: 0;
  border: 0; background: none; cursor: pointer;
  font-family: inherit; font-size: 12px; font-weight: 600;
  color: var(--text-muted);
  border-radius: 999px;
  transition: color 0.2s;
}
.theme-opt:hover { color: var(--text-secondary); }
.theme-opt span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The active half takes the text colour; the other stays quiet. */
:root .theme-opt[data-set-theme="light"] { color: var(--text-primary); }
:root[data-theme="dark"] .theme-opt[data-set-theme="light"] { color: var(--text-muted); }
:root[data-theme="dark"] .theme-opt[data-set-theme="dark"] { color: var(--forest); }

/* ═══ RESPONSIVE ═════════════════════════════════════════════

   Under 900px the sidebar stops being a sidebar. Navigation moves to
   a fixed bottom bar within thumb reach — this gets checked on a
   phone far more often than at a desk — while the brand and the
   system vitals become a compact header strip.
   ═══════════════════════════════════════════════════════════════ */

.menu-toggle { display: none; }
.scrim { display: none; }

@media (max-width: 900px) {
  :root { --sidebar-w: 0px; }

  .app { display: block; }

  .sidebar {
    position: static; width: auto; height: auto;
    border-right: none; border-bottom: 1px solid var(--border);
    display: block; background: var(--glass);
    transform: none !important;
    /* Must be none: backdrop-filter makes this a containing block for
       position:fixed children, which would anchor the bottom tab bar
       to the header strip instead of to the viewport. The bar carries
       its own blur. */
    backdrop-filter: none;
  }
  .sidebar-brand { padding: 13px 16px; border-bottom: none; }

  .sidebar-foot {
    flex-direction: row; align-items: center; flex-wrap: wrap;
    gap: 8px 16px; padding: 0 16px 12px;
    border-top: none; font-size: 11.5px;
  }
  .sidebar-foot > div { margin-top: 0 !important; }
  .theme-switch { width: auto; min-width: 148px; }

  .sidebar-nav {
    position: fixed; inset: auto 0 0 0; z-index: 200;
    flex-direction: row; gap: 2px;
    padding: 6px 6px calc(6px + env(safe-area-inset-bottom));
    background: var(--glass);
    backdrop-filter: blur(20px);
    border-top: 1px solid var(--border);
    box-shadow: 0 -8px 26px rgba(0, 0, 0, 0.22);
    overflow: visible;
  }
  .nav-btn {
    flex: 1; min-width: 0;
    flex-direction: column; gap: 3px;
    padding: 8px 2px; text-align: center;
    font-size: 10.5px; font-weight: 600; line-height: 1.25;
  }
  .nav-btn.active::before {
    left: 50%; right: auto; top: 0; bottom: auto;
    width: 22px; height: 3px; margin-left: -11px;
    border-radius: 0 0 3px 3px;
  }
  .nav-btn > span:not(.nav-icon):not(.nav-count) {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    max-width: 100%;
  }
  .nav-btn > .icon { width: 21px; height: 21px; }
  .nav-count {
    position: absolute; top: 2px; right: 6px;
    margin: 0; line-height: 1.5;
    font-size: 9.5px; padding: 0 5px;
    background: var(--rose); color: var(--on-rose); border-color: transparent;
  }

  .card-head { flex-wrap: wrap; gap: 10px; }
  .card-head h2 { width: 100%; }
  .card-head .spacer { display: none; }
  .card-head .btn { flex: 1 1 auto; min-width: 0; }
  .card-head .search-box { flex: 1 1 100%; max-width: none; }
  .card-head select { flex: 1 1 120px; width: auto !important; min-width: 0; }

  .main { margin-left: 0; padding-bottom: 74px; }
  .content { padding: 18px 15px 30px; }
  .topbar { padding: 14px 15px; }
  .topbar h1 { font-size: 17px; }

  .stat-grid { gap: 11px; }
  .stat-grid > .stat { flex: 1 1 142px; }
  .stat { padding: 15px 16px; }
  .stat .value { font-size: 25px; }

  #toasts { left: 14px; right: 14px; bottom: 82px; max-width: none; }
}

/* Tables stop scrolling sideways and become stacked cards. Each cell
   carries its own label, so nothing depends on a header row that is
   no longer above it. */
@media (max-width: 700px) {
  .tracker-grid { grid-template-columns: 1fr; }
  .field-row { grid-template-columns: 1fr; }

  .table-wrap { overflow-x: visible; }
  table, thead, tbody, tr, td { display: block; width: 100%; }
  thead { display: none; }

  tbody tr {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    margin: 12px; padding: 4px 0;
    background: var(--bg-secondary);
  }
  tbody tr:hover { background: var(--blush); }

  tbody td {
    display: flex; align-items: center; gap: 14px;
    padding: 9px 14px; border-bottom: none;
    text-align: left !important;
  }
  tbody td::before {
    content: attr(data-label);
    font-family: var(--font-mono);
    font-size: 10px; font-weight: 600; letter-spacing: 1px;
    text-transform: uppercase; color: var(--text-muted);
    flex: 0 0 92px;
  }
  tbody td:not([data-label])::before { display: none; }
  tbody td:not([data-label]) { display: block; }

  .row-actions { justify-content: flex-start; }
  .empty { padding: 40px 18px; }
  .card-head { padding: 14px 15px; }
  .card-body { padding: 15px; }
  .modal-body { padding: 18px; }
  .modal-head, .modal-foot { padding: 15px 18px; }
}

@media (max-width: 520px) {
  .auth-card { padding: 32px 22px 28px; }
  .auth-card h1 { font-size: 22px; }
  .search-box { min-width: 0; max-width: none; width: 100%; }
  .card-head { gap: 9px; }
  .stat-grid > .stat { flex: 1 1 calc(50% - 6px); }
  .tracker-actions { flex-wrap: wrap; }
  .user-chip .who { display: none; }
  .user-chip { padding: 4px; }
}

/* Six bottom tabs need the labels gone before they start wrapping. */
@media (max-width: 380px) {
  .nav-btn > span:not(.nav-icon):not(.nav-count) { display: none; }
  .nav-btn { padding: 11px 2px; }
  .nav-count { top: 1px; right: 4px; }
}

@media (min-width: 1600px) {
  .content { max-width: 1500px; }
}

@media print {
  .sidebar, .topbar, .btn, #cursor-fx, #toasts { display: none !important; }
  .main { margin: 0; }
  body { background: #fff; }
  body::before { display: none; }
}
