/* ==========================================================================
   Killfeed setup v2 (/killfeed/setup/v2) — built on Skynet UI
   (https://skynetui.com).

   Only page-local layout lives here; components come from skynet-ui.css and
   the palettes from themes-v2.css, so this page follows whichever of the
   twelve a user picked. Nothing below declares a colour that is not a token.

   The second half of the file is the INJECTED VOCABULARY: the class names
   public/js/killfeed-dashboard-setup.js writes into the page at runtime. That
   controller is shared with the classic wizard and is not this page's to
   rewrite, so its component classes (.btn-primary, .setting-item,
   .toggle-switch, .server-card, .alert …) are re-expressed here in theme
   tokens. Its Tailwind *utility* classes are handled separately, by the
   generated killfeed-setup-v2-compat.css.
   ========================================================================== */

:root {
  /* partials/header-v2.ejs is fixed at 60px and renders its own .nav-spacer;
     the sticky stepper hangs off this. Same figure as --skv2-h / --dv2-header
     — change it in header-v2.css and change it here. */
  --ksv2-header: 60px;
}
body.has-partnership-widget { --ksv2-header: 104px; }
@media (max-width: 640px) {
  body.has-partnership-widget { --ksv2-header: 100px; }
}

body.ksv2 { background: var(--sk-bg); color: var(--sk-text); }

/* Skynet UI declares `.hidden` before its component display rules, so a
   hidden `.sk-alert` would still render — and the wizard's whole
   show/hide contract is `.hidden`. */
.ksv2 .hidden { display: none !important; }
/* Same guard for the attribute: every `display` rule in this file is a class
   selector, which ties with `[hidden]` on specificity and then wins on order. */
.ksv2 [hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */
.ksv2-main { padding-bottom: 3rem; }

.ksv2-hero {
  position: relative;
  isolation: isolate;
  border-bottom: 1px solid var(--sk-border);
  background: var(--sk-surface);
}
/* A tint over the surface, never a fill: the same wash has to read on a white
   page as on a near-black one. */
.ksv2-hero-wash {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(120% 140% at 8% 0%,
      color-mix(in srgb, var(--sk-primary) 34%, transparent) 0%,
      color-mix(in srgb, var(--sk-primary) 10%, transparent) 46%,
      transparent 74%),
    var(--sk-surface-2);
}
.ksv2-hero-inner {
  padding-top: 2rem;
  padding-bottom: 1.75rem;
  max-width: 54rem;
}
.ksv2-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.6rem;
  color: var(--sk-primary-text);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.ksv2-title {
  margin: 0;
  font-size: clamp(1.7rem, 1.1rem + 2.2vw, 2.6rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
}
.ksv2-lede {
  margin: 0.7rem 0 0;
  max-width: 46rem;
  color: var(--sk-text-2);
  font-size: 1.03rem;
  line-height: 1.6;
}
.ksv2-hero-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 1.25rem;
  margin: 1rem 0 0;
  padding: 0;
  list-style: none;
  color: var(--sk-muted);
  font-size: 0.87rem;
}
/* The row is flex; each item is NOT. A flex li makes its text node and its
   <b> two separate boxes, so on a phone the bold phrase lands on its own line
   away from the sentence it belongs to. As a block it just wraps. */
.ksv2-hero-facts li { display: block; }
.ksv2-hero-facts i { color: var(--sk-primary-text); margin-right: 0.45rem; }
.ksv2-hero-facts b { color: var(--sk-text-2); font-weight: 600; }
.ksv2-hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.35rem;
}

/* --------------------------------------------------------------------------
   Stepper rail — sticky under the site header
   -------------------------------------------------------------------------- */
.ksv2-rail {
  position: sticky;
  top: var(--ksv2-header);
  z-index: 40;
  padding: 0.7rem 0;
  background: color-mix(in srgb, var(--sk-bg) 90%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--sk-border);
}
/* A <ol> for the reading order; .sk-step already sets its own display. */
.ksv2-steps {
  margin: 0;
  padding: 0;
  list-style: none;
  max-width: 46rem;
}
/* goToStep() marks finished steps `.completed`; Skynet UI's own state class is
   `.done`. Mapping it here beats teaching the shared controller a second
   vocabulary — it would have to stay right on the classic page too. */
