:root {
  --bg: #0e1116;
  --bg-1: #151a21;
  --bg-2: #1b212a;
  --bg-3: #232b36;
  --line: #2b3441;
  --fg: #e6eaf0;
  --fg-dim: #9aa6b5;
  --fg-mute: #6b7787;
  --accent: #4c8dff;
  --accent-fg: #ffffff;
  --ok: #3fb950;
  --warn: #d29922;
  --err: #f85149;
  --radius: 8px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f6f8fa; --bg-1: #ffffff; --bg-2: #f0f3f6; --bg-3: #e6ebf1;
    --line: #d6dee8; --fg: #1c2430; --fg-dim: #55606e; --fg-mute: #7d8896;
    --ok: #1a7f37; --warn: #9a6700; --err: #cf222e;
  }
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--fg);
  font: 14px/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
}
code, pre, .mono { font-family: var(--mono); font-size: 12.5px; }
a { color: var(--accent); }
h1, h2, h3 { margin: 0 0 .6rem; font-weight: 600; letter-spacing: -0.01em; }
h2 { font-size: 15px; }
h3 { font-size: 14px; }
.muted { color: var(--fg-dim); }
.spacer { flex: 1; }
[hidden] { display: none !important; }

/* ── Auth ─────────────────────────────────────────────────────────────── */
.auth-shell { min-height: 100vh; display: grid; place-items: center; padding: 2rem 1rem; }
.auth-card {
  width: 100%; max-width: 380px; background: var(--bg-1);
  border: 1px solid var(--line); border-radius: 12px; padding: 1.75rem;
  box-shadow: 0 8px 40px rgb(0 0 0 / .25);
}
.auth-card-wide { max-width: 460px; }
.auth-title { font-size: 19px; margin-bottom: .25rem; }
.auth-title span { color: var(--fg-dim); font-weight: 400; }
.auth-sub { color: var(--fg-dim); margin: 0 0 1.25rem; font-size: 13px; }
.auth-card label { display: block; margin: .85rem 0 .3rem; font-size: 12.5px; color: var(--fg-dim); }
.enroll-steps { color: var(--fg-dim); font-size: 13px; padding-left: 1.2rem; margin: 0 0 1rem; }
.qr-wrap { display: grid; place-items: center; background: #fff; padding: 12px; border-radius: 8px; }
.qr-wrap img { display: block; }
.enroll-secret { font-size: 12px; color: var(--fg-dim); text-align: center; margin: .85rem 0; }
.enroll-secret code { background: var(--bg-3); padding: .25em .5em; border-radius: 4px; word-break: break-all; }

/* ── Forms ────────────────────────────────────────────────────────────── */
input[type=text], input[type=password], input[type=email], input[type=search], select, textarea {
  width: 100%; padding: .55rem .7rem; background: var(--bg-2); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius); font: inherit; outline: none;
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgb(76 141 255 / .18); }
textarea { font-family: var(--mono); font-size: 12.5px; line-height: 1.55; resize: vertical; }
label.chk { display: flex; align-items: center; gap: .45rem; font-size: 13px; color: var(--fg-dim); margin: .4rem 0; cursor: pointer; }
label.chk input { width: auto; accent-color: var(--accent); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  padding: .5rem .85rem; background: var(--bg-3); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius);
  font: inherit; font-weight: 500; cursor: pointer; white-space: nowrap;
}
.btn:hover:not(:disabled) { background: var(--line); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
.btn-primary:hover:not(:disabled) { filter: brightness(1.1); }
.btn-danger { background: transparent; border-color: var(--err); color: var(--err); }
.btn-danger:hover:not(:disabled) { background: var(--err); color: #fff; }
.btn-ghost { background: transparent; }
.btn-sm { padding: .35rem .6rem; font-size: 12.5px; }
.btn-block { width: 100%; margin-top: 1.1rem; }

/* ── Layout ───────────────────────────────────────────────────────────── */
.topbar {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: .6rem 1rem; background: var(--bg-1); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
.brand { font-weight: 700; letter-spacing: -0.02em; }
.brand span { font-weight: 400; color: var(--fg-dim); }
.tabs { display: flex; gap: .15rem; flex-wrap: wrap; }
.tab {
  padding: .4rem .7rem; background: transparent; border: 0; border-radius: 6px;
  color: var(--fg-dim); font: inherit; font-weight: 500; cursor: pointer;
}
.tab:hover { background: var(--bg-2); color: var(--fg); }
.tab.active { background: var(--bg-3); color: var(--fg); }
.topbar-right { display: flex; align-items: center; gap: .5rem; margin-left: auto; }
.user { font-size: 12.5px; color: var(--fg-dim); }
main { padding: 1.25rem; max-width: 1280px; margin: 0 auto; }
.view { display: none; }
.view.active { display: block; }

.panel {
  background: var(--bg-1); border: 1px solid var(--line);
  border-radius: 10px; padding: 1.1rem; margin-bottom: 1rem;
}
.toolbar { display: flex; align-items: center; gap: .75rem; margin-bottom: .9rem; flex-wrap: wrap; }
.filter { max-width: 340px; }

/* ── Cards ────────────────────────────────────────────────────────────── */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: .75rem; margin-bottom: 1rem; }
.card { background: var(--bg-1); border: 1px solid var(--line); border-radius: 10px; padding: .9rem 1rem; }
.card .k { font-size: 11.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--fg-mute); }
.card .v { font-size: 26px; font-weight: 600; letter-spacing: -0.02em; margin-top: .2rem; }
.card .s { font-size: 12px; color: var(--fg-dim); margin-top: .15rem; }
.card.ok .v { color: var(--ok); } .card.warn .v { color: var(--warn); } .card.err .v { color: var(--err); }

/* ── Lists ────────────────────────────────────────────────────────────── */
.list { display: flex; flex-direction: column; gap: .5rem; }
.row {
  background: var(--bg-1); border: 1px solid var(--line); border-radius: 10px; padding: .75rem .9rem;
}
.row-head { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.row-title { font-weight: 600; }
.row-meta { color: var(--fg-dim); font-size: 12.5px; display: flex; gap: .9rem; flex-wrap: wrap; margin-top: .3rem; }
.row-actions { margin-left: auto; display: flex; gap: .35rem; flex-wrap: wrap; }
.row-body { margin-top: .75rem; border-top: 1px solid var(--line); padding-top: .75rem; }

.badge {
  display: inline-block; padding: .12rem .45rem; border-radius: 999px;
  font-size: 11px; font-weight: 600; border: 1px solid var(--line); color: var(--fg-dim);
}
.badge-ok { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.badge-warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.badge-err { color: var(--err); border-color: color-mix(in srgb, var(--err) 45%, transparent); }
.badge-accent { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }

.grid-modules { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: .5rem; }
.mod {
  display: flex; align-items: center; gap: .6rem; padding: .6rem .75rem;
  background: var(--bg-1); border: 1px solid var(--line); border-radius: 8px;
}
.mod-name { font-family: var(--mono); font-size: 12.5px; font-weight: 600; }
.mod-desc { font-size: 11.5px; color: var(--fg-mute); }
.mod .btn { margin-left: auto; }

/* ── Output / alerts ──────────────────────────────────────────────────── */
.output {
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: .7rem .85rem; margin: 0; overflow-x: auto; white-space: pre-wrap;
  word-break: break-word; max-height: 420px; overflow-y: auto; color: var(--fg-dim);
}
.alert { padding: .6rem .8rem; border-radius: 8px; font-size: 13px; margin: .75rem 0; border: 1px solid; }
.alert-error { background: color-mix(in srgb, var(--err) 12%, transparent); border-color: color-mix(in srgb, var(--err) 40%, transparent); color: var(--err); }
.alert-warn  { background: color-mix(in srgb, var(--warn) 12%, transparent); border-color: color-mix(in srgb, var(--warn) 40%, transparent); color: var(--warn); }
.alert-ok    { background: color-mix(in srgb, var(--ok) 12%, transparent);  border-color: color-mix(in srgb, var(--ok) 40%, transparent);  color: var(--ok); }
.alert-info  { background: color-mix(in srgb, var(--accent) 12%, transparent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); color: var(--accent); }

/* ── Deploy wizard ────────────────────────────────────────────────────── */
.form-grid { display: grid; gap: .35rem; }
.form-narrow { display: grid; gap: .35rem; max-width: 380px; }
.form-narrow label, .field label:not(.chk) { font-size: 12.5px; color: var(--fg-dim); margin-top: .5rem; }
.field { margin-bottom: .35rem; }
.actions { display: flex; gap: .5rem; margin-top: 1rem; flex-wrap: wrap; }

.steps { display: flex; flex-direction: column; gap: .3rem; margin-top: .75rem; }
.step { display: flex; align-items: flex-start; gap: .55rem; font-size: 13px; padding: .35rem 0; }
.step-icon { width: 16px; text-align: center; flex-shrink: 0; }
.step.ok .step-icon { color: var(--ok); }
.step.error .step-icon { color: var(--err); }
.step.running .step-icon { color: var(--accent); }
.step-detail { color: var(--fg-dim); font-size: 12px; }

/* ── Modal / toasts ───────────────────────────────────────────────────── */
.modal-backdrop {
  position: fixed; inset: 0; background: rgb(0 0 0 / .55); backdrop-filter: blur(2px);
  display: grid; place-items: center; z-index: 100; padding: 1rem;
}
.modal {
  background: var(--bg-1); border: 1px solid var(--line); border-radius: 12px;
  padding: 1.25rem; width: 100%; max-width: 720px; max-height: 85vh; overflow-y: auto;
}
.modal-actions { display: flex; gap: .5rem; justify-content: flex-end; margin-top: 1rem; }

.toasts { position: fixed; bottom: 1rem; right: 1rem; display: flex; flex-direction: column; gap: .5rem; z-index: 200; max-width: min(440px, calc(100vw - 2rem)); }
.toast {
  background: var(--bg-1); border: 1px solid var(--line); border-left-width: 3px;
  border-radius: 8px; padding: .65rem .85rem; font-size: 13px;
  box-shadow: 0 6px 24px rgb(0 0 0 / .3); animation: slide-in .18s ease-out;
}
.toast.ok { border-left-color: var(--ok); }
.toast.error { border-left-color: var(--err); }
.toast.info { border-left-color: var(--accent); }
.toast pre { margin: .4rem 0 0; white-space: pre-wrap; font-size: 11.5px; color: var(--fg-dim); max-height: 180px; overflow-y: auto; }
@keyframes slide-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

.spinner {
  display: inline-block; width: 13px; height: 13px; border: 2px solid var(--line);
  border-top-color: var(--accent); border-radius: 50%; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

@media (max-width: 720px) {
  .topbar { gap: .5rem; }
  .tabs { order: 3; width: 100%; }
  .topbar-right { margin-left: auto; }
  main { padding: .85rem; }
}

/* ── Portal ──────────────────────────────────────────────────────────────
   One card per application (not per vhost file): the :80 redirect and the
   :443 vhost of the same ServerName are the same site and are grouped by the
   API before they get here. */
.portal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: .75rem;
}

.portal-card {
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  min-width: 0;              /* let long hostnames wrap instead of stretching the grid */
}

.portal-card.is-disabled { opacity: .55; }

.portal-card-head {
  display: flex;
  align-items: center;
  gap: .4rem;
  flex-wrap: wrap;
}

.portal-name {
  font-weight: 600;
  font-size: 1.02rem;
  overflow-wrap: anywhere;
}

.portal-link {
  font-family: var(--mono);
  font-size: .88rem;
  overflow-wrap: anywhere;
}

.portal-meta {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  font-size: .8rem;
  color: var(--fg-dim);
}

.portal-meta .mono { font-family: var(--mono); overflow-wrap: anywhere; }

/* Used by the self-signed dialog to make the "not trusted by anything" point
   impossible to skim past. */
.warn-block {
  border-left: 3px solid var(--warn);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  padding: .5rem .7rem;
  border-radius: 0 4px 4px 0;
}

/* ── Modal editor ────────────────────────────────────────────────────────
   The vhost editor is tall, and the modal scrolls internally (max-height:
   85vh), so a static action row sits below the fold — the Save buttons look
   absent until you scroll the dialog. Pinning them to the bottom of the scroll
   area keeps them on screen whatever the file's length. */
.actions-sticky {
  position: sticky;
  bottom: -1.25rem;                 /* cancels .modal's padding */
  margin-top: .75rem;
  padding: .75rem 0 1.25rem;
  background: var(--bg-1);
  border-top: 1px solid var(--line);
}

/* Sized against the viewport rather than a fixed row count, so the buttons
   below stay visible on a laptop screen as well as a large one. */
textarea.editor {
  width: 100%;
  height: min(52vh, 32rem);
  min-height: 12rem;
}

/* ── Progress bar ────────────────────────────────────────────────────────
   Indeterminate on purpose. Save runs write → configtest → reload inside a
   single request, so there is no intermediate state to report and any
   percentage would be invented. The bar says "working", the step list says
   what the work is, and both resolve from the actual response. */
.progress {
  height: 4px; width: 100%; margin: .75rem 0 .25rem;
  background: var(--bg-3); border-radius: 2px; overflow: hidden;
}
.progress-bar {
  height: 100%; width: 40%; border-radius: 2px;
  background: var(--accent);
  animation: progress-slide 1.1s ease-in-out infinite;
}
@keyframes progress-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}
.step.muted .step-icon, .step.muted { color: var(--fg-mute); }
