/* ============================================================
   AIPlayerX Web Viewer — stylesheet
   - Auth panel matches MAUI LoginPage
   - Responsive app shell + auto-fit camera grid
   - Per-tile floating control bar (play/pause, mute, rotate, reconnect)
   ============================================================ */

:root {
  --bg: #F2F2F7;
  --card: #FFFFFF;
  --text: #1C1C1E;
  --muted: #636366;
  --border: #E5E5EA;
  --placeholder: #C7C7CC;
  --primary: #3478F6;
  --primary-dim: rgba(52, 120, 246, .15);
  --danger: #EA4335;
  --success: #34C759;
  --tile-bg: #111827;
  --tile-overlay: rgba(0, 0, 0, .55);
  --shadow-sm: 0 4px 12px rgba(15, 23, 42, .06);
  --shadow-md: 0 12px 32px -12px rgba(15, 23, 42, .18);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body { height: 100%; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC",
               "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

button { font: inherit; }

/* ───── Auth panel (matches MAUI LoginPage) ───── */
.auth-panel {
  position: fixed; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg);
  z-index: 1000;
  padding: 24px;
  overflow-y: auto;
}
.auth-card {
  background: var(--card);
  border-radius: 16px;
  box-shadow: var(--shadow-md);
  padding: 32px;
  width: 100%; max-width: 400px;
  text-align: center;
}
.logo-circle {
  width: 72px; height: 72px;
  border-radius: 36px;
  background: #0C1730;
  margin: 0 auto 20px;
}
.logo-circle.small { width: 36px; height: 36px; border-radius: 18px; margin: 0; }

.auth-card h1 {
  margin: 0 0 6px;
  font-size: 28px; font-weight: 700;
  color: var(--text);
}
.auth-subtitle { margin: 0 0 24px; font-size: 14px; color: var(--muted); }

.auth-form { display: flex; flex-direction: column; gap: 6px; text-align: left; }
.auth-form label {
  font-size: 13px; color: var(--muted);
  margin-top: 8px; font-weight: 500;
}
.auth-form input {
  height: 52px;
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  font-size: 15px;
  background: #fff; color: var(--text);
  width: 100%;
}
.auth-form input::placeholder { color: var(--placeholder); }
.auth-form input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-dim);
}
.auth-form button[type="submit"] {
  margin-top: 20px;
  height: 52px;
  background: var(--primary);
  color: #fff;
  border: none; border-radius: 12px;
  font-size: 16px; font-weight: 600;
  cursor: pointer;
  transition: filter .15s;
}
.auth-form button[type="submit"]:hover { filter: brightness(1.05); }
.auth-form button[type="submit"]:active { filter: brightness(.95); }

.auth-link {
  display: block;
  margin-top: 16px;
  text-align: center;
  color: var(--primary);
  font-size: 13px;
  text-decoration: none;
  cursor: pointer;
}
.auth-link:hover { text-decoration: underline; }

.auth-error {
  margin-top: 16px;
  padding: 10px 14px;
  border-radius: 10px;
  background: rgba(234, 67, 53, .1);
  color: var(--danger);
  font-size: 13px;
  text-align: left;
}

/* ───── App shell ───── */
.app-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.app-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 12px 20px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 50;
}
.brand { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1 1 auto; }
.brand-text { line-height: 1.1; min-width: 0; }
.brand-title { font-size: 16px; font-weight: 700; color: var(--text); }
.brand-sub   { font-size: 12px; color: var(--muted); margin-top: 2px; }

.header-actions {
  display: flex; align-items: center; gap: 10px;
  flex-wrap: nowrap;
  justify-content: flex-end;
  min-width: 0;
  flex: 0 0 auto;
}

.header-actions > .icon-btn,
.header-actions > .link-btn,
.header-actions > .conn-pill {
  flex: 0 0 auto;
}

.header-actions > .conn-pill {
  max-width: 180px;
}