.ksv2-steps .step.completed .sk-step-dot {
  background: var(--sk-primary-soft);
  border-color: var(--sk-primary);
  color: var(--sk-primary-text);
}
.ksv2-steps .step.completed .sk-step-label { color: var(--sk-text-2); }
.ksv2-steps .step.completed::before { background: var(--sk-primary); }
@media (max-width: 420px) {
  /* Four labels do not fit a 320px phone; the dots and the active label do. */
  .ksv2-steps .sk-step-label { font-size: 0.72rem; }
}

/* --------------------------------------------------------------------------
   Body columns
   -------------------------------------------------------------------------- */
.ksv2-body { padding-top: 1.25rem; }
.ksv2-cols { display: grid; gap: 1.25rem; }
.ksv2-col-main { min-width: 0; }
/* On a phone the rail is what you need first: the bot invite and the token are
   the two things that block the wizard before it can say anything useful. */
.ksv2-col-side { min-width: 0; order: -1; }
@media (min-width: 1024px) {
  .ksv2-cols { grid-template-columns: minmax(0, 1fr) 20rem; align-items: start; }
  .ksv2-col-side { order: 0; position: sticky; top: calc(var(--ksv2-header) + 5rem); }
}

.ksv2-alerts:not(:empty) { margin-bottom: 1rem; }

/* --------------------------------------------------------------------------
   Step cards
   -------------------------------------------------------------------------- */
.ksv2-step { margin-bottom: 1rem; }
.ksv2-step-head { align-items: flex-start; gap: 0.75rem; }
.ksv2-step-head-main { display: flex; align-items: flex-start; gap: 0.75rem; min-width: 0; }
.ksv2-step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  background: var(--sk-primary);
  color: #fff;
  font-size: 0.9rem;
  font-weight: 700;
}
.ksv2-step-text { display: block; min-width: 0; }
.ksv2-step-text b { display: block; color: var(--sk-text); font-size: 1.02rem; }
.ksv2-step-text small {
  display: block;
  margin-top: 0.15rem;
  color: var(--sk-muted);
  font-size: 0.84rem;
  font-weight: 400;
  line-height: 1.45;
  white-space: normal;
}
.ksv2-step-glyph { color: var(--sk-primary-text); font-size: 1.1rem; flex: none; }
@media (max-width: 560px) { .ksv2-step-glyph { display: none; } }

.ksv2-step-intro { margin: 0 0 1.1rem; line-height: 1.6; }

/* Skynet UI paints an inline link with the raw accent (`--sk-primary`). That
   token is a FILL — picked so white sits on it — not a colour meant to be read
   as text on a surface, and `npm run audit:themes` measured the help line's
   Nitrado link at 3.14–3.55:1 on all six dark palettes (AA wants 4.5 for body
   text). `--sk-primary-text` is the readable half of the pair in both modes:
   6.9:1 on dark skynet, 10.8:1 on light. The framework's own list rows are
   left alone — they measure clean. */
.ksv2 .sk-help a,
.ksv2 p a { color: var(--sk-primary-text); }
.ksv2-sub {
  margin: 1.5rem 0 0.75rem;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--sk-text);
}
.ksv2-sub:first-child { margin-top: 0; }

.ksv2-step-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
}
.ksv2-foot-note { margin-right: auto; }
@media (max-width: 480px) {
  .ksv2-foot-note { display: none; }
  .ksv2-step-foot .sk-btn { flex: 1 1 auto; justify-content: center; }
}

/* An alert that sits in the flow of a step rather than in the live region. */
.ksv2-inline-alert { display: flex; align-items: flex-start; gap: 0.6rem; margin-bottom: 1rem; }
.ksv2-inline-alert b { color: inherit; }

/* --------------------------------------------------------------------------
   Step 1 bits
   -------------------------------------------------------------------------- */
.ksv2-modes { margin-bottom: 1.25rem; flex-wrap: wrap; }
/* setTokenMode() owns `.active` here. Skynet UI styles `.active` inside a
   segmented group, but only when data-sk-segment drives it, so the look of the
   chosen half is declared rather than borrowed. */
