/* ── Design tokens ─────────────────────────────────────────────────────── */
:root {
  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  --radius: 10px;
  --radius-sm: 6px;
  --space: 16px;
  --maxw: 1100px;

  /* Light palette (default). */
  --bg: #f6f7f9;
  --bg-elevated: #ffffff;
  --bg-sunken: #eceef1;
  --fg: #1b1f24;
  --fg-muted: #5c6670;
  --border: #d8dce1;
  --border-strong: #c2c8cf;
  --accent: #2563eb;
  --accent-fg: #ffffff;
  --accent-weak: #e8f0fe;
  --success: #15803d;
  --success-weak: #e6f4ea;
  --danger: #b91c1c;
  --danger-weak: #fdecec;
  --warning: #b45309;
  --shadow: 0 1px 2px rgba(16, 24, 40, 0.06), 0 4px 12px rgba(16, 24, 40, 0.08);
  --focus: 0 0 0 3px rgba(37, 99, 235, 0.4);
}

[data-theme="dark"] {
  --bg: #0f1318;
  --bg-elevated: #171c23;
  --bg-sunken: #0b0e12;
  --fg: #e6e9ee;
  --fg-muted: #9aa4b0;
  --border: #2a313b;
  --border-strong: #3a434f;
  --accent: #4f8bff;
  --accent-fg: #0a0d11;
  --accent-weak: #16263f;
  --success: #4ade80;
  --success-weak: #11261a;
  --danger: #f87171;
  --danger-weak: #2a1414;
  --warning: #fbbf24;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 6px 18px rgba(0, 0, 0, 0.45);
  --focus: 0 0 0 3px rgba(79, 139, 255, 0.45);
}

/* ── Base ──────────────────────────────────────────────────────────────── */
* { box-sizing: border-box; }

html, body { height: 100%; }
/* Allow panning/scrolling but disable pinch-zoom and double-tap zoom on touch
   devices (the terminal owns its own layout). */
html { touch-action: pan-x pan-y; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.5;
  color: var(--fg);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

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

:focus-visible { outline: none; box-shadow: var(--focus); border-radius: var(--radius-sm); }

h1, h2, h3 { margin: 0 0 0.4em; line-height: 1.25; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.2rem; }
p { margin: 0 0 0.8em; }

code, kbd, .mono { font-family: var(--font-mono); font-size: 0.92em; }

/* ── Layout ────────────────────────────────────────────────────────────── */
#app { min-height: 100%; display: flex; flex-direction: column; }

.app-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--space);
  padding: calc(10px + env(safe-area-inset-top)) calc(20px + env(safe-area-inset-right))
           10px calc(20px + env(safe-area-inset-left));
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
}
.app-header .brand {
  font-weight: 700;
  letter-spacing: 0.2px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.app-header .brand .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent);
}
.app-header .spacer { flex: 1; }
.app-header .meta { color: var(--fg-muted); font-size: 0.9rem; }

.app-main {
  flex: 1;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 24px calc(20px + env(safe-area-inset-right))
           calc(48px + env(safe-area-inset-bottom)) calc(20px + env(safe-area-inset-left));
}

.center-host {
  flex: 1;
  display: grid;
  place-items: center;
  padding: 24px;
}

/* ── Components ─────────────────────────────────────────────────────────── */
.card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 24px;
}
.card.pad-lg { padding: 32px; }
.card.narrow { width: 100%; max-width: 420px; }

.stack { display: flex; flex-direction: column; gap: 14px; }
.row { display: flex; align-items: center; gap: 10px; }
.row.wrap { flex-wrap: wrap; }
.grow { flex: 1; }

.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 0.85rem; color: var(--fg-muted); font-weight: 600; }
.field .hint { font-size: 0.8rem; color: var(--fg-muted); }

input[type="text"], input[type="password"], input[type="url"], input[type="number"], select {
  width: 100%;
  padding: 10px 12px;
  font: inherit;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
}
input:focus-visible { box-shadow: var(--focus); border-color: var(--accent); }

.check { display: flex; align-items: center; gap: 8px; font-size: 0.9rem; }
.check input { width: auto; }

