/* ─────────────────────────────────────────────────────────────────────────────
   Cloud Co-Study Room — 深色极简样式（零构建，手写 CSS）
   布局要点：顶部状态条 + 左侧自适应网格 + 右侧固定宽度侧栏。
   ───────────────────────────────────────────────────────────────────────────── */

:root {
  --bg: #111827;
  --bg-raised: #1f2937;
  --bg-input: #0b1220;
  --border: #374151;
  --text: #f9fafb;
  --text-muted: #9ca3af;
  --accent: #22c55e;
  --danger: #dc2626;
  --warn: #f59e0b;
  --radius: 8px;
}

* {
  box-sizing: border-box;
}

/*
  hidden 属性必须能压过 .card-tools { display: flex } 这类类选择器，
  否则 hidden=true 实际不生效（元素照样显示）。
*/
[hidden] {
  display: none !important;
}

html,
body {
  height: 100%;
  margin: 0;
}

/*
  禁止移动端浏览器的"字体自动放大"（Android Chrome / WebView 的 text autosizing，俗称 font boosting）。

  为什么：它会按**布局宽度**把文字放大，而且旋转（布局变宽）后会重新计算 ——
  用户看到的就是“切横屏后整页像是被放大了、要手动缩回”。
  声明 100% 等于告诉浏览器“别动我的字号”。
*/ 
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif;
  font-size: 14px;
  line-height: 1.5;
  overflow: hidden;
}

h1,
h2 {
  margin: 0;
  font-weight: 600;
}

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

.error {
  color: #f87171;
  margin: 0;
  font-size: 12px;
}

.hint {
  color: var(--text-muted);
  font-size: 12px;
  margin: 6px 0 0;
}

/* ── 通用控件 ─────────────────────────────────────────────────────────────── */

.btn {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text);
  font: inherit;
  cursor: pointer;
}

.btn:hover {
  background: var(--bg-raised);
}

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

.btn-block {
  display: block;
}

.btn-solid {
  background: #f3f4f6;
  border-color: #f3f4f6;
  color: #111827;
  font-weight: 500;
}

.btn-solid:hover {
  background: #e5e7eb;
}

.btn-start {
  background: #16a34a;
  border-color: #16a34a;
}

.btn-start:hover {
  background: #22c55e;
}

.btn-stop {
  background: var(--danger);
  border-color: var(--danger);
}

.btn-stop:hover {
  background: #ef4444;
}

.link-btn {
  border: 0;
  background: none;
  color: var(--text-muted);
  font: inherit;
  cursor: pointer;
}

.link-btn:hover {
  color: var(--text);
}

input,
select {
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-input);
  color: var(--text);
  font: inherit;
}

input:focus,
select:focus {
  outline: none;
  border-color: #6b7280;
}

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