.ksv2-modes .sk-btn.active {
  background: var(--sk-primary);
  border-color: var(--sk-primary);
  color: #fff;
}
/* Skynet UI's input group rounds the right end of every .sk-btn in it, which
   assumes one. The token field has two (reveal, then verify), so only the last
   keeps the rounded end. */
.ksv2 .sk-input-group > .sk-btn:not(:last-child) { border-radius: 0; }
.ksv2-peek { flex: none; }
.ksv2-server-config { margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--sk-border); }

/* Containers the wizard fills with .server-card rows. */
.ksv2-cards { display: grid; gap: 0.55rem; }
.ksv2-injected:not(:empty) { margin-top: 1.25rem; }

/* --------------------------------------------------------------------------
   Catalogues (channels, roles) and switches (settings)
   -------------------------------------------------------------------------- */
.ksv2-catalogue {
  display: grid;
  gap: 0.55rem;
  grid-template-columns: 1fr;
}
@media (min-width: 640px) { .ksv2-catalogue { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1280px) { .ksv2-catalogue { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Five roles with a sentence each read better two-up than three-up. */
@media (min-width: 1280px) { .ksv2-catalogue-wide { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.ksv2-cat-item {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--sk-border);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-surface-2);
}
.ksv2-cat-item b { display: block; font-size: 0.9rem; color: var(--sk-text); }
.ksv2-cat-item small {
  display: block;
  margin-top: 0.1rem;
  color: var(--sk-muted);
  font-size: 0.8rem;
  line-height: 1.45;
}
.ksv2-cat-icon { color: var(--sk-primary-text); width: 1.1rem; text-align: center; margin-top: 0.15rem; flex: none; }

.ksv2-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.25rem; }
@media (max-width: 480px) { .ksv2-actions .sk-btn { flex: 1 1 100%; justify-content: center; } }

.ksv2-switches { display: grid; gap: 0.55rem; }
@media (min-width: 768px) { .ksv2-switches { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.ksv2-switch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--sk-border);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-surface-2);
}
.ksv2-switch-text { min-width: 0; }
.ksv2-switch-text label {
  display: block;
  color: var(--sk-text);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
}
.ksv2-switch-text label i { color: var(--sk-primary-text); margin-right: 0.35rem; }
.ksv2-switch-text small {
  display: block;
  margin-top: 0.15rem;
  color: var(--sk-muted);
  font-size: 0.8rem;
  line-height: 1.45;
}
.ksv2-toggle { flex: none; gap: 0; }
/* The knob is white on both tracks, and on a light palette the OFF track is a
   pale grey it disappears into (~1.2:1). The shadow gives it an edge in either
   mode — the same fix the dashboard and admin switches carry. */
.ksv2 .sk-toggle span::after { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); }

/* --------------------------------------------------------------------------
   Loading, rail cards, signed-out state
   -------------------------------------------------------------------------- */
.ksv2-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  padding: 3rem 1rem;
  border: 1px solid var(--sk-border);
  border-radius: var(--sk-radius-lg);
  background: var(--sk-surface);
}

.ksv2-prereqs { margin: 0; padding: 0; list-style: none; display: grid; gap: 1rem; }
.ksv2-prereqs li { display: flex; align-items: flex-start; gap: 0.65rem; }
.ksv2-prereqs b { display: block; font-size: 0.92rem; }
.ksv2-prereqs small { display: block; margin-top: 0.2rem; color: var(--sk-muted); font-size: 0.82rem; line-height: 1.5; }
.ksv2-prereq-n,
.ksv2-preview-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--sk-primary-soft);
  border: 1px solid var(--sk-primary);
  color: var(--sk-primary-text);
  font-size: 0.78rem;
  font-weight: 700;
}
.ksv2-links a { gap: 0.55rem; }
.ksv2-links i { color: var(--sk-primary-text); width: 1.1rem; text-align: center; }