/* Radio group (settings panel: dimension preset + text size). */
.radio-group { display: flex; flex-direction: column; gap: 8px; }
.radio-group.inline { flex-direction: row; flex-wrap: wrap; gap: 12px; }
.radio { display: flex; align-items: center; gap: 8px; font-size: 0.9rem; }
.radio input { width: auto; }
.settings-panel h3 { font-size: 1rem; margin: 0; }
/* In the flex column, hr's default margin-inline:auto collapses it to 0 width;
   reset it to a full-width divider. */
.settings-panel hr { width: 100%; margin: 0; border: 0; border-top: 1px solid var(--border); }
.settings-panel .custom-dims { gap: 12px; flex-wrap: wrap; }
.settings-panel .custom-dims .field { flex: 1; min-width: 90px; }
.settings-panel .custom-dims.disabled { opacity: 0.5; }

button {
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg-elevated);
  color: var(--fg);
  transition: background 0.12s ease, border-color 0.12s ease;
}
button:hover { background: var(--bg-sunken); }
button:disabled { opacity: 0.55; cursor: not-allowed; }
button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-fg);
}
button.primary:hover { filter: brightness(1.06); background: var(--accent); }
button.ghost { background: transparent; border-color: transparent; }
button.ghost:hover { background: var(--bg-sunken); }
button.icon { padding: 8px; line-height: 1; }
/* The settings gear reads a touch small at the base size; nudge the glyph up. */
button.settings-icon { font-size: 1.2rem; }
button.danger { color: var(--danger); border-color: var(--border-strong); }
button.danger:hover { background: var(--danger-weak); }
/* Compact button (e.g. the sessions toolbar's New Session / Refresh); the
   `button.` prefix keeps it winning over the larger mobile touch padding. */
button.btn-sm { padding: 6px 10px; font-size: 0.82rem; }

.badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.78rem; font-weight: 600;
  padding: 3px 9px; border-radius: 999px;
  background: var(--bg-sunken); color: var(--fg-muted);
  border: 1px solid var(--border);
}
.badge.online { background: var(--success-weak); color: var(--success); border-color: transparent; }
.badge.offline { background: var(--bg-sunken); color: var(--fg-muted); }
.badge .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

.banner {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 14px; border-radius: var(--radius-sm);
  border: 1px solid transparent;
}
.banner.error { background: var(--danger-weak); color: var(--danger); border-color: var(--danger); }
.banner.warning { background: var(--danger-weak); color: var(--warning); }
.banner .hint { color: var(--fg-muted); font-size: 0.85rem; margin-top: 2px; }

.muted { color: var(--fg-muted); }
.version-footer { text-align: center; margin: 4px 0 0; opacity: 0.8; }
.empty {
  text-align: center; color: var(--fg-muted);
  padding: 48px 16px;
}

.spinner {
  width: 18px; height: 18px;
  border: 2px solid var(--border-strong);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 1.6s; }
  button, input { transition: none; }
}

.list { display: flex; flex-direction: column; gap: 10px; }
.list-item {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 16px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-align: left;
  width: 100%;
  cursor: pointer;
  color: inherit;
}
.list-item:hover { border-color: var(--border-strong); background: var(--bg-elevated); }
/* The last-interacted session in the list gets an accent ring so it's easy to
   spot after arriving from a node's session preview. */
