/* HXS Voicebot control panel.
   A deliberately dark console: it sits open next to Discord, so it commits to
   one visual world rather than following a theme toggle. Every colour is
   painted explicitly for that reason. */

:root {
  --ground: #0e1116;
  --panel: #161b22;
  --panel-2: #1b212a;
  --line: #232a33;
  --line-soft: #1d242d;
  --text: #e6eaf0;
  --muted: #8b96a5;
  --faint: #616c7c;

  --accent: #9d7cff;
  --accent-dim: #6f56c9;
  --accent-wash: rgba(157, 124, 255, 0.12);

  --live: #3fb950;
  --warn: #d29922;
  --danger: #f85149;

  --radius: 8px;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  background: var(--ground);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

button, input, select { font: inherit; color: inherit; }

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

.hidden { display: none !important; }

/* ---- layout ------------------------------------------------------------ */

.app {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 16px;
  height: 52px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font-weight: 650;
  letter-spacing: -0.01em;
}

/* A small level-meter mark: three bars, the middle one lit. Speech made
   visible, which is the whole job of this service. */
.mark {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 16px;
}
.mark i {
  display: block;
  width: 3px;
  border-radius: 2px;
  background: var(--accent-dim);
}
.mark i:nth-child(1) { height: 7px; }
.mark i:nth-child(2) { height: 15px; background: var(--accent); }
.mark i:nth-child(3) { height: 10px; }

.spacer { flex: 1; }

.body {
  display: grid;
  grid-template-columns: 280px 1fr;
  flex: 1;
  min-height: 0;
}

.rail {
  background: var(--panel);
  border-right: 1px solid var(--line);
  overflow-y: auto;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.main {
  overflow-y: auto;
  padding: 18px 20px 40px;
  min-width: 0;
}

/* ---- shared bits ------------------------------------------------------- */

.label {
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.07em;
  color: var(--faint);
  font-weight: 600;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 12px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  white-space: nowrap;
}
.chip:empty { display: none; }
.chip.on { color: var(--live); border-color: rgba(63, 185, 80, 0.35); }
.chip.off { color: var(--faint); }
.chip.speaking { color: var(--accent); border-color: var(--accent-dim); background: var(--accent-wash); }

.dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
.dot.pulse { animation: pulse 1.6s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) {
  .dot.pulse { animation: none; }
}

.btn {
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 6px 12px;
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.btn:hover { border-color: var(--accent-dim); }
.btn.primary {
  background: var(--accent-dim);
  border-color: var(--accent-dim);
  font-weight: 600;
}
.btn.primary:hover { background: var(--accent); border-color: var(--accent); }
.btn.sm { padding: 3px 9px; font-size: 12px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

input[type="text"], input[type="password"], input[type="search"], select {
  background: var(--ground);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 7px 10px;
  width: 100%;
}
input:focus, select:focus { border-color: var(--accent-dim); outline: none; }

.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ---- server rail ------------------------------------------------------- */

.guild {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 8px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  background: transparent;
  cursor: pointer;
}
.guild:hover { background: var(--panel-2); }
.guild.active {
  background: var(--accent-wash);
  border-color: var(--accent-dim);
}
.guild img, .guild .fallback {
  width: 30px; height: 30px;
  border-radius: 8px;
  flex: 0 0 auto;
  object-fit: cover;
}
.guild .fallback {
  display: grid;
  place-items: center;
  background: var(--panel-2);
  color: var(--muted);
  font-weight: 600;
  font-size: 13px;
}
.guild .meta { min-width: 0; flex: 1; }
.guild .name {
  font-weight: 550;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.guild .sub { font-size: 12px; color: var(--faint); }

.rail-group { display: flex; flex-direction: column; gap: 6px; }

.status-card {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* Chips are pills: let them hug their text instead of stretching to the
     full width of the column. */
  align-items: flex-start;
}
.status-card .status-row { align-self: stretch; }
.status-row { display: flex; align-items: center; gap: 8px; justify-content: space-between; }

/* ---- role access ------------------------------------------------------- */

.roles-card { max-width: none; margin: 14px 0 0; gap: 10px; }
.role-list { display: flex; flex-wrap: wrap; gap: 8px; }

.role-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  cursor: pointer;
  font-size: 13px;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.role-pill:hover { border-color: var(--accent-dim); }
.role-pill[aria-pressed="true"] {
  border-color: var(--accent-dim);
  background: var(--accent-wash);
  color: var(--text);
}
/* The swatch carries the role's own Discord colour, so roles are recognisable
   at a glance rather than by name alone. */
.role-pill .swatch {
  width: 9px; height: 9px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: currentColor;
}
.role-pill .count { color: var(--faint); font-size: 12px; }

/* ---- members ----------------------------------------------------------- */

.head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.head h1 {
  font-size: 19px;
  margin: 0;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.head .sub { color: var(--muted); font-size: 13px; }

.toolbar {
  display: flex;
  gap: 10px;
  align-items: center;
  margin: 14px 0;
  flex-wrap: wrap;
}
.toolbar .search { flex: 1; min-width: 200px; }

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

.row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto auto auto;
  grid-auto-columns: auto;
  grid-auto-flow: column;
  gap: 12px;
  align-items: center;
  padding: 9px 12px;
  border-bottom: 1px solid var(--line-soft);
}
.row:last-child { border-bottom: none; }
.row:hover { background: var(--panel-2); }
/* A granted user gets a left stripe, so who has TTS reads at a glance
   without parsing every toggle. */
.row.granted { box-shadow: inset 3px 0 0 var(--accent); }

.row .avatar { width: 28px; height: 28px; border-radius: 50%; }
.row .who { min-width: 0; }
.row .display {
  font-weight: 550;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.row .handle { font-size: 12px; color: var(--faint); }
.row select { width: 172px; }

.switch {
  position: relative;
  width: 40px; height: 22px;
  flex: 0 0 auto;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--ground);
  cursor: pointer;
  padding: 0;
  transition: background 0.15s, border-color 0.15s;
}
.switch::after {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--faint);
  transition: transform 0.15s, background 0.15s;
}
.switch[aria-checked="true"] {
  background: var(--accent-dim);
  border-color: var(--accent-dim);
}
.switch[aria-checked="true"]::after {
  transform: translateX(18px);
  background: #fff;
}
@media (prefers-reduced-motion: reduce) {
  .switch, .switch::after { transition: none; }
}

.chip.err { color: var(--danger); border-color: rgba(248, 81, 73, 0.4); }

/* Shown when the bot cannot reach Discord, carrying the fix rather than just
   the symptom. */
.banner {
  border: 1px solid rgba(248, 81, 73, 0.4);
  background: rgba(248, 81, 73, 0.08);
  border-left: 3px solid var(--danger);
  border-radius: var(--radius);
  padding: 12px 14px;
  margin-bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.banner strong { color: var(--danger); }
.banner p { margin: 0; color: var(--text); }
.banner code {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--muted);
  background: var(--ground);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 4px 6px;
  overflow-x: auto;
}

.empty {
  padding: 28px 16px;
  text-align: center;
  color: var(--muted);
}

/* ---- login ------------------------------------------------------------- */

.login-wrap {
  height: 100%;
  display: grid;
  place-items: center;
  padding: 20px;
}
.login {
  width: 100%;
  max-width: 330px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.login h1 { margin: 0; font-size: 17px; }
.login .hint { color: var(--muted); font-size: 13px; margin: -4px 0 4px; }

.error {
  color: var(--danger);
  font-size: 13px;
  min-height: 18px;
}

/* ---- settings ---------------------------------------------------------- */

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 520px;
}
.field { display: flex; flex-direction: column; gap: 5px; }
.field .desc { color: var(--faint); font-size: 12px; }
.field .val { color: var(--accent); font-weight: 600; margin-left: 6px; }
.field .scale {
  display: flex;
  justify-content: space-between;
  color: var(--faint);
  font-size: 11px;
  margin-top: -2px;
}

input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 22px;
  background: transparent;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 999px;
  background: var(--line);
}
input[type="range"]::-moz-range-track {
  height: 4px;
  border-radius: 999px;
  background: var(--line);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--accent);
  border: none;
}
input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent);
  border: none;
}

