:root {
  --bg: #0f1115;
  --panel: #171a21;
  --panel2: #1f2430;
  --accent: #3b82f6;
  --danger: #ef4444;
  --ok: #22c55e;
  --fg: #e5e7eb;
  --muted: #9ca3af;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  background: var(--bg);
  color: var(--fg);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1rem;
}
header { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
/* Account, dashboard link and language picker sit together at the far right, so
   none of them lands between the title and the status badge, which are read as
   a pair. The group carries the margin-left:auto rather than its first child,
   so it still sits right when the dashboard link is hidden. */
.header-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}
.user-email {
  font-size: 0.78rem;
  color: var(--muted);
  max-width: 14rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#logoutBtn { font-size: 0.8rem; padding: 0.2rem 0.5rem; }
#disconnectBtn { padding: 0.3rem 0.7rem; font-size: 0.8rem; }
#dashLink {
  font-size: 0.8rem;
  color: var(--muted);
  text-decoration: none;
  border: 1px solid #2a2f3a;
  border-radius: 6px;
  padding: 0.2rem 0.5rem;
  white-space: nowrap;
}
#dashLink:hover { color: var(--fg); filter: brightness(1.15); }
#langSelect { font-size: 0.8rem; padding: 0.2rem 0.35rem; }
h1 { font-size: 1.2rem; margin: 0; }
h3 { font-size: 0.85rem; margin: 0 0 0.4rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.badge { font-size: 0.75rem; padding: 2px 8px; border-radius: 999px; }
.badge.idle { background: #2a2f3a; color: var(--muted); }
.badge.connecting { background: #4d3a17; color: #fbbf24; }
.badge.live { background: #12331f; color: var(--ok); }
.badge.error { background: #3a1212; color: var(--danger); }

.panel { background: var(--panel); border: 1px solid #2a2f3a; border-radius: 8px; padding: 0.85rem; }
#host-panel { border-color: #1f3a5f; }
#host-panel .host-grid { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: end; }
#host-panel input[readonly] {
  width: 9rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.06em; color: var(--ok);
}
button.ghost { background: var(--panel2); border: 1px solid #2a2f3a; color: var(--fg); }
button.ghost:hover { filter: brightness(1.15); }
#hostStatus { margin-top: 0.4rem; }
#connect-panel { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: end; }
label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.75rem; color: var(--muted); }
input[type=text], input:not([type]) {
  background: var(--panel2); border: 1px solid #2a2f3a; color: var(--fg);
  padding: 0.5rem 0.6rem; border-radius: 6px; width: 13rem; font-size: 0.95rem;
}
button {
  background: var(--accent); color: #fff; border: 0; padding: 0.55rem 0.9rem;
  border-radius: 6px; cursor: pointer; font-size: 0.9rem;
}
button:hover { filter: brightness(1.1); }
button.danger { background: var(--danger); }
.hidden { display: none !important; }
.muted { color: var(--muted); font-size: 0.8rem; }

/* ---- signed-in home ---- */
/* Sign-in prompt. Vertically centred in what is left of the window rather than
   pinned under the header, so the one action on the page is where the eye
   already is. */
.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  text-align: center;
  min-height: min(60vh, 26rem);
  padding: 2rem 1rem;
}
.hero-mark {
  width: 3rem; height: 3rem;
  fill: none; stroke: var(--accent); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0.9;
}
.hero h2 { font-size: 1.35rem; margin: 0; font-weight: 600; }
.hero p { margin: 0; color: var(--muted); font-size: 0.9rem; max-width: 28rem; line-height: 1.5; }
.btn-primary {
  margin-top: 0.5rem;
  background: var(--accent); color: #fff; text-decoration: none;
  padding: 0.6rem 2rem; border-radius: 6px; font-size: 0.95rem; font-weight: 500;
  display: inline-block;
}
.btn-primary:hover { filter: brightness(1.1); }

.picker-bar { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.7rem; }
.picker-bar h3 { margin: 0; flex: none; }
#pcSearch { margin-left: auto; width: 12rem; font-size: 0.85rem; padding: 0.35rem 0.55rem; }
/* Keeps Refresh at the far right whether or not the search box is showing. */
#pcSearch.hidden + #pcRefresh { margin-left: auto; }
#pcRefresh { font-size: 0.8rem; padding: 0.35rem 0.7rem; }

.pc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.75rem;
}
.pc-card {
  background: var(--panel);
  border: 1px solid #2a2f3a;
  border-radius: 10px;
  padding: 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-height: 8.5rem;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.pc-card.online { border-color: #24402f; }
.pc-card.online:hover { border-color: var(--ok); transform: translateY(-2px); }
.pc-top { display: flex; align-items: center; gap: 0.45rem; min-width: 0; }
.pc-name {
  font-size: 0.98rem; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dot {
  flex: none; width: 8px; height: 8px; border-radius: 50%;
  background: #3a4150;
}
.dot.on { background: var(--ok); box-shadow: 0 0 0 3px rgb(34 197 94 / 18%); }
.pc-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8rem; color: var(--muted); letter-spacing: 0.06em;
}
.pc-status { font-size: 0.78rem; color: var(--muted); }
.pc-card.online .pc-status { color: var(--ok); }
/* margin-top:auto pins the button to the bottom, so a row of cards with names
   of different lengths still has its buttons on one line. */
.pc-connect { margin-top: auto; width: 100%; }
.pc-connect:disabled {
  background: var(--panel2); color: var(--muted);
  border: 1px solid #2a2f3a; cursor: default; filter: none;
}

/* Placeholder cards while the list loads, so the page never flashes an empty
   state it is about to contradict. */
.pc-card.skeleton {
  background: linear-gradient(90deg, var(--panel) 25%, var(--panel2) 50%, var(--panel) 75%);
  background-size: 300% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
  border-color: #22262f;
}
@keyframes shimmer {
  from { background-position: 150% 0; }
  to { background-position: -150% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .pc-card { transition: none; }
  .pc-card.online:hover { transform: none; }
  .pc-card.skeleton { animation: none; }
}

/* Home-page chrome. Chat needs a peer, so it is never right here. The log is
   the only place a diagnostic line lands, so it stays — smaller — once someone
   is signed in and can actually cause one; before sign-in there is nothing in
   it but the ready line. */
body.home-idle #chat-panel { display: none; }
body.home-idle #log { height: 88px; }
body.home-signin #log-panel { display: none; }
/* "idle" is a session status, and there is no session to have a status about
   until someone has signed in and picked a PC. */
body.home-signin #status { display: none; }
/* With the panels gone there is room to centre the sign-in prompt properly. */
body.home-signin #home { flex: 1 1 auto; display: flex; }
body.home-signin .hero { flex: 1 1 auto; }

.pc-empty {
  color: var(--muted); font-size: 0.85rem; line-height: 1.5;
  padding: 1.5rem 0.25rem; text-align: center;
}
#pcError { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; }
#pcErrorText { margin: 0; font-size: 0.85rem; color: var(--danger); }

#session { display: flex; flex-direction: column; gap: 0.5rem; }
.stage {
  position: relative; background: #000; border-radius: 8px; overflow: hidden;
  display: flex; justify-content: center; align-items: center; min-height: 200px;
  border: 1px solid #2a2f3a;
}
#video {
  max-width: 100%;
  max-height: calc(100vh - 100px);
  display: block;
  cursor: none;
  background: #000;
  object-fit: contain;
}
/* Browser cursor is always hidden over the video so only the smooth overlay
   cursor is visible. Outside the video (buttons, URL field, etc.) the normal
   cursor shows. */
#video.grabbing { cursor: none; }

/* Host advisory banner. Anchored to the top of the stage so it is legible over
   a black picture, which is exactly when it matters. */
#notice {
  position: absolute;
  top: 0.6rem;
  left: 50%;
  transform: translateX(-50%);
  max-width: min(46rem, calc(100% - 1.2rem));
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.5rem 0.75rem;
  border-radius: 6px;
  background: #4a2b12;
  border: 1px solid #b5651d;
  color: #ffdcb0;
  font-size: 0.85rem;
  line-height: 1.35;
  box-shadow: 0 2px 10px rgb(0 0 0 / 45%);
  z-index: 3;
  /* The banner overlaps the video, whose pointer handlers drive remote input.
     Letting it swallow events would make that strip of the remote screen
     unclickable, so only the close button is interactive. */
  pointer-events: none;
}
#noticeClose {
  pointer-events: auto;
  flex: none;
  padding: 0 0.25rem;
  background: none;
  border: none;
  color: inherit;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

/* Live session: shrink page chrome so the stage can use almost the whole
   window. Encode/input lag paths are unchanged — this is CSS only. */
body.session-live {
  padding: 0.35rem 0.5rem;
  gap: 0.35rem;
  height: 100%;
  overflow: hidden;
}
body.session-live header { gap: 0.5rem; }
body.session-live header h1 { font-size: 1rem; }
body.session-live #connect-panel { padding: 0.45rem 0.65rem; }
body.session-live #chat-panel,
body.session-live #log-panel { display: none; }
body.session-live #session {
  flex: 1 1 auto;
  min-height: 0;
  gap: 0.35rem;
}
body.session-live .stage {
  flex: 1 1 auto;
  min-height: 0;
  border-radius: 4px;
}
body.session-live #video {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
}

/* True browser fullscreen on the stage: edge-to-edge, no page padding. */
.stage:fullscreen,
.stage:-webkit-full-screen {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  background: #000;
  border-radius: 0;
  border: none;
  display: flex;
  justify-content: center;
  align-items: center;
}
.stage:fullscreen #video,
.stage:-webkit-full-screen #video {
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
#cursor {
  position: fixed; left: 0; top: 0;
  pointer-events: none; z-index: 5;
  will-change: transform; display: none;
}
#cursor svg { display: block; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.4)); }
.toolbar { display: flex; flex-wrap: wrap; gap: 0.5rem 0.9rem; align-items: center; }
/* Each concern (View / Input / Transfer) is one flex item that itself lays its
   controls out inline, so the bar wraps BETWEEN groups, never mid-group. */
.tb-group { display: inline-flex; align-items: center; gap: 0.5rem; }
/* Divider before every group after the first. */
.tb-group + .tb-group { border-left: 1px solid #2a2f3a; padding-left: 0.9rem; }
/* Small muted caption naming the group, matching the panel <h3> label idiom. */
.tb-group-label {
  font-size: 0.7rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.05em;
}
/* Override the stacked label layout for the inline toolbar. */
.toolbar label { flex-direction: row; align-items: center; gap: 0.4rem; }
.toolbar select {
  background: var(--panel2); border: 1px solid #2a2f3a; color: var(--fg);
  padding: 0.45rem 0.6rem; border-radius: 6px; font-size: 0.85rem; cursor: pointer;
}
/* Inline "type text" field: a password box + send button, revealed by the
   Type-text toolbar button. */
.typebox { display: inline-flex; align-items: center; gap: 0.4rem; }
.typebox.hidden { display: none; }
.typebox input {
  background: var(--panel2); border: 1px solid #2a2f3a; color: var(--fg);
  padding: 0.45rem 0.6rem; border-radius: 6px; font-size: 0.85rem; min-width: 12rem;
}
#fileName { font-size: 0.82rem; }
#sendBtn { min-width: 5rem; }
#sendBtn:disabled { opacity: 0.5; cursor: default; }
#filestatus { font-size: 0.82rem; }
#filestatus.status-busy { color: #fbbf24; }
#filestatus.status-ok { color: var(--ok); font-weight: 600; }
#filestatus.status-err { color: var(--danger); }

/* ---- remote file browser ---- */
.files-bar { display: flex; align-items: center; gap: 0.4rem; margin-bottom: 0.5rem; flex-wrap: wrap; }
/* The path can be long and is not breakable at spaces, so let it scroll on its
   own rather than forcing the panel wider than the viewport. */
#filesPath { font-family: ui-monospace, monospace; overflow-x: auto; white-space: nowrap; flex: 1; min-width: 0; }
#filesList { max-height: 18rem; overflow-y: auto; border: 1px solid #2a2f3a; border-radius: 6px; }
#filesList:empty { display: none; }
/* Icons are inline SVG drawn as strokes on a 20x20 grid, sized in em so they
   track whatever text they sit beside and coloured by currentColor so they
   inherit a ghost button, a disabled control or a folder row without a rule
   each. */
.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Any control holding an icon and a label is a row of two things, not a line of
   text — otherwise the SVG sits on the baseline and the label rides above it. */
button:has(> .icon) { display: inline-flex; align-items: center; gap: 0.4rem; }
/* Folders and drives read at a glance; the icon is dimmer than the name so the
   name is still what the eye lands on. */
.file-row .icon { color: var(--muted); }
.file-row.dir .icon { color: var(--accent); }

.file-row { display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0.5rem; font-size: 0.85rem; }
.file-row + .file-row { border-top: 1px solid #22262f; }
/* The icon and the name are one clickable pair, so the span is a flex row and
   the ellipsis moves onto the label inside it. */
.file-row .fname { flex: 1; min-width: 0; display: flex; align-items: center; gap: 0.45rem; }
.file-row .fname > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-row .fsize { color: var(--muted); font-variant-numeric: tabular-nums; }
.file-row button { font-size: 0.75rem; padding: 0.1rem 0.5rem; }
/* Directories are the navigable half of the list, so they get the affordance. */
.file-row.dir .fname { color: var(--fg); cursor: pointer; }
.file-row.dir .fname > span { text-decoration: underline; text-underline-offset: 2px; }
.file-row.dir .fname:hover { filter: brightness(1.2); }
#filesErr { color: var(--danger); font-size: 0.8rem; }

#chat-panel { display: flex; flex-direction: column; gap: 0.4rem; }
#chatLog {
  height: 120px; overflow: auto; background: #0b0d12; border-radius: 6px;
  padding: 0.5rem; font-size: 0.82rem; line-height: 1.4;
}
.chat-msg { padding: 2px 0; white-space: pre-wrap; word-break: break-word; }
.chat-msg.me { color: var(--accent); }
.chat-msg.them { color: var(--fg); }
.chat-row { display: flex; gap: 0.5rem; }
#chatInput { flex: 1; width: auto; }
.filebtn {
  display: inline-flex; align-items: center; background: var(--panel2);
  border: 1px solid #2a2f3a; padding: 0.45rem 0.8rem; border-radius: 6px;
  cursor: pointer; color: var(--fg); font-size: 0.85rem;
}

#log {
  height: 140px; overflow: auto; background: #0b0d12; border-radius: 6px;
  padding: 0.5rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem; line-height: 1.35;
}
#log div { border-bottom: 1px solid #161a22; padding: 1px 0; white-space: pre-wrap; word-break: break-all; }
#log .err { color: var(--danger); }
#log .ok { color: var(--ok); }