.ksv2-signin { padding-top: 2rem; padding-bottom: 2rem; }
.ksv2-preview { margin: 0; padding: 0; list-style: none; display: grid; gap: 0.85rem; }
.ksv2-preview li { display: flex; align-items: flex-start; gap: 0.65rem; }
.ksv2-preview b { display: block; font-size: 0.92rem; }
.ksv2-preview small { display: block; margin-top: 0.15rem; color: var(--sk-muted); font-size: 0.82rem; line-height: 1.5; }

/* --------------------------------------------------------------------------
   Touch targets
   Skynet UI sizes for a mouse (.sk-btn 39px, inputs 37px with a sub-16px
   face). This page is a form on a phone more often than not, so on a coarse
   pointer everything interactive takes 44px and the fields go to 16px —
   below that, iOS Safari zooms the page on focus and the form jumps.
   -------------------------------------------------------------------------- */
@media (pointer: coarse) {
  /* Including .sk-btn-sm: the only small buttons on this page are the two in
     the rail, and "Add the bot" is the most important thing on a phone. */
  .ksv2 .sk-btn,
  .ksv2 .sk-btn-sm { min-height: 44px; }
  .ksv2 .sk-input,
  .ksv2 .sk-select,
  .ksv2 .form-input,
  .ksv2 .form-select { min-height: 44px; font-size: 16px; }
  .ksv2 .sk-list a { min-height: 44px; }
  /* The switch TRACK is 42x24 by design and stays that size; what has to reach
     44px is the thing you tap. The label is the hit area, so it takes the
     height and centres the track inside it. Measured: 42x24 before, 42x44
     after, with the track unchanged on screen. */
  .ksv2 .ksv2-toggle,
  .ksv2 .toggle-switch { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ==========================================================================
   INJECTED VOCABULARY
   Everything below is markup public/js/killfeed-dashboard-setup.js writes at
   runtime. The classic page declares these in a <style> block full of slate
   and purple hexes; here they are the same components in theme tokens, so
   injected content follows the palette instead of rendering as a dark island
   on a light page (or unstyled).
   ========================================================================== */

/* ---- alerts: showAlert() writes .alert + .alert-success|error|warning ---- */
.ksv2 .alert {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  padding: 0.85rem 1rem;
  margin-bottom: 0.75rem;
  border: 1px solid var(--sk-border-2);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-surface-2);
  color: var(--sk-text-2);
  font-size: 0.9rem;
  line-height: 1.5;
}
.ksv2 .alert-success {
  background: var(--sk-success-soft);
  border-color: color-mix(in srgb, var(--sk-success) 40%, transparent);
  color: var(--sk-success-text);
}
.ksv2 .alert-error {
  background: var(--sk-danger-soft);
  border-color: color-mix(in srgb, var(--sk-danger) 40%, transparent);
  color: var(--sk-danger-text);
}
.ksv2 .alert-warning {
  background: var(--sk-warning-soft);
  border-color: color-mix(in srgb, var(--sk-warning) 40%, transparent);
  color: var(--sk-warning-text);
}
/* showConfirmDialog() styles its wrapper INLINE — a purple wash with
   `color: white` on it, which is a dark-page assumption: on the six light
   palettes that is white text on pale lilac (~1.6:1) and the dialog's own
   title is the part that disappears. An inline style can only be beaten with
   !important, so this is the one place in the file that uses it. */
.ksv2 #alert-container > .alert[style] {
  background: var(--sk-primary-soft) !important;
  border-color: color-mix(in srgb, var(--sk-primary) 40%, transparent) !important;
  color: var(--sk-text) !important;
}