.toast {
  position: fixed;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--panel-2);
  border: 1px solid var(--accent-dim);
  color: var(--text);
  padding: 9px 16px;
  border-radius: 999px;
  font-size: 13px;
  z-index: 50;
}

/* ---- responsive -------------------------------------------------------- */

@media (max-width: 820px) {
  .body { grid-template-columns: 1fr; }
  .topbar { gap: 8px; padding: 0 10px; }
  .btn.sm, .topbar .btn { padding: 5px 9px; font-size: 13px; }
  .brand { flex: 0 1 auto; min-width: 0; }
  .brand span:last-child {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* The dot still carries the state; the words are what does not fit. */
  #bot-text { display: none; }
  #bot-chip { padding: 2px 7px; }
  .rail {
    border-right: none;
    border-bottom: 1px solid var(--line);
    max-height: 40vh;
  }
  /* Narrow screens: drop the rigid grid and let the row wrap, so the toggle
     stays beside the name it belongs to instead of stranding below the
     dropdown. Name row first, then the voice controls. */
  .row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
  }
  /* Visual order differs from DOM order here, so state it explicitly. */
  .row .avatar { flex: 0 0 auto; order: 1; }
  .row .who { flex: 1 1 auto; min-width: 0; order: 2; }
  .row .switch { flex: 0 0 auto; order: 3; }
  .row .chip { flex: 0 0 auto; order: 4; }
  .row select { flex: 1 1 150px; width: auto; order: 5; }
}