.field label {
  display: block;
  margin-bottom: 4px;
  color: #d1d5db;
  font-size: 13px;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

.time-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.panel {
  margin-bottom: 22px;
}

.panel-title {
  margin-bottom: 10px;
  font-size: 13px;
  color: #d1d5db;
}

/* ── 登录 ─────────────────────────────────────────────────────────────────── */

.login-view {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  height: 100dvh;
  padding: 16px;
}

.card {
  width: 100%;
  max-width: 360px;
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: rgba(31, 41, 55, 0.45);
}

.card-title {
  font-size: 20px;
}

.login-card .muted {
  margin: 4px 0 8px;
}

.login-card .btn {
  margin-top: 18px;
}

/* ── 自习室骨架 ───────────────────────────────────────────────────────────── */

.room-view {
  display: flex;
  flex-direction: column;
  /* 移动端地址栏会占用 100vh 的额度，底部按钮会被推出视口；dvh 跟随动态视口。
     不支持的浏览器回退到上一行的 100vh（= 改动前行为）。 */
  height: 100vh;
  height: 100dvh;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  border-bottom: 1px solid var(--bg-raised);
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  /* 允许子元素 ellipsis：flex 子项默认不肯收缩，窄屏上会把“退出”挤出视口。 */
  min-width: 0;
  flex: 1;
}

/* 标题可被截断，人数与按钮不允许被挤走（窄屏实测清单见 03-改动报告）。 */
.brand-title {
  font-size: 16px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.brand-count {
  flex-shrink: 0;
}

/* 顶栏右侧按钮组：包一层容器才能在窄屏整体缩排（否则 space-between 会把三个按钮摊开）。 */
.topbar-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

/* "重连接"：只在已停止自动重连时出现，用琥珀色区别于普通链接按钮。 */
.retry-btn {
  color: var(--warn);
}

/*
  默认为"不存在"：桌面档位下这两个元素必须完全不参与布局与 tab 顺序。
  它们的可见/定位规则全部写在响应式分区的 @media (max-width: 900px) 里；
  因此旋转到 >900px 时不需要任何 JS 清理——打开态只是一个类名而已。
*/
.menu-btn {
  display: none;
}

.sidebar-scrim {
  display: none;
}

.dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
}

.dot-ok {
  background: var(--accent);
}

.dot-warn {
  background: var(--warn);
}

.layout {
  /* 放大浮层的定位基准：浮层只覆盖顶栏以下区域，顶栏因此天然保持可交互。 */
  position: relative;
  display: flex;
  flex: 1;
  min-height: 0;
}

.grid {
  display: grid;
  flex: 1;
  gap: 8px;
  min-height: 0;
  padding: 12px;
}

.cols-1 {
  grid-template-columns: repeat(1, minmax(0, 1fr));
}

.cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.cols-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.cols-4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.placeholder {
  align-self: center;
  justify-self: center;
  color: var(--text-muted);
}

.sidebar {
  width: 256px;
  flex-shrink: 0;
  overflow-y: auto;
  padding: 16px;
  border-left: 1px solid var(--bg-raised);
}

/* ── 成员卡片 ─────────────────────────────────────────────────────────────── */

.study-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  background: #000;
}

/* 画面层容器：网格卡片里铺满卡片，放大视图里作为弹性主体。 */
.frame {
  overflow: hidden;
  background: #000;
}

.study-card .frame {
  position: absolute;
  inset: 0;
}

/* 两层 <img> 叠加 + opacity 过渡：任何一帧都不做 src 直换，因此不会闪。 */
.frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 300ms ease;
}

.layer-hidden {
  opacity: 0;
}

.layer-active {
  opacity: 1;
}

.card-tools {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 2px;
}

.card-id {
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.55);
  color: #d1d5db;
  font-size: 12px;
  white-space: nowrap;
}

/* 窄屏下 ID 角标让位到状态条，默认不显示（见文件末尾唯一的媒体查询）。 */
.card-id-bottom {
  display: none;
}

.tool-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.55);
  color: #e5e7eb;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}

.tool-btn:hover {
  background: rgba(0, 0, 0, 0.85);
}

.card-badge-left {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 3;
  padding: 1px 6px;
  border-radius: 4px;
  color: #fff;
  font-size: 12px;
  font-weight: 500;
}

.badge-break {
  background: rgba(245, 158, 11, 0.9);
}

/* 放假角标：与休息角标共用左上角定位类（放假时休息状态已被清除，不会叠加）。 */
.badge-vacation {
  background: rgba(56, 189, 248, 0.9);
  color: #06202e;
}

/* 处于放假时，按钮变成"结束放假"，用同色系提示当前状态。 */
.btn-vacation-on {
  background: rgba(56, 189, 248, 0.9);
  color: #06202e;
}

.card-offline {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 13px;
}

.card-status {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.82), rgba(0, 0, 0, 0));
}

.card-status .dot {
  flex-shrink: 0;
}

.dot-schedule {
  background: var(--accent);
}

.dot-break {
  background: var(--warn);
}

.dot-idle {
  background: #6b7280;
}

.card-status-text {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 13px;
}

