:root {
  --bg: #0e1116;
  --bg-elevated: #161b23;
  --bg-inset: #0a0d12;
  --border: #262d38;
  --text: #e6e9ef;
  --text-muted: #8b95a5;
  --accent: #4c8dff;
  --accent-strong: #2f6fe4;
  --ok: #3fbf7f;
  --warn: #e5a13a;
  --error: #f0616d;
  --radius: 10px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* {
  box-sizing: border-box;
}

/* `hidden` only removes an element in the UA stylesheet's `display: none`, and
   ANY author `display` declaration beats that regardless of specificity. Both
   top-level views set their own: `.centered` is a grid, `.workspace` is a flex
   column. So `hidden` did nothing on them, and because each is `flex: 1 1 auto`
   they split the page: the login card stayed on screen after signing in and the
   viewport was squeezed into the lower half of the window.

   Per-element `[hidden]` overrides existed for `#viewport-canvas`, `.placeholder`
   and `.overlay`, which is exactly how the two views came to be forgotten. One
   authoritative rule is harder to get wrong than remembering to add a fourth. */
[hidden] {
  display: none !important;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  display: flex;
  flex-direction: column;
}

/* ------------------------------------------------------------------ brand */

.brand {
  font-weight: 650;
  letter-spacing: 0.2px;
}

.panel-head-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.user {
  color: var(--text-muted);
  font-size: 13px;
}

.badge {
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.badge-demo {
  background: rgba(229, 161, 58, 0.15);
  border: 1px solid rgba(229, 161, 58, 0.45);
  color: var(--warn);
}

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

main {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

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

.card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px;
}

.login-card {
  width: min(380px, 100%);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.login-card h1 {
  margin: 0;
  font-size: 20px;
}

/* The stage is the whole tab and the panel floats over it, so neither the grid
   column nor the padding that used to surround the viewport exists any more. */
.workspace {
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
  overflow: hidden;
}

/* --------------------------------------------------------- panel controls */

.panel-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* The panel is narrow, so the address bar stacks: the field takes a row of its
   own and the four buttons share the row below, instead of five controls
   competing for one line. */
.urlbar {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 12px;
}

.urlbar-actions {
  display: flex;
  gap: 6px;
}

.urlbar-actions .btn-icon {
  flex: 1 1 0;
}

.urlbar input {
  width: 100%;
  min-width: 0;
  background: var(--bg-inset);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  padding: 8px 10px;
  font-family: var(--mono);
  font-size: 12.5px;
}

.urlbar input:disabled {
  opacity: 0.55;
}

.urlbar input:focus {
  outline: none;
  border-color: var(--accent);
}

.pill {
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  background: rgba(139, 149, 165, 0.15);
  color: var(--text-muted);
  border: 1px solid var(--border);
}

.pill[data-status='starting'],
.pill[data-status='stopping'] {
  background: rgba(229, 161, 58, 0.15);
  border-color: rgba(229, 161, 58, 0.45);
  color: var(--warn);
}

.pill[data-status='running'] {
  background: rgba(63, 191, 127, 0.15);
  border-color: rgba(63, 191, 127, 0.45);
  color: var(--ok);
}

.pill[data-status='failed'] {
  background: rgba(240, 97, 109, 0.15);
  border-color: rgba(240, 97, 109, 0.45);
  color: var(--error);
}

/* ----------------------------------------------------------------- buttons */

.btn {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text);
  border-radius: 8px;
  padding: 8px 14px;
  font: inherit;
  font-weight: 550;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, opacity 120ms ease;
}

.btn:hover:not(:disabled) {
  border-color: #39424f;
  background: #1c222c;
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-primary {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: #fff;
}

.btn-primary:hover:not(:disabled) {
  background: var(--accent);
  border-color: var(--accent);
}

.btn-ghost {
  background: transparent;
}

.btn-icon {
  padding: 8px 10px;
  min-width: 38px;
  font-size: 14px;
  line-height: 1;
}

/* ---------------------------------------------------------------- viewport */

/* The stream gets the whole tab. The surfaces stay sized to the stream's own
   aspect ratio and are centred here rather than stretched to fill, because
   each element's box has to equal the picture it shows -- the input mapping
   scales pointer positions by that box (see `#toRemote`), so an element larger
   than its picture would send every click to the wrong place. */
.viewport {
  position: absolute;
  inset: 0;
  background: #000;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* One sizing rule for both viewport surfaces: the WebRTC <video> humans watch and
   the <canvas> used when a machine is driving the browser over CDP. They are never
   shown at once, but they occupy the same box, so they must be sized identically or
   switching between them would jump.

   Both fill the stage outright and `object-fit` fits the picture inside that box, so
   the letterbox is the element's own background rather than a gap the surrounding
   layout has to leave. That is the largest the picture can be without cropping or
   distorting it.

   The element is therefore NOT the picture -- it is bigger in one axis. Input is
   mapped against the painted area rather than this box, which is what
   `containedRect` is for; mapping against the box offsets every click by half the
   bar. Enlarging the element does not cost any stream quality, because the encoder
   works from the remote resolution and only the display scale changes. */
.viewport-surface {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  outline: none;
  touch-action: none;
  background: #000;
}

/* Stands in for "you can watch but not type": until the server grants control,
   input is discarded, so this has to be visible rather than silent. */
.control-btn {
  position: absolute;
  top: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  box-shadow: 0 2px 10px rgb(0 0 0 / 45%);
}

.placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-align: center;
  color: var(--text-muted);
  padding: 24px;
}

.overlay {
  position: absolute;
  left: 12px;
  bottom: 12px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(10, 13, 18, 0.85);
  border: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-muted);
}

/* ------------------------------------------------------------------- panel */

/* Floats over the right of the stage rather than taking a column, so the stream
   is never squeezed. `visibility` is transitioned alongside the slide so the
   panel is genuinely gone to the tab order and the accessibility tree while
   closed, without losing the animation to `display: none`. */
.panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(360px, 100%);
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
  overflow-y: auto;
  padding: 14px;
  background: rgba(14, 17, 22, 0.96);
  backdrop-filter: blur(8px);
  border-left: 1px solid var(--border);
  box-shadow: -18px 0 40px rgb(0 0 0 / 45%);
  transform: translateX(102%);
  visibility: hidden;
  transition: transform 180ms ease, visibility 0s linear 180ms;
}

.panel.is-open {
  transform: none;
  visibility: visible;
  transition: transform 180ms ease, visibility 0s linear 0s;
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  /* Keeps the header clear of the toggle, which floats in this corner in both
     states rather than moving with the panel. */
  padding-right: 46px;
}

.panel-toggle {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 6;
  width: 40px;
  height: 40px;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: rgba(22, 27, 35, 0.9);
  backdrop-filter: blur(8px);
  color: var(--text);
  font: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 12px rgb(0 0 0 / 45%);
}

.panel-toggle:hover {
  border-color: #39424f;
  background: #1c222c;
}

.panel-block {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
}

.panel-block h2 {
  margin: 0 0 10px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.7px;
  color: var(--text-muted);
}

.kv {
  display: grid;
  grid-template-columns: 108px minmax(0, 1fr);
  gap: 4px 10px;
  margin: 0;
  font-size: 12.5px;
}

.kv dt {
  color: var(--text-muted);
}

.kv dd {
  margin: 0;
  word-break: break-word;
}

.mono {
  font-family: var(--mono);
  font-size: 11.5px;
}

.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ------------------------------------------------------------------ inputs */

.field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12.5px;
  color: var(--text-muted);
}