/* ---- buttons: .btn-primary / .btn-secondary ----------------------------- */
.ksv2 .btn-primary,
.ksv2 .btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: 0.55rem 1rem;
  min-height: 39px;
  border-radius: var(--sk-radius-sm);
  border: 1px solid transparent;
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--sk-transition), border-color var(--sk-transition), color var(--sk-transition);
}
.ksv2 .btn-primary { background: var(--sk-primary); color: #fff; }
.ksv2 .btn-primary:hover:not(:disabled) { background: var(--sk-primary-hover); }
.ksv2 .btn-secondary { background: transparent; border-color: var(--sk-border-2); color: var(--sk-text); }
.ksv2 .btn-secondary:hover:not(:disabled) { background: var(--sk-surface-2); border-color: var(--sk-primary); }
.ksv2 .btn-primary:disabled,
.ksv2 .btn-secondary:disabled { opacity: 0.55; cursor: not-allowed; }
.ksv2 .btn-primary:focus-visible,
.ksv2 .btn-secondary:focus-visible { outline: 2px solid var(--sk-primary); outline-offset: 2px; }

/* ---- fields: .form-input / .form-select --------------------------------- */
.ksv2 .form-input,
.ksv2 .form-select {
  width: 100%;
  padding: 0.55rem 0.75rem;
  min-height: 37px;
  border: 1px solid var(--sk-border-2);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-bg);
  color: var(--sk-text);
  font-family: inherit;
  font-size: 0.925rem;
}
.ksv2 .form-input:focus,
.ksv2 .form-select:focus {
  outline: none;
  border-color: var(--sk-primary);
  box-shadow: 0 0 0 3px var(--sk-primary-soft);
}
.ksv2 .form-input:disabled,
.ksv2 .form-select:disabled { opacity: 0.55; cursor: not-allowed; }

/* ---- rows: .setting-item / .setting-info (channel picker, step 2) ------- */
.ksv2 .setting-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--sk-border);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-surface-2);
}
.ksv2 .setting-info { flex: 1; min-width: 0; }
.ksv2 .setting-info strong { color: var(--sk-text); font-size: 0.9rem; }

/* ---- the injected switch: .toggle-switch > input + .toggle-slider ------- */
.ksv2 .toggle-switch { position: relative; display: inline-block; width: 42px; height: 24px; flex: none; }
.ksv2 .toggle-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.ksv2 .toggle-slider {
  position: absolute;
  inset: 0;
  cursor: pointer;
  border-radius: 9999px;
  background: var(--sk-border-2);
  transition: background var(--sk-transition);
}
.ksv2 .toggle-slider::before {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  /* Same edge the page's own switches carry, for the same reason. */
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  transition: transform var(--sk-transition);
}
.ksv2 .toggle-switch input:checked + .toggle-slider { background: var(--sk-primary); }
.ksv2 .toggle-switch input:checked + .toggle-slider::before { transform: translateX(18px); }
.ksv2 .toggle-switch input:focus-visible + .toggle-slider { outline: 2px solid var(--sk-primary); outline-offset: 2px; }
.ksv2 .toggle-switch input:disabled + .toggle-slider { opacity: 0.55; cursor: not-allowed; }

/* ---- pick-one rows: .server-card (and the two names never injected today,
        .channel-card / .role-card, kept so a later branch of the shared
        controller does not render unstyled) ---------------------------- */
.ksv2 .server-card,
.ksv2 .channel-card,
.ksv2 .role-card {
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--sk-border);
  border-radius: var(--sk-radius-sm);
  background: var(--sk-surface-2);
  cursor: pointer;
  transition: border-color var(--sk-transition), background var(--sk-transition);
}
.ksv2 .server-card:hover,
.ksv2 .channel-card:hover,
.ksv2 .role-card:hover { border-color: var(--sk-primary); background: var(--sk-surface); }
.ksv2 .server-card.selected,
.ksv2 .channel-card.selected,
.ksv2 .role-card.selected {
  border-color: var(--sk-primary);
  background: var(--sk-primary-soft);
}
@media (pointer: coarse) {
  .ksv2 .server-card,
  .ksv2 .channel-card,
  .ksv2 .role-card,
  .ksv2 .btn-primary,
  .ksv2 .btn-secondary { min-height: 44px; }
}

/* ---- the classic spinner, in case a shared path still injects it -------- */
.ksv2 .loading-spinner {
  width: 2rem;
  height: 2rem;
  border: 3px solid var(--sk-border-2);
  border-top-color: var(--sk-primary);
  border-radius: 50%;
  animation: ksv2-spin 0.8s linear infinite;
}
@keyframes ksv2-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .ksv2 .loading-spinner,
  .ksv2 .sk-spinner { animation-duration: 2.4s; }
  .ksv2 * { scroll-behavior: auto; }
}