/* ── 日程 ─────────────────────────────────────────────────────────────────── */

.schedule-list {
  margin: 0 0 10px;
  padding: 0;
  list-style: none;
}

.schedule-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4px;
  padding: 4px 8px;
  border-radius: 6px;
  background: rgba(31, 41, 55, 0.7);
  font-size: 13px;
}

.schedule-item.empty {
  background: none;
  color: var(--text-muted);
  font-size: 12px;
  padding-left: 0;
}

.schedule-remove {
  border: 0;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 13px;
}

.schedule-remove:hover {
  color: #f87171;
}

.break-box {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.break-timer {
  padding: 6px 8px;
  border-radius: 6px;
  background: rgba(245, 158, 11, 0.18);
  color: #fcd34d;
  font-size: 13px;
}

.btn-break-end {
  border-color: rgba(245, 158, 11, 0.6);
  color: #fde68a;
}

/* ── 弹窗 / 浮层 ──────────────────────────────────────────────────────────── */

/* ── 放大浮层（点卡片右上角「⛶」）──────────────────────────────────────────── */

/* 挂在 .layout 内，只覆盖顶栏以下区域（REQ-expand-002）。
   z-index 40 高于网格与侧栏，低于 .modal-backdrop(50) 与 .toast(60)。 */
.live-layer {
  position: absolute;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  background: rgba(0, 0, 0, 0.86);
}

.live-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: #000;
}

.live-card .frame {
  position: relative;
  flex: 1;
  min-height: 0;
}

/* 放大时完整显示画面、不裁切（REQ-expand-007）；网格里仍是 cover。 */
.live-card .frame img {
  object-fit: contain;
}

.live-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 12px;
  background: var(--bg-raised);
}

.live-title-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.live-title {
  flex-shrink: 0;
  font-weight: 600;
}

.live-status {
  overflow: hidden;
  color: var(--text-muted);
  font-size: 13px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.live-close {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font-size: 15px;
  cursor: pointer;
}

.live-close:hover {
  background: rgba(255, 255, 255, 0.12);
}

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.6);
}

.modal {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 420px;
  max-height: 80vh;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: #111827;
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--bg-raised);
}

.modal-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 16px;
}

.peer-day {
  display: flex;
  gap: 12px;
  margin-bottom: 10px;
}

.peer-day-name {
  width: 40px;
  flex-shrink: 0;
  padding-top: 2px;
  color: #d1d5db;
  font-size: 13px;
}