.list-item.highlight {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.list-item .title { font-weight: 600; }
.list-item .sub { color: var(--fg-muted); font-size: 0.85rem; }

/* Session action buttons stay compact and on one line. */
.session-actions { flex-wrap: nowrap; gap: 6px; flex: 0 0 auto; }
.session-actions button { padding: 6px 10px; font-size: 0.82rem; }

/* The running-program badge sits beside the state badge, accented and
   truncated so a long command doesn't blow out the row. */
.badge.cmd {
  background: var(--success-weak);
  color: var(--success);
  border-color: transparent;
  max-width: 16ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-block;
}

.kv { display: grid; grid-template-columns: max-content 1fr; gap: 6px 18px; }
.kv dt { color: var(--fg-muted); }
/* Truncate long values (e.g. a UID) with an ellipsis so they don't widen the
   page on a phone. min-width:0 lets the grid cell shrink below its content. */
.kv dd {
  margin: 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 18px; }
.toolbar h1 { margin: 0; }
/* The sessions toolbar heading sits above the big node-id title, so keep it a
   touch smaller. */
.toolbar h1.sessions-title { font-size: 1.15rem; }
/* The node id title grows to push the action buttons right, truncating rather
   than overflowing the toolbar on narrow screens. */
.node-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tighten the terminal screen: no extra gap between the toolbar and the
   terminal (the generic toolbar margin would otherwise stack with the column
   gap and waste vertical space). */
.terminal-screen { gap: 8px; }
.terminal-screen .toolbar { margin-bottom: 0; }

/* The terminal screen's toolbar carries several controls (Sessions, Fullscreen,
   Detach, Terminate); keep its buttons compact and let the title shrink so the
   whole row fits a phone width. */
.terminal-screen .toolbar { gap: 8px; }
.terminal-screen .toolbar h1 {
  font-size: 1rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.terminal-screen .toolbar button { padding: 6px 10px; font-size: 0.85rem; }

/* Terminal */
.terminal-card { padding: 8px; background: #0b0e12; }
.terminal-host {
  width: 100%;
  /* dvh so the terminal shrinks (rather than hiding under) the soft keyboard;
     vh fallback for browsers without dvh, and a min-height floor so the fit
     addon always has a measurable, non-zero container. */
  height: 60vh;
  height: 60dvh;
  min-height: 260px;
  max-height: 600px;
}
.terminal-host .xterm { height: 100%; }
/* The terminal palette is dark; in the light theme invert it so it reads as a
   light terminal instead of a dark box on a light page. The card frame is
   painted to match the terminal background, so set it to the inverted+hue-
   rotated colour (#eff2f6) — otherwise a dark border rings the inverted box. */
[data-theme="light"] .terminal-host { filter: invert(1) hue-rotate(180deg); }
[data-theme="light"] .terminal-card { background: #eff2f6; }

/* On-screen accessory key bar (touch devices lack Esc/Tab/Ctrl/arrows). */
.term-accessory {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding: 8px;
  padding-bottom: calc(8px + env(safe-area-inset-bottom));
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.term-accessory button {
  flex: 0 0 auto;
  /* Sized via --term-key-scale (set per session from the terminal font) so the
     keys shrink/grow with the text; defaults to 1 = the original sizing. */
  min-width: calc(44px * var(--term-key-scale, 1));
  min-height: calc(40px * var(--term-key-scale, 1));
  padding: calc(8px * var(--term-key-scale, 1)) calc(12px * var(--term-key-scale, 1));
  font-size: calc(0.9rem * var(--term-key-scale, 1));
}
.term-accessory button.active {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
}
/* The last-used quick key and the `Ctrl ▾` trigger read as one segmented
   control: joined edges, a single shared divider, only the outer corners
   rounded. */
.term-ctrl-group { display: inline-flex; flex: 0 0 auto; }
.term-ctrl-group > button { border-radius: 0; position: relative; }
.term-ctrl-group > button:first-child {
  border-top-left-radius: var(--radius);
  border-bottom-left-radius: var(--radius);
}
.term-ctrl-group > button:last-child {
  border-top-right-radius: var(--radius);
  border-bottom-right-radius: var(--radius);
  margin-left: -1px; /* collapse the shared border into one divider */
}
/* Raise the focused/hovered/armed edge so its border sits above the overlap. */
.term-ctrl-group > button:hover,
.term-ctrl-group > button:focus,
.term-ctrl-group > button.active { z-index: 1; }

/* The Ctrl button carries a caret to signal it opens a menu. */
.term-accessory button.has-menu::after {
  content: '▾';
  margin-left: 4px;
  font-size: 0.8em;
  opacity: 0.7;
}

/* Ctrl combinations popup, anchored above the Ctrl button (positioned in JS). */
.ctrl-menu {
  z-index: 45;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
  max-width: calc(100vw - 16px);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.ctrl-menu-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
}
.ctrl-menu-item { min-width: 40px; min-height: 36px; padding: 6px 8px; }
.ctrl-menu-custom { display: flex; align-items: center; gap: 6px; }
.ctrl-menu-label { opacity: 0.8; }
.ctrl-custom-input {
  width: 44px;
  text-align: center;
  text-transform: uppercase;
}
.ctrl-menu-sep { height: 1px; background: var(--border); margin: 2px 0; }
.ctrl-menu-arm { width: 100%; }

/* Terminal screen: lock the page to the viewport so only the terminal's own
   content scrolls — no body/page scroll or mobile rubber-banding. The terminal
   card flexes to fill the space below the toolbar, and xterm's viewport handles
   scrollback. Scoped to `.terminal-active` so other screens scroll normally. */
html.terminal-active { height: 100%; overflow: hidden; overscroll-behavior: none; }
/* Anchor the app to the *visible* viewport, not the layout viewport: on iOS
   `height: 100%` is the layout viewport, which is taller than the visible area
   (Safari's bottom toolbar), so the key bar/last row would fall below the edge
   with no scroll to reach them. --vvh (visualViewport) is exact; 100dvh is the
   CSS fallback that already accounts for the dynamic toolbars. */
html.terminal-active body {
  height: var(--vvh, 100dvh);
  overflow: hidden;
  overscroll-behavior: none;
}
html.terminal-active #app { height: 100%; min-height: 0; }
html.terminal-active .app-main {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
/* Fill the available height ONLY when the screen is in normal (in-flow) layout.
   In fullscreen / keyboard-open it is `position: fixed` and sized by inset +
   `bottom: var(--kb)`; setting `height: 100%` there would over-constrain the box
   (top + height wins, so the keyboard `bottom` lift is ignored) and on iOS a
   fixed `height: 100%` resolves taller than the visible viewport. */
html.terminal-active:not(.term-fullscreen):not(.keyboard-open) .terminal-screen {
  height: 100%;
  min-height: 0;
}
html.terminal-active .terminal-card { flex: 1; min-height: 0; }
html.terminal-active .terminal-host { height: 100%; min-height: 0; max-height: none; }
/* The terminal's definite height (viewport − header/toolbar − key bar) is applied
   inline by the screen controller (overriding the 100% above), and cleared in
   fullscreen / while the keyboard is open so the fixed-layout CSS sizes it. The
   100% here is only the pre-script fallback. */
/* Keep scroll chaining inside the terminal even at the scrollback ends. */
.terminal-host .xterm-viewport { overscroll-behavior: contain; }

/* Fullscreen terminal: fill the whole window, hide app chrome. */
.term-fullscreen .app-header { display: none; }
.term-fullscreen .app-main { max-width: none; padding: 0; }

/* Bind the terminal to the *visual* viewport — the area above the on-screen
   keyboard — in fullscreen, and whenever the soft keyboard is open in any mode.
   The keyboard overlays content (it doesn't shrink the layout viewport), so
   without this the accessory key bar and the prompt sit hidden behind it. As a
   fixed, --vvh-tall flex column [toolbar, terminal(flex:1), accessory], the key
   bar lands just above the keyboard and the terminal (re-fit from Dart on
   visualViewport resize) keeps its prompt row visible. --vvh is set by boot.js;
   it falls back to the full dynamic viewport when there's no keyboard. */
.term-fullscreen .terminal-screen,
.keyboard-open .terminal-screen {
  position: fixed;
  /* Pin to the layout viewport, then raise the bottom edge above the keyboard
     by its measured inset (--kb, 0 when closed). On iOS `position: fixed`
     resolves against the layout viewport — which the keyboard does NOT shrink —
     so lifting `bottom` is far more reliable than sizing by visual height. */
  inset: 0;
  bottom: var(--kb, 0px);
  z-index: 40;
  gap: 0;
  background: #0b0e12;
}
/* Hide the screen toolbar (Sessions / Detach / Terminate / toggle) in
   fullscreen. With just the keyboard open it stays, so its controls remain
   reachable while typing. */
.term-fullscreen .terminal-screen > .toolbar { display: none; }

.term-fullscreen .terminal-card,
.keyboard-open .terminal-card {
  flex: 1;
  border: 0;
  border-radius: 0;
  padding: 4px;
  padding-top: calc(4px + env(safe-area-inset-top));
}
.term-fullscreen .terminal-host,
.keyboard-open .terminal-host {
  height: 100%;
  min-height: 0;
  max-height: none;
}
.term-fullscreen .term-accessory,
.keyboard-open .term-accessory { border-radius: 0; }
/* The keyboard already covers the home-indicator inset, so drop the extra
   bottom padding that would otherwise lift the key bar away from it. */
.keyboard-open .term-accessory { padding-bottom: 8px; }

/* Floating exit button — only shown in fullscreen. */
.term-exit-fullscreen { display: none; }
.term-fullscreen .term-exit-fullscreen {
  display: inline-flex;
  position: fixed;
  top: calc(8px + env(safe-area-inset-top));
  right: calc(8px + env(safe-area-inset-right));
  z-index: 41;
  opacity: 0.85;
}

/* Modal */
.modal-overlay {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(8, 11, 15, 0.55);
  display: grid; place-items: center; padding: 20px;
}
.modal {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  width: 100%; max-width: 720px;
  max-height: 85vh; display: flex; flex-direction: column;
}
.modal-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 18px; border-bottom: 1px solid var(--border);
}
.modal-head h2 { margin: 0; flex: 1; font-size: 1.05rem; }
.modal-body { padding: 18px; overflow: auto; }
.modal-foot {
  display: flex; justify-content: flex-end; gap: 10px;
  padding: 14px 18px; border-top: 1px solid var(--border);
}
.screen-capture {
  margin: 0; padding: 14px;
  background: var(--bg-sunken); color: var(--fg);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: 0.85rem;
  white-space: pre-wrap; word-break: break-word;
  max-height: 60vh; overflow: auto;
}

/* Toast container */
#toasts {
  position: fixed;
  right: calc(16px + env(safe-area-inset-right));
  bottom: calc(16px + env(safe-area-inset-bottom));
  z-index: 100;
  display: flex; flex-direction: column; gap: 8px;
}
.toast {
  background: var(--bg-elevated); color: var(--fg);
  border: 1px solid var(--border); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); box-shadow: var(--shadow);
  padding: 10px 14px; max-width: 360px;
}
.toast.error { border-left-color: var(--danger); }
.toast.success { border-left-color: var(--success); }

@media (max-width: 640px) {
  .app-header {
    padding: calc(10px + env(safe-area-inset-top)) calc(14px + env(safe-area-inset-right))
             10px calc(14px + env(safe-area-inset-left));
  }
  .app-main {
    padding: 18px calc(14px + env(safe-area-inset-right))
             calc(40px + env(safe-area-inset-bottom)) calc(14px + env(safe-area-inset-left));
  }
  .app-header .meta.hide-sm { display: none; }
  /* Buttons get comfortable touch targets on small screens. */
  button { padding: 11px 16px; }
  .list-item { padding: 16px; }
  /* Sessions: stack the info over a full-width action row so the (smaller)
     buttons fit on a single line on a phone / vertical screen. */
  .session-item { flex-direction: column; align-items: stretch; gap: 10px; }
  .session-actions { justify-content: flex-start; }
  .session-actions button { flex: 1 1 auto; padding: 8px 6px; font-size: 0.82rem; }
  /* Terminal toolbar stays compact and wraps instead of overflowing; the
     spacer is dropped so the controls pack tightly across the row. */
  .terminal-screen .toolbar { flex-wrap: wrap; gap: 6px; }
  .terminal-screen .toolbar .grow { display: none; }
  .terminal-screen .toolbar button { padding: 6px 9px; font-size: 0.8rem; }
  .terminal-screen .toolbar h1 { font-size: 0.9rem; }
  /* Login card uses more of the screen on phones: trim the outer gutter
     (respecting safe-area insets) and the card's inner padding so the form
     fields get extra width instead of being boxed in. The 420px cap still
     keeps it from sprawling on larger phones/tablets. */
  .center-host {
    padding: 16px calc(12px + env(safe-area-inset-right))
             16px calc(12px + env(safe-area-inset-left));
  }
  .card.pad-lg { padding: 24px; }
}