.field input,
.field select {
  background: var(--bg-inset);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  padding: 9px 10px;
  font: inherit;
}

.field input:focus,
.field select:focus {
  outline: none;
  border-color: var(--accent);
}

.panel-block .field {
  margin-top: 12px;
}

.error {
  margin: 0;
  color: var(--error);
  font-size: 13px;
}

.muted {
  color: var(--text-muted);
}

/* ------------------------------------------------------------------ toasts */

.toasts {
  position: fixed;
  right: 16px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 50;
  max-width: min(380px, calc(100vw - 32px));
}

.toast {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 13px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  animation: toast-in 140ms ease-out;
}

.toast[data-kind='error'] {
  border-left-color: var(--error);
}

.toast[data-kind='success'] {
  border-left-color: var(--ok);
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* --------------------------------------------------- capture and downloads */

/* The two controls that float next to the panel toggle while the panel is shut.
   They deliberately mirror `.panel-toggle` so the corner reads as one cluster,
   and they hide themselves when they have nothing to offer -- a button that is
   present but useless is worse than no button, because it costs attention. */
.fab {
  position: absolute;
  top: 14px;
  /* Under the toggle (z-index 6), which must stay clickable above everything. */
  z-index: 5;
  height: 40px;
  min-width: 40px;
  padding: 0 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: rgba(22, 27, 35, 0.9);
  backdrop-filter: blur(8px);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 12px rgb(0 0 0 / 45%);
}

.fab:hover {
  border-color: #39424f;
  background: #1c222c;
}

.fab[hidden] {
  display: none;
}

/* Laid out to the left of the panel toggle rather than tucked under it. */
#capture-fab {
  right: 64px;
}