.peer-blocks {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.peer-block {
  padding: 1px 8px;
  border-radius: 4px;
  background: var(--bg-raised);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.toast {
  position: fixed;
  bottom: 16px;
  left: 50%;
  z-index: 60;
  transform: translateX(-50%);
  padding: 8px 16px;
  border-radius: 6px;
  background: #f3f4f6;
  color: #111827;
  font-size: 13px;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.45);
  pointer-events: none;
}

/* ─────────────────────────────────────────────────────────────────────────────
   响应式：**唯一**存放断点的地方。全文件只有 5 条媒体查询：
     ① max-width:900 且**竖屏**     侧栏折叠为抽屉
     ② 561–900 且**竖屏**           网格最多 3 列
     ③ max-width:560 且**竖屏**     网格最多 2 列
     ④ max-width:560                卡片内部让位 + 顶栏紧凑（与方向无关）
     ⑤ max-width:900                表单控件字号 ≥ 16px（防 iOS 聚焦自动放大；与方向无关）

   为什么折叠/列数上限要限定**竖屏**（2026-10-07 真机反馈后新增，见 02 的 D5）：
   横屏手机宽度 640–930 也落在"≤900"里，但它富余的是**宽度**、缺的是**高度**。
   一旦在横屏折叠侧栏，网格变宽而行高不变 → object-fit:cover 竖向裁得更狠；
   再把列数压少，格子会变成 3:1 以上（实测：844×390 + 10 人从 99% 可见降到 37%）。
   横屏下侧栏只占 256px，旁边的网格反而更接近 4:3（实测 844×390 + 4 人 75% 可见）。

   ② 用区间条件（min-width:561）而不是纯 max-width，是为了消除“后写的赢”的顺序依赖：
   两条 max-width 同时命中时后者会覆盖前者，手机竖屏就会退回 3 列（每格约 108px）。
   注意 560 同时管卡片内部与列数上限，改它前先全局搜一遍。
   ───────────────────────────────────────────────────────────────────────────── */

@media (max-width: 900px) and (orientation: portrait) {
  /* 侧栏变抽屉：同一份 DOM，只有 transform 变化。
     z-index 30 低于 .live-layer(40) / .modal-backdrop(50) / .toast(60)，高于 #grid。 */
  .sidebar {
    position: absolute;
    inset-block: 0;
    right: 0;
    z-index: 30;
    width: min(88vw, 320px);
    transform: translateX(100%);
    transition: transform 200ms ease;
    border-left: 1px solid var(--border);
    background: var(--bg);
  }

  .layout.sidebar-open .sidebar {
    transform: translateX(0);
  }

  .sidebar-scrim {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 20;
    background: rgba(0, 0, 0, 0.55);
    opacity: 0;
    /* 关闭态必须不拦截点击，否则"点卡片没反应"。 */
    pointer-events: none;
    transition: opacity 200ms ease;
  }

  .layout.sidebar-open .sidebar-scrim {
    opacity: 1;
    pointer-events: auto;
  }

  .menu-btn {
    display: inline-flex;
    align-items: center;
  }
}

@media (min-width: 561px) and (max-width: 900px) and (orientation: portrait) {
  .cols-4 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

}

/*
  窄屏列数上限（**仅竖屏**）：真正的受益场景是 5 人以上——4 人时 gridClass 本来就给 cols-2，
  所以 4 人下的“看得清”靠的是侧栏折叠，而不是这条规则；5–9 人（cols-3）与 ≥10 人（cols-4）
  才会被这条规则从 3/4 列收敛到 2 列。横屏不加上限：那里列越多格子越接近 4:3（见文件头的分区说明）。
*/
@media (max-width: 560px) and (orientation: portrait) {
  .cols-3,
  .cols-4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/*
  窄视口（与方向无关）：卡片内部让位 + 顶栏紧凑。
  这两件事在横屏小屏上同样需要：一个 480×320 的横屏设备宽度也 ≤560，
  侧栏常驻后每格只有 ~96px，不隐藏 ID 角标就会被 overflow:hidden 裁掉按钮。
  卡片内部只需把 ID 角标让位到状态条，按钮保持 22px（触控目标优先）——不再缩到 18px。
*/
@media (max-width: 560px) {
  /* 顶栏紧凑化：320px 机型下“退出/重连接”不被挤出视口，且触控目标 ≥ 32px。 */
  .topbar {
    padding: 8px 10px;
    gap: 8px;
  }

  .brand {
    gap: 6px;
  }

  .topbar-actions .link-btn {
    min-width: 32px;
    min-height: 32px;
    padding: 4px 6px;
  }

  .card-id-top {
    display: none;
  }

  .card-id-bottom {
    display: inline;
  }
}

/*
  移动端表单控件字号必须 ≥ 16px（与方向无关）。

  为什么：iOS Safari 在聚焦 font-size < 16px 的 input/select/textarea 时会**自动放大整页**；
  而这个放大在旋转到横屏后仍然保留 —— 用户看到的就是"切横屏后需要手动双指缩回"。
  本页的输入控件全部 `font: inherit`（body 是 14px），而用户进房间必然点过密钥输入框，
  所以这个自动放大几乎必然发生。宁可把控件字号提到 16px，也不要把可读性交给浏览器的缩放。
  注：提高字号会让输入框略大，但不会引起横向溢出（各控件都是 width:100% 的块内元素）。
*/
@media (max-width: 900px) {
  input,
  select,
  textarea {
    font-size: 16px;
  }