.conn-pill {
  font-size: 12px; padding: 4px 10px; border-radius: 999px;
  background: var(--border); color: var(--muted);
  white-space: nowrap;
  min-width: 0;
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.conn-pill.conn-ok    { background: rgba(52, 199, 89, .15);  color: #1f8a3b; }
.conn-pill.conn-warn  { background: rgba(255, 159, 10, .18); color: #b45309; }
.conn-pill.conn-error { background: rgba(234, 67, 53, .15);  color: var(--danger); }

.icon-btn {
  width: 36px; height: 36px;
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: 10px;
  cursor: pointer;
  font-size: 16px; line-height: 1;
  color: var(--text);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s, border-color .15s;
}
.icon-btn:hover { background: #fafbff; border-color: var(--primary); color: var(--primary); }
.icon-btn:active { transform: scale(.96); }

.user-badge {
  font-size: 13px; color: var(--muted);
  min-width: 0;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 0 1 220px;
}
.link-btn {
  background: none; border: none;
  color: var(--primary); cursor: pointer;
  font-size: 13px; padding: 0;
}
.link-btn:hover { text-decoration: underline; }

.primary-btn {
  background: var(--primary); color: #fff;
  border: none; border-radius: 10px;
  padding: 10px 22px; font-size: 14px; font-weight: 600;
  cursor: pointer;
}
.primary-btn:hover { filter: brightness(1.05); }

/* ───── Content layout ───── */
.content {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 16px;
  padding: 16px;
  flex: 1;
  min-height: 0;
}
/* When the detection panel is hidden, collapse the second column so the
   camera area extends all the way to the browser's right edge. */
.content:has(#detectionPanel[hidden]) {
  grid-template-columns: minmax(0, 1fr);
}
.grid-wrap {
  position: relative;
  min-height: 60vh;
}

/* ───── Camera grid ─────
   Horizontally-scrolling row inside each aspect group. Tiles are
   fixed-size (height set per group, width derived from `aspect-ratio`)
   so the row can hold any number of cameras without affecting the
   page's vertical layout. */
.camera-grid {
  display: flex;
  flex-direction: row;
  gap: 14px;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 4px 4px 14px;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.camera-grid::-webkit-scrollbar { height: 10px; }
.camera-grid::-webkit-scrollbar-thumb {
  background: var(--border); border-radius: 999px;
}
.camera-grid::-webkit-scrollbar-track { background: transparent; }

.camera-tile {
  --tile-header-h: 40px;
  --tile-controls-h: 48px;
  --rot-scale: 1;
  container-type: inline-size;
  background: var(--tile-bg);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: var(--shadow-md);
  position: relative;
  isolation: isolate;
  flex: 0 0 auto;
  /* Default group (16:9). Overridden per-group below. */
  height: 260px;
  width: auto;
  aspect-ratio: 16 / 9;
  scroll-snap-align: start;
  cursor: grab;
  transition: transform .15s ease, box-shadow .15s ease;
}
.camera-tile:active { cursor: grabbing; }
.camera-tile.is-dragging { opacity: .35; }
.camera-tile.drop-before { box-shadow: -3px 0 0 0 var(--primary), var(--shadow-md); }
.camera-tile.drop-after  { box-shadow:  3px 0 0 0 var(--primary), var(--shadow-md); }

.tile-header {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  min-height: var(--tile-header-h);
  background: var(--tile-overlay);
  color: #fff; font-size: 13px;
  position: absolute; top: 0; left: 0; right: 0;
  z-index: 2;
  pointer-events: none; /* let clicks pass to controls when overlapped */
}
.status-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #f59e0b;
  flex-shrink: 0;
}
.status-dot.ok    { background: var(--success); }
.status-dot.warn  { background: #f59e0b; }
.status-dot.error { background: var(--danger); }

.tile-title  {
  font-size: 10px;
  font-weight: 500;
  min-width: 0;
  max-width: 80px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tile-status {
  color: #d1d5db;
  font-size: 11px;
  min-width: 0;
  max-width: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  justify-self: end;
  text-align: right;
}

.tile-video {
  position: absolute;
  inset: 0;
  background: #000;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.tile-video video,
.tile-video img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  /* Fill the target group aspect region. This may crop edges when source
     aspect differs, but ensures the displayed area always matches group ratio. */
  object-fit: cover;
  background: #000;
  transition: transform .25s ease;
  transform-origin: center;
}

/* Per-tile rotation — applied to <video>/<img>, not to the grid.
   Switch to contain so the rotation-scale math (which assumes no crop)
   produces the intended "full frame visible" result. */
.camera-tile.rotate-90  .tile-video video,
.camera-tile.rotate-90  .tile-video img { object-fit: contain; transform: rotate(90deg) scale(var(--rot-scale, 1)); }
.camera-tile.rotate-180 .tile-video video,
.camera-tile.rotate-180 .tile-video img { object-fit: contain; transform: rotate(180deg) scale(var(--rot-scale, 1)); }
.camera-tile.rotate-270 .tile-video video,
.camera-tile.rotate-270 .tile-video img { object-fit: contain; transform: rotate(270deg) scale(var(--rot-scale, 1)); }
/* Popup stage already uses contain (defined on .tile-popup-stage > video). */
.tile-popup-stage.rotate-90 > video,
.tile-popup-stage.rotate-90 > img { transform: rotate(90deg) scale(var(--rot-scale, 1)); }
.tile-popup-stage.rotate-180 > video,
.tile-popup-stage.rotate-180 > img { transform: rotate(180deg) scale(var(--rot-scale, 1)); }
.tile-popup-stage.rotate-270 > video,
.tile-popup-stage.rotate-270 > img { transform: rotate(270deg) scale(var(--rot-scale, 1)); }

/* Aspect-ratio variants are driven by the *group* (parent .camera-grid)
   so every tile in a group shares the same fixed box. The aspect-ratio
   lives on the .camera-tile itself; a fixed `height` per group fully
   determines its width — no cropping (object-fit: contain) and no
   horizontal-scroll layout shift when sources change resolution. */
.camera-grid                    .camera-tile { height: 512px; aspect-ratio: 3 / 4; }
.camera-grid.ratio-9-16         .camera-tile { height: 512px; aspect-ratio: 9 / 16; }
.camera-grid.ratio-3-4          .camera-tile { height: 512px; aspect-ratio: 3 / 4; }
.camera-grid.ratio-1-1          .camera-tile { height: 360px; aspect-ratio: 1 / 1; }

@media (max-width: 720px) {
  .camera-grid                .camera-tile { height: 448px; }
  .camera-grid.ratio-9-16     .camera-tile { height: 448px; }
  .camera-grid.ratio-3-4      .camera-tile { height: 448px; }
  .camera-grid.ratio-1-1      .camera-tile { height: 288px; }
}

/* ───── Aspect groups (one section per ratio) ───── */
.aspect-groups {
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.aspect-group {
  display: flex; flex-direction: column; gap: 10px;
}
.aspect-group-header {
  display: flex; align-items: baseline; gap: 10px;
  padding: 0 2px;
}
.aspect-group-title {
  font-size: 14px; font-weight: 700; color: var(--text);
  letter-spacing: .02em;
}
.aspect-group-count {
  font-size: 12px; color: var(--muted);
  background: var(--border);
  padding: 2px 8px; border-radius: 999px;
}
/* Empty group placeholder — show a dashed drop target so users know
   they can drag tiles into 4:3 / 1:1 groups even when those start empty. */
.aspect-group.is-empty .camera-grid {
  min-height: 120px;
  border: 1.5px dashed var(--border);
  border-radius: 12px;
  background: rgba(0, 0, 0, .015);
  align-items: center;
  justify-content: center;
}
.aspect-group.is-empty .camera-grid::before {
  content: '将摄像头拖入此分组';
  color: var(--muted);
  font-size: 13px;
  pointer-events: none;
}
.aspect-group.is-empty.is-empty .camera-grid { padding: 16px; }
/* Highlight the destination grid while a tile is being dragged over. */
.camera-grid.drop-target {
  background: rgba(52, 120, 246, .06);
  outline: 2px dashed var(--primary);
  outline-offset: -4px;
  border-radius: 12px;
}

/* ───── Tile controls (bottom overlay) ───── */
.tile-controls {
  position: absolute; bottom: 0; left: 0; right: 0;
  display: flex; align-items: center; gap: 6px;
  padding: 8px 8px 8px 6px;
  min-height: var(--tile-controls-h);
  background: linear-gradient(to top, rgba(0,0,0,.7), rgba(0,0,0,0));
  color: #fff;
  z-index: 3;
  opacity: 0;
  transition: opacity .2s ease;
}
.camera-tile:hover .tile-controls,
.camera-tile:focus-within .tile-controls,
.camera-tile.controls-pinned .tile-controls { opacity: 1; }

@media (hover: none) {
  /* On touch devices controls stay visible */
  .tile-controls { opacity: 1; }
}

.ctrl {
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .15);
  color: #fff;
  border-radius: 8px;
  padding: 6px 8px;
  font-size: 13px;
  cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 32px; min-width: 32px;
  justify-content: center;
  transition: background .15s, border-color .15s, transform .1s;
  -webkit-tap-highlight-color: transparent;
}
.ctrl:hover { background: rgba(255, 255, 255, .22); border-color: rgba(255,255,255,.3); }
.ctrl:active { transform: scale(.95); }
.ctrl[disabled] { opacity: .4; cursor: not-allowed; }

.ctrl select {
  /* Give the *closed* display (the value the user sees on the bar) an
     explicit dark pill so the white text always has contrast. The native
     popup that opens still uses the light scheme below so the option
     list is dark text on white. */
  background: rgba(0, 0, 0, .55);
  border: 1px solid rgba(255, 255, 255, .2);
  color: #fff; font: inherit; outline: none;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
  /* Force the native dropdown popup to use the light theme so the options
     (rendered by the browser, not by us) appear as dark text on a light
     background — otherwise some browsers inherit our white `color: #fff`
     onto the popup and produce white-on-white unreadable options. */
  color-scheme: light;
}
.ctrl select option,
.ctrl select optgroup {
  color: #1f2937;            /* slate-800 — readable on white */
  background-color: #ffffff;
}
.ctrl select option:checked,
.ctrl select option:hover {
  color: #ffffff;
  background-color: #2563eb; /* blue-600 — clear selection contrast */
}

.tile-popup-viewer {
  position: fixed;
  inset: 0;
  z-index: 1200;
  background: rgba(0, 0, 0, .66);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.tile-popup-stage {
  position: relative;
  background: #000;
  border-radius: 14px;
  box-shadow: 0 20px 48px rgba(0, 0, 0, .45);
  overflow: hidden;
  max-width: 96vw;
  max-height: 92vh;
}

.tile-popup-stage > video,
.tile-popup-stage > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
  transition: transform .25s ease;
  transform-origin: center;
}

.tile-popup-close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 36px;
  height: 36px;
  border: 1px solid rgba(255, 255, 255, .28);
  border-radius: 10px;
  color: #fff;
  background: rgba(0, 0, 0, .42);
  cursor: pointer;
  z-index: 3;
  font-size: 18px;
  line-height: 1;
}

body.tile-popup-open {
  overflow: hidden;
}

.tile-fps {
  position: absolute;
  right: 6px;
  bottom: 6px;
  font-size: 11px;
  color: #fff;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  z-index: 5;
  pointer-events: none;
  background: rgba(0, 0, 0, .55);
  padding: 2px 6px;
  border-radius: 4px;
  line-height: 1.2;
}

/* On narrow tiles prioritize connection status over FPS. */
@container (max-width: 240px) {
  .tile-header {
    grid-template-columns: auto minmax(0, 1fr) auto;
  }
  .tile-status {
    display: block;
    max-width: 108px;
  }
  .tile-fps {
    bottom: 48px;
    font-size: 10px;
  }
}

/* ───── Empty state ───── */
.empty-state {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  padding: 48px 24px;
  background: var(--card);
  border-radius: 14px;
  box-shadow: var(--shadow-sm);
  color: var(--text);
  gap: 8px;
}
.empty-icon { font-size: 40px; }
.empty-title { font-size: 16px; font-weight: 600; margin: 4px 0 0; }
.empty-sub   { font-size: 13px; color: var(--muted); margin: 0 0 12px; max-width: 320px; }

/* ───── Detection panel (right aside) ───── */
.detection-panel {
  background: var(--card);
  border-radius: 14px;
  box-shadow: var(--shadow-sm);
  padding: 12px 14px;
  display: flex; flex-direction: column;
  min-height: 0;
  align-self: start;
  position: sticky; top: 76px;
  max-height: calc(100vh - 100px);
}
.panel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding-bottom: 8px; border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
}
.panel-head h3 { margin: 0; font-size: 14px; font-weight: 600; }

#detectionList {
  list-style: none; margin: 0; padding: 0;
  overflow-y: auto;
  flex: 1;
}
#detectionList li {
  padding: 8px 6px;
  border-bottom: 1px solid #f1f3f8;
  font-size: 12px;
  color: var(--text);
  display: flex; gap: 8px; align-items: baseline;
  font-variant-numeric: tabular-nums;
}
#detectionList li:last-child { border-bottom: none; }
#detectionList .det-time { color: var(--muted); white-space: nowrap; }
#detectionList .det-label { font-weight: 600; }

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

/* ───── Responsive ───── */
@media (max-width: 1024px) {
  .content { grid-template-columns: 1fr; }
  .detection-panel { position: static; max-height: 280px; }
}
@media (max-width: 640px) {
  .app-header { padding: 10px 12px; gap: 8px; }
  .header-actions { gap: 6px; }
  .header-actions > .conn-pill { max-width: 120px; }
  .user-badge { display: none; }
  .brand-sub { display: none; }
  .content { padding: 10px; gap: 10px; }
  .camera-grid { gap: 10px; padding-bottom: 10px; }
  .camera-tile { --tile-header-h: 36px; --tile-controls-h: 44px; }
  .tile-controls { opacity: 1; padding: 6px 8px; }
  .ctrl { padding: 6px 8px; font-size: 12px; }
}
/* ───── Loading spinner overlay (per-tile) ─────
   Absolutely positioned over the video so toggling it never reflows the
   tile layout. Visible whenever the tile carries the .is-loading class. */
.tile-loading {
  position: absolute; inset: 0;
  display: none;
  align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .35);
  z-index: 2;
  pointer-events: none;
}
.camera-tile.is-loading .tile-loading { display: flex; }
.tile-loading .spinner {
  width: 44px; height: 44px;
  border: 3px solid rgba(255, 255, 255, .25);
  border-top-color: #fff;
  border-radius: 50%;
  animation: aipx-spin 0.9s linear infinite;
}
@keyframes aipx-spin { to { transform: rotate(360deg); } }

/* (Per-tile aspect overrides removed — aspect is now group-driven.) */

/* ───── Pre-paint visibility (set by inline boot script in <head>) ─────
   When the user had a session last time we hide the login overlay and
   reveal the main shell *before first paint* so we never flash the login
   screen on cold start. The full auth.js init still runs and may toggle
   things back if the refresh token has been revoked server-side. */
html.aipx-prelogged #authPanel { display: none !important; }
/* Use flex (matches .app-shell's natural display) so the grid/flex layout
   chain inside the shell isn't clobbered by a forced `display:block`. */
html.aipx-prelogged #appPanel  { display: flex !important; }