#downloads-fab {
  right: 114px;
}

.fab-time {
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  color: var(--text-muted);
}

/* RED means there is something to record. The control is always present while a
   session runs, so colour is what carries the answer to "is a video available?" --
   waiting to be read from a tooltip is not an answer. */
.fab.is-ready {
  border-color: rgba(240, 97, 109, 0.8);
  background: rgba(240, 97, 109, 0.22);
  color: #fff;
}

/* Recording: the same red, and actually blinking, because the elapsed time alone
   is easy to miss while watching the stream. The pulse was a faint glow before,
   which is not a blink -- this drops the opacity, so it is unmistakable. */
.fab.is-recording {
  border-color: rgba(240, 97, 109, 0.95);
  background: rgba(240, 97, 109, 0.38);
  color: #fff;
  animation: fab-blink 1.2s steps(1, end) infinite;
}

/* A fallback download: busy, and amber rather than red, because it is not a recording.
   It blinks for the same reason a recording does -- it is an operation with a
   beginning and an end that the viewer has to be able to see is still running. */
.fab.is-downloading {
  border-color: rgba(229, 161, 58, 0.9);
  background: rgba(229, 161, 58, 0.22);
  color: #fff;
  animation: fab-blink 1.2s steps(1, end) infinite;
}

/* There is a session but nothing worth recording yet: dimmed and neutral, which
   reads as "not now" rather than as a broken control. */
.fab.is-idle {
  opacity: 0.5;
  cursor: default;
}

.fab.is-idle:hover {
  border-color: var(--border);
  background: rgba(22, 27, 35, 0.9);
}

/* Half the cycle at full opacity, half dimmed: a square wave rather than a fade,
   which is what makes it read as blinking instead of breathing. */
@keyframes fab-blink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.3;
  }
}

.fab-count {
  min-width: 17px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--accent-strong);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 17px;
  text-align: center;
}

.btn-record {
  border-color: rgba(240, 97, 109, 0.5);
}

/* Same convention as the floating button: red means a video is ready to record. */
.btn-record.is-ready {
  background: rgba(240, 97, 109, 0.16);
  border-color: rgba(240, 97, 109, 0.8);
  color: #fff;
}

.btn-record.is-recording {
  background: rgba(240, 97, 109, 0.3);
  border-color: rgba(240, 97, 109, 0.9);
  color: #fff;
}

/* Matching the floating button: a download is amber, not red. */
.btn-record.is-downloading {
  background: rgba(229, 161, 58, 0.18);
  border-color: rgba(229, 161, 58, 0.85);
  color: #fff;
}

.badge-recording {
  background: rgba(240, 97, 109, 0.16);
  border: 1px solid rgba(240, 97, 109, 0.45);
  color: var(--error);
}

/* The word "red" in the explanatory copy really is red, so it points at the colour
   it is describing. */
.rec {
  color: var(--error);
  font-weight: 650;
}

.small {
  margin: 10px 0 0;
  font-size: 11.5px;
  line-height: 1.5;
}

/* The remote downloads list. It scrolls inside the panel because a few
   recordings are taller than the panel itself. */
.file-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 260px;
  overflow-y: auto;
}

.file-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 8px;
  padding: 7px 9px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-inset);
  font-size: 12.5px;
}

.file-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-meta {
  color: var(--text-muted);
  font-size: 11.5px;
  white-space: nowrap;
}
